From ee24f03d09393e9235de730b076cbb9d5e1efcae Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 31 Jul 2020 09:27:36 -0400 Subject: [PATCH 001/426] Modify genqra64.f90 so it can also be used for a "QRA66" mode. --- lib/genqra64.f90 | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/genqra64.f90 b/lib/genqra64.f90 index 4931342eb..babe7d692 100644 --- a/lib/genqra64.f90 +++ b/lib/genqra64.f90 @@ -4,15 +4,17 @@ subroutine genqra64(msg0,ichk,msgsent,itone,itype) use packjt character*22 msg0 - character*22 message !Message to be generated - character*22 msgsent !Message as it will be received - integer itone(84) - character*3 cok !' ' or 'OOO' + character*22 message !Message to be generated + character*22 msgsent !Message as it will be received + integer itone(85) !QRA64 uses only 84 + character*3 cok !' ' or 'OOO' logical old_qra_sync integer dgen(13) integer sent(63) + integer b11(11) !Barker 11 code integer icos7(0:6) - data icos7/2,5,6,0,4,1,3/ !Defines a 7x7 Costas array + data icos7/2,5,6,0,4,1,3/ !Defines a 7x7 Costas array + data b11/1,1,1,0,0,0,1,0,0,1,0/ !Barker 11 definition save if(msg0(1:1).eq.'@') then @@ -39,18 +41,31 @@ subroutine genqra64(msg0,ichk,msgsent,itone,itype) call chkmsg(message,cok,nspecial,flip) call packmsg(message,dgen,itype) !Pack message into 72 bits call unpackmsg(dgen,msgsent) !Unpack to get message sent - if(ichk.ne.0) go to 999 !Return if checking only + if(ichk.eq.1) go to 999 !Return if checking only call qra64_enc(dgen,sent) !Encode using QRA64 - nsync=10 - inquire(file='old_qra_sync',exist=old_qra_sync) - if(old_qra_sync) nsync=1 - - itone(1:7)=nsync*icos7 !Insert 7x7 Costas array in 3 places - itone(8:39)=sent(1:32) - itone(40:46)=nsync*icos7 - itone(47:77)=sent(33:63) - itone(78:84)=nsync*icos7 + if(ichk.eq.66) then +! Experimental QRA66 (FST66?) mode + j=0 + k=0 + do i=1,85 + if(mod(i,4).eq.1) then + j=j+1 !Index for next sync symbol + if(j.eq.12) j=1 + itone(i)=b11(j) !Insert a sync symbol + else + k=k+1 + itone(i)=sent(k) + 2 + endif + enddo + else +! Original QRA64 mode + itone(1:7)=10*icos7 !Insert 7x7 Costas array in 3 places + itone(8:39)=sent(1:32) + itone(40:46)=10*icos7 + itone(47:77)=sent(33:63) + itone(78:84)=10*icos7 + endif endif 999 return From 9146ce9667855f12f885344145c1182d94e2070f Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 31 Jul 2020 10:58:30 -0400 Subject: [PATCH 002/426] Correct a flaw in qra64sim. --- lib/qra/qra64/qra64sim.f90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/qra/qra64/qra64sim.f90 b/lib/qra/qra64/qra64sim.f90 index b7ca2e130..278c1797d 100644 --- a/lib/qra/qra64/qra64sim.f90 +++ b/lib/qra/qra64/qra64sim.f90 @@ -107,7 +107,7 @@ program qra64sim twopi=8*atan(1.0) cspread(0)=1.0 cspread(NH)=0. - b=6.0 !Lorenzian 3/28 onward + b=6.0 !Use truncated Lorenzian shape for fspread do i=1,NH f=i*df x=b*f/fspread @@ -129,13 +129,13 @@ program qra64sim cspread(NFFT-i)=z enddo - do i=0,NFFT-1 - f=i*df - if(i.gt.NH) f=(i-nfft)*df - s=real(cspread(i))**2 + aimag(cspread(i))**2 +! do i=0,NFFT-1 +! f=i*df +! if(i.gt.NH) f=(i-nfft)*df +! s=real(cspread(i))**2 + aimag(cspread(i))**2 ! write(13,3000) i,f,s,cspread(i) !3000 format(i5,f10.3,3f12.6) - enddo +! enddo ! s=real(cspread(0))**2 + aimag(cspread(0))**2 ! write(13,3000) 1024,0.0,s,cspread(0) From 09686b99584ef6f642fe73475265fe8071efa0ac Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 31 Jul 2020 10:59:05 -0400 Subject: [PATCH 003/426] Add a simulator for experimental mode QRA66. --- CMakeLists.txt | 3 + lib/qra/qra66/qra66sim.f90 | 160 +++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 lib/qra/qra66/qra66sim.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 35ab08fc3..376401c46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1324,6 +1324,9 @@ target_link_libraries (sumsim wsjt_fort wsjt_cxx) add_executable (qra64sim lib/qra/qra64/qra64sim.f90 wsjtx.rc) target_link_libraries (qra64sim wsjt_fort wsjt_cxx) +add_executable (qra66sim lib/qra/qra66/qra66sim.f90 wsjtx.rc) +target_link_libraries (qra66sim wsjt_fort wsjt_cxx) + add_executable (jt49sim lib/jt49sim.f90 wsjtx.rc) target_link_libraries (jt49sim wsjt_fort wsjt_cxx) diff --git a/lib/qra/qra66/qra66sim.f90 b/lib/qra/qra66/qra66sim.f90 new file mode 100644 index 000000000..6043b3aed --- /dev/null +++ b/lib/qra/qra66/qra66sim.f90 @@ -0,0 +1,160 @@ +program qra66sim + +! Generate simulated QRA66 data for testing the decoder. + + use wavhdr + use packjt + parameter (NMAX=15*12000) !180,000 + parameter (NFFT=NMAX,NH=NFFT/2) + type(hdr) h !Header for .wav file + integer*2 iwave(NMAX) !Generated waveform + integer*4 itone(85) !Channel symbols (values 0-65) + real*4 xnoise(NMAX) !Generated random noise + real*4 dat(NMAX) !Generated real data + complex cdat(NMAX) !Generated complex waveform + complex cspread(0:NFFT-1) !Complex amplitude for Rayleigh fading + complex z + real*8 f0,dt,twopi,phi,dphi,baud,fsample,freq + character msg*22,fname*13,csubmode*1,arg*12 + character msgsent*22 + + nargs=iargc() + if(nargs.ne.6) then + print *, 'Usage: qra66sim "msg" A|B fDop DT Nfiles SNR' + print *, 'Example qra66sim "K1ABC W9XYZ EN37" A 0.2 0.0 1 -10' + go to 999 + endif + call getarg(1,msg) + call getarg(2,csubmode) + mode66=2**(ichar(csubmode)-ichar('A')) + call getarg(3,arg) + read(arg,*) fspread + call getarg(4,arg) + read(arg,*) xdt + call getarg(5,arg) + read(arg,*) nfiles + call getarg(6,arg) + read(arg,*) snrdb + + rms=100. + fsample=12000.d0 !Sample rate (Hz) + dt=1.d0/fsample !Sample interval (s) + twopi=8.d0*atan(1.d0) + npts=NMAX !Total samples in .wav file + nsps=1920 + baud=12000.d0/nsps !Keying rate = 6.25 baud + nsym=85 !Number of channel symbols + 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('-')) + + do ifile=1,nfiles !Loop over requested number of files + write(fname,1002) ifile !Output filename +1002 format('000000_',i6.6) + open(10,file=fname//'.wav',access='stream',status='unknown') + xnoise=0. + cdat=0. + if(snrdb.lt.90) then + do i=1,npts + xnoise(i)=gran() !Generate gaussian noise + enddo + endif + + f0=1500.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 + 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 + dphi=0.d0 + k=(xdt+0.5)*12000 !Start audio at t = xdt + 0.5 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=f0 + itone(isym)*baud*mode66 + 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 + + 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 + call random_number(r1) + phi1=twopi*r1 !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 + call random_number(r2) + phi2=twopi*r2 + z=a*cmplx(cos(phi2),sin(phi2)) + endif + cspread(NFFT-i)=z + enddo + +! do i=0,NFFT-1 +! f=i*df +! if(i.gt.NH) f=(i-nfft)*df +! s=real(cspread(i))**2 + aimag(cspread(i))**2 +! write(13,3000) i,f,s,cspread(i) +!3000 format(i5,f10.3,3f12.6) +! enddo +! s=real(cspread(0))**2 + aimag(cspread(0))**2 +! write(13,3000) 1024,0.0,s,cspread(0) + + 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) + +! do i=1,NMAX +! write(15,3020) i/12000.0,iwave(i) +!3020 format(f10.6,i8) +! enddo + enddo + +999 end program qra66sim From 77b7e714249d00871bde27ce4b9eae5a8bfcbff6 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 31 Jul 2020 11:16:03 -0400 Subject: [PATCH 004/426] Enable mode QRA66B in qra66sim. --- lib/qra/qra66/qra66sim.f90 | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/qra/qra66/qra66sim.f90 b/lib/qra/qra66/qra66sim.f90 index 6043b3aed..934239189 100644 --- a/lib/qra/qra66/qra66sim.f90 +++ b/lib/qra/qra66/qra66sim.f90 @@ -42,8 +42,12 @@ program qra66sim twopi=8.d0*atan(1.d0) npts=NMAX !Total samples in .wav file nsps=1920 - baud=12000.d0/nsps !Keying rate = 6.25 baud nsym=85 !Number of channel symbols + if(csubmode.eq.'B') then + nsps=960 + nsym=169 + endif + baud=12000.d0/nsps !Keying rate = 6.25 baud h=default_header(12000,npts) ichk=66 !Flag sent to genqra64 @@ -76,8 +80,9 @@ program qra66sim do i=1,npts !Add this signal into cdat() isym=i/nsps + 1 if(isym.gt.nsym) exit + if(csubmode.eq.'B' .and. isym.gt.84) isym=isym-84 if(isym.ne.isym0) then - freq=f0 + itone(isym)*baud*mode66 + freq=f0 + itone(isym)*baud dphi=twopi*freq*dt isym0=isym endif From 4b0ae524bfedc23c5ca50fd6ded8c0f21fe112db Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Fri, 31 Jul 2020 14:15:49 -0500 Subject: [PATCH 005/426] Set unpk77_success=.false. for messages with i3=0 and n3>6. --- lib/77bit/packjt77.f90 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/77bit/packjt77.f90 b/lib/77bit/packjt77.f90 index a2499625c..a110864fb 100644 --- a/lib/77bit/packjt77.f90 +++ b/lib/77bit/packjt77.f90 @@ -278,6 +278,7 @@ subroutine unpack77(c77,nrx,msg,unpk77_success) read(c77(72:77),'(2b3)') n3,i3 msg=repeat(' ',37) + if(i3.eq.0 .and. n3.eq.0) then ! 0.0 Free text call unpacktext77(c77(1:71),msg(1:13)) @@ -421,7 +422,10 @@ subroutine unpack77(c77,nrx,msg,unpk77_success) endif - + + else if(i3.eq.0 .and. n3.gt.6) then + unpk77_success=.false. + else if(i3.eq.1 .or. i3.eq.2) then ! Type 1 (standard message) or Type 2 ("/P" form for EU VHF contest) read(c77,1000) n28a,ipa,n28b,ipb,ir,igrid4,i3 From c4ef1e3e25d01bfaf2ca2049891de46061b1d3cc Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 31 Jul 2020 16:38:21 -0400 Subject: [PATCH 006/426] Starting on a decoder for QRA66. Now have found xdt and f0 from the sync vector. --- CMakeLists.txt | 2 ++ lib/decoder.f90 | 78 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/jt9.f90 | 7 +++-- 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 376401c46..9726caee0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -377,6 +377,7 @@ set (wsjt_FSRCS lib/options.f90 lib/packjt.f90 lib/77bit/packjt77.f90 + lib/qra66_decode.f90 lib/readwav.f90 lib/timer_C_wrapper.f90 lib/timer_impl.f90 @@ -575,6 +576,7 @@ set (wsjt_FSRCS lib/symspec65.f90 lib/sync4.f90 lib/sync64.f90 + lib/sync66.f90 lib/sync65.f90 lib/ft4/getcandidates4.f90 lib/ft4/get_ft4_bitmetrics.f90 diff --git a/lib/decoder.f90 b/lib/decoder.f90 index a3269aef9..4faae181b 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -9,6 +9,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) use ft8_decode use ft4_decode use fst4_decode + use qra66_decode include 'jt9com.f90' include 'timer_common.inc' @@ -37,6 +38,10 @@ subroutine multimode_decoder(ss,id2,params,nfsample) integer :: decoded end type counting_fst4_decoder + type, extends(qra66_decoder) :: counting_qra66_decoder + integer :: decoded + end type counting_qra66_decoder + real ss(184,NSMAX) logical baddata,newdat65,newdat9,single_decode,bVHF,bad0,newdat,ex integer*2 id2(NTMAX*12000) @@ -54,6 +59,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) type(counting_ft8_decoder) :: my_ft8 type(counting_ft4_decoder) :: my_ft4 type(counting_fst4_decoder) :: my_fst4 + type(counting_qra66_decoder) :: my_qra66 !cast C character arrays to Fortran character strings datetime=transfer(params%datetime, datetime) @@ -69,6 +75,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) my_ft8%decoded = 0 my_ft4%decoded = 0 my_fst4%decoded = 0 + my_qra66%decoded = 0 ! For testing only: return Rx messages stored in a file as decodes inquire(file='rx_messages.txt',exist=ex) @@ -187,6 +194,16 @@ subroutine multimode_decoder(ss,id2,params,nfsample) go to 800 endif + if(params%nmode.eq.66) then +! We're in QRA66 mode + call timer('decqra66',0) + call my_qra66%decode(qra66_decoded,id2,params%nutc,params%nfa, & + params%nfb,params%nfqso,params%ndepth,logical(params%lapcqonly), & + mycall,hiscall,hisgrid) + call timer('decqra66',1) + go to 800 + endif + if(params%nmode.eq.240) then ! We're in FST4 mode ndepth=iand(params%ndepth,3) @@ -759,4 +776,65 @@ contains return end subroutine fst4_decoded + subroutine qra66_decoded (this,nutc,sync,nsnr,dt,freq,decoded,nap, & + qual,ntrperiod,fmid,w50) + + use qra66_decode + 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 + + character*2 annot + character*37 decoded0 + character*70 line + + decoded0=decoded + annot=' ' + if(nap.ne.0) then + write(annot,'(a1,i1)') 'a',nap + if(qual.lt.0.17) decoded0(37:37)='?' + endif + + if(ntrperiod.lt.60) then + write(line,1001) nutc,nsnr,dt,nint(freq),decoded0,annot +1001 format(i6.6,i4,f5.1,i5,' ` ',1x,a37,1x,a2) + write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded0 +1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' FST4') + else + write(line,1003) nutc,nsnr,dt,nint(freq),decoded0,annot +1003 format(i4.4,i4,f5.1,i5,' ` ',1x,a37,1x,a2,2f7.3) + write(13,1004) nutc,nint(sync),nsnr,dt,freq,0,decoded0 +1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' FST4') + endif + + if(fmid.ne.-999.0) then + if(w50.lt.0.95) write(line(65:70),'(f6.3)') w50 + if(w50.ge.0.95) write(line(65:70),'(f6.2)') w50 + endif + + write(*,1005) line +1005 format(a70) + + call flush(6) + call flush(13) + + select type(this) + type is (counting_qra66_decoder) + this%decoded = this%decoded + 1 + end select + + return + end subroutine qra66_decoded + end subroutine multimode_decoder diff --git a/lib/jt9.f90 b/lib/jt9.f90 index a08a20cb2..3c017ac18 100644 --- a/lib/jt9.f90 +++ b/lib/jt9.f90 @@ -26,7 +26,7 @@ program jt9 fhigh=4000,nrxfreq=1500,ndepth=1,nexp_decode=0,nQSOProg=0 logical :: read_files = .true., tx9 = .false., display_help = .false., & bLowSidelobes = .false. - type (option) :: long_options(29) = [ & + type (option) :: long_options(30) = [ & option ('help', .false., 'h', 'Display this help message', ''), & option ('shmem',.true.,'s','Use shared memory for sample data','KEY'), & option ('tr-period', .true., 'p', 'Tx/Rx period, default SECONDS=60', & @@ -51,6 +51,7 @@ program jt9 option ('fft-threads', .true., 'm', & 'Number of threads to process large FFTs, default THREADS=1', & 'THREADS'), & + option ('qra66', .false., '3', 'QRA66 mode', ''), & option ('jt4', .false., '4', 'JT4 mode', ''), & option ('ft4', .false., '5', 'FT4 mode', ''), & option ('jt65', .false.,'6', 'JT65 mode', ''), & @@ -86,7 +87,7 @@ program jt9 TRperiod=60.d0 do - call getopt('hs:e:a:b:r:m:p:d:f:w:t:987654WqTL:S:H:c:G:x:g:X:Q:', & + call getopt('hs:e:a:b:r:m:p:d:f:w:t:9876543WqTL:S:H:c:G:x:g:X:Q:', & long_options,c,optarg,arglen,stat,offset,remain,.true.) if (stat .ne. 0) then exit @@ -123,6 +124,8 @@ program jt9 mode = 164 case ('Q') read (optarg(:arglen), *) nQSOProg + case ('3') + mode = 66 case ('4') mode = 4 case ('5') From f45c6174226032596d7e4e3a3893bae30f64c3e8 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 1 Aug 2020 09:24:59 -0400 Subject: [PATCH 007/426] 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 From e751a4404f945411065700a82add4fdee1935cdd Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Sat, 1 Aug 2020 10:58:21 -0500 Subject: [PATCH 008/426] Use squared metric for fst4 - works better on fading channel. --- lib/fst4/get_fst4_bitmetrics.f90 | 2 +- lib/fst4/get_fst4_bitmetrics2.f90 | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/fst4/get_fst4_bitmetrics.f90 b/lib/fst4/get_fst4_bitmetrics.f90 index 76a00dc2e..125777568 100644 --- a/lib/fst4/get_fst4_bitmetrics.f90 +++ b/lib/fst4/get_fst4_bitmetrics.f90 @@ -105,7 +105,7 @@ subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,badsync) csum=csum+cs(graymap(ntone),ks+j)*cterm cterm=cterm*conjg(cp(graymap(ntone))) enddo - s2(i)=abs(csum) + s2(i)=abs(csum)**2 enddo ipt=1+(ks-1)*2 if(nsym.eq.1) ibmax=1 diff --git a/lib/fst4/get_fst4_bitmetrics2.f90 b/lib/fst4/get_fst4_bitmetrics2.f90 index da0a6a230..6a669bd4c 100644 --- a/lib/fst4/get_fst4_bitmetrics2.f90 +++ b/lib/fst4/get_fst4_bitmetrics2.f90 @@ -49,21 +49,21 @@ subroutine get_fst4_bitmetrics2(cd,nss,hmod,nsizes,bitmetrics,s4hmod,badsync) i1=(k-1)*NSS csymb=cd(i1:i1+NSS-1) do itone=0,3 - s4(itone,k,1)=abs(sum(csymb*conjg(c1(:,itone)))) - s4(itone,k,2)=abs(sum(csymb( 1:nss/2)*conjg(c1( 1:nss/2,itone)))) + & - abs(sum(csymb(nss/2+1: nss)*conjg(c1(nss/2+1: nss,itone)))) - s4(itone,k,3)=abs(sum(csymb( 1: nss/4)*conjg(c1( 1: nss/4,itone)))) + & - abs(sum(csymb( nss/4+1: nss/2)*conjg(c1( nss/4+1: nss/2,itone)))) + & - abs(sum(csymb( nss/2+1:3*nss/4)*conjg(c1( nss/2+1:3*nss/4,itone)))) + & - abs(sum(csymb(3*nss/4+1: nss)*conjg(c1(3*nss/4+1: nss,itone)))) - s4(itone,k,4)=abs(sum(csymb( 1: nss/8)*conjg(c1( 1: nss/8,itone)))) + & - abs(sum(csymb( nss/8+1: nss/4)*conjg(c1( nss/8+1: nss/4,itone)))) + & - abs(sum(csymb( nss/4+1:3*nss/8)*conjg(c1( nss/4+1:3*nss/8,itone)))) + & - abs(sum(csymb(3*nss/8+1: nss/2)*conjg(c1(3*nss/8+1: nss/2,itone)))) + & - abs(sum(csymb( nss/2+1:5*nss/8)*conjg(c1( nss/2+1:5*nss/8,itone)))) + & - abs(sum(csymb(5*nss/8+1:3*nss/4)*conjg(c1(5*nss/8+1:3*nss/4,itone)))) + & - abs(sum(csymb(3*nss/4+1:7*nss/8)*conjg(c1(3*nss/4+1:7*nss/8,itone)))) + & - abs(sum(csymb(7*nss/8+1: nss)*conjg(c1(7*nss/8+1: nss,itone)))) + s4(itone,k,1)=abs(sum(csymb*conjg(c1(:,itone))))**2 + s4(itone,k,2)=abs(sum(csymb( 1:nss/2)*conjg(c1( 1:nss/2,itone))))**2 + & + abs(sum(csymb(nss/2+1: nss)*conjg(c1(nss/2+1: nss,itone))))**2 + s4(itone,k,3)=abs(sum(csymb( 1: nss/4)*conjg(c1( 1: nss/4,itone))))**2 + & + abs(sum(csymb( nss/4+1: nss/2)*conjg(c1( nss/4+1: nss/2,itone))))**2 + & + abs(sum(csymb( nss/2+1:3*nss/4)*conjg(c1( nss/2+1:3*nss/4,itone))))**2 + & + abs(sum(csymb(3*nss/4+1: nss)*conjg(c1(3*nss/4+1: nss,itone))))**2 + s4(itone,k,4)=abs(sum(csymb( 1: nss/8)*conjg(c1( 1: nss/8,itone))))**2 + & + abs(sum(csymb( nss/8+1: nss/4)*conjg(c1( nss/8+1: nss/4,itone))))**2 + & + abs(sum(csymb( nss/4+1:3*nss/8)*conjg(c1( nss/4+1:3*nss/8,itone))))**2 + & + abs(sum(csymb(3*nss/8+1: nss/2)*conjg(c1(3*nss/8+1: nss/2,itone))))**2 + & + abs(sum(csymb( nss/2+1:5*nss/8)*conjg(c1( nss/2+1:5*nss/8,itone))))**2 + & + abs(sum(csymb(5*nss/8+1:3*nss/4)*conjg(c1(5*nss/8+1:3*nss/4,itone))))**2 + & + abs(sum(csymb(3*nss/4+1:7*nss/8)*conjg(c1(3*nss/4+1:7*nss/8,itone))))**2 + & + abs(sum(csymb(7*nss/8+1: nss)*conjg(c1(7*nss/8+1: nss,itone))))**2 enddo enddo From 3867c452e60a4989f97bfcac2f8ad5ae0006a09b Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 1 Aug 2020 12:13:49 -0400 Subject: [PATCH 009/426] Changes to GUI to accommodate QRA66. It's basically functional, I think. --- lib/decoder.f90 | 38 +++++------------------------- lib/qra66_decode.f90 | 9 ++++--- widgets/mainwindow.cpp | 53 +++++++++++++++++++++++++++++++++++------- widgets/mainwindow.h | 2 ++ widgets/mainwindow.ui | 9 +++++++ 5 files changed, 66 insertions(+), 45 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 4faae181b..41f1d0f7e 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -776,7 +776,7 @@ contains return end subroutine fst4_decoded - subroutine qra66_decoded (this,nutc,sync,nsnr,dt,freq,decoded,nap, & + subroutine qra66_decoded (this,nutc,sync,nsnr,dt,freq,decoded,irc, & qual,ntrperiod,fmid,w50) use qra66_decode @@ -789,42 +789,16 @@ contains real, intent(in) :: dt real, intent(in) :: freq character(len=37), intent(in) :: decoded - integer, intent(in) :: nap + integer, intent(in) :: irc real, intent(in) :: qual integer, intent(in) :: ntrperiod real, intent(in) :: fmid real, intent(in) :: w50 - character*2 annot - character*37 decoded0 - character*70 line - - decoded0=decoded - annot=' ' - if(nap.ne.0) then - write(annot,'(a1,i1)') 'a',nap - if(qual.lt.0.17) decoded0(37:37)='?' - endif - - if(ntrperiod.lt.60) then - write(line,1001) nutc,nsnr,dt,nint(freq),decoded0,annot -1001 format(i6.6,i4,f5.1,i5,' ` ',1x,a37,1x,a2) - write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded0 -1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' FST4') - else - write(line,1003) nutc,nsnr,dt,nint(freq),decoded0,annot -1003 format(i4.4,i4,f5.1,i5,' ` ',1x,a37,1x,a2,2f7.3) - write(13,1004) nutc,nint(sync),nsnr,dt,freq,0,decoded0 -1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' FST4') - endif - - if(fmid.ne.-999.0) then - if(w50.lt.0.95) write(line(65:70),'(f6.3)') w50 - if(w50.ge.0.95) write(line(65:70),'(f6.2)') w50 - endif - - write(*,1005) line -1005 format(a70) + write(*,1001) nutc,nsnr,dt,nint(freq),decoded,irc +1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i2) + write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded +1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' QRA66') call flush(6) call flush(13) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index 53243e78c..d402acc15 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -52,6 +52,8 @@ contains data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/ save +! print*,nutc,nfa,nfb,nfqso,ndepth,lapdx,mycall,hiscall,hisgrid + this%callback => callback nsps=1920 baud=12000.0/nsps @@ -141,15 +143,12 @@ contains decoded=' ' irc=-1 endif - nft=100 + irc nsnr=nint(snr2) + call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & + irc,qual,ntrperiod,fmid,w50) 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 diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 528184278..ceea26659 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -592,6 +592,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, ui->actionISCAT->setActionGroup(modeGroup); ui->actionMSK144->setActionGroup(modeGroup); ui->actionQRA64->setActionGroup(modeGroup); + ui->actionQRA66->setActionGroup(modeGroup); ui->actionFreqCal->setActionGroup(modeGroup); QActionGroup* saveGroup = new QActionGroup(this); @@ -1365,6 +1366,8 @@ void MainWindow::fixStop() } else if (m_mode=="QRA64"){ m_hsymStop=179; if(m_config.decode_at_52s()) m_hsymStop=186; + } else if (m_mode=="QRA66"){ + m_hsymStop=49; } else if (m_mode=="FreqCal"){ m_hsymStop=((int(m_TRperiod/0.288))/8)*8; } else if (m_mode=="FT8") { @@ -2341,6 +2344,8 @@ void MainWindow::setup_status_bar (bool vhf) mode_label.setStyleSheet ("QLabel{background-color: #66ff66}"); } else if ("QRA64" == m_mode) { mode_label.setStyleSheet ("QLabel{background-color: #99ff33}"); + } else if ("QRA66" == m_mode) { + mode_label.setStyleSheet ("QLabel{background-color: #99ff33}"); } else if ("MSK144" == m_mode) { mode_label.setStyleSheet ("QLabel{background-color: #ff6666}"); } else if ("FT4" == m_mode) { @@ -2540,8 +2545,8 @@ void MainWindow::on_actionCopyright_Notice_triggered() "General Public License, you must display the following copyright " "notice prominently in your derivative work:\n\n" "\"The algorithms, source code, look-and-feel of WSJT-X and related " - "programs, and protocol specifications for the modes FSK441, FT8, JT4, " - "JT6M, JT9, JT65, JTMS, QRA64, ISCAT, MSK144 are Copyright (C) " + "programs, and protocol specifications for the modes FSK441, FST4, FT8, " + "JT4, JT6M, JT9, JT65, JTMS, QRA64, QRA66, ISCAT, MSK144 are Copyright (C) " "2001-2020 by one or more of the following authors: Joseph Taylor, " "K1JT; Bill Somerville, G4WJS; Steven Franke, K9AN; Nico Palermo, " "IV3NWV; Greg Beam, KI7MT; Michael Black, W9MDB; Edson Pereira, PY2SDR; " @@ -3065,6 +3070,8 @@ void MainWindow::decode() //decode() ui->actionEnable_AP_JT65->isChecked (); if(m_mode=="QRA64") dec_data.params.nmode=164; if(m_mode=="QRA64") dec_data.params.ntxmode=164; + if(m_mode=="QRA66") dec_data.params.nmode=66; + if(m_mode=="QRA66") dec_data.params.ntxmode=66; if(m_mode=="JT9+JT65") dec_data.params.nmode=9+65; // = 74 if(m_mode=="JT4") { dec_data.params.nmode=4; @@ -3465,8 +3472,8 @@ void MainWindow::readFromStdout() //readFromStdout //### I think this is where we are preventing Hounds from spotting Fox ### if(m_mode!="FT8" or (SpecOp::HOUND != m_config.special_op_id())) { - if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="QRA64" or m_mode=="JT4" - or m_mode=="JT65" or m_mode=="JT9" or m_mode=="FST4") { + if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="QRA64" or m_mode=="QRA66" + or m_mode=="JT4" or m_mode=="JT65" or m_mode=="JT9" or m_mode=="FST4") { auto_sequence (decodedtext, 25, 50); } @@ -3677,6 +3684,7 @@ void MainWindow::guiUpdate() if(m_modeTx=="JT9") txDuration=1.0 + 85.0*m_nsps/12000.0; // JT9 if(m_modeTx=="JT65") txDuration=1.0 + 126*4096/11025.0; // JT65 if(m_modeTx=="QRA64") txDuration=1.0 + 84*6912/12000.0; // QRA64 + if(m_modeTx=="QRA66") txDuration=0.5 + 85*1920/12000.0; // QRA66 if(m_modeTx=="WSPR") txDuration=2.0 + 162*8192/12000.0; // WSPR if(m_modeTx=="FST4" or m_mode=="FST4W") { //FST4, FST4W if(m_TRperiod==15) txDuration=1.0 + 160*720/12000.0; @@ -3936,6 +3944,11 @@ void MainWindow::guiUpdate() &m_currentMessageType, 22, 22); if(m_modeTx=="QRA64") genqra64_(message, &ichk, msgsent, const_cast (itone), &m_currentMessageType, 22, 22); + if(m_modeTx=="QRA66") { + int ichk66=66; + genqra64_(message, &ichk66, msgsent, const_cast (itone), + &m_currentMessageType, 22, 22); + } if(m_modeTx=="WSPR") genwspr_(message, msgsent, const_cast (itone), 22, 22); if(m_modeTx=="MSK144" or m_modeTx=="FT8" or m_modeTx=="FT4" @@ -4669,7 +4682,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; //Currently we do auto-sequencing only in FT4, FT8, MSK144, and FST4 + return; //Currently we do auto-sequencing only in FT4, FT8, MSK144, FST4, and QRA66 } //Skip the rest if no decoded text extracted @@ -4777,7 +4790,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!="FT4") { + m_mode != "QRA64" && m_mode != "QRA66" && m_mode!="FT8" && m_mode!="FT4") { return; } } @@ -6401,6 +6414,20 @@ void MainWindow::on_actionQRA64_triggered() statusChanged(); } +void MainWindow::on_actionQRA66_triggered() +{ + on_actionFST4_triggered(); + m_mode="QRA66"; + m_modeTx="QRA66"; + ui->actionQRA66->setChecked(true); + switch_mode (Modes::QRA64); + setup_status_bar (false); + m_hsymStop=49; + m_wideGraph->setMode(m_mode); + m_wideGraph->setModeTx(m_modeTx); + m_TRperiod=15.0; +} + void MainWindow::on_actionISCAT_triggered() { m_mode="ISCAT"; @@ -6449,8 +6476,9 @@ void MainWindow::on_actionMSK144_triggered() if("JT65"==m_mode) ui->actionJT65->setChecked(true); if("JT9_JT65"==m_mode) ui->actionJT9_JT65->setChecked(true); if("ISCAT"==m_mode) ui->actionISCAT->setChecked(true); - if("QRA64"==m_mode) ui->actionQRA64->setChecked(true); - if("WSPR"==m_mode) ui->actionWSPR->setChecked(true); + if("QRA64"==m_mode) ui->actionQRA64->setChecked(true); + if("QRA66"==m_mode) ui->actionQRA66->setChecked(true); + if("WSPR"==m_mode) ui->actionWSPR->setChecked(true); if("Echo"==m_mode) ui->actionEcho->setChecked(true); if("FreqCal"==m_mode) ui->actionFreqCal->setChecked(true); if("FST4"==m_mode) ui->actionFST4->setChecked(true); @@ -7358,6 +7386,14 @@ void MainWindow::transmit (double snr) true, false, snr, m_TRperiod); } + if (m_modeTx == "QRA66") { + toneSpacing=12000.0/1920.0; + Q_EMIT sendMessage (m_mode, NUM_QRA66_SYMBOLS, + 1920.0, ui->TxFreqSpinBox->value () - m_XIT, + toneSpacing, m_soundOutput, m_config.audio_output_channel (), + true, false, snr, m_TRperiod); + } + if (m_modeTx == "JT9") { int nsub=pow(2,m_nSubMode); int nsps[]={480,240,120,60}; @@ -9278,6 +9314,7 @@ void MainWindow::set_mode (QString const& mode) else if ("JT9+JT65" == mode) on_actionJT9_JT65_triggered (); else if ("JT65" == mode) on_actionJT65_triggered (); else if ("QRA64" == mode) on_actionQRA64_triggered (); + else if ("QRA66" == mode) on_actionQRA66_triggered (); else if ("FreqCal" == mode) on_actionFreqCal_triggered (); else if ("ISCAT" == mode) on_actionISCAT_triggered (); else if ("MSK144" == mode) on_actionMSK144_triggered (); diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index 79f65d080..ed528cc26 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -48,6 +48,7 @@ #define NUM_ISCAT_SYMBOLS 1291 //30*11025/256 #define NUM_MSK144_SYMBOLS 144 //s8 + d48 + s8 + d80 #define NUM_QRA64_SYMBOLS 84 //63 data + 21 sync +#define NUM_QRA66_SYMBOLS 85 //63 data + 22 sync #define NUM_FT8_SYMBOLS 79 #define NUM_FT4_SYMBOLS 105 #define NUM_FST4_SYMBOLS 160 //240/2 data + 5*8 sync @@ -305,6 +306,7 @@ private slots: void on_cbCQTx_toggled(bool b); void on_actionMSK144_triggered(); void on_actionQRA64_triggered(); + void on_actionQRA66_triggered(); void on_actionFreqCal_triggered(); void splash_done (); void on_measure_check_box_stateChanged (int); diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index c777609b0..c586e9892 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -3001,6 +3001,7 @@ QLabel[oob="true"] { + @@ -3642,6 +3643,14 @@ QLabel[oob="true"] { Also FST4W + + + true + + + QRA66 + + From e32c5b699d709bc9ae130b34f5a0c184227305c0 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 1 Aug 2020 13:37:31 -0400 Subject: [PATCH 010/426] Update Wide Graph and plotter.cpp for QRA66. --- widgets/mainwindow.cpp | 3 ++- widgets/plotter.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index ceea26659..0fc6ec684 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6423,9 +6423,10 @@ void MainWindow::on_actionQRA66_triggered() switch_mode (Modes::QRA64); setup_status_bar (false); m_hsymStop=49; + m_TRperiod=15.0; m_wideGraph->setMode(m_mode); m_wideGraph->setModeTx(m_modeTx); - m_TRperiod=15.0; + m_wideGraph->setPeriod(m_TRperiod,6912); } void MainWindow::on_actionISCAT_triggered() diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index c21d7e5f0..15057f4a8 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -471,6 +471,9 @@ void CPlotter::DrawOverlay() //DrawOverlay() if(m_nSubMode==4) bw=16*bw; //E } + if(m_mode=="QRA66") { //QRA66 + bw=65.0*12000.0/1920.0; + } if(m_modeTx=="JT65") { //JT65 bw=65.0*11025.0/4096.0; if(m_nSubMode==1) bw=2*bw; //B @@ -502,7 +505,7 @@ void CPlotter::DrawOverlay() //DrawOverlay() int yTxTop=12; int yRxBottom=yTxTop + 2*yh + 4; 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" + or m_mode=="QRA64" or m_mode=="QRA66" or m_mode=="FT8" or m_mode=="FT4" or m_mode.startsWith("FST4")) { if(m_mode=="QRA64" or (m_mode=="JT65" and m_bVHF)) { @@ -543,7 +546,7 @@ 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=="QRA66" or m_mode=="FT8" or m_mode=="FT4" or m_mode.startsWith("FST4")) { painter0.setPen(penRed); x1=XfromFreq(m_txFreq); From 910e8bfd816ec3190cd998bb55962233f99fc3b1 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 1 Aug 2020 13:52:52 -0400 Subject: [PATCH 011/426] Correct the Tx start time for QRA66 in Modulator.cpp. --- Modulator/Modulator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modulator/Modulator.cpp b/Modulator/Modulator.cpp index 52319eb2a..bd19f374b 100644 --- a/Modulator/Modulator.cpp +++ b/Modulator/Modulator.cpp @@ -70,7 +70,7 @@ void Modulator::start (QString mode, unsigned symbolsLength, double framesPerSym m_bFastMode=fastMode; m_TRperiod=TRperiod; unsigned delay_ms=1000; - if(mode=="FT8" or (mode=="FST4" and m_nsps==720)) delay_ms=500; //FT8, FST4-15 + if(mode=="FT8" or (mode=="FST4" and m_nsps==720) or mode=="QRA66") delay_ms=500; //FT8, FST4-15, QRA66 if(mode=="FT4") delay_ms=300; //FT4 // noise generator parameters From fb3c23492f22f5253b669cf380c3b180851c2c27 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 1 Aug 2020 15:12:37 -0400 Subject: [PATCH 012/426] Adjustments to the QRA66 decoder. Still need to implement looping over b90 values. --- lib/qra66_decode.f90 | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index d402acc15..749fccd9c 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -50,23 +50,21 @@ contains real s3(-64:127,63) real a(5) data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/ - save - -! print*,nutc,nfa,nfb,nfqso,ndepth,lapdx,mycall,hiscall,hisgrid + save nc1z,nc2z,ng2z,maxaptypez 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 + if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning ! 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 + b90=10.0 nFadingModel=1 maxaptype=4 if(iand(ndepth,64).ne.0) maxaptype=5 @@ -115,11 +113,14 @@ contains call sync66(c0,f0,jpk,sync) !c0 is analytic signal at 6000 S/s xdt=jpk/6000.0 - 0.5 +! write(*,3003) jpk,f0,sync +!3003 format('A',i6,f8.2,f12.1) + 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) +! s3=s3a/maxval(s3a) ! do j=1,63 ! ipk=maxloc(s3(-64:127,j)) ! write(54,3054) j,ipk(1)-65 @@ -148,8 +149,15 @@ contains irc,qual,ntrperiod,fmid,w50) else snr2=0. + nsnr=nint(db(sync)) +!### TEMPORARY? ### + call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & + irc,qual,ntrperiod,fmid,w50) +!### endif - +! write(*,3001) snr2,xdt,f0,decoded(1:22) +!3001 format('B',f5.1,f6.2,f7.1,2x,a22) + return end subroutine decode From 45dac5bd18295fd16da6ae3177f75416d3869ed7 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 2 Aug 2020 09:42:22 -0400 Subject: [PATCH 013/426] Change random number generation in qra66sim so results are exactly repeateble. --- lib/gran.c | 6 ++++++ lib/qra/qra66/qra66sim.f90 | 6 ++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/gran.c b/lib/gran.c index 24b986503..ac41c7fe4 100644 --- a/lib/gran.c +++ b/lib/gran.c @@ -26,3 +26,9 @@ float gran_() iset++; return v2*fac; } + +/* Generates evenly distributed numbers between 0 and 1. */ +float rran_() +{ + return (float)rand()/(float)RAND_MAX; +} diff --git a/lib/qra/qra66/qra66sim.f90 b/lib/qra/qra66/qra66sim.f90 index a482e4cc3..0e1910490 100644 --- a/lib/qra/qra66/qra66sim.f90 +++ b/lib/qra/qra66/qra66sim.f90 @@ -110,15 +110,13 @@ program qra66sim a=0. if(x.lt.3.0) then !Cutoff beyond x=3 a=sqrt(1.111/(1.0+x*x)-0.1) !Lorentzian amplitude - call random_number(r1) - phi1=twopi*r1 !Random phase + 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 - call random_number(r2) - phi2=twopi*r2 + phi2=twopi*rran() z=a*cmplx(cos(phi2),sin(phi2)) endif cspread(NFFT-i)=z From dd471c6b5e8e2f7bd4a364891855d828007c8281 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 2 Aug 2020 11:15:10 -0400 Subject: [PATCH 014/426] Improvements to QRA66 decoder. Still needs work. --- lib/qra66_decode.f90 | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index 749fccd9c..8891d394a 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -64,10 +64,15 @@ contains call packcall(hiscall(1:6),nc2,ltext) call packgrid(hisgrid(1:4),ng2,ltext) nSubmode=0 - b90=10.0 + b90=20 nFadingModel=1 - maxaptype=4 - if(iand(ndepth,64).ne.0) maxaptype=5 + +! These probably need work: + maxaptype=0 + if(ndepth.eq.2) maxaptype=3 + if(ndepth.eq.3) maxaptype=11 + +! Prime the decoder for possible AP decoding if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & maxaptype.ne.maxaptypez) then do naptype=0,maxaptype @@ -119,18 +124,24 @@ contains 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) + call spec66(c0(jpk:jpk+85*NSPS-1),s3) + s3a=s3/maxval(s3) ! do j=1,63 -! ipk=maxloc(s3(-64:127,j)) +! ipk=maxloc(s3a(-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 +! write(53,3053) i,2*s3a(i,j)+j-1 !3053 format(i8,f12.6) ! enddo ! enddo - + + call pctile(s3a,192*63,40,base) + s3a=s3a/base + print*,'A',maxval(s3a),ndepth,maxaptype,naptype + s3lim=10. + where(s3a(-64:127,1:63)>s3lim) s3a(-64:127,1:63)=s3lim + 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) From dc5d85f850726fe44089eaf8ee82d0a768ee55d1 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 2 Aug 2020 11:40:03 -0400 Subject: [PATCH 015/426] Remove a diagnostic print. Correct the maxAP value. --- lib/qra66_decode.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index 8891d394a..289c34427 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -70,7 +70,7 @@ contains ! These probably need work: maxaptype=0 if(ndepth.eq.2) maxaptype=3 - if(ndepth.eq.3) maxaptype=11 + if(ndepth.eq.3) maxaptype=5 ! Prime the decoder for possible AP decoding if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & @@ -138,7 +138,7 @@ contains call pctile(s3a,192*63,40,base) s3a=s3a/base - print*,'A',maxval(s3a),ndepth,maxaptype,naptype +! print*,'A',maxval(s3a),ndepth,maxaptype,naptype s3lim=10. where(s3a(-64:127,1:63)>s3lim) s3a(-64:127,1:63)=s3lim From 2846a6422a6eae46234f5d1df025fb0680599849 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 2 Aug 2020 13:33:15 -0400 Subject: [PATCH 016/426] Temporary: limit Rx search range to 15-- +/- 50 Hz. --- lib/qra66_decode.f90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index 289c34427..c605d0694 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -104,6 +104,10 @@ contains enddo enddo call smo121(s,iz) + ia=1450/df2 + ib=1550/df2 + s(:ia)=0. + s(ib:)=0. ipk=maxloc(s) f0=df2*ipk(1) - 0.5*baud !Candidate sync frequency From c91baddb2ced6d8f784e79898b8b5ee986acc145 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 3 Aug 2020 16:30:29 -0400 Subject: [PATCH 017/426] New sync66a subroutine for QRA66. --- CMakeLists.txt | 5 ++- lib/qra66_decode.f90 | 34 ++++++++-------- lib/sync66a.f90 | 94 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 lib/sync66a.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e24f521c..fe32cc8f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -577,7 +577,7 @@ set (wsjt_FSRCS lib/symspec65.f90 lib/sync4.f90 lib/sync64.f90 - lib/sync66.f90 + lib/sync66a.f90 lib/sync65.f90 lib/ft4/getcandidates4.f90 lib/ft4/get_ft4_bitmetrics.f90 @@ -1327,6 +1327,9 @@ target_link_libraries (qra64sim wsjt_fort wsjt_cxx) add_executable (qra66sim lib/qra/qra66/qra66sim.f90 wsjtx.rc) target_link_libraries (qra66sim wsjt_fort wsjt_cxx) +#add_executable (test_sync66 lib/test_sync66.f90 wsjtx.rc) +#target_link_libraries (test_sync66 wsjt_fort wsjt_cxx) + add_executable (jt49sim lib/jt49sim.f90 wsjtx.rc) target_link_libraries (jt49sim wsjt_fort wsjt_cxx) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index c605d0694..fc907c73f 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -119,30 +119,30 @@ contains 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 +! call sync66(c0,f0,jpk,sync) !c0 is analytic signal at 6000 S/s -! write(*,3003) jpk,f0,sync -!3003 format('A',i6,f8.2,f12.1) + ntol=100 + call sync66a(iwave,15*12000,nsps,nfqso,ntol,xdt,f0) + jpk=(xdt+0.5)*6000 + if(jpk.lt.0) jpk=0 + +! Genie sync: +! jpk=3600 +! xdt=jpk/6000.0 - 0.5 +! f0=1510 +! f0=1490 a=0. - a(1)=-(f0 + 1.5*baud) - call twkfreq(c0,c0,85*NSPS,6000.0,a) +! a(1)=-(f0 + 1.5*baud) !For sync66 + a(1)=-(f0 + 2.0*baud) !For sync66a + + + call twkfreq(c0,c0,15*6000,6000.0,a) call spec66(c0(jpk:jpk+85*NSPS-1),s3) s3a=s3/maxval(s3) -! do j=1,63 -! ipk=maxloc(s3a(-64:127,j)) -! write(54,3054) j,ipk(1)-65 -!3054 format(2i8) -! do i=-64,127 -! write(53,3053) i,2*s3a(i,j)+j-1 -!3053 format(i8,f12.6) -! enddo -! enddo call pctile(s3a,192*63,40,base) s3a=s3a/base -! print*,'A',maxval(s3a),ndepth,maxaptype,naptype s3lim=10. where(s3a(-64:127,1:63)>s3lim) s3a(-64:127,1:63)=s3lim @@ -170,8 +170,6 @@ contains irc,qual,ntrperiod,fmid,w50) !### endif -! write(*,3001) snr2,xdt,f0,decoded(1:22) -!3001 format('B',f5.1,f6.2,f7.1,2x,a22) return end subroutine decode diff --git a/lib/sync66a.f90 b/lib/sync66a.f90 new file mode 100644 index 000000000..ba88616cb --- /dev/null +++ b/lib/sync66a.f90 @@ -0,0 +1,94 @@ +subroutine sync66a(iwave,nmax,nsps,nfqso,ntol,xdt,f0) + + parameter (NSTEP=4) !Quarter-symbol steps + parameter (IZ=1600,JZ=352,NSPSMAX=1920) + integer*2 iwave(0:nmax-1) !Raw data + integer b11(11) !Barker 11 code + integer ijpk(2) !Indices i and j at peak of sync_sig + real s1(IZ,JZ) !Symbol spectra + real x(JZ) !Work array; 2FSK sync modulation + real sync(4*85) !sync vector + real sync_sig(-64:64,-15:15) + complex c0(0:NSPSMAX) !Complex spectrum of symbol + data b11/1,1,1,0,0,0,1,0,0,1,0/ !Barker 11 code + data sync(1)/99.0/ + save sync + + if(sync(1).eq.99.0) then + sync=0. + do k=1,22 + kk=k + if(kk.gt.11) kk=k-11 + sync(16*k-15)=2.0*b11(kk) - 1.0 + enddo + endif + + nfft=2*NSPS + df=12000.0/nfft !3.125 Hz + istep=nsps/NSTEP + fac=1/32767.0 + do j=1,JZ !Compute symbol spectra + ia=(j-1)*istep + ib=ia+nsps-1 + k=-1 + do i=ia,ib,2 + xx=iwave(i) + yy=iwave(i+1) + k=k+1 + c0(k)=fac*cmplx(xx,yy) + enddo + c0(k+1:nfft/2)=0. + call four2a(c0,nfft,1,-1,0) !r2c FFT + do i=1,IZ + s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 + enddo + enddo + + i0=nint(nfqso/df) + call pctile(s1(i0-64:i0+192,1:JZ),129*JZ,40,base) + s1=s1/base + s1max=20.0 + +! Make this simpler: just add the 22 nonzero values. + do j=1,JZ + x(j)=maxval(s1(i0-64:i0+192,j)) + if(x(j).gt.s1max) s1(i0-64:i0+192,j)=s1(i0-64:i0+192,j)*s1max/x(j) + enddo + + dt4=nsps/(NSTEP*12000.0) + j0=0.5/dt4 + + sync_sig=0. + ia=min(64,nint(ntol/df)) + do i=-ia,ia + x=s1(i0+2+i,:)-s1(i0+i,:) + do lag=-15,15 + do n=1,4*85 + j=n+lag+11 + if(j.ge.1 .and. j.le.JZ) sync_sig(i,lag)=sync_sig(i,lag) + sync(n)*x(j) + enddo + enddo + enddo + + ijpk=maxloc(sync_sig) + ii=ijpk(1)-65 + jj=ijpk(2)-16 + +! Use peakup() here? + f0=nfqso + ii*df + jdt=jj + tsym=nsps/12000.0 + xdt=jdt*tsym/4.0 + +! do i=-64,64 +! write(13,3013) nfqso+i*df,sync_sig(i,jj) +!3013 format(2f12.3) +! enddo + +! do j=-15,15 +! write(14,3014) j,j*dt4,sync_sig(ii,j) +!3014 format(i5,2f12.3) +! enddo + + return +end subroutine sync66a From 802f078386244fe5e30f63b7718c2a7912985f7c Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Tue, 4 Aug 2020 09:15:44 -0500 Subject: [PATCH 018/426] Fix SNR calculation for B,C,D submodes. --- lib/fst4/get_fst4_bitmetrics.f90 | 2 +- lib/fst4/get_fst4_bitmetrics2.f90 | 11 ++++------- lib/fst4_decode.f90 | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/fst4/get_fst4_bitmetrics.f90 b/lib/fst4/get_fst4_bitmetrics.f90 index 125777568..9cf1e2470 100644 --- a/lib/fst4/get_fst4_bitmetrics.f90 +++ b/lib/fst4/get_fst4_bitmetrics.f90 @@ -52,7 +52,7 @@ subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,badsync) do itone=0,3 cs(itone,k)=sum(csymb*conjg(c1(:,itone))) enddo - s4(0:3,k)=abs(cs(0:3,k)) + s4(0:3,k)=abs(cs(0:3,k))**2 enddo ! Sync quality check diff --git a/lib/fst4/get_fst4_bitmetrics2.f90 b/lib/fst4/get_fst4_bitmetrics2.f90 index 6a669bd4c..9badef231 100644 --- a/lib/fst4/get_fst4_bitmetrics2.f90 +++ b/lib/fst4/get_fst4_bitmetrics2.f90 @@ -1,4 +1,4 @@ -subroutine get_fst4_bitmetrics2(cd,nss,hmod,nsizes,bitmetrics,s4hmod,badsync) +subroutine get_fst4_bitmetrics2(cd,nss,hmod,nsizes,bitmetrics,s4snr,badsync) include 'fst4_params.f90' complex cd(0:NN*nss-1) @@ -15,7 +15,7 @@ subroutine get_fst4_bitmetrics2(cd,nss,hmod,nsizes,bitmetrics,s4hmod,badsync) logical badsync real bitmetrics(2*NN,4) real s2(0:65535) - real s4(0:3,NN,4),s4hmod(0:3,NN) + real s4(0:3,NN,4),s4snr(0:3,NN) data isyncword1/0,1,3,2,1,0,2,3/ data isyncword2/2,3,1,0,3,2,0,1/ data graymap/0,1,3,2/ @@ -121,11 +121,8 @@ subroutine get_fst4_bitmetrics2(cd,nss,hmod,nsizes,bitmetrics,s4hmod,badsync) call normalizebmet(bitmetrics(:,3),2*NN) call normalizebmet(bitmetrics(:,4),2*NN) -! Return the s4 array corresponding to N=1/hmod. Will be used for SNR calculation - if(hmod.eq.1) s4hmod(:,:)=s4(:,:,1) - if(hmod.eq.2) s4hmod(:,:)=s4(:,:,2) - if(hmod.eq.4) s4hmod(:,:)=s4(:,:,3) - if(hmod.eq.8) s4hmod(:,:)=s4(:,:,4) +! Return the s4 array corresponding to N=1. Will be used for SNR calculation + s4snr(:,:)=s4(:,:,1) return end subroutine get_fst4_bitmetrics2 diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index caf9a4b4f..72041bc3f 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -550,7 +550,7 @@ contains endif xsig=0 do i=1,NN - xsig=xsig+s4(itone(i),i)**2 + xsig=xsig+s4(itone(i),i) enddo arg=600.0*(xsig/base)-1.0 if(arg.gt.0.0) then From 7b2e9c4e5dc636a78066eaa6df315c6386d862dd Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 4 Aug 2020 10:49:14 -0400 Subject: [PATCH 019/426] Many improvements to QRA66 decoder. --- lib/qra66_decode.f90 | 54 ++++++++++++++++++++------------------------ lib/sync66a.f90 | 16 ++++++++----- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index fc907c73f..b24dd9cd9 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -44,9 +44,9 @@ contains integer ipk(1) integer dat4(12) logical lapdx,ltext - complex c0(0:NFFT1-1) !Spectrum, then analytic signal + complex c00(0:NFFT1-1) !Spectrum, then analytic signal + complex c0(0:NFFT1-1) !Snalytic 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/ @@ -64,7 +64,7 @@ contains call packcall(hiscall(1:6),nc2,ltext) call packgrid(hisgrid(1:4),ng2,ltext) nSubmode=0 - b90=20 + b90=20.0 !8 to 25 is OK; not very critical nFadingModel=1 ! These probably need work: @@ -111,43 +111,36 @@ contains 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 ntol=100 - call sync66a(iwave,15*12000,nsps,nfqso,ntol,xdt,f0) - jpk=(xdt+0.5)*6000 + call sync66a(iwave,15*12000,nsps,nfqso,ntol,xdt,f0,snr1) + jpk=(xdt+0.5)*6000 - 384 !### Empirical ### if(jpk.lt.0) jpk=0 - -! Genie sync: -! jpk=3600 -! xdt=jpk/6000.0 - 0.5 -! f0=1510 -! f0=1490 - + c00=c0 a=0. -! a(1)=-(f0 + 1.5*baud) !For sync66 a(1)=-(f0 + 2.0*baud) !For sync66a + call twkfreq(c00,c0,15*6000,6000.0,a) + xdt=jpk/6000.0 - 0.5 + call spec66(c0(jpk:jpk+85*NSPS/2-1),s3) - - call twkfreq(c0,c0,15*6000,6000.0,a) - call spec66(c0(jpk:jpk+85*NSPS-1),s3) - s3a=s3/maxval(s3) + do j=1,63 + call pctile(s3(:,j),192,40,base) + s3(:,j)=s3(:,j)/base + enddo - call pctile(s3a,192*63,40,base) - s3a=s3a/base - s3lim=10. - where(s3a(-64:127,1:63)>s3lim) s3a(-64:127,1:63)=s3lim - - call qra64_dec(s3a,nc1,nc2,ng2,naptype,0,nSubmode,b90, & +! Apply AGC + s3max=20.0 + do j=1,63 + xx=maxval(s3(-64:127,j)) + if(xx.gt.s3max) s3(-64:127,j)=s3(-64:127,j)*s3max/xx + enddo + + call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & nFadingModel,dat4,snr2,irc) + snr2=snr2 + 6.0 if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) decoded=' ' @@ -171,6 +164,9 @@ contains !### endif +! write(61,3061) nutc,irc,xdt,f0,snr1,snr2,trim(decoded) +!3061 format(i6.6,i4,4f10.2,2x,a) + return end subroutine decode diff --git a/lib/sync66a.f90 b/lib/sync66a.f90 index ba88616cb..249c3b742 100644 --- a/lib/sync66a.f90 +++ b/lib/sync66a.f90 @@ -1,4 +1,4 @@ -subroutine sync66a(iwave,nmax,nsps,nfqso,ntol,xdt,f0) +subroutine sync66a(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) parameter (NSTEP=4) !Quarter-symbol steps parameter (IZ=1600,JZ=352,NSPSMAX=1920) @@ -49,7 +49,7 @@ subroutine sync66a(iwave,nmax,nsps,nfqso,ntol,xdt,f0) s1=s1/base s1max=20.0 -! Make this simpler: just add the 22 nonzero values. +! Apply AGC do j=1,JZ x(j)=maxval(s1(i0-64:i0+192,j)) if(x(j).gt.s1max) s1(i0-64:i0+192,j)=s1(i0-64:i0+192,j)*s1max/x(j) @@ -63,6 +63,7 @@ subroutine sync66a(iwave,nmax,nsps,nfqso,ntol,xdt,f0) do i=-ia,ia x=s1(i0+2+i,:)-s1(i0+i,:) do lag=-15,15 +! Make this simpler: just add the 22 nonzero values? do n=1,4*85 j=n+lag+11 if(j.ge.1 .and. j.le.JZ) sync_sig(i,lag)=sync_sig(i,lag) + sync(n)*x(j) @@ -80,15 +81,18 @@ subroutine sync66a(iwave,nmax,nsps,nfqso,ntol,xdt,f0) tsym=nsps/12000.0 xdt=jdt*tsym/4.0 + snr1=maxval(sync_sig)/22.0 + ! do i=-64,64 -! write(13,3013) nfqso+i*df,sync_sig(i,jj) -!3013 format(2f12.3) +! write(62,3062) nfqso+i*df,sync_sig(i,jj) +!3062 format(2f12.3) ! enddo ! do j=-15,15 -! write(14,3014) j,j*dt4,sync_sig(ii,j) -!3014 format(i5,2f12.3) +! write(63,3063) j,j*dt4,sync_sig(ii,j) +!3063 format(i5,2f12.3) ! enddo return end subroutine sync66a + From 55d83f068b3862652d750fc906dbf2b82a7c9161 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Tue, 4 Aug 2020 09:15:44 -0500 Subject: [PATCH 020/426] Fix SNR calculation for B,C,D submodes. --- lib/fst4/get_fst4_bitmetrics.f90 | 2 +- lib/fst4/get_fst4_bitmetrics2.f90 | 11 ++++------- lib/fst4_decode.f90 | 28 +++++++++++++++------------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/lib/fst4/get_fst4_bitmetrics.f90 b/lib/fst4/get_fst4_bitmetrics.f90 index 125777568..9cf1e2470 100644 --- a/lib/fst4/get_fst4_bitmetrics.f90 +++ b/lib/fst4/get_fst4_bitmetrics.f90 @@ -52,7 +52,7 @@ subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,badsync) do itone=0,3 cs(itone,k)=sum(csymb*conjg(c1(:,itone))) enddo - s4(0:3,k)=abs(cs(0:3,k)) + s4(0:3,k)=abs(cs(0:3,k))**2 enddo ! Sync quality check diff --git a/lib/fst4/get_fst4_bitmetrics2.f90 b/lib/fst4/get_fst4_bitmetrics2.f90 index 6a669bd4c..9badef231 100644 --- a/lib/fst4/get_fst4_bitmetrics2.f90 +++ b/lib/fst4/get_fst4_bitmetrics2.f90 @@ -1,4 +1,4 @@ -subroutine get_fst4_bitmetrics2(cd,nss,hmod,nsizes,bitmetrics,s4hmod,badsync) +subroutine get_fst4_bitmetrics2(cd,nss,hmod,nsizes,bitmetrics,s4snr,badsync) include 'fst4_params.f90' complex cd(0:NN*nss-1) @@ -15,7 +15,7 @@ subroutine get_fst4_bitmetrics2(cd,nss,hmod,nsizes,bitmetrics,s4hmod,badsync) logical badsync real bitmetrics(2*NN,4) real s2(0:65535) - real s4(0:3,NN,4),s4hmod(0:3,NN) + real s4(0:3,NN,4),s4snr(0:3,NN) data isyncword1/0,1,3,2,1,0,2,3/ data isyncword2/2,3,1,0,3,2,0,1/ data graymap/0,1,3,2/ @@ -121,11 +121,8 @@ subroutine get_fst4_bitmetrics2(cd,nss,hmod,nsizes,bitmetrics,s4hmod,badsync) call normalizebmet(bitmetrics(:,3),2*NN) call normalizebmet(bitmetrics(:,4),2*NN) -! Return the s4 array corresponding to N=1/hmod. Will be used for SNR calculation - if(hmod.eq.1) s4hmod(:,:)=s4(:,:,1) - if(hmod.eq.2) s4hmod(:,:)=s4(:,:,2) - if(hmod.eq.4) s4hmod(:,:)=s4(:,:,3) - if(hmod.eq.8) s4hmod(:,:)=s4(:,:,4) +! Return the s4 array corresponding to N=1. Will be used for SNR calculation + s4snr(:,:)=s4(:,:,1) return end subroutine get_fst4_bitmetrics2 diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index caf9a4b4f..d32873c42 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -49,7 +49,7 @@ contains complex, allocatable :: cframe(:) complex, allocatable :: c_bigfft(:) !Complex waveform real llr(240),llra(240),llrb(240),llrc(240),llrd(240) - real candidates(100,4) + real candidates(200,4) real bitmetrics(320,4) real s4(0:3,NN) real minsync @@ -253,6 +253,7 @@ contains call four2a(c_bigfft,nfft1,1,-1,0) !r2c ! call blank2(nfa,nfb,nfft1,c_bigfft,iwave) + nhicoh=0 if(hmod.eq.1) then if(fMHz.lt.2.0) then nsyncoh=8 ! Use N=8 for sync @@ -277,7 +278,7 @@ contains if(hmod.eq.1) then if(ntrperiod.eq.15) minsync=1.15 - if(ntrperiod.gt.15) minsync=1.20 + if(ntrperiod.gt.15) minsync=1.25 elseif(hmod.gt.1) then minsync=1.2 endif @@ -410,7 +411,7 @@ contains ns4=count(hbits(229:244).eq.(/1,1,1,0,0,1,0,0,1,0,1,1,0,0,0,1/)) ns5=count(hbits(305:320).eq.(/0,0,0,1,1,0,1,1,0,1,0,0,1,1,1,0/)) nsync_qual=ns1+ns2+ns3+ns4+ns5 -! if(nsync_qual.lt. 46) cycle !### Value ?? ### + if(nsync_qual.lt. 46) cycle !### Value ?? ### scalefac=2.83 llra( 1: 60)=bitmetrics( 17: 76, 1) llra( 61:120)=bitmetrics( 93:152, 1) @@ -457,7 +458,7 @@ contains if(itry.gt.nblock) then llr=llra if(nblock.gt.1) then - if(hmod.eq.1) llr=llrd + if(hmod.eq.1) llr=llrc if(hmod.eq.2) llr=llrb if(hmod.eq.4) llr=llrc if(hmod.eq.8) llr=llrd @@ -550,7 +551,7 @@ contains endif xsig=0 do i=1,NN - xsig=xsig+s4(itone(i),i)**2 + xsig=xsig+s4(itone(i),i) enddo arg=600.0*(xsig/base)-1.0 if(arg.gt.0.0) then @@ -737,7 +738,7 @@ contains complex c_bigfft(0:nfft1/2) !Full length FFT of raw data integer hmod !Modulation index (submode) integer im(1) !For maxloc - real candidates(100,4) !Candidate list + real candidates(200,4) !Candidate list real, allocatable :: s(:) !Low resolution power spectrum real, allocatable :: s2(:) !CCF of s() with 4 tones real xdb(-3:3) !Model 4-tone CCF peaks @@ -794,17 +795,18 @@ contains ! Find candidates, using the CLEAN algorithm to remove a model of each one ! from s2() after it has been found. pval=99.99 - do while(ncand.lt.100) + do while(ncand.lt.200) im=maxloc(s2(ia:ib)) iploc=ia+im(1)-1 !Index of CCF peak pval=s2(iploc) !Peak value if(pval.lt.minsync) exit - do i=-3,+3 !Remove 0.9 of a model CCF at - k=iploc+2*hmod*i !this frequency from s2() - if(k.ge.ia .and. k.le.ib) then - s2(k)=max(0.,s2(k)-0.9*pval*xdb(i)) - endif - enddo +! do i=-3,+3 !Remove 0.9 of a model CCF at +! k=iploc+2*hmod*i !this frequency from s2() +! if(k.ge.ia .and. k.le.ib) then +! s2(k)=max(0.,s2(k)-0.9*pval*xdb(i)) +! endif +! enddo + s2(max(1,iploc-2*hmod*3):min(nnw,iploc+2*hmod*3))=0.0 ncand=ncand+1 candidates(ncand,1)=df2*iploc !Candidate frequency candidates(ncand,2)=pval !Rough estimate of SNR From 4225241720b653b56562f304279fcbdb6ea0dcb6 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Tue, 4 Aug 2020 10:25:09 -0500 Subject: [PATCH 021/426] Changes to the llrs that are used as the basis for AP decoding. --- lib/fst4_decode.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index d32873c42..88572dfb7 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -459,9 +459,9 @@ contains llr=llra if(nblock.gt.1) then if(hmod.eq.1) llr=llrc - if(hmod.eq.2) llr=llrb - if(hmod.eq.4) llr=llrc - if(hmod.eq.8) llr=llrd + if(hmod.eq.2) llr=llra + if(hmod.eq.4) llr=llrb + if(hmod.eq.8) llr=llrc endif iaptype=naptypes(nQSOProgress,itry-nblock) if(lapcqonly) iaptype=1 From aff22a904d8f2494e940404ac2959b4d0be3702a Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 4 Aug 2020 11:51:40 -0400 Subject: [PATCH 022/426] Better marking of QRA66 Rx freq range and Tol on Wide Graph. --- widgets/plotter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 15057f4a8..fde2a707b 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -508,13 +508,13 @@ void CPlotter::DrawOverlay() //DrawOverlay() or m_mode=="QRA64" or m_mode=="QRA66" or m_mode=="FT8" or m_mode=="FT4" or m_mode.startsWith("FST4")) { - if(m_mode=="QRA64" or (m_mode=="JT65" and m_bVHF)) { + if(m_mode=="QRA64" or m_mode=="QRA66" or (m_mode=="JT65" and m_bVHF)) { painter0.setPen(penGreen); x1=XfromFreq(m_rxFreq-m_tol); x2=XfromFreq(m_rxFreq+m_tol); - painter0.drawLine(x1,28,x2,28); + painter0.drawLine(x1,27,x2,27); x1=XfromFreq(m_rxFreq); - painter0.drawLine(x1,24,x1,30); + painter0.drawLine(x1,21,x1,27); if(m_mode=="JT65") { painter0.setPen(penOrange); @@ -527,7 +527,7 @@ void CPlotter::DrawOverlay() //DrawOverlay() } painter0.setPen(penGreen); x6=XfromFreq(m_rxFreq+bw); //Highest tone - painter0.drawLine(x6,24,x6,30); + painter0.drawLine(x6,21,x6,27); } else { // Draw the green "goal post" From 668631180d68bd1c9686ddba51245509bc4d1caa Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 4 Aug 2020 11:52:29 -0400 Subject: [PATCH 023/426] Code cleanup. --- lib/decoder.f90 | 5 ++--- lib/qra66_decode.f90 | 49 +++++++++++--------------------------------- 2 files changed, 14 insertions(+), 40 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 41f1d0f7e..de2a31df5 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -197,9 +197,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample) if(params%nmode.eq.66) then ! We're in QRA66 mode call timer('decqra66',0) - call my_qra66%decode(qra66_decoded,id2,params%nutc,params%nfa, & - params%nfb,params%nfqso,params%ndepth,logical(params%lapcqonly), & - mycall,hiscall,hisgrid) + call my_qra66%decode(qra66_decoded,id2,params%nutc,params%nfqso, & + params%ntol,params%ndepth,mycall,hiscall,hisgrid) call timer('decqra66',1) go to 800 endif diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index b24dd9cd9..ade80d638 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -28,7 +28,7 @@ module qra66_decode contains - subroutine decode(this,callback,iwave,nutc,nfa,nfb,nfqso,ndepth,lapdx, & + subroutine decode(this,callback,iwave,nutc,nfqso,ntol,ndepth, & mycall,hiscall,hisgrid) use timer_module, only: timer @@ -41,12 +41,9 @@ contains character(len=6) :: hisgrid character*37 decoded integer*2 iwave(NFFT1) !Raw data - integer ipk(1) integer dat4(12) logical lapdx,ltext - complex c00(0:NFFT1-1) !Spectrum, then analytic signal - complex c0(0:NFFT1-1) !Snalytic signal - real s(900) + complex c0(0:NFFT1-1) !Analytic signal, 6000 S/s real s3(-64:127,63) real a(5) data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/ @@ -67,12 +64,11 @@ contains b90=20.0 !8 to 25 is OK; not very critical nFadingModel=1 -! These probably need work: +! AP control could be done differently, but this works well: maxaptype=0 if(ndepth.eq.2) maxaptype=3 if(ndepth.eq.3) maxaptype=5 -! Prime the decoder for possible AP decoding if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & maxaptype.ne.maxaptypez) then do naptype=0,maxaptype @@ -87,60 +83,39 @@ contains endif naptype=maxaptype -! Compute the full-length spectrum +! Downsample to give complex data at 6000 S/s 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) - ia=1450/df2 - ib=1550/df2 - s(:ia)=0. - s(ib:)=0. - ipk=maxloc(s) - f0=df2*ipk(1) - 0.5*baud !Candidate sync frequency - 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 ntol=100 call sync66a(iwave,15*12000,nsps,nfqso,ntol,xdt,f0,snr1) - jpk=(xdt+0.5)*6000 - 384 !### Empirical ### + jpk=(xdt+0.5)*6000 - 384 !### Empirical ### if(jpk.lt.0) jpk=0 - c00=c0 a=0. - a(1)=-(f0 + 2.0*baud) !For sync66a - call twkfreq(c00,c0,15*6000,6000.0,a) + a(1)=-(f0 + 2.0*baud) !Data tones start 2 bins higher + call twkfreq(c0,c0,15*6000,6000.0,a) xdt=jpk/6000.0 - 0.5 call spec66(c0(jpk:jpk+85*NSPS/2-1),s3) - do j=1,63 + do j=1,63 !Normalize to symbol baseline call pctile(s3(:,j),192,40,base) s3(:,j)=s3(:,j)/base enddo -! Apply AGC s3max=20.0 - do j=1,63 + do j=1,63 !Apply AGC to suppress pings xx=maxval(s3(-64:127,j)) if(xx.gt.s3max) s3(-64:127,j)=s3(-64:127,j)*s3max/xx enddo - + +!Call Nico's QRA64 decoder call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & nFadingModel,dat4,snr2,irc) - snr2=snr2 + 6.0 + snr2=snr2 + 5.563 !10*log(6912/1920) if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) decoded=' ' From 902bb4d5e070196d5ce17299ddbf6724936a57fd Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 4 Aug 2020 11:58:09 -0400 Subject: [PATCH 024/426] Add timer calls in qra66_decode. --- lib/qra66_decode.f90 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index ade80d638..ecef6a870 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -92,7 +92,9 @@ contains call four2a(c0,nfft2,1,1,1) !Inverse c2c FFT ntol=100 + call timer('sync66 ',0) call sync66a(iwave,15*12000,nsps,nfqso,ntol,xdt,f0,snr1) + call timer('sync66 ',1) jpk=(xdt+0.5)*6000 - 384 !### Empirical ### if(jpk.lt.0) jpk=0 a=0. @@ -113,8 +115,10 @@ contains enddo !Call Nico's QRA64 decoder + call timer('qra64_de',0) call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & nFadingModel,dat4,snr2,irc) + call timer('qra64_de',1) snr2=snr2 + 5.563 !10*log(6912/1920) if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) From 88102af49d72337e34754e8c4cd5c6488cb92d36 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 4 Aug 2020 12:53:37 -0400 Subject: [PATCH 025/426] Fic the transfer of ntol from GUI to qra66_decode(). --- lib/qra66_decode.f90 | 1 - widgets/plotter.cpp | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index ecef6a870..8892769d0 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -91,7 +91,6 @@ contains c0(0)=0.5*c0(0) call four2a(c0,nfft2,1,1,1) !Inverse c2c FFT - ntol=100 call timer('sync66 ',0) call sync66a(iwave,15*12000,nsps,nfqso,ntol,xdt,f0,snr1) call timer('sync66 ',1) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index fde2a707b..d3416db02 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -512,22 +512,22 @@ void CPlotter::DrawOverlay() //DrawOverlay() painter0.setPen(penGreen); x1=XfromFreq(m_rxFreq-m_tol); x2=XfromFreq(m_rxFreq+m_tol); - painter0.drawLine(x1,27,x2,27); + painter0.drawLine(x1,26,x2,26); x1=XfromFreq(m_rxFreq); - painter0.drawLine(x1,21,x1,27); + painter0.drawLine(x1,20,x1,26); if(m_mode=="JT65") { painter0.setPen(penOrange); x3=XfromFreq(m_rxFreq+20.0*bw/65.0); //RO - painter0.drawLine(x3,24,x3,30); + painter0.drawLine(x3,20,x3,26); x4=XfromFreq(m_rxFreq+30.0*bw/65.0); //RRR - painter0.drawLine(x4,24,x4,30); + painter0.drawLine(x4,20,x4,26); x5=XfromFreq(m_rxFreq+40.0*bw/65.0); //73 - painter0.drawLine(x5,24,x5,30); + painter0.drawLine(x5,20,x5,26); } painter0.setPen(penGreen); x6=XfromFreq(m_rxFreq+bw); //Highest tone - painter0.drawLine(x6,21,x6,27); + painter0.drawLine(x6,20,x6,26); } else { // Draw the green "goal post" From 9b4b3a6623d78ebf87b258851b9ee0931715fa51 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Tue, 4 Aug 2020 11:56:32 -0500 Subject: [PATCH 026/426] Remove some unneeded code. --- lib/fst4_decode.f90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index 88572dfb7..e56fa27a8 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -149,8 +149,7 @@ contains if(i3.ne.1 .or. (msg.ne.msgsent) .or. .not.unpk77_success) go to 10 read(c77,'(77i1)') message77 message77=mod(message77+rvec,2) - call encode174_91(message77,cw) - apbits=2*cw-1 + apbits(1:77)=2*message77-1 if(nohiscall) apbits(30)=99 10 continue From 2e893e8994e75e567bdce0f8804ded20da076149 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 4 Aug 2020 13:12:51 -0400 Subject: [PATCH 027/426] Add Tx freq as command-line argument for qra66sim. --- lib/qra/qra66/qra66sim.f90 | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/qra/qra66/qra66sim.f90 b/lib/qra/qra66/qra66sim.f90 index 0e1910490..e7903eee4 100644 --- a/lib/qra/qra66/qra66sim.f90 +++ b/lib/qra/qra66/qra66sim.f90 @@ -19,21 +19,23 @@ program qra66sim character msgsent*22 nargs=iargc() - if(nargs.ne.6) then - print *, 'Usage: qra66sim "msg" A|B fDop DT Nfiles SNR' - print *, 'Example qra66sim "K1ABC W9XYZ EN37" A 0.2 0.0 1 -10' + if(nargs.ne.7) then + print *, 'Usage: qra66sim "msg" A|B freq fDop DT Nfiles SNR' + print *, 'Example: qra66sim "K1ABC W9XYZ EN37" A 1500 0.2 0.0 1 -10' go to 999 endif call getarg(1,msg) call getarg(2,csubmode) mode66=2**(ichar(csubmode)-ichar('A')) call getarg(3,arg) - read(arg,*) fspread + read(arg,*) f0 call getarg(4,arg) - read(arg,*) xdt + read(arg,*) fspread call getarg(5,arg) - read(arg,*) nfiles + read(arg,*) xdt call getarg(6,arg) + read(arg,*) nfiles + call getarg(7,arg) read(arg,*) snrdb rms=100. @@ -71,7 +73,6 @@ program qra66sim enddo endif - f0=1500.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 From 335cc41faa09462ee60c8e78d59c468a6579d03d Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 4 Aug 2020 13:13:49 -0400 Subject: [PATCH 028/426] More code cleanup. Rename sync66a to sync66. --- CMakeLists.txt | 2 +- lib/qra66_decode.f90 | 2 +- lib/sync66.f90 | 142 +++++++++++++++++++++++++++---------------- lib/sync66a.f90 | 98 ----------------------------- 4 files changed, 92 insertions(+), 152 deletions(-) delete mode 100644 lib/sync66a.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index fe32cc8f5..de57adccb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -577,7 +577,7 @@ set (wsjt_FSRCS lib/symspec65.f90 lib/sync4.f90 lib/sync64.f90 - lib/sync66a.f90 + lib/sync66.f90 lib/sync65.f90 lib/ft4/getcandidates4.f90 lib/ft4/get_ft4_bitmetrics.f90 diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index 8892769d0..f914a1fb2 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -92,7 +92,7 @@ contains call four2a(c0,nfft2,1,1,1) !Inverse c2c FFT call timer('sync66 ',0) - call sync66a(iwave,15*12000,nsps,nfqso,ntol,xdt,f0,snr1) + call sync66(iwave,15*12000,nsps,nfqso,ntol,xdt,f0,snr1) call timer('sync66 ',1) jpk=(xdt+0.5)*6000 - 384 !### Empirical ### if(jpk.lt.0) jpk=0 diff --git a/lib/sync66.f90 b/lib/sync66.f90 index bc8b41805..e259f04ac 100644 --- a/lib/sync66.f90 +++ b/lib/sync66.f90 @@ -1,60 +1,98 @@ -subroutine sync66(c0,f0,jpk,sync) +subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) -! 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 + parameter (NSTEP=4) !Quarter-symbol steps + parameter (IZ=1600,JZ=352,NSPSMAX=1920) + integer*2 iwave(0:nmax-1) !Raw data + integer b11(11) !Barker 11 code + integer ijpk(2) !Indices i and j at peak of sync_sig + real s1(IZ,JZ) !Symbol spectra + real x(JZ) !Work array; 2FSK sync modulation + real sync(4*85) !sync vector + real sync_sig(-64:64,-15:15) + complex c0(0:NSPSMAX) !Complex spectrum of symbol + data b11/1,1,1,0,0,0,1,0,0,1,0/ !Barker 11 code + data sync(1)/99.0/ + save sync - 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. + if(sync(1).eq.99.0) then + sync=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 + kk=k + if(kk.gt.11) kk=k-11 + sync(16*k-15)=2.0*b11(kk) - 1.0 + enddo + endif + + nfft=2*NSPS + df=12000.0/nfft !3.125 Hz + istep=nsps/NSTEP + fac=1/32767.0 + do j=1,JZ !Compute symbol spectra + ia=(j-1)*istep + ib=ia+nsps-1 + k=-1 + do i=ia,ib,2 + xx=iwave(i) + yy=iwave(i+1) + k=k+1 + c0(k)=fac*cmplx(xx,yy) + enddo + c0(k+1:nfft/2)=0. + call four2a(c0,nfft,1,-1,0) !r2c FFT + do i=1,IZ + s1(i,j)=real(c0(i))**2 + aimag(c0(i))**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 + + i0=nint(nfqso/df) + call pctile(s1(i0-64:i0+192,1:JZ),129*JZ,40,base) + s1=s1/base + s1max=20.0 + +! Apply AGC + do j=1,JZ + x(j)=maxval(s1(i0-64:i0+192,j)) + if(x(j).gt.s1max) s1(i0-64:i0+192,j)=s1(i0-64:i0+192,j)*s1max/x(j) + enddo + + dt4=nsps/(NSTEP*12000.0) + j0=0.5/dt4 + + sync_sig=0. + ia=min(64,nint(ntol/df)) + do i=-ia,ia + x=s1(i0+2+i,:)-s1(i0+i,:) + do lag=-15,15 +! Make this simpler: just add the 22 nonzero values? + do n=1,4*85 + j=n+lag+11 + if(j.ge.1 .and. j.le.JZ) sync_sig(i,lag)=sync_sig(i,lag) + sync(n)*x(j) + enddo + enddo + enddo + + ijpk=maxloc(sync_sig) + ii=ijpk(1)-65 + jj=ijpk(2)-16 + +! Use peakup() here? + f0=nfqso + ii*df + jdt=jj + tsym=nsps/12000.0 + xdt=jdt*tsym/4.0 + + snr1=maxval(sync_sig)/22.0 + +! do i=-64,64 +! write(62,3062) nfqso+i*df,sync_sig(i,jj) +!3062 format(2f12.3) +! enddo + +! do j=-15,15 +! write(63,3063) j,j*dt4,sync_sig(ii,j) +!3063 format(i5,2f12.3) +! enddo return end subroutine sync66 + diff --git a/lib/sync66a.f90 b/lib/sync66a.f90 deleted file mode 100644 index 249c3b742..000000000 --- a/lib/sync66a.f90 +++ /dev/null @@ -1,98 +0,0 @@ -subroutine sync66a(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) - - parameter (NSTEP=4) !Quarter-symbol steps - parameter (IZ=1600,JZ=352,NSPSMAX=1920) - integer*2 iwave(0:nmax-1) !Raw data - integer b11(11) !Barker 11 code - integer ijpk(2) !Indices i and j at peak of sync_sig - real s1(IZ,JZ) !Symbol spectra - real x(JZ) !Work array; 2FSK sync modulation - real sync(4*85) !sync vector - real sync_sig(-64:64,-15:15) - complex c0(0:NSPSMAX) !Complex spectrum of symbol - data b11/1,1,1,0,0,0,1,0,0,1,0/ !Barker 11 code - data sync(1)/99.0/ - save sync - - if(sync(1).eq.99.0) then - sync=0. - do k=1,22 - kk=k - if(kk.gt.11) kk=k-11 - sync(16*k-15)=2.0*b11(kk) - 1.0 - enddo - endif - - nfft=2*NSPS - df=12000.0/nfft !3.125 Hz - istep=nsps/NSTEP - fac=1/32767.0 - do j=1,JZ !Compute symbol spectra - ia=(j-1)*istep - ib=ia+nsps-1 - k=-1 - do i=ia,ib,2 - xx=iwave(i) - yy=iwave(i+1) - k=k+1 - c0(k)=fac*cmplx(xx,yy) - enddo - c0(k+1:nfft/2)=0. - call four2a(c0,nfft,1,-1,0) !r2c FFT - do i=1,IZ - s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 - enddo - enddo - - i0=nint(nfqso/df) - call pctile(s1(i0-64:i0+192,1:JZ),129*JZ,40,base) - s1=s1/base - s1max=20.0 - -! Apply AGC - do j=1,JZ - x(j)=maxval(s1(i0-64:i0+192,j)) - if(x(j).gt.s1max) s1(i0-64:i0+192,j)=s1(i0-64:i0+192,j)*s1max/x(j) - enddo - - dt4=nsps/(NSTEP*12000.0) - j0=0.5/dt4 - - sync_sig=0. - ia=min(64,nint(ntol/df)) - do i=-ia,ia - x=s1(i0+2+i,:)-s1(i0+i,:) - do lag=-15,15 -! Make this simpler: just add the 22 nonzero values? - do n=1,4*85 - j=n+lag+11 - if(j.ge.1 .and. j.le.JZ) sync_sig(i,lag)=sync_sig(i,lag) + sync(n)*x(j) - enddo - enddo - enddo - - ijpk=maxloc(sync_sig) - ii=ijpk(1)-65 - jj=ijpk(2)-16 - -! Use peakup() here? - f0=nfqso + ii*df - jdt=jj - tsym=nsps/12000.0 - xdt=jdt*tsym/4.0 - - snr1=maxval(sync_sig)/22.0 - -! do i=-64,64 -! write(62,3062) nfqso+i*df,sync_sig(i,jj) -!3062 format(2f12.3) -! enddo - -! do j=-15,15 -! write(63,3063) j,j*dt4,sync_sig(ii,j) -!3063 format(i5,2f12.3) -! enddo - - return -end subroutine sync66a - From f1c8f041361bd8bb69e97655fc6e9771adf1d206 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 4 Aug 2020 14:18:23 -0400 Subject: [PATCH 029/426] Report failed QRA66 decode as SNR = -22 dB. --- lib/qra66_decode.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index f914a1fb2..3f4b63f4a 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -135,7 +135,7 @@ contains irc,qual,ntrperiod,fmid,w50) else snr2=0. - nsnr=nint(db(sync)) + nsnr=-22 !### TEMPORARY? ### call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) From 2bb9566731049106a1f3f82df5830f73cc20dd6e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 5 Aug 2020 09:37:03 -0400 Subject: [PATCH 030/426] In QRA66 mode, make unused widgets invisible. --- widgets/mainwindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 0fc6ec684..d78d05ec4 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3389,7 +3389,7 @@ void MainWindow::readFromStdout() //readFromStdout //Right (Rx Frequency) window bool bDisplayRight=bAvgMsg; int audioFreq=decodedtext.frequencyOffset(); - if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="FST4") { + if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="FST4" or m_mode=="QRA66") { auto const& parts = decodedtext.string().remove("<").remove(">") .split (' ', SkipEmptyParts); if (parts.size() > 6) { @@ -6427,7 +6427,9 @@ void MainWindow::on_actionQRA66_triggered() m_wideGraph->setMode(m_mode); m_wideGraph->setModeTx(m_modeTx); m_wideGraph->setPeriod(m_TRperiod,6912); -} +// 0123456789012345678901234567890123 + displayWidgets(nWidgets("1111100001001100000100000001000000")); + statusChanged();} void MainWindow::on_actionISCAT_triggered() { From 7256d70016646454085a2d91087b668d5fc17748 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 5 Aug 2020 11:06:18 -0400 Subject: [PATCH 031/426] QRA66: change NSPS from 1920 to 1800. TxT=85*1800/12000=12.75 s. --- lib/jt9.f90 | 1 + lib/qra/qra66/qra66sim.f90 | 6 +++--- lib/qra66_decode.f90 | 6 ++++-- lib/spec66.f90 | 30 ++++++++++++++---------------- widgets/mainwindow.cpp | 4 ++-- 5 files changed, 24 insertions(+), 23 deletions(-) diff --git a/lib/jt9.f90 b/lib/jt9.f90 index 3c017ac18..052d35cb9 100644 --- a/lib/jt9.f90 +++ b/lib/jt9.f90 @@ -226,6 +226,7 @@ program jt9 endif shared_data%id2=0 !??? Why is this necessary ??? if(mode.eq.5) npts=21*3456 + if(mode.eq.66) npts=15*12000 do iblk=1,npts/kstep k=iblk*kstep if(mode.eq.8 .and. k.gt.179712) exit diff --git a/lib/qra/qra66/qra66sim.f90 b/lib/qra/qra66/qra66sim.f90 index e7903eee4..901f72e84 100644 --- a/lib/qra/qra66/qra66sim.f90 +++ b/lib/qra/qra66/qra66sim.f90 @@ -43,11 +43,11 @@ program qra66sim dt=1.d0/fsample !Sample interval (s) twopi=8.d0*atan(1.d0) npts=NMAX !Total samples in .wav file - nsps=1920 + nsps=1800 nsym=85 !Number of channel symbols if(csubmode.eq.'B') then - nsps=960 - nsym=169 + nsps=nsps/2 + nsym=2*nsym-1 endif ichk=66 !Flag sent to genqra64 diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index 3f4b63f4a..0fdd47579 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -50,7 +50,7 @@ contains save nc1z,nc2z,ng2z,maxaptypez this%callback => callback - nsps=1920 + nsps=1800 baud=12000.0/nsps df1=12000.0/NFFT1 @@ -100,11 +100,13 @@ contains a(1)=-(f0 + 2.0*baud) !Data tones start 2 bins higher call twkfreq(c0,c0,15*6000,6000.0,a) xdt=jpk/6000.0 - 0.5 - call spec66(c0(jpk:jpk+85*NSPS/2-1),s3) + call spec66(c0(jpk:),nsps/2,s3) do j=1,63 !Normalize to symbol baseline call pctile(s3(:,j),192,40,base) s3(:,j)=s3(:,j)/base + write(71,3071)j,maxloc(s3(:,j)) +3071 format(2i5) enddo s3max=20.0 diff --git a/lib/spec66.f90 b/lib/spec66.f90 index 9242f6a59..0d381cf78 100644 --- a/lib/spec66.f90 +++ b/lib/spec66.f90 @@ -1,30 +1,28 @@ -subroutine spec66(c0,s3) +subroutine spec66(c0,nsps,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 + parameter (LL=3*64) !Frequency channels + parameter (NN=63) !Data symbols + complex c0(0:85*nsps-1) !Synchronized complex data at 6000 S/s + 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 + 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 + 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 + 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 + if(i.lt.0) i=i+nsps s3(ii,j)=real(cs(i))**2 + aimag(cs(i))**2 enddo enddo - df=6000.0/NSPS + df=6000.0/nsps do i=1,LL call pctile(s3(i,1:NN),NN,45,xbase0(i)) !Get baseline for passband shape enddo diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index d78d05ec4..557f95488 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -7390,9 +7390,9 @@ void MainWindow::transmit (double snr) } if (m_modeTx == "QRA66") { - toneSpacing=12000.0/1920.0; + toneSpacing=12000.0/1800.0; Q_EMIT sendMessage (m_mode, NUM_QRA66_SYMBOLS, - 1920.0, ui->TxFreqSpinBox->value () - m_XIT, + 1800.0, ui->TxFreqSpinBox->value () - m_XIT, toneSpacing, m_soundOutput, m_config.audio_output_channel (), true, false, snr, m_TRperiod); } From 6c8c9183aae50b683c402ea6efdf2b5af85c476e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 5 Aug 2020 11:16:56 -0400 Subject: [PATCH 032/426] SNR for QRA66 non-decodes now set to -25. --- lib/qra66_decode.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index 0fdd47579..6eb76352d 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -137,7 +137,7 @@ contains irc,qual,ntrperiod,fmid,w50) else snr2=0. - nsnr=-22 + nsnr=-25 !### TEMPORARY? ### call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) From 30e34bf1c46d0467d613829972b467fc944ea478 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 5 Aug 2020 16:34:49 -0400 Subject: [PATCH 033/426] Implement message averaging (very basic) for QRA66. --- lib/qra66_decode.f90 | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index 6eb76352d..39e7371f6 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -45,9 +45,10 @@ contains logical lapdx,ltext complex c0(0:NFFT1-1) !Analytic signal, 6000 S/s real s3(-64:127,63) + real s3a(-64:127,63) real a(5) data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/ - save nc1z,nc2z,ng2z,maxaptypez + save nc1z,nc2z,ng2z,maxaptypez,nsave,s3a this%callback => callback nsps=1800 @@ -80,6 +81,8 @@ contains nc2z=nc2 ng2z=ng2 maxaptypez=maxaptype + s3a=0. + nsave=0 endif naptype=maxaptype @@ -115,16 +118,31 @@ contains if(xx.gt.s3max) s3(-64:127,j)=s3(-64:127,j)*s3max/xx enddo -!Call Nico's QRA64 decoder +! Call Nico's QRA64 decoder call timer('qra64_de',0) call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & nFadingModel,dat4,snr2,irc) call timer('qra64_de',1) + + if(irc.lt.0) then +! No luck so far. Try for an average decode. + call timer('qra64_av',0) + s3a=s3a+s3 + nsave=nsave+1 + if(nsave.ge.2) then + call qra64_dec(s3a,nc1,nc2,ng2,naptype,0,nSubmode,b90, & + nFadingModel,dat4,snr2,irc) + if(irc.ge.0) irc=10*nsave + irc + endif + call timer('qra64_av',1) + endif snr2=snr2 + 5.563 !10*log(6912/1920) if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) decoded=' ' if(irc.ge.0) then + nsave=0 + s3a=0. call unpackmsg(dat4,decoded) !Unpack the user message call fmtmsg(decoded,iz) if(index(decoded,"000AAA ").ge.1) then From 254e2f61fa08da93a82333712d465f6cc4b9952e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 6 Aug 2020 07:07:16 -0400 Subject: [PATCH 034/426] Remove diagnostic write to fort.71. --- lib/qra66_decode.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index 39e7371f6..308dd97a2 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -108,8 +108,8 @@ contains do j=1,63 !Normalize to symbol baseline call pctile(s3(:,j),192,40,base) s3(:,j)=s3(:,j)/base - write(71,3071)j,maxloc(s3(:,j)) -3071 format(2i5) +! write(71,3071)j,maxloc(s3(:,j)) +!3071 format(2i5) enddo s3max=20.0 From 2de28746724bd65e3d5f6619548d89b2db7f9091 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 6 Aug 2020 14:23:39 -0400 Subject: [PATCH 035/426] Allow for larger irc created by message averaging. Commit test_qra66.f90 --- lib/decoder.f90 | 2 +- lib/test_qra66.f90 | 100 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 lib/test_qra66.f90 diff --git a/lib/decoder.f90 b/lib/decoder.f90 index de2a31df5..887201dda 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -795,7 +795,7 @@ contains real, intent(in) :: w50 write(*,1001) nutc,nsnr,dt,nint(freq),decoded,irc -1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i2) +1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i4) write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded 1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' QRA66') diff --git a/lib/test_qra66.f90 b/lib/test_qra66.f90 new file mode 100644 index 000000000..cd20ceaf9 --- /dev/null +++ b/lib/test_qra66.f90 @@ -0,0 +1,100 @@ +program test_qra66 + + character*70 cmd1,cmd2,line + character*22 msg + character*8 arg + integer nretcode(0:11) + integer fDop + real fspread + logical decok + + nargs=iargc() + if(nargs.ne.7) then + print*,'Usage: test_qra66 "msg" ndepth freq DT fDop nfiles SNR' + print*,'Example: test_qra66 "K1ABC W9XYZ EN37" 3 1500 0.0 5 100 0' + print*,' SNR = 0 to loop over all relevant SNRs' + go to 999 + endif + call getarg(1,msg) + call getarg(2,arg) + read(arg,*) ndepth + call getarg(3,arg) + read(arg,*) nf0 + call getarg(4,arg) + read(arg,*) dt + call getarg(5,arg) + read(arg,*) fDop + call getarg(6,arg) + read(arg,*) nfiles + call getarg(7,arg) + read(arg,*) nsnr + +! 1 2 3 4 5 6 +! 1234567890123456789012345678901234567890123456789012345678901234' + cmd1='qra66sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 100 -10 > junk0' + cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 1 *.wav > junk' + + write(cmd1(10:33),'(a)') '"'//msg//'"' + write(cmd1(37:40),'(i4)') nf0 + write(cmd1(41:45),'(i5)') fDop + write(cmd1(46:50),'(f5.2)') dt + write(cmd1(51:55),'(i5)') nfiles + write(cmd2(32:32),'(i1)') ndepth + call system('rm -f *.wav') + + write(*,1000) (j,j=0,11) + write(12,1000) (j,j=0,11) +1000 format(/'SNR d Dop Sync Dec1 DecN Bad',i5,11i4,' tdec'/83('-')) + ia=-12 + ib=-30 + if(nsnr.ne.0) then + ia=nsnr + ib=nsnr + endif + + do nsnr=ia,ib,-1 + nsync=0 + ndecodes=0 + nfalse=0 + nretcode=0 + navg=0 + write(cmd1(57:59),'(i3)') nsnr + call system(cmd1) + call sec0(0,tdec) + call system(cmd2) + call sec0(1,tdec) + open(10,file='junk',status='unknown') + n=0 + do iline=1,9999 + read(10,'(a70)',end=10) line + if(len(trim(line)).lt.60) cycle + read(line(11:20),*) xdt,nf + if(abs(xdt-dt).lt.0.15 .and. abs(nf-nf0).lt.4) nsync=nsync+1 + read(line(60:),*) irc + if(irc.lt.0) cycle + decok=index(line,'W9XYZ').gt.0 + if(decok) then + i=irc + if(i.le.11) then + ndecodes=ndecodes + 1 + navg=navg + 1 + else + i=mod(i,10) + navg=navg + 1 + endif + nretcode(i)=nretcode(i) + 1 + else + nfalse=nfalse + 1 + print*,'False: ',line + endif + enddo +10 close(10) + write(*,1100) nsnr,ndepth,fDop,nsync,ndecodes,navg,nfalse,nretcode,tdec/nfiles + write(12,1100) nsnr,ndepth,fDop,nsync,ndecodes,navg,nfalse,nretcode,tdec/nfiles +1100 format(i3,i2,i3,3i5,i4,i6,11i4,f6.2) + enddo + +999 end program test_qra66 + + include 'sec0.f90' + From bf38f4416c9186943090e1b0b03b4010aeb9c238 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 8 Aug 2020 09:14:12 -0400 Subject: [PATCH 036/426] Modifications to give QRA64 use of longer TR periods and tone-spacing submodes. Noy yet finished, or tested! --- displayWidgets.txt | 1 + lib/decoder.f90 | 8 +++-- lib/jt9.f90 | 2 +- lib/qra/qra66/qra66sim.f90 | 68 +++++++++++++++++++++++--------------- lib/qra66_decode.f90 | 46 ++++++++++++++++++-------- lib/sync66.f90 | 19 +++++++---- lib/test_qra66.f90 | 4 ++- widgets/mainwindow.cpp | 3 +- widgets/plotter.cpp | 2 +- 9 files changed, 98 insertions(+), 55 deletions(-) diff --git a/displayWidgets.txt b/displayWidgets.txt index 2f6c382e2..afdbd44e1 100644 --- a/displayWidgets.txt +++ b/displayWidgets.txt @@ -11,6 +11,7 @@ JT9+JT65 1110100000011110000100000000000010 JT65 1110100000001110000100000000000010 JT65/VHF 1111100100001101101011000100000000 QRA64 1111100101101101100000000010000000 +QRA66 1111110101101101000100000011000000 ISCAT 1001110000000001100000000000000000 MSK144 1011111101000000000100010000000000 WSPR 0000000000000000010100000000000000 diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 887201dda..269d70423 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -197,7 +197,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) if(params%nmode.eq.66) then ! We're in QRA66 mode call timer('decqra66',0) - call my_qra66%decode(qra66_decoded,id2,params%nutc,params%nfqso, & + call my_qra66%decode(qra66_decoded,id2,params%nutc,params%ntr,params%nfqso, & params%ntol,params%ndepth,mycall,hiscall,hisgrid) call timer('decqra66',1) go to 800 @@ -793,9 +793,11 @@ contains integer, intent(in) :: ntrperiod real, intent(in) :: fmid real, intent(in) :: w50 + integer navg - write(*,1001) nutc,nsnr,dt,nint(freq),decoded,irc -1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i4) + navg=irc/100 + write(*,1001) nutc,nsnr,dt,nint(freq),decoded,mod(irc,100),navg +1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i2,i4) write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded 1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' QRA66') diff --git a/lib/jt9.f90 b/lib/jt9.f90 index 052d35cb9..1f23f3c5d 100644 --- a/lib/jt9.f90 +++ b/lib/jt9.f90 @@ -226,7 +226,7 @@ program jt9 endif shared_data%id2=0 !??? Why is this necessary ??? if(mode.eq.5) npts=21*3456 - if(mode.eq.66) npts=15*12000 + if(mode.eq.66) npts=TRperiod*12000 do iblk=1,npts/kstep k=iblk*kstep if(mode.eq.8 .and. k.gt.179712) exit diff --git a/lib/qra/qra66/qra66sim.f90 b/lib/qra/qra66/qra66sim.f90 index 901f72e84..0ed23af30 100644 --- a/lib/qra/qra66/qra66sim.f90 +++ b/lib/qra/qra66/qra66sim.f90 @@ -4,24 +4,23 @@ program qra66sim use wavhdr use packjt - parameter (NMAX=15*12000) !180,000 - parameter (NFFT=NMAX,NH=NFFT/2) + parameter (NMAX=300*12000) !Total samples in .wav file type(hdr) h !Header for .wav file integer*2 iwave(NMAX) !Generated waveform integer*4 itone(85) !Channel symbols (values 0-65) real*4 xnoise(NMAX) !Generated random noise real*4 dat(NMAX) !Generated real data complex cdat(NMAX) !Generated complex waveform - complex cspread(0:NFFT-1) !Complex amplitude for Rayleigh fading + complex cspread(0:NMAX-1) !Complex amplitude for Rayleigh fading complex z real*8 f0,dt,twopi,phi,dphi,baud,fsample,freq character msg*22,fname*13,csubmode*1,arg*12 character msgsent*22 nargs=iargc() - if(nargs.ne.7) then - print *, 'Usage: qra66sim "msg" A|B freq fDop DT Nfiles SNR' - print *, 'Example: qra66sim "K1ABC W9XYZ EN37" A 1500 0.2 0.0 1 -10' + if(nargs.ne.8) then + print *, 'Usage: qra66sim "msg" A-E freq fDop DT TRp Nfiles SNR' + print *, 'Example: qra66sim "K1ABC W9XYZ EN37" A 1500 0.2 0.0 15 1 -10' go to 999 endif call getarg(1,msg) @@ -34,32 +33,48 @@ program qra66sim call getarg(5,arg) read(arg,*) xdt call getarg(6,arg) - read(arg,*) nfiles + read(arg,*) ntrperiod call getarg(7,arg) + read(arg,*) nfiles + call getarg(8,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=7680 + 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) - npts=NMAX !Total samples in .wav file - nsps=1800 nsym=85 !Number of channel symbols - if(csubmode.eq.'B') then - nsps=nsps/2 - nsym=2*nsym-1 - endif + mode66=2**(ichar(csubmode) - ichar('A')) + print*,csubmode,mode66 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 + baud=12000.d0/nsps !Keying rate (6.67 baud fot 15-s sequences) h=default_header(12000,npts) write(*,1000) -1000 format('File Freq A|B S/N DT Dop Message'/60('-')) +1000 format('File Freq A-E S/N DT Dop Message'/60('-')) do ifile=1,nfiles !Loop over requested number of files write(fname,1002) ifile !Output filename @@ -85,9 +100,8 @@ program qra66sim do i=1,npts !Add this signal into cdat() isym=i/nsps + 1 if(isym.gt.nsym) exit - if(csubmode.eq.'B' .and. isym.gt.84) isym=isym-84 if(isym.ne.isym0) then - freq=f0 + itone(isym)*baud + freq=f0 + itone(isym)*baud*mode66 dphi=twopi*freq*dt isym0=isym endif @@ -102,9 +116,9 @@ program qra66sim if(fspread.ne.0) then !Apply specified Doppler spread df=12000.0/nfft cspread(0)=1.0 - cspread(NH)=0. + cspread(nh)=0. b=6.0 !Use truncated Lorenzian shape for fspread - do i=1,NH + do i=1,nh f=i*df x=b*f/fspread z=0. @@ -120,12 +134,12 @@ program qra66sim phi2=twopi*rran() z=a*cmplx(cos(phi2),sin(phi2)) endif - cspread(NFFT-i)=z + cspread(nfft-i)=z enddo -! do i=0,NFFT-1 +! do i=0,nfft-1 ! f=i*df -! if(i.gt.NH) f=(i-nfft)*df +! if(i.gt.nh) f=(i-nfft)*df ! s=real(cspread(i))**2 + aimag(cspread(i))**2 ! write(13,3000) i,f,s,cspread(i) !3000 format(i5,f10.3,3f12.6) @@ -133,19 +147,19 @@ program qra66sim ! s=real(cspread(0))**2 + aimag(cspread(0))**2 ! write(13,3000) 1024,0.0,s,cspread(0) - call four2a(cspread,NFFT,1,1,1) !Transform to time domain + call four2a(cspread,nfft,1,1,1) !Transform to time domain sum=0. - do i=0,NFFT-1 + do i=0,nfft-1 p=real(cspread(i))**2 + aimag(cspread(i))**2 sum=sum+p enddo - avep=sum/NFFT + 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 +! 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) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index 308dd97a2..c1d6dd804 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -28,32 +28,51 @@ module qra66_decode contains - subroutine decode(this,callback,iwave,nutc,nfqso,ntol,ndepth, & + subroutine decode(this,callback,iwave,nutc,ntrperiod,nfqso,ntol,ndepth, & mycall,hiscall,hisgrid) use timer_module, only: timer use packjt use, intrinsic :: iso_c_binding - parameter (NFFT1=15*12000,NFFT2=15*6000) + parameter (NMAX=60*12000) !### Needs to be 300*12000 ### 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*2 iwave(NMAX) !Raw data integer dat4(12) logical lapdx,ltext - complex c0(0:NFFT1-1) !Analytic signal, 6000 S/s + complex c0(0:NMAX-1) !Analytic signal, 6000 S/s real s3(-64:127,63) real s3a(-64:127,63) real a(5) data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/ save nc1z,nc2z,ng2z,maxaptypez,nsave,s3a - this%callback => callback - nsps=1800 + nfft1=ntrperiod*12000 + nfft2=ntrperiod*6000 + if(ntrperiod.eq.15) then + nsps=1800 + else if(ntrperiod.eq.30) then + nsps=3600 + else if(ntrperiod.eq.60) then + nsps=7680 + else if(ntrperiod.eq.120) then + nsps=16000 + else if(ntrperiod.eq.300) then + nsps=41472 + else + stop 'Invalid TR period' + endif baud=12000.0/nsps - df1=12000.0/NFFT1 + df1=12000.0/nfft1 + print*,'aaa',ntrperiod,hisgrid,nsps,baud + do i=1,NMAX + write(61,3061) i/12000.0,iwave(i)/32767.0 +3061 format(2f12.6) + enddo + this%callback => callback if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning @@ -87,15 +106,14 @@ contains naptype=maxaptype ! Downsample to give complex data at 6000 S/s - fac=2.0/NFFT1 + fac=2.0/nfft1 c0=fac*iwave - call four2a(c0,NFFT1,1,-1,1) !Forward c2c FFT - c0(NFFT2/2+1:NFFT2)=0. !Zero the top half + call four2a(c0,nfft1,1,-1,1) !Forward c2c FFT + 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 timer('sync66 ',0) - call sync66(iwave,15*12000,nsps,nfqso,ntol,xdt,f0,snr1) + call sync66(iwave,60*12000,nsps,nfqso,ntol,xdt,f0,snr1) !### 300*12000 ### call timer('sync66 ',1) jpk=(xdt+0.5)*6000 - 384 !### Empirical ### if(jpk.lt.0) jpk=0 @@ -132,11 +150,11 @@ contains if(nsave.ge.2) then call qra64_dec(s3a,nc1,nc2,ng2,naptype,0,nSubmode,b90, & nFadingModel,dat4,snr2,irc) - if(irc.ge.0) irc=10*nsave + irc + if(irc.ge.0) irc=100*nsave + irc endif call timer('qra64_av',1) endif - snr2=snr2 + 5.563 !10*log(6912/1920) + snr2=snr2 + db(6912.0/nsps) if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) decoded=' ' diff --git a/lib/sync66.f90 b/lib/sync66.f90 index e259f04ac..6fbd89b27 100644 --- a/lib/sync66.f90 +++ b/lib/sync66.f90 @@ -1,19 +1,27 @@ subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) parameter (NSTEP=4) !Quarter-symbol steps - parameter (IZ=1600,JZ=352,NSPSMAX=1920) integer*2 iwave(0:nmax-1) !Raw data integer b11(11) !Barker 11 code integer ijpk(2) !Indices i and j at peak of sync_sig - real s1(IZ,JZ) !Symbol spectra - real x(JZ) !Work array; 2FSK sync modulation + real, allocatable :: s1(:,:) !Symbol spectra + real, allocatable :: x(:) !Work array; 2FSK sync modulation real sync(4*85) !sync vector real sync_sig(-64:64,-15:15) - complex c0(0:NSPSMAX) !Complex spectrum of symbol + complex, allocatable :: c0(:) !Complex spectrum of symbol data b11/1,1,1,0,0,0,1,0,0,1,0/ !Barker 11 code data sync(1)/99.0/ save sync + nfft=2*NSPS + df=12000.0/nfft + istep=nsps/NSTEP + iz=5000.0/df + jz=352 + allocate(s1(iz,jz)) + allocate(x(jz)) + allocate(c0(0:nsps)) + if(sync(1).eq.99.0) then sync=0. do k=1,22 @@ -23,9 +31,6 @@ subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) enddo endif - nfft=2*NSPS - df=12000.0/nfft !3.125 Hz - istep=nsps/NSTEP fac=1/32767.0 do j=1,JZ !Compute symbol spectra ia=(j-1)*istep diff --git a/lib/test_qra66.f90 b/lib/test_qra66.f90 index cd20ceaf9..8ff97829c 100644 --- a/lib/test_qra66.f90 +++ b/lib/test_qra66.f90 @@ -70,7 +70,7 @@ program test_qra66 if(len(trim(line)).lt.60) cycle read(line(11:20),*) xdt,nf if(abs(xdt-dt).lt.0.15 .and. abs(nf-nf0).lt.4) nsync=nsync+1 - read(line(60:),*) irc + read(line(60:),*) irc,iavg if(irc.lt.0) cycle decok=index(line,'W9XYZ').gt.0 if(decok) then @@ -92,6 +92,8 @@ program test_qra66 write(*,1100) nsnr,ndepth,fDop,nsync,ndecodes,navg,nfalse,nretcode,tdec/nfiles write(12,1100) nsnr,ndepth,fDop,nsync,ndecodes,navg,nfalse,nretcode,tdec/nfiles 1100 format(i3,i2,i3,3i5,i4,i6,11i4,f6.2) + flush(6) + flush(12) enddo 999 end program test_qra66 diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 557f95488..168bf5505 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6428,7 +6428,8 @@ void MainWindow::on_actionQRA66_triggered() m_wideGraph->setModeTx(m_modeTx); m_wideGraph->setPeriod(m_TRperiod,6912); // 0123456789012345678901234567890123 - displayWidgets(nWidgets("1111100001001100000100000001000000")); +//displayWidgets(nWidgets("1111100001001100000100000001000000")); + displayWidgets(nWidgets("1111110101101101000100000011000000")); statusChanged();} void MainWindow::on_actionISCAT_triggered() diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index d3416db02..96f8e81df 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -472,7 +472,7 @@ void CPlotter::DrawOverlay() //DrawOverlay() } if(m_mode=="QRA66") { //QRA66 - bw=65.0*12000.0/1920.0; + bw=65.0*12000.0/1800.0; } if(m_modeTx=="JT65") { //JT65 bw=65.0*11025.0/4096.0; From e4fad77fa52aa67a86131214fd61c4c8db166a59 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 8 Aug 2020 13:57:24 -0400 Subject: [PATCH 037/426] QRA66 now works with qra66sim and jt9 and TR periods 15 30 60 120 300 s. --- lib/jt9.f90 | 2 +- lib/qra/qra66/qra66sim.f90 | 19 ++++---------- lib/qra66_decode.f90 | 26 +++++++++++-------- lib/sync66.f90 | 51 ++++++++++++++++++++++++-------------- 4 files changed, 54 insertions(+), 44 deletions(-) diff --git a/lib/jt9.f90 b/lib/jt9.f90 index 1f23f3c5d..2139c66aa 100644 --- a/lib/jt9.f90 +++ b/lib/jt9.f90 @@ -249,7 +249,7 @@ program jt9 call timer('symspec ',1) endif nhsym0=nhsym - if(nhsym.ge.181 .and. mode.ne.240 .and. mode.ne.241) exit + if(nhsym.ge.181 .and. mode.ne.240 .and. mode.ne.241 .and. mode.ne.66) exit endif enddo close(unit=wav%lun) diff --git a/lib/qra/qra66/qra66sim.f90 b/lib/qra/qra66/qra66sim.f90 index 0ed23af30..41aa55c50 100644 --- a/lib/qra/qra66/qra66sim.f90 +++ b/lib/qra/qra66/qra66sim.f90 @@ -74,7 +74,7 @@ program qra66sim h=default_header(12000,npts) write(*,1000) -1000 format('File Freq A-E S/N DT Dop Message'/60('-')) +1000 format('File TR Freq Mode S/N DT Dop Message'/60('-')) do ifile=1,nfiles !Loop over requested number of files write(fname,1002) ifile !Output filename @@ -91,11 +91,12 @@ 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 - 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) + write(*,1020) ifile,ntrperiod,f0,csubmode,xsnr,xdt,fspread,msgsent +1020 format(i4,i6,f7.1,2x,a1,2x,f5.1,f6.2,f6.1,1x,a22) phi=0.d0 dphi=0.d0 - k=(xdt+0.5)*12000 !Start audio at t = xdt + 0.5 s + 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 @@ -137,16 +138,6 @@ program qra66sim cspread(nfft-i)=z enddo -! do i=0,nfft-1 -! f=i*df -! if(i.gt.nh) f=(i-nfft)*df -! s=real(cspread(i))**2 + aimag(cspread(i))**2 -! write(13,3000) i,f,s,cspread(i) -!3000 format(i5,f10.3,3f12.6) -! enddo -! s=real(cspread(0))**2 + aimag(cspread(0))**2 -! write(13,3000) 1024,0.0,s,cspread(0) - call four2a(cspread,nfft,1,1,1) !Transform to time domain sum=0. diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index c1d6dd804..3996ffdf4 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -34,7 +34,7 @@ contains use timer_module, only: timer use packjt use, intrinsic :: iso_c_binding - parameter (NMAX=60*12000) !### Needs to be 300*12000 ### + parameter (NMAX=300*12000) !### Needs to be 300*12000 ### class(qra66_decoder), intent(inout) :: this procedure(qra66_decode_callback) :: callback character(len=12) :: mycall, hiscall @@ -43,7 +43,7 @@ contains integer*2 iwave(NMAX) !Raw data integer dat4(12) logical lapdx,ltext - complex c0(0:NMAX-1) !Analytic signal, 6000 S/s + complex, allocatable :: c0(:) !Analytic signal, 6000 S/s real s3(-64:127,63) real s3a(-64:127,63) real a(5) @@ -52,6 +52,8 @@ contains nfft1=ntrperiod*12000 nfft2=ntrperiod*6000 + allocate (c0(0:nfft1-1)) + if(ntrperiod.eq.15) then nsps=1800 else if(ntrperiod.eq.30) then @@ -67,11 +69,12 @@ contains endif baud=12000.0/nsps df1=12000.0/nfft1 - print*,'aaa',ntrperiod,hisgrid,nsps,baud - do i=1,NMAX - write(61,3061) i/12000.0,iwave(i)/32767.0 -3061 format(2f12.6) - enddo + +! do i=1,NMAX +! write(61,3061) i/12000.0,iwave(i)/32767.0 +!3061 format(2f12.6) +! enddo + this%callback => callback if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning @@ -107,19 +110,20 @@ contains ! Downsample to give complex data at 6000 S/s fac=2.0/nfft1 - c0=fac*iwave + c0=fac*iwave(1:nfft1) call four2a(c0,nfft1,1,-1,1) !Forward c2c FFT 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 timer('sync66 ',0) - call sync66(iwave,60*12000,nsps,nfqso,ntol,xdt,f0,snr1) !### 300*12000 ### + call sync66(iwave,ntrperiod*12000,nsps,nfqso,ntol,xdt,f0,snr1) call timer('sync66 ',1) - jpk=(xdt+0.5)*6000 - 384 !### Empirical ### + jpk=(xdt+0.5)*6000 - 384 !### Empirical ### + if(ntrperiod.ge.60) jpk=(xdt+1.0)*6000 - 384 !### TBD ### if(jpk.lt.0) jpk=0 a=0. a(1)=-(f0 + 2.0*baud) !Data tones start 2 bins higher - call twkfreq(c0,c0,15*6000,6000.0,a) + call twkfreq(c0,c0,ntrperiod*6000,6000.0,a) xdt=jpk/6000.0 - 0.5 call spec66(c0(jpk:),nsps/2,s3) diff --git a/lib/sync66.f90 b/lib/sync66.f90 index 6fbd89b27..0c966ebf3 100644 --- a/lib/sync66.f90 +++ b/lib/sync66.f90 @@ -5,7 +5,7 @@ subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) integer b11(11) !Barker 11 code integer ijpk(2) !Indices i and j at peak of sync_sig real, allocatable :: s1(:,:) !Symbol spectra - real, allocatable :: x(:) !Work array; 2FSK sync modulation + real, allocatable :: x(:) !Work array; demoduated 2FSK sync signal real sync(4*85) !sync vector real sync_sig(-64:64,-15:15) complex, allocatable :: c0(:) !Complex spectrum of symbol @@ -13,11 +13,13 @@ subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) data sync(1)/99.0/ save sync - nfft=2*NSPS + nfft=2*nsps df=12000.0/nfft istep=nsps/NSTEP - iz=5000.0/df - jz=352 + iz=5000.0/df !Uppermost frequency bin, at 5000 Hz + txt=85.0*nsps/12000.0 + jz=(txt+1.0)*12000.0/istep !Number of quarter-symbol steps + if(nsps.ge.7680) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher allocate(s1(iz,jz)) allocate(x(jz)) allocate(c0(0:nsps)) @@ -27,12 +29,13 @@ subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) do k=1,22 kk=k if(kk.gt.11) kk=k-11 - sync(16*k-15)=2.0*b11(kk) - 1.0 + i=4*NSTEP*(k-1) + 1 + sync(i)=2.0*b11(kk) - 1.0 enddo endif fac=1/32767.0 - do j=1,JZ !Compute symbol spectra + do j=1,jz !Compute symbol spectra at quarter-symbol steps ia=(j-1)*istep ib=ia+nsps-1 k=-1 @@ -44,34 +47,43 @@ subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) enddo c0(k+1:nfft/2)=0. call four2a(c0,nfft,1,-1,0) !r2c FFT - do i=1,IZ + do i=1,iz s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 enddo enddo i0=nint(nfqso/df) - call pctile(s1(i0-64:i0+192,1:JZ),129*JZ,40,base) + call pctile(s1(i0-64:i0+192,1:jz),129*jz,40,base) s1=s1/base s1max=20.0 ! Apply AGC - do j=1,JZ + do j=1,jz x(j)=maxval(s1(i0-64:i0+192,j)) if(x(j).gt.s1max) s1(i0-64:i0+192,j)=s1(i0-64:i0+192,j)*s1max/x(j) enddo - dt4=nsps/(NSTEP*12000.0) - j0=0.5/dt4 - sync_sig=0. ia=min(64,nint(ntol/df)) + dt4=nsps/(NSTEP*12000.0) !duration of 1/4 symbol + lag2=nint(0.5/dt4) + if(nsps.ge.7680) lag2=nint(1.0/dt4) + lag1=-lag2 + + jadd=11 + if(nsps.ge.3600) jadd=7 + if(nsps.ge.7680) jadd=6 + if(nsps.ge.16000) jadd=3 + if(nsps.ge.41472) jadd=1 + do i=-ia,ia - x=s1(i0+2+i,:)-s1(i0+i,:) + x=s1(i0+2+i,:)-s1(i0+i,:) !Do the 2FSK demodulation +! do lag=lag1,lag2 do lag=-15,15 -! Make this simpler: just add the 22 nonzero values? - do n=1,4*85 - j=n+lag+11 - if(j.ge.1 .and. j.le.JZ) sync_sig(i,lag)=sync_sig(i,lag) + sync(n)*x(j) + do k=1,22 + n=4*NSTEP*(k-1) + 1 + j=n+lag+jadd + if(j.ge.1 .and. j.le.jz) sync_sig(i,lag)=sync_sig(i,lag) + sync(n)*x(j) enddo enddo enddo @@ -83,6 +95,10 @@ subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) ! Use peakup() here? f0=nfqso + ii*df jdt=jj + +! j0=0.5/dt4 +! if(nsps.ge.7680) j0=1.0/dt4 + tsym=nsps/12000.0 xdt=jdt*tsym/4.0 @@ -100,4 +116,3 @@ subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) return end subroutine sync66 - From d683cb6b8ee82e932e0c3d17d64058c551964adf Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 8 Aug 2020 14:37:01 -0400 Subject: [PATCH 038/426] Updates to GUI for different QRA66 sequence lengths and submodes. --- widgets/mainwindow.cpp | 47 ++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 168bf5505..678dfce87 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -1367,7 +1367,11 @@ void MainWindow::fixStop() m_hsymStop=179; if(m_config.decode_at_52s()) m_hsymStop=186; } else if (m_mode=="QRA66"){ - m_hsymStop=49; + m_hsymStop=48; + if(m_TRperiod==30) m_hsymStop=96; + if(m_TRperiod==60) m_hsymStop=196; + if(m_TRperiod==120) m_hsymStop=401; + if(m_TRperiod==300) m_hsymStop=1027; } else if (m_mode=="FreqCal"){ m_hsymStop=((int(m_TRperiod/0.288))/8)*8; } else if (m_mode=="FT8") { @@ -3684,7 +3688,13 @@ void MainWindow::guiUpdate() if(m_modeTx=="JT9") txDuration=1.0 + 85.0*m_nsps/12000.0; // JT9 if(m_modeTx=="JT65") txDuration=1.0 + 126*4096/11025.0; // JT65 if(m_modeTx=="QRA64") txDuration=1.0 + 84*6912/12000.0; // QRA64 - if(m_modeTx=="QRA66") txDuration=0.5 + 85*1920/12000.0; // QRA66 + if(m_modeTx=="QRA66") { // QRA66 + if(m_TRperiod==15) txDuration=0.5 + 85*1800/12000.0; + if(m_TRperiod==30) txDuration=0.5 + 85*3600/12000.0; + if(m_TRperiod==60) txDuration=1.0 + 85*7680/12000.0; + if(m_TRperiod==120) txDuration=1.0 + 85*16000/12000.0; + if(m_TRperiod==300) txDuration=1.0 + 85*41472/12000.0; + } if(m_modeTx=="WSPR") txDuration=2.0 + 162*8192/12000.0; // WSPR if(m_modeTx=="FST4" or m_mode=="FST4W") { //FST4, FST4W if(m_TRperiod==15) txDuration=1.0 + 160*720/12000.0; @@ -4248,7 +4258,7 @@ void MainWindow::guiUpdate() //Once per second: if(nsec != m_sec0) { -// qDebug() << "AAA" << nsec; +// qDebug() << "onesec" << m_hsymStop << m_TRperiod; m_currentBand=m_config.bands()->find(m_freqNominal); if( SpecOp::HOUND == m_config.special_op_id() ) { qint32 tHound=QDateTime::currentMSecsSinceEpoch()/1000 - m_tAutoOn; @@ -6423,7 +6433,8 @@ void MainWindow::on_actionQRA66_triggered() switch_mode (Modes::QRA64); setup_status_bar (false); m_hsymStop=49; - m_TRperiod=15.0; + ui->sbTR->values ({15, 30, 60, 120, 300}); + on_sbTR_valueChanged (ui->sbTR->value()); m_wideGraph->setMode(m_mode); m_wideGraph->setModeTx(m_modeTx); m_wideGraph->setPeriod(m_TRperiod,6912); @@ -7391,9 +7402,15 @@ void MainWindow::transmit (double snr) } if (m_modeTx == "QRA66") { - toneSpacing=12000.0/1800.0; + int nsps=1800; + if(m_TRperiod==30) nsps=3600; + if(m_TRperiod==60) nsps=7680; + if(m_TRperiod==120) nsps=16000; + if(m_TRperiod==300) nsps=41472; + int mode66=pow(2.0,double(m_nSubMode)); + toneSpacing=mode66*12000.0/nsps; Q_EMIT sendMessage (m_mode, NUM_QRA66_SYMBOLS, - 1800.0, ui->TxFreqSpinBox->value () - m_XIT, + double(nsps), ui->TxFreqSpinBox->value () - m_XIT, toneSpacing, m_soundOutput, m_config.audio_output_channel (), true, false, snr, m_TRperiod); } @@ -7651,23 +7668,17 @@ void::MainWindow::VHF_features_enabled(bool b) void MainWindow::on_sbTR_valueChanged(int value) { // if(!m_bFastMode and n>m_nSubMode) m_MinW=m_nSubMode; - if(m_bFastMode or m_mode=="FreqCal" or m_mode=="FST4" or m_mode=="FST4W") { + if(m_bFastMode or m_mode=="FreqCal" or m_mode=="FST4" or m_mode=="FST4W" or m_mode=="QRA66") { m_TRperiod = value; - if (m_mode == "FST4" || m_mode == "FST4W") - { - if (m_TRperiod < 60) - { + if (m_mode == "FST4" or m_mode == "FST4W" or m_mode=="QRA66") { + if (m_TRperiod < 60) { ui->decodedTextLabel->setText(" UTC dB DT Freq " + tr ("Message")); - if (m_mode != "FST4W") - { + if (m_mode != "FST4W") { ui->decodedTextLabel2->setText(" UTC dB DT Freq " + tr ("Message")); } - } - else - { + } else { ui->decodedTextLabel->setText("UTC dB DT Freq " + tr ("Message")); - if (m_mode != "FST4W") - { + if (m_mode != "FST4W") { ui->decodedTextLabel2->setText("UTC dB DT Freq " + tr ("Message")); } } From de0476bf3c70b0eb0ea48f763f530226c74879f0 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 8 Aug 2020 16:53:28 -0400 Subject: [PATCH 039/426] More changes to allow longer sequences in QRA66 mode. --- lib/decoder.f90 | 15 +++++++++++---- lib/qra/qra66/qra66sim.f90 | 14 ++++++++++---- widgets/plotter.cpp | 9 ++++++++- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 269d70423..33146a593 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -197,8 +197,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample) if(params%nmode.eq.66) then ! We're in QRA66 mode call timer('decqra66',0) - call my_qra66%decode(qra66_decoded,id2,params%nutc,params%ntr,params%nfqso, & - params%ntol,params%ndepth,mycall,hiscall,hisgrid) + call my_qra66%decode(qra66_decoded,id2,params%nutc,params%ntr, & + params%nfqso,params%ntol,params%ndepth,mycall,hiscall,hisgrid) call timer('decqra66',1) go to 800 endif @@ -796,11 +796,18 @@ contains integer navg navg=irc/100 - write(*,1001) nutc,nsnr,dt,nint(freq),decoded,mod(irc,100),navg -1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i2,i4) + if(ntrperiod.lt.60) then + write(*,1001) nutc,nsnr,dt,nint(freq),decoded,mod(irc,100),navg +1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i2,i4) write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded 1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' QRA66') + else + write(*,1003) nutc,nsnr,dt,nint(freq),decoded,mod(irc,100),navg +1003 format(i4.4,i4,f5.1,i5,' + ',1x,a37,1x,i2,i4) + write(13,1004) nutc,nint(sync),nsnr,dt,freq,0,decoded +1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' QRA66') + endif call flush(6) call flush(13) diff --git a/lib/qra/qra66/qra66sim.f90 b/lib/qra/qra66/qra66sim.f90 index 41aa55c50..1171542ad 100644 --- a/lib/qra/qra66/qra66sim.f90 +++ b/lib/qra/qra66/qra66sim.f90 @@ -14,7 +14,7 @@ program qra66sim complex cspread(0:NMAX-1) !Complex amplitude for Rayleigh fading complex z real*8 f0,dt,twopi,phi,dphi,baud,fsample,freq - character msg*22,fname*13,csubmode*1,arg*12 + character msg*22,fname*17,csubmode*1,arg*12 character msgsent*22 nargs=iargc() @@ -77,9 +77,15 @@ program qra66sim 1000 format('File TR Freq Mode S/N DT Dop Message'/60('-')) do ifile=1,nfiles !Loop over requested number of files - write(fname,1002) ifile !Output filename -1002 format('000000_',i6.6) - open(10,file=fname//'.wav',access='stream',status='unknown') + if(ntrperiod.lt.60) then + write(fname,1002) ifile !Output filename +1002 format('000000_',i6.6,'.wav') + else + write(fname,1104) ifile +1104 format('000000_',i4.4,'.wav') + endif + + open(10,file=trim(fname),access='stream',status='unknown') xnoise=0. cdat=0. if(snrdb.lt.90) then diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 96f8e81df..fcac2d707 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -472,7 +472,14 @@ void CPlotter::DrawOverlay() //DrawOverlay() } if(m_mode=="QRA66") { //QRA66 - bw=65.0*12000.0/1800.0; + int h=int(pow(2.0,m_nSubMode)); + int nsps=1800; + if(m_TRperiod==30) nsps=3600; + if(m_TRperiod==60) nsps=7680; + if(m_TRperiod==120) nsps=16000; + if(m_TRperiod==300) nsps=41472; + float baud=12000.0/nsps; + bw=65.0*h*baud; } if(m_modeTx=="JT65") { //JT65 bw=65.0*11025.0/4096.0; From 84e53960cda3c9e4286a4d6bc771beccbfdb33da Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 9 Aug 2020 11:04:49 -0400 Subject: [PATCH 040/426] QRA66 now works up for submodes A-D up to 300 s periods. Submode E presently NG. --- lib/decoder.f90 | 7 ++++--- lib/qra66_decode.f90 | 40 +++++++++++++++++++++++++--------------- lib/spec66.f90 | 8 ++++---- lib/sync66.f90 | 12 ++++++------ 4 files changed, 39 insertions(+), 28 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 33146a593..d3fb4c866 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -197,8 +197,9 @@ subroutine multimode_decoder(ss,id2,params,nfsample) if(params%nmode.eq.66) then ! We're in QRA66 mode call timer('decqra66',0) - call my_qra66%decode(qra66_decoded,id2,params%nutc,params%ntr, & - params%nfqso,params%ntol,params%ndepth,mycall,hiscall,hisgrid) + call my_qra66%decode(qra66_decoded,id2,params%nutc,params%ntr, & + params%nsubmode,params%nfqso,params%ntol,params%ndepth, & + mycall,hiscall,hisgrid) call timer('decqra66',1) go to 800 endif @@ -209,7 +210,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) iwspr=0 if(iand(params%ndepth,128).ne.0) iwspr=2 call timer('dec240 ',0) - call my_fst4%decode(fst4_decoded,id2,params%nutc, & + call my_fst4%decode(fst4_decoded,id2,params%nutc, & params%nQSOProgress,params%nfqso,params%nfa,params%nfb, & params%nsubmode,ndepth,params%ntr,params%nexp_decode, & params%ntol,params%emedelay, & diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index 3996ffdf4..f175fb0ba 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -28,8 +28,8 @@ module qra66_decode contains - subroutine decode(this,callback,iwave,nutc,ntrperiod,nfqso,ntol,ndepth, & - mycall,hiscall,hisgrid) + subroutine decode(this,callback,iwave,nutc,ntrperiod,nsubmode,nfqso, & + ntol,ndepth,mycall,hiscall,hisgrid) use timer_module, only: timer use packjt @@ -44,15 +44,23 @@ contains integer dat4(12) logical lapdx,ltext complex, allocatable :: c0(:) !Analytic signal, 6000 S/s - real s3(-64:127,63) - real s3a(-64:127,63) + real, allocatable, save :: s3(:,:) !Symbol spectra + real, allocatable, save :: s3a(:,:) !Symbol spectra for avg messages real a(5) - data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/ - save nc1z,nc2z,ng2z,maxaptypez,nsave,s3a + data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/,nsubmodez/-1/ + save nc1z,nc2z,ng2z,maxaptypez,nsave,nsubmodez + mode66=2**nsubmode nfft1=ntrperiod*12000 nfft2=ntrperiod*6000 allocate (c0(0:nfft1-1)) + + if(nsubmode.ne.nsubmodez) then + if(allocated(s3)) deallocate(s3) + if(allocated(s3a)) deallocate(s3a) + allocate(s3(-64:64*mode66+63,63)) + allocate(s3a(-64:64*mode66+63,63)) + endif if(ntrperiod.eq.15) then nsps=1800 @@ -83,7 +91,6 @@ contains call packcall(mycall(1:6),nc1,ltext) call packcall(hiscall(1:6),nc2,ltext) call packgrid(hisgrid(1:4),ng2,ltext) - nSubmode=0 b90=20.0 !8 to 25 is OK; not very critical nFadingModel=1 @@ -116,28 +123,31 @@ contains c0(0)=0.5*c0(0) call four2a(c0,nfft2,1,1,1) !Inverse c2c FFT call timer('sync66 ',0) - call sync66(iwave,ntrperiod*12000,nsps,nfqso,ntol,xdt,f0,snr1) + call sync66(iwave,ntrperiod*12000,mode66,nsps,nfqso,ntol,xdt,f0,snr1) call timer('sync66 ',1) + jpk=(xdt+0.5)*6000 - 384 !### Empirical ### if(ntrperiod.ge.60) jpk=(xdt+1.0)*6000 - 384 !### TBD ### if(jpk.lt.0) jpk=0 a=0. - a(1)=-(f0 + 2.0*baud) !Data tones start 2 bins higher + a(1)=-(f0 + 2.0*mode66*baud) !Data tones start 2*mode66 bins higher call twkfreq(c0,c0,ntrperiod*6000,6000.0,a) xdt=jpk/6000.0 - 0.5 - call spec66(c0(jpk:),nsps/2,s3) + + LL=64*(mode66+2) + NN=63 + call spec66(c0(jpk:),nsps/2,s3,LL,NN) !Compute the synchronized symbol spectra do j=1,63 !Normalize to symbol baseline - call pctile(s3(:,j),192,40,base) + call pctile(s3(:,j),LL,40,base) s3(:,j)=s3(:,j)/base -! write(71,3071)j,maxloc(s3(:,j)) -!3071 format(2i5) enddo + LL2=64*(mode66+1)-1 s3max=20.0 do j=1,63 !Apply AGC to suppress pings - xx=maxval(s3(-64:127,j)) - if(xx.gt.s3max) s3(-64:127,j)=s3(-64:127,j)*s3max/xx + xx=maxval(s3(-64:LL2,j)) + if(xx.gt.s3max) s3(-64:LL2,j)=s3(-64:LL2,j)*s3max/xx enddo ! Call Nico's QRA64 decoder diff --git a/lib/spec66.f90 b/lib/spec66.f90 index 0d381cf78..0d049fddb 100644 --- a/lib/spec66.f90 +++ b/lib/spec66.f90 @@ -1,11 +1,11 @@ -subroutine spec66(c0,nsps,s3) +subroutine spec66(c0,nsps,s3,LL,NN) + +! Compute synchronized symbol spectra. - parameter (LL=3*64) !Frequency channels - parameter (NN=63) !Data symbols complex c0(0:85*nsps-1) !Synchronized complex data at 6000 S/s complex cs(0:nsps-1) !Complex symbol spectrum real s3(LL,NN) !Synchronized symbol spectra - real xbase0(LL),xbase(LL) + real xbase0(LL),xbase(LL) !Work arrays fac=1.0/nsps ja=-nsps diff --git a/lib/sync66.f90 b/lib/sync66.f90 index 0c966ebf3..3cd0188bb 100644 --- a/lib/sync66.f90 +++ b/lib/sync66.f90 @@ -1,4 +1,4 @@ -subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) +subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) parameter (NSTEP=4) !Quarter-symbol steps integer*2 iwave(0:nmax-1) !Raw data @@ -35,7 +35,7 @@ subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) endif fac=1/32767.0 - do j=1,jz !Compute symbol spectra at quarter-symbol steps + do j=1,jz !Compute symbol spectra at quarter-symbol steps ia=(j-1)*istep ib=ia+nsps-1 k=-1 @@ -66,9 +66,9 @@ subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) sync_sig=0. ia=min(64,nint(ntol/df)) dt4=nsps/(NSTEP*12000.0) !duration of 1/4 symbol - lag2=nint(0.5/dt4) - if(nsps.ge.7680) lag2=nint(1.0/dt4) - lag1=-lag2 +! lag2=nint(0.5/dt4) +! if(nsps.ge.7680) lag2=nint(1.0/dt4) +! lag1=-lag2 jadd=11 if(nsps.ge.3600) jadd=7 @@ -77,7 +77,7 @@ subroutine sync66(iwave,nmax,nsps,nfqso,ntol,xdt,f0,snr1) if(nsps.ge.41472) jadd=1 do i=-ia,ia - x=s1(i0+2+i,:)-s1(i0+i,:) !Do the 2FSK demodulation + x=s1(i0+2*mode66+i,:)-s1(i0+i,:) !Do the 2FSK demodulation ! do lag=lag1,lag2 do lag=-15,15 do k=1,22 From 42626009b57f3a8f5f7be17559d66ea7342e59f1 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 9 Aug 2020 14:21:25 -0400 Subject: [PATCH 041/426] TEMPORARY: Diagnostic routine for the "Save All" missing files problem. --- lib/symspec.f90 | 32 ++++++++++++++++++++++++++++++++ widgets/mainwindow.cpp | 2 ++ 2 files changed, 34 insertions(+) diff --git a/lib/symspec.f90 b/lib/symspec.f90 index a3bed192f..64c88815e 100644 --- a/lib/symspec.f90 +++ b/lib/symspec.f90 @@ -126,3 +126,35 @@ subroutine symspec(shared_data,k,TRperiod,nsps,ingain,bLowSidelobes, & return end subroutine symspec + +subroutine chk_samples(ihsym,k,nstop) + + integer*8 count0,count1,clkfreq + integer itime(8) + real*8 dtime,fsample + character*12 ctime + data count0/-1/,k0/99999999/,maxhsym/0/ + save count0,k0,maxhsym + + if(k.lt.k0 .or. count0.eq.-1) then + call system_clock(count0,clkfreq) + maxhsym=0 + endif + if((mod(ihsym,100).eq.0 .or. ihsym.ge.nstop-100) .and. & + k0.ne.99999999) then + call system_clock(count1,clkfreq) + dtime=dfloat(count1-count0)/dfloat(clkfreq) + if(dtime.lt.28.0) return + if(dtime.gt.1.d-6) fsample=(k-3456)/dtime + call date_and_time(values=itime) + sec=itime(7)+0.001*itime(8) + write(ctime,3000) itime(5)-itime(4)/60,itime(6),sec +3000 format(i2.2,':',i2.2,':',f6.3) + write(33,3033) ctime,dtime,ihsym,nstop,k,fsample +3033 format(a12,f12.6,2i7,i10,f15.3) + flush(33) + endif + k0=k + + return +end subroutine chk_samples diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 678dfce87..9b1bd2eb7 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -178,6 +178,7 @@ extern "C" { void get_ft4msg_(int* idecode, char* line, int len); + void chk_samples_(int* m_ihsym,int* k, int* m_hsymStop); } int volatile itone[NUM_ISCAT_SYMBOLS]; //Audio tones for all Tx symbols @@ -1436,6 +1437,7 @@ void MainWindow::dataSink(qint64 frames) if(m_mode.startsWith("FST4")) npct=ui->sbNB->value(); symspec_(&dec_data,&k,&m_TRperiod,&nsps,&m_inGain,&bLowSidelobes,&nsmo,&m_px,s, &m_df3,&m_ihsym,&m_npts8,&m_pxmax,&npct); + chk_samples_(&m_ihsym,&k,&m_hsymStop); if(m_mode=="WSPR" or m_mode=="FST4W") wspr_downsample_(dec_data.d2,&k); if(m_ihsym <=0) return; if(ui) ui->signal_meter_widget->setValue(m_px,m_pxmax); // Update thermometer From 07f63cdb63da22b7b7de135a1d582b987ec71274 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 9 Aug 2020 15:48:56 -0400 Subject: [PATCH 042/426] Small adjustment to make file names for QRA66-30 modes correct. --- widgets/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 9b1bd2eb7..956fb7e9d 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3018,7 +3018,7 @@ void MainWindow::decode() //decode() if(m_TRperiod>=60) imin=imin - (imin % (int(m_TRperiod)/60)); dec_data.params.nutc=100*ihr + imin; if(m_TRperiod < 60) { - qint64 ms=1000.0*(2.0-m_TRperiod); + qint64 ms=1000.0*(3.5-m_TRperiod); if(m_mode=="FST4") ms=1000.0*(6.0-m_TRperiod); //Adjust for FT8 early decode: if(m_mode=="FT8" and m_ihsym==m_earlyDecode and !m_diskData) ms+=(m_hsymStop-m_earlyDecode)*288; From b6d14853c43b503aa562a85e344e6b8cbab19eea Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 10 Aug 2020 09:31:44 -0400 Subject: [PATCH 043/426] User Guide edits from Dave, KC3GPM. --- doc/user_guide/en/install-from-source.adoc | 6 +- doc/user_guide/en/install-linux.adoc | 18 +++--- doc/user_guide/en/install-mac.adoc | 8 +-- doc/user_guide/en/introduction.adoc | 10 ++-- doc/user_guide/en/logging.adoc | 24 ++++---- doc/user_guide/en/make-qso.adoc | 8 +-- doc/user_guide/en/measurement_tools.adoc | 66 +++++++++++----------- doc/user_guide/en/protocols.adoc | 21 ++++--- 8 files changed, 84 insertions(+), 77 deletions(-) diff --git a/doc/user_guide/en/install-from-source.adoc b/doc/user_guide/en/install-from-source.adoc index f106aa59b..b70e89c55 100644 --- a/doc/user_guide/en/install-from-source.adoc +++ b/doc/user_guide/en/install-from-source.adoc @@ -1,7 +1,7 @@ -// Status=review +// Status=edited Source code for _WSJT-X_ is available from a public repository at -{devrepo}. To compile the program you will need to install at least the +{devrepo}. To compile the program, at a minimum you must install the following packages: - Git @@ -19,7 +19,7 @@ cd wsjtx git checkout wsjtx-{VERSION} ===== -and for the current development branch, +and for the current development branch: ===== git clone git://git.code.sf.net/p/wsjt/wsjtx diff --git a/doc/user_guide/en/install-linux.adoc b/doc/user_guide/en/install-linux.adoc index b3075d66a..7c198ca84 100644 --- a/doc/user_guide/en/install-linux.adoc +++ b/doc/user_guide/en/install-linux.adoc @@ -1,16 +1,14 @@ -// Status=review +// Status=edited Debian, Ubuntu, and other Debian-based systems including Raspbian: -NOTE: The project team release binary installer packages for Linux -when a new _WSJT-X_ release is announced. These are built to -target one contemporary version of a Linux distribution. Although -these may work on newer Linux versions or even different -distributions, it is unlikely that they will work on older -versions. Check the notes provided with the release for details of the -targeted Linux distributions and versions. If the binary package is -not compatible with your Linux distribution or version you must build -the application from sources. +NOTE: The project team release binary installer packages targeted for +one contemporary version of a Linux distribution. Although these may +work on newer Linux versions or even different distributions, it is +unlikely that they work on older versions. Check the notes provided +with the release for details of the targeted Linux distributions and +versions. If the binary package is not compatible with your Linux +distribution or version, you must build the application from sources. * 32-bit: {debian32} - To install: diff --git a/doc/user_guide/en/install-mac.adoc b/doc/user_guide/en/install-mac.adoc index 26384805f..6595ba1c9 100644 --- a/doc/user_guide/en/install-mac.adoc +++ b/doc/user_guide/en/install-mac.adoc @@ -1,12 +1,12 @@ // These instructions are up-to-date for WSJT-X v2.2 -*OS X 10.12* and later: Download the file {osx} to your desktop, -double-click on it and consult its `ReadMe` file for important +*macOS10.13* and later: Download the file {osx} to your desktop, +double-click it and consult its `ReadMe` file for important installation notes. If you have already installed a previous version, you can retain it by -changing its name in the *Applications* folder (say, from _WSJT-X_ to -_WSJT-X_2.1_). You can then proceed to the installation phase. +changing its name in the *Applications* folder (such as from _WSJT-X_ to +_WSJT-X_2.2_). You can then proceed to the installation phase. Take note also of the following: diff --git a/doc/user_guide/en/introduction.adoc b/doc/user_guide/en/introduction.adoc index a592e5325..f172b5d35 100644 --- a/doc/user_guide/en/introduction.adoc +++ b/doc/user_guide/en/introduction.adoc @@ -4,7 +4,7 @@ _WSJT-X_ is a computer program designed to facilitate basic amateur radio communication using very weak signals. The first four letters in the program name stand for "`**W**eak **S**ignal communication by K1**JT**,`" while the suffix "`-X`" indicates that _WSJT-X_ started as -an e**Xt**ended and e**X**perimental branch of the program _WSJT_, +an extended and experimental branch of the program _WSJT_, first released in 2001. Bill Somerville, G4WJS, and Steve Franke, K9AN, have been major contributors to program development since 2013 and 2015, respectively. @@ -16,7 +16,7 @@ making reliable QSOs under weak-signal conditions. They use nearly identical message structure and source encoding. JT65 and QRA64 were designed for EME ("`moonbounce`") on the VHF/UHF bands and have also proven very effective for worldwide QRP communication on the HF bands. -QRA64 has a some advantages over JT65, including better performance +QRA64 has some advantages over JT65, including better performance for EME on the higher microwave bands. JT9 was originally designed for the LF, MF, and lower HF bands. Its submode JT9A is 2 dB more sensitive than JT65 while using less than 10% of the bandwidth. JT4 @@ -27,7 +27,7 @@ reception, so a minimal QSO takes four to six minutes — two or three transmissions by each station, one sending in odd UTC minutes and the other even. FT8 is operationally similar but four times faster (15-second T/R sequences) and less sensitive by a few dB. FT4 is -faster still (7.5 s T/R sequences) and especially well suited for +faster still (7.5 s T/R sequences) and especially well-suited for radio contesting. On the HF bands, world-wide QSOs are possible with any of these modes using power levels of a few watts (or even milliwatts) and compromise antennas. On VHF bands and higher, QSOs @@ -45,7 +45,7 @@ protocols designed to take advantage of brief signal enhancements from ionized meteor trails, aircraft scatter, and other types of scatter propagation. These modes use timed sequences of 5, 10, 15, or 30 s duration. User messages are transmitted repeatedly at high rate (up -to 250 characters per second, for MSK144) to make good use of the +to 250 characters per second for MSK144) to make good use of the shortest meteor-trail reflections or "`pings`". ISCAT uses free-form messages up to 28 characters long, while MSK144 uses the same structured messages as the slow modes and optionally an abbreviated @@ -80,4 +80,4 @@ be beta releases leading up to the final release of v2.1.0. Release candidates should be used _only_ during a short testing period. They carry an implied obligation to provide feedback to the program development group. Candidate releases should not be used on -the air after a full release with the same number has been made. +the air after a full release with the same number is made. diff --git a/doc/user_guide/en/logging.adoc b/doc/user_guide/en/logging.adoc index 4176a2a40..a3193ba25 100644 --- a/doc/user_guide/en/logging.adoc +++ b/doc/user_guide/en/logging.adoc @@ -1,7 +1,9 @@ +//status: edited + A basic logging facility in _WSJT-X_ saves QSO information to files named `wsjtx.log` (in comma-separated text format) and `wsjtx_log.adi` (in standard ADIF format). These files can be imported directly into -other programs, for example spreadsheets and popular logging programs. +other programs (such as spreadsheets and popular logging programs). As described in the <> and <> sections, different operating systems may place your local log files in different locations. You can always navigate to @@ -12,30 +14,32 @@ applications like {jtalert}, which can log QSOs automatically to other applications including {hrd}, {dxlsuite}, and {log4om}. The program option *Show DXCC entity and worked before status* -(selectable on the *Settings | General* tab) is intended mostly for +(selectable on the *File | Settings | General* tab) is intended mostly for use on non-Windows platforms, where {jtalert} is not available. When -this option is checked _WSJT-X_ appends some additional information to +this option is checked, _WSJT-X_ appends some additional information to all CQ messages displayed in the _Band Activity_ window. The name of the DXCC entity is shown, abbreviated if necessary. Your "`worked before`" status for this callsign (according to log file `wsjtx_log.adi`) is indicated by highlighting colors, if that option -has been selected. +is selected. _WSJT-X_ includes a built-in `cty.dat` file containing DXCC prefix information. Updated files can be downloaded from the {cty_dat} web -site when required. If an updated `cty.dat` is present in the logs -folder and readable, it will be used in preference to the built-in -one. +site when required. If an updated and readable `cty.dat` file is +present in the logs folder, it is used in preference to the +built-in file. The log file `wsjtx_log.adi` is updated whenever you log a QSO from -_WSJT-X_. (Keep in mind that if you erase this file you will lose all +_WSJT-X_. (Keep in mind that if you erase this file, you lose all "`worked before`" information.) You can append or overwrite the `wsjtx_log.adi` file by exporting your QSO history as an ADIF file from another logging program. Turning *Show DXCC entity and worked -before status* off and then on again will cause _WSJT-X_ to re-read +before status* off and then on again causes _WSJT-X_ to re-read the log file. Very large log files may cause _WSJT-X_ to slow down -when searching for calls. If the ADIF log file has been changed +when searching for calls. If the ADIF log file has been changed outside of _WSJT-X_ you can force _WSJT-X_ to reload the file from the *Settings | Colors* tab using the *Rescan ADIF Log* button, see <>. +Additional features are provided for *Contest* and *Fox* logging. +(more to come, here ...) diff --git a/doc/user_guide/en/make-qso.adoc b/doc/user_guide/en/make-qso.adoc index 03f02fd46..725bafe73 100644 --- a/doc/user_guide/en/make-qso.adoc +++ b/doc/user_guide/en/make-qso.adoc @@ -37,7 +37,7 @@ assigns more reliable numbers to relatively strong signals. NOTE: Signals become visible on the waterfall around S/N = –26 dB and audible (to someone with very good hearing) around –15 dB. Thresholds for decodability are around -20 dB for FT8, -23 dB for JT4, –25 dB for -JT65, –27 dB for JT9. +JT65, and –27 dB for JT9. NOTE: Several options are available for circumstances where fast QSOs are desirable. Double-click the *Tx1* control under _Now_ or _Next_ @@ -75,7 +75,7 @@ When calling CQ you may also choose to check the box *Call 1st*. _WSJT-X_ will then respond automatically to the first decoded responder to your CQ. -NOTE: When *Auto-Seq* is enabled the program de-activates *Enable Tx* +NOTE: When *Auto-Seq* is enabled, the program de-activates *Enable Tx* at the end of each QSO. It is not intended that _WSJT-X_ should make fully automated QSOs. @@ -259,7 +259,7 @@ that a second callsign is never permissible in these messages. NOTE: During a transmission your outgoing message is displayed in the first label on the *Status Bar* and shown exactly as another station -will receive it. You can check to see that you are actually +receives it. You can check to see that you are actually transmitting the message you wish to send. QSOs involving *Type 2* compound callsigns might look like either @@ -287,7 +287,7 @@ standard structured messages without callsign prefix or suffix. TIP: If you are using a compound callsign, you may want to experiment with the option *Message generation for type 2 compound -callsign holders* on the *Settings | General* tab, so that messages +callsign holders* on the *File | Settings | General* tab, so that messages will be generated that best suit your needs. === Pre-QSO Checklist diff --git a/doc/user_guide/en/measurement_tools.adoc b/doc/user_guide/en/measurement_tools.adoc index 766939e7c..fe501d650 100644 --- a/doc/user_guide/en/measurement_tools.adoc +++ b/doc/user_guide/en/measurement_tools.adoc @@ -1,6 +1,8 @@ +//Status: edited + === Frequency Calibration -Many _WSJT-X_ capabilities depend on signal-detection bandwidths no +Many _WSJT-X_ capabilities depend on signal-detection bandwidths of no more than a few Hz. Frequency accuracy and stability are therefore unusually important. We provide tools to enable accurate frequency calibration of your radio, as well as precise frequency measurement of @@ -11,11 +13,11 @@ measuring the error in dial frequency for each signal. You will probably find it convenient to define and use a special <> dedicated to frequency calibration. -Then complete the following steps, as appropriate for your system. +Then complete the following steps, as appropriate, for your system. - Switch to FreqCal mode -- In the _Working Frequencies_ box on the *Settings -> Frequencies* +- In the _Working Frequencies_ box on the *File | Settings | Frequencies* tab, delete any default frequencies for *FreqCal* mode that are not relevant for your location. You may want to replace some of them with reliably known frequencies receivable at your location. @@ -29,14 +31,14 @@ of WWV at 2.500, 5.000, 10.000, 15.000, and 20.000 MHz, and CHU at 3.330, 7.850, and 14.670 MHz. Similar shortwave signals are available in other parts of the world. -- In most cases you will want to start by deleting any existing file -`fmt.all` in the directory where your log files are kept. +- In most cases, start by deleting any existing file `fmt.all` in the +directory where your log files are kept. - To cycle automatically through your chosen list of calibration frequencies, check *Execute frequency calibration cycle* on the *Tools* menu. _WSJT-X_ will spend 30 seconds at each frequency. Initially no measurement data is saved to the `fmt.all` -file although it is displayed on screen, this allows you to check your +file although it is displayed on screen; this allows you to check your current calibration parameters. - During the calibration procedure, the radio's USB dial frequency is @@ -61,7 +63,7 @@ the nominal frequency itself (in MHz). For example, the 20 MHz measurement for WWV shown above produced a measured tone offset of 24.6 Hz, displayed in the _WSJT-X_ decoded text window. The resulting calibration constant is 24.6/20=1.23 parts per million. This number -may be entered as *Slope* on the *settings -> Frequencies* tab. +may be entered as *Slope* on the *File | Settings | Frequencies* tab. A more precise calibration can be effected by fitting the intercept and slope of a straight line to the whole sequence of calibration @@ -81,19 +83,19 @@ After running *Execute frequency calibration cycle* at least once with good results, check and edit the file `fmt.all` in the log directory and delete any spurious or outlier measurements. The line-fitting procedure can then be carried out automatically by clicking *Solve for -calibration parameters* on the *Tools* menu. The results will be +calibration parameters* on the *Tools* menu. The results are displayed as in the following screen shot. Estimated uncertainties are included for slope and intercept; `N` is the number of averaged frequency measurements included in the fit, and `StdDev` is the root mean square deviation of averaged measurements from the fitted -straight line. If the solution seems valid you will be offered an -*Apply* button to push that will automatically set the calibration -parameters in *Settings -> Frequencies -> Frequency Calibration*. +straight line. If the solution seems valid, you are offered an +*Apply* button to push that automatically sets the calibration +parameters in *File | Settings | Frequencies | Frequency Calibration*. image::FreqCal_Results.png[align="center",alt="FreqCal_Results"] For a quick visual check of the resulting calibration, stay in -*FreqCal* mode with the *Measure* option cleared. _WSJT-X_ will show +*FreqCal* mode with the *Measure* option cleared. _WSJT-X_ shows the adjusted results directly on the waterfall and the displayed records. @@ -103,8 +105,8 @@ _WSJT-X_ provides a tool that can be used to determine the detailed shape of your receiver's passband. Disconnect your antenna or tune to a quiet frequency with no signals. With _WSJT-X_ running in one of the slow modes, select *Measure reference spectrum* from the *Tools* -menu. Wait for about a minute and then hit the *Stop* button. A file -named `refspec.dat` will appear in your log directory. When you check +menu. Wait for about a minute and then click *Stop*. A file +named `refspec.dat` appears in your log directory. When you check *Ref Spec* on the *Wide Graph*, the recorded reference spectrum will then be used to flatten your overall effective passband. @@ -122,39 +124,39 @@ response* generates an undistorted audio waveform equal to the one generated by the transmitting station. Its Fourier transform is then used as a frequency-dependent phase reference to compare with the phase of the received frame's Fourier coefficients. Phase differences -between the reference spectrum and received spectrum will include +between the reference spectrum and received spectrum include contributions from the originating station's transmit filter, the propagation channel, and filters in the receiver. If the received frame originates from a station known to transmit signals having -little phase distortion (say, a station known to use a properly -adjusted software-defined-transceiver) and if the received signal is +little phase distortion (such as a station known to use a properly +adjusted software-defined transceiver), and if the received signal is relatively free from multipath distortion so that the channel phase is close to linear, the measured phase differences will be representative of the local receiver's phase response. Complete the following steps to generate a phase equalization curve: -- Record a number of wav files that contain decodable signals from -your chosen reference station. Best results will be obtained when the +- Record a number of `wav` files that contain decodable signals from +your chosen reference station. Best results are obtained when the signal-to-noise ratio of the reference signals is 10 dB or greater. - Enter the callsign of the reference station in the DX Call box. - Select *Measure phase response* from the *Tools* menu, and open each -of the wav files in turn. The mode character on decoded text lines -will change from `&` to `^` while _WSJT-X_ is measuring the phase -response, and it will change back to `&` after the measurement is +of the `wav` files in turn. The mode character on decoded text lines +changes from `&` to `^` while _WSJT-X_ is measuring the phase +response, and it changes back to `&` after the measurement is completed. The program needs to average a number of high-SNR frames to accurately estimate the phase, so it may be necessary to process -several wav files. The measurement can be aborted at any time by +several `wav` files. The measurement can be aborted at any time by selecting *Measure phase response* again to toggle the phase measurement off. + -When the measurement is complete _WSJT-X_ will save the measured +When the measurement is complete, _WSJT-X_ saves the measured phase response in the *Log directory*, in a file with suffix -".pcoeff". The filename will contain the callsign of the reference +".pcoeff". The filename contains the callsign of the reference station and a timestamp, for example `K0TPP_170923_112027.pcoeff`. - Select *Equalization tools ...* under the *Tools* menu and click the @@ -165,23 +167,23 @@ the proposed phase equalization curve. It's a good idea to repeat the phase measurement several times, using different wav files for each measurement, to ensure that your measurements are repeatable. -- Once you are satisfied with a fitted curve, push the *Apply* button -to save the proposed response. The red curve will be replaced with a +- Once you are satisfied with a fitted curve, click the *Apply* button +to save the proposed response. The red curve is replaced with a light green curve labeled "Current" to indicate that the phase equalization curve is now being applied to the received data. Another -curve labeled "Group Delay" will appear. The "Group Delay" curve shows +curve labeled "Group Delay" appears. The "Group Delay" curve shows the group delay variation across the passband, in ms. Click the *Discard Measured* button to remove the captured data from the plot, leaving only the applied phase equalization curve and corresponding group delay curve. -- To revert to no phase equalization, push the *Restore Defaults* +- To revert to no phase equalization, click the *Restore Defaults* button followed by the *Apply* button. The three numbers printed at the end of each MSK144 decode line can be used to assess the improvement provided by equalization. These numbers are: `N` = Number of frames averaged, `H` = Number of hard bit errors -corrected, `E` = Size of MSK eye diagram opening. +corrected, and `E` = Size of MSK eye diagram opening. Here is a decode of K0TPP obtained while *Measure phase response* was measuring the phase response: @@ -196,7 +198,7 @@ scale. Here's how the same decode looks after phase equalization: 103900 17 6.5 1493 & WA8CLT K0TPP +07 1 0 1.6 -In this case, equalization has increased the eye opening from 1.2 to +In this case, equalization has increased the eye-opening from 1.2 to 1.6. Larger positive eye openings are associated with reduced likelihood of bit errors and higher likelihood that a frame will be successfully decoded. In this case, the larger eye-opening tells us @@ -206,7 +208,7 @@ equalization curve is going to improve decoding of signals other than those from the reference station, K0TPP. It's a good idea to carry out before and after comparisons using a -large number of saved wav files with signals from many different +large number of saved `wav` files with signals from many different stations, to help decide whether your equalization curve improves decoding for most signals. When doing such comparisons, keep in mind that equalization may cause _WSJT-X_ to successfully decode a frame diff --git a/doc/user_guide/en/protocols.adoc b/doc/user_guide/en/protocols.adoc index d9a7d8ada..e22911266 100644 --- a/doc/user_guide/en/protocols.adoc +++ b/doc/user_guide/en/protocols.adoc @@ -1,3 +1,5 @@ +//status: edited + [[PROTOCOL_OVERVIEW]] === Overview @@ -30,17 +32,17 @@ of 4-digit Maidenhead grid locators on earth is 180×180 = 32,400, which is less than 2^15^ = 32,768; so a grid locator requires 15 bits. Some 6 million of the possible 28-bit values are not needed for -callsigns. A few of these slots have been assigned to special message +callsigns. A few of these slots are assigned to special message components such as `CQ`, `DE`, and `QRZ`. `CQ` may be followed by three digits to indicate a desired callback frequency. (If K1ABC transmits -on a standard calling frequency, say 50.280, and sends `CQ 290 K1ABC +on a standard calling frequency such as 50.280, and sends `CQ 290 K1ABC FN42`, it means that s/he will listen on 50.290 and respond there to any replies.) A numerical signal report of the form `–nn` or `R–nn` can be sent in place of a grid locator. (As originally defined, numerical signal reports `nn` were required to fall between -01 -and -30 dB. Program versions 2.3 and later accommodate reports between --50 and +50 dB.) A country prefix or portable suffix may be -attached to one of the callsigns. When this feature is used the +and -30 dB. Recent program versions 2.3 and later accommodate reports between +-50 and +49 dB.) A country prefix or portable suffix may be +attached to one of the callsigns. When this feature is used, the additional information is sent in place of the grid locator or by encoding additional information into some of the 6 million available slots mentioned above. @@ -147,7 +149,8 @@ following pseudo-random sequence: The synchronizing tone is normally sent in each interval having a "`1`" in the sequence. Modulation is 65-FSK at 11025/4096 = 2.692 baud. Frequency spacing between tones is equal to the keying rate for -JT65A, and 2 and 4 times larger for JT65B and JT65C. For EME QSOs the +JT65A, and 2 and 4 times larger for JT65B and JT65C, respectively. +For EME QSOs the signal report OOO is sometimes used instead of numerical signal reports. It is conveyed by reversing sync and data positions in the transmitted sequence. Shorthand messages for RO, RRR, and 73 dispense @@ -155,7 +158,7 @@ with the sync vector entirely and use time intervals of 16384/11025 = 1.486 s for pairs of alternating tones. The lower frequency is the same as that of the sync tone used in long messages, and the frequency separation is 110250/4096 = 26.92 Hz multiplied by n for JT65A, with n -= 2, 3, 4 used to convey the messages RO, RRR, and 73. += 2, 3, 4 used to convey the messages RO, RRR, and 73, respectively. [[QRA64_PROTOCOL]] ==== QRA64 @@ -225,7 +228,7 @@ the sync bit. [[SLOW_SUMMARY]] ==== Summary -Table 7 provides a brief summary parameters for the slow modes in +Table 7 provides a brief summary of parameters for the slow modes in _WSJT-X_. Parameters K and r specify the constraint length and rate of the convolutional codes; n and k specify the sizes of the (equivalent) block codes; Q is the alphabet size for the @@ -305,7 +308,7 @@ available character set is: Transmissions consist of sequences of 24 symbols: a synchronizing pattern of four symbols at tone numbers 0, 1, 3, and 2, followed by two symbols with tone number corresponding to (message length) and -(message length + 5), and finally 18 symbols conveying the user's +(message length + 5), and, finally, 18 symbols conveying the user's message, sent repeatedly character by character. The message always starts with `@`, the beginning-of-message symbol, which is not displayed to the user. The sync pattern and message-length indicator From 683cd081032b2a5dd3b0f9e8495df538b971b464 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Mon, 17 Aug 2020 14:12:08 -0500 Subject: [PATCH 044/426] Add timer call for bit metric calculation. Improve some comments. Make fort.21 ntype parameter more informative. --- lib/fst4/decode240_101.f90 | 2 +- lib/fst4/decode240_74.f90 | 2 +- lib/fst4/get_fst4_bitmetrics.f90 | 2 +- lib/fst4_decode.f90 | 8 +++++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/fst4/decode240_101.f90 b/lib/fst4/decode240_101.f90 index 80e42eeb0..e924f00ec 100644 --- a/lib/fst4/decode240_101.f90 +++ b/lib/fst4/decode240_101.f90 @@ -141,7 +141,7 @@ subroutine decode240_101(llr,Keff,maxosd,norder,apmask,message101,cw,ntype,nhard where(llr .ge. 0) hdec=1 nxor=ieor(hdec,cw) dmin=sum(nxor*abs(llr)) - ntype=2 + ntype=1+nosd return endif enddo diff --git a/lib/fst4/decode240_74.f90 b/lib/fst4/decode240_74.f90 index 3f8a6a952..224a2860d 100644 --- a/lib/fst4/decode240_74.f90 +++ b/lib/fst4/decode240_74.f90 @@ -141,7 +141,7 @@ subroutine decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw,ntype,nharder where(llr .ge. 0) hdec=1 nxor=ieor(hdec,cw) dmin=sum(nxor*abs(llr)) - ntype=2 + ntype=1+nosd return endif enddo diff --git a/lib/fst4/get_fst4_bitmetrics.f90 b/lib/fst4/get_fst4_bitmetrics.f90 index 9cf1e2470..f248171f0 100644 --- a/lib/fst4/get_fst4_bitmetrics.f90 +++ b/lib/fst4/get_fst4_bitmetrics.f90 @@ -84,7 +84,7 @@ subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,badsync) endif bitmetrics=0.0 - do nseq=1,nmax !Try coherent sequences of 1, 2, and 4 symbols + do nseq=1,nmax !Try coherent sequences of 1,2,3,4 or 1,2,4,8 symbols if(nseq.eq.1) nsym=1 if(nseq.eq.2) nsym=2 if(nhicoh.eq.0) then diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index e56fa27a8..9a6683fe4 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -395,11 +395,13 @@ contains if(is0.lt.0) cycle cframe=c2(is0:is0+160*nss-1) bitmetrics=0 + call timer('bitmetrc',0) if(hmod.eq.1) then call get_fst4_bitmetrics(cframe,nss,hmod,nblock,nhicoh,bitmetrics,s4,badsync) else call get_fst4_bitmetrics2(cframe,nss,hmod,nblock,bitmetrics,s4,badsync) endif + call timer('bitmetrc',1) if(badsync) cycle hbits=0 @@ -410,7 +412,8 @@ contains ns4=count(hbits(229:244).eq.(/1,1,1,0,0,1,0,0,1,0,1,1,0,0,0,1/)) ns5=count(hbits(305:320).eq.(/0,0,0,1,1,0,1,1,0,1,0,0,1,1,1,0/)) nsync_qual=ns1+ns2+ns3+ns4+ns5 - if(nsync_qual.lt. 46) cycle !### Value ?? ### + + if(nsync_qual.lt. 46) cycle !### Value ?? ### scalefac=2.83 llra( 1: 60)=bitmetrics( 17: 76, 1) llra( 61:120)=bitmetrics( 93:152, 1) @@ -768,7 +771,7 @@ contains nnw=nint(48000.*nsps*2./fs) allocate (s(nnw)) - s=0. !Compute low-resloution power spectrum + s=0. !Compute low-resolution power spectrum do i=ina,inb ! noise analysis window includes signal analysis window j0=nint(i*df2/df1) do j=j0-ndh,j0+ndh @@ -785,7 +788,6 @@ contains enddo call pctile(s2(ina+hmod*3:inb-hmod*3),inb-ina+1-hmod*6,30,base) s2=s2/base !Normalize wrt noise level - ncand=0 candidates=0 if(ia.lt.3) ia=3 From 07486ee0c7864b485539aee94d495e9f75244ebf Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Wed, 19 Aug 2020 09:20:48 -0500 Subject: [PATCH 045/426] Simplify some code in fst4_decode.f90 - no functional change. --- lib/fst4_decode.f90 | 49 +++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index 9a6683fe4..6f3e36105 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -48,7 +48,7 @@ contains complex, allocatable :: c2(:) complex, allocatable :: cframe(:) complex, allocatable :: c_bigfft(:) !Complex waveform - real llr(240),llra(240),llrb(240),llrc(240),llrd(240) + real llr(240),llrs(240,4) real candidates(200,4) real bitmetrics(320,4) real s4(0:3,NN) @@ -415,28 +415,15 @@ contains if(nsync_qual.lt. 46) cycle !### Value ?? ### scalefac=2.83 - llra( 1: 60)=bitmetrics( 17: 76, 1) - llra( 61:120)=bitmetrics( 93:152, 1) - llra(121:180)=bitmetrics(169:228, 1) - llra(181:240)=bitmetrics(245:304, 1) - llra=scalefac*llra - llrb( 1: 60)=bitmetrics( 17: 76, 2) - llrb( 61:120)=bitmetrics( 93:152, 2) - llrb(121:180)=bitmetrics(169:228, 2) - llrb(181:240)=bitmetrics(245:304, 2) - llrb=scalefac*llrb - llrc( 1: 60)=bitmetrics( 17: 76, 3) - llrc( 61:120)=bitmetrics( 93:152, 3) - llrc(121:180)=bitmetrics(169:228, 3) - llrc(181:240)=bitmetrics(245:304, 3) - llrc=scalefac*llrc - llrd( 1: 60)=bitmetrics( 17: 76, 4) - llrd( 61:120)=bitmetrics( 93:152, 4) - llrd(121:180)=bitmetrics(169:228, 4) - llrd(181:240)=bitmetrics(245:304, 4) - llrd=scalefac*llrd + do il=1,4 + llrs( 1: 60,il)=bitmetrics( 17: 76, il) + llrs( 61:120,il)=bitmetrics( 93:152, il) + llrs(121:180,il)=bitmetrics(169:228, il) + llrs(181:240,il)=bitmetrics(245:304, il) + enddo + llrs=scalefac*llrs - apmag=maxval(abs(llra))*1.1 + apmag=maxval(abs(llrs(:,1)))*1.1 ntmax=nblock+nappasses(nQSOProgress) if(lapcqonly) ntmax=nblock+1 if(ndepth.eq.1) ntmax=nblock @@ -448,22 +435,22 @@ contains endif do itry=1,ntmax - if(itry.eq.1) llr=llra - if(itry.eq.2.and.itry.le.nblock) llr=llrb - if(itry.eq.3.and.itry.le.nblock) llr=llrc - if(itry.eq.4.and.itry.le.nblock) llr=llrd + if(itry.eq.1) llr=llrs(:,1) + if(itry.eq.2.and.itry.le.nblock) llr=llrs(:,2) + if(itry.eq.3.and.itry.le.nblock) llr=llrs(:,3) + if(itry.eq.4.and.itry.le.nblock) llr=llrs(:,4) if(itry.le.nblock) then apmask=0 iaptype=0 endif if(itry.gt.nblock) then - llr=llra + llr=llrs(:,1) if(nblock.gt.1) then - if(hmod.eq.1) llr=llrc - if(hmod.eq.2) llr=llra - if(hmod.eq.4) llr=llrb - if(hmod.eq.8) llr=llrc + if(hmod.eq.1) llr=llrs(:,3) + if(hmod.eq.2) llr=llrs(:,1) + if(hmod.eq.4) llr=llrs(:,2) + if(hmod.eq.8) llr=llrs(:,4) endif iaptype=naptypes(nQSOProgress,itry-nblock) if(lapcqonly) iaptype=1 From e28215fce54c7ff50976b40c44109a707fcb8bc1 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Wed, 19 Aug 2020 14:10:28 -0500 Subject: [PATCH 046/426] Reconfigure to optimize decoder for MF/LF (high coherence) channels. --- lib/fst4/get_fst4_bitmetrics.f90 | 2 +- lib/fst4_decode.f90 | 22 +++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/lib/fst4/get_fst4_bitmetrics.f90 b/lib/fst4/get_fst4_bitmetrics.f90 index f248171f0..f224854e2 100644 --- a/lib/fst4/get_fst4_bitmetrics.f90 +++ b/lib/fst4/get_fst4_bitmetrics.f90 @@ -105,7 +105,7 @@ subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,badsync) csum=csum+cs(graymap(ntone),ks+j)*cterm cterm=cterm*conjg(cp(graymap(ntone))) enddo - s2(i)=abs(csum)**2 + s2(i)=abs(csum) enddo ipt=1+(ks-1)*2 if(nsym.eq.1) ibmax=1 diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index 6f3e36105..448e699c3 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -275,12 +275,8 @@ contains fb=min(4800,nfb) endif - if(hmod.eq.1) then - if(ntrperiod.eq.15) minsync=1.15 - if(ntrperiod.gt.15) minsync=1.25 - elseif(hmod.gt.1) then - minsync=1.2 - endif + minsync=1.2 + if(ntrperiod.eq.15) minsync=1.15 ! Get first approximation of candidate frequencies call get_candidates_fst4(c_bigfft,nfft1,nsps,hmod,fs,fa,fb, & @@ -788,13 +784,13 @@ contains iploc=ia+im(1)-1 !Index of CCF peak pval=s2(iploc) !Peak value if(pval.lt.minsync) exit -! do i=-3,+3 !Remove 0.9 of a model CCF at -! k=iploc+2*hmod*i !this frequency from s2() -! if(k.ge.ia .and. k.le.ib) then -! s2(k)=max(0.,s2(k)-0.9*pval*xdb(i)) -! endif -! enddo - s2(max(1,iploc-2*hmod*3):min(nnw,iploc+2*hmod*3))=0.0 + do i=-3,+3 !Remove 0.9 of a model CCF at + k=iploc+2*hmod*i !this frequency from s2() + if(k.ge.ia .and. k.le.ib) then + s2(k)=max(0.,s2(k)-0.9*pval*xdb(i)) + endif + enddo +! s2(max(1,iploc-2*hmod*3):min(nnw,iploc+2*hmod*3))=0.0 ncand=ncand+1 candidates(ncand,1)=df2*iploc !Candidate frequency candidates(ncand,2)=pval !Rough estimate of SNR From 0e0349e87aec6a4e34455456381264bbe53f034a Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Thu, 20 Aug 2020 09:48:32 -0500 Subject: [PATCH 047/426] Streamline fst4_decode. Add timer for downsampling. --- lib/decoder.f90 | 4 +- lib/fst4/get_fst4_bitmetrics.f90 | 19 ++++ lib/fst4_decode.f90 | 162 +++++++++++++------------------ 3 files changed, 88 insertions(+), 97 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 925dc0d43..c339ab523 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -213,7 +213,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) params%nQSOProgress,params%nfqso,params%nfa,params%nfb, & params%nsubmode,ndepth,params%ntr,params%nexp_decode, & params%ntol,params%emedelay, & - logical(params%lapcqonly),mycall,hiscall,params%nfsplit,iwspr) + logical(params%lapcqonly),mycall,hiscall,iwspr) call timer('dec240 ',1) go to 800 endif @@ -227,7 +227,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) params%nQSOProgress,params%nfqso,params%nfa,params%nfb, & params%nsubmode,ndepth,params%ntr,params%nexp_decode, & params%ntol,params%emedelay, & - logical(params%lapcqonly),mycall,hiscall,params%nfsplit,iwspr) + logical(params%lapcqonly),mycall,hiscall,iwspr) call timer('dec240 ',1) go to 800 endif diff --git a/lib/fst4/get_fst4_bitmetrics.f90 b/lib/fst4/get_fst4_bitmetrics.f90 index f224854e2..f34cb9322 100644 --- a/lib/fst4/get_fst4_bitmetrics.f90 +++ b/lib/fst4/get_fst4_bitmetrics.f90 @@ -11,6 +11,7 @@ subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,badsync) integer graymap(0:3) integer ip(1) integer hmod + integer hbits(2*NN) logical one(0:65535,0:15) ! 65536 8-symbol sequences, 16 bits logical first logical badsync @@ -122,10 +123,28 @@ subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,badsync) enddo enddo + hbits=0 + where(bitmetrics(:,1).ge.0) hbits=1 + ns1=count(hbits( 1: 16).eq.(/0,0,0,1,1,0,1,1,0,1,0,0,1,1,1,0/)) + ns2=count(hbits( 77: 92).eq.(/1,1,1,0,0,1,0,0,1,0,1,1,0,0,0,1/)) + ns3=count(hbits(153:168).eq.(/0,0,0,1,1,0,1,1,0,1,0,0,1,1,1,0/)) + ns4=count(hbits(229:244).eq.(/1,1,1,0,0,1,0,0,1,0,1,1,0,0,0,1/)) + ns5=count(hbits(305:320).eq.(/0,0,0,1,1,0,1,1,0,1,0,0,1,1,1,0/)) + nsync_qual=ns1+ns2+ns3+ns4+ns5 + + if(nsync_qual.lt. 46) then + badsync=.true. + return + endif + call normalizebmet(bitmetrics(:,1),2*NN) call normalizebmet(bitmetrics(:,2),2*NN) call normalizebmet(bitmetrics(:,3),2*NN) call normalizebmet(bitmetrics(:,4),2*NN) + + scalefac=2.83 + bitmetrics=scalefac*bitmetrics + return end subroutine get_fst4_bitmetrics diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index 448e699c3..8a116bbbf 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -31,7 +31,7 @@ contains subroutine decode(this,callback,iwave,nutc,nQSOProgress,nfqso, & nfa,nfb,nsubmode,ndepth,ntrperiod,nexp_decode,ntol, & - emedelay,lapcqonly,mycall,hiscall,nfsplit,iwspr) + emedelay,lapcqonly,mycall,hiscall,iwspr) use timer_module, only: timer use packjt77 @@ -252,29 +252,10 @@ contains call four2a(c_bigfft,nfft1,1,-1,0) !r2c ! call blank2(nfa,nfb,nfft1,c_bigfft,iwave) - nhicoh=0 - if(hmod.eq.1) then - if(fMHz.lt.2.0) then - nsyncoh=8 ! Use N=8 for sync - nhicoh=1 ! Use N=1,2,4,8 for symbol estimation - else - nsyncoh=4 ! Use N=4 for sync - nhicoh=0 ! Use N=1,2,3,4 for symbol estimation - endif - else - if(hmod.eq.2) nsyncoh=1 - if(hmod.eq.4) nsyncoh=-2 - if(hmod.eq.8) nsyncoh=-4 - endif - - if( single_decode ) then - fa=max(100,nint(nfqso+1.5*hmod*baud-ntol)) - fb=min(4800,nint(nfqso+1.5*hmod*baud+ntol)) - else - fa=max(100,nfa) - fb=min(4800,nfb) - endif - + nhicoh=1 + nsyncoh=8 + fa=max(100,nint(nfqso+1.5*hmod*baud-ntol)) + fb=min(4800,nint(nfqso+1.5*hmod*baud+ntol)) minsync=1.2 if(ntrperiod.eq.15) minsync=1.15 @@ -296,54 +277,15 @@ contains ! Output array c2 is complex baseband sampled at 12000/ndown Sa/sec. ! The size of the downsampled c2 array is nfft2=nfft1/ndown + call timer('dwnsmpl ',0) call fst4_downsample(c_bigfft,nfft1,ndown,fc0,sigbw,c2) + call timer('dwnsmpl ',1) call timer('sync240 ',0) - fc1=0.0 - if(emedelay.lt.0.1) then ! search offsets from 0 s to 2 s - is0=1.5*nspsec - ishw=1.5*nspsec - else ! search plus or minus 1.5 s centered on emedelay - is0=nint((emedelay+1.0)*nspsec) - ishw=1.5*nspsec - endif - - smax=-1.e30 - do if=-12,12 - fc=fc1 + 0.1*baud*if - do istart=max(1,is0-ishw),is0+ishw,4*hmod - call sync_fst4(c2,istart,fc,hmod,nsyncoh,nfft2,nss, & - ntrperiod,fs2,sync) - if(sync.gt.smax) then - fc2=fc - isbest=istart - smax=sync - endif - enddo - enddo - - fc1=fc2 - is0=isbest - ishw=4*hmod - isst=1*hmod - - smax=0.0 - do if=-7,7 - fc=fc1 + 0.02*baud*if - do istart=max(1,is0-ishw),is0+ishw,isst - call sync_fst4(c2,istart,fc,hmod,nsyncoh,nfft2,nss, & - ntrperiod,fs2,sync) - if(sync.gt.smax) then - fc2=fc - isbest=istart - smax=sync - endif - enddo - enddo - + call fst4_sync_search(c2,nfft2,hmod,fs2,nss,ntrperiod,nsyncoh,emedelay,sbest,fcbest,isbest) call timer('sync240 ',1) - fc_synced = fc0 + fc2 + fc_synced = fc0 + fcbest dt_synced = (isbest-fs2)*dt2 !nominal dt is 1 second so frame starts at sample fs2 candidates(icand,3)=fc_synced candidates(icand,4)=isbest @@ -382,7 +324,11 @@ contains isbest=nint(candidates(icand,4)) xdt=(isbest-nspsec)/fs2 if(ntrperiod.eq.15) xdt=(isbest-real(nspsec)/2.0)/fs2 + + call timer('dwnsmpl ',0) call fst4_downsample(c_bigfft,nfft1,ndown,fc_synced,sigbw,c2) + call timer('dwnsmpl ',1) + do ijitter=0,jittermax if(ijitter.eq.0) ioffset=0 if(ijitter.eq.1) ioffset=1 @@ -392,32 +338,16 @@ contains cframe=c2(is0:is0+160*nss-1) bitmetrics=0 call timer('bitmetrc',0) - if(hmod.eq.1) then - call get_fst4_bitmetrics(cframe,nss,hmod,nblock,nhicoh,bitmetrics,s4,badsync) - else - call get_fst4_bitmetrics2(cframe,nss,hmod,nblock,bitmetrics,s4,badsync) - endif + call get_fst4_bitmetrics(cframe,nss,hmod,nblock,nhicoh,bitmetrics,s4,badsync) call timer('bitmetrc',1) if(badsync) cycle - hbits=0 - where(bitmetrics(:,1).ge.0) hbits=1 - ns1=count(hbits( 1: 16).eq.(/0,0,0,1,1,0,1,1,0,1,0,0,1,1,1,0/)) - ns2=count(hbits( 77: 92).eq.(/1,1,1,0,0,1,0,0,1,0,1,1,0,0,0,1/)) - ns3=count(hbits(153:168).eq.(/0,0,0,1,1,0,1,1,0,1,0,0,1,1,1,0/)) - ns4=count(hbits(229:244).eq.(/1,1,1,0,0,1,0,0,1,0,1,1,0,0,0,1/)) - ns5=count(hbits(305:320).eq.(/0,0,0,1,1,0,1,1,0,1,0,0,1,1,1,0/)) - nsync_qual=ns1+ns2+ns3+ns4+ns5 - - if(nsync_qual.lt. 46) cycle !### Value ?? ### - scalefac=2.83 do il=1,4 llrs( 1: 60,il)=bitmetrics( 17: 76, il) llrs( 61:120,il)=bitmetrics( 93:152, il) llrs(121:180,il)=bitmetrics(169:228, il) llrs(181:240,il)=bitmetrics(245:304, il) enddo - llrs=scalefac*llrs apmag=maxval(abs(llrs(:,1)))*1.1 ntmax=nblock+nappasses(nQSOProgress) @@ -440,14 +370,8 @@ contains iaptype=0 endif - if(itry.gt.nblock) then - llr=llrs(:,1) - if(nblock.gt.1) then - if(hmod.eq.1) llr=llrs(:,3) - if(hmod.eq.2) llr=llrs(:,1) - if(hmod.eq.4) llr=llrs(:,2) - if(hmod.eq.8) llr=llrs(:,4) - endif + if(itry.gt.nblock) then ! do ap passes + llr=llrs(:,nblock) ! Use largest blocksize as the basis for AP passes iaptype=naptypes(nQSOProgress,itry-nblock) if(lapcqonly) iaptype=1 if(iaptype.ge.2 .and. apbits(1).gt.1) cycle ! No, or nonstandard, mycall @@ -486,7 +410,7 @@ contains if(iwspr.eq.0) then maxosd=2 Keff=91 - norder=3 + norder=4 call timer('d240_101',0) call decode240_101(llr,Keff,maxosd,norder,apmask,message101, & cw,ntype,nharderrors,dmin) @@ -556,8 +480,8 @@ contains fsig=fc_synced - 1.5*hmod*baud if(ex) then write(21,3021) nutc,icand,itry,nsyncoh,iaptype, & - ijitter,ntype,nsync_qual,nharderrors,dmin, & - sync,xsnr,xdt,fsig,w50,trim(msg) + ijitter,ntype,nsync_qual,nharderrors,dmin, & + sync,xsnr,xdt,fsig,w50,trim(msg) 3021 format(i6.6,6i3,2i4,f6.1,f7.2,f6.1,f6.2,f7.1,f7.3,1x,a) flush(21) endif @@ -799,6 +723,54 @@ contains return end subroutine get_candidates_fst4 + subroutine fst4_sync_search(c2,nfft2,hmod,fs2,nss,ntrperiod,nsyncoh,emedelay,sbest,fcbest,isbest) + complex c2(0:nfft2-1) + integer hmod + nspsec=int(fs2) + baud=fs2/real(nss) + fc1=0.0 + if(emedelay.lt.0.1) then ! search offsets from 0 s to 2 s + is0=1.5*nspsec + ishw=1.5*nspsec + else ! search plus or minus 1.5 s centered on emedelay + is0=nint((emedelay+1.0)*nspsec) + ishw=1.5*nspsec + endif + + sbest=-1.e30 + do if=-12,12 + fc=fc1 + 0.1*baud*if + do istart=max(1,is0-ishw),is0+ishw,4*hmod + call sync_fst4(c2,istart,fc,hmod,nsyncoh,nfft2,nss, & + ntrperiod,fs2,sync) + if(sync.gt.sbest) then + fcbest=fc + isbest=istart + sbest=sync + endif + enddo + enddo + + fc1=fcbest + is0=isbest + ishw=4*hmod + isst=1*hmod + + sbest=0.0 + do if=-7,7 + fc=fc1 + 0.02*baud*if + do istart=max(1,is0-ishw),is0+ishw,isst + call sync_fst4(c2,istart,fc,hmod,nsyncoh,nfft2,nss, & + ntrperiod,fs2,sync) + if(sync.gt.sbest) then + fcbest=fc + isbest=istart + sbest=sync + endif + enddo + enddo + end subroutine fst4_sync_search + subroutine dopspread(itone,iwave,nsps,nmax,ndown,hmod,i0,fc,fmid,w50) ! On "plotspec" special request, compute Doppler spread for a decoded signal From 091d6d0b1a3bf53394538c681d59b68b8a93c953 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Fri, 21 Aug 2020 09:18:59 -0500 Subject: [PATCH 048/426] Tweaks to update the diagnostics that are written to fort.21. --- lib/fst4/decode240_101.f90 | 3 ++- lib/fst4/decode240_74.f90 | 3 ++- lib/fst4/get_fst4_bitmetrics.f90 | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/fst4/decode240_101.f90 b/lib/fst4/decode240_101.f90 index e924f00ec..4271a464c 100644 --- a/lib/fst4/decode240_101.f90 +++ b/lib/fst4/decode240_101.f90 @@ -140,8 +140,9 @@ subroutine decode240_101(llr,Keff,maxosd,norder,apmask,message101,cw,ntype,nhard hdec=0 where(llr .ge. 0) hdec=1 nxor=ieor(hdec,cw) + nharderror=sum(nxor) ! re-calculate nharderror based on input llrs dmin=sum(nxor*abs(llr)) - ntype=1+nosd + ntype=1+i return endif enddo diff --git a/lib/fst4/decode240_74.f90 b/lib/fst4/decode240_74.f90 index 224a2860d..be18f6e09 100644 --- a/lib/fst4/decode240_74.f90 +++ b/lib/fst4/decode240_74.f90 @@ -140,8 +140,9 @@ subroutine decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw,ntype,nharder hdec=0 where(llr .ge. 0) hdec=1 nxor=ieor(hdec,cw) + nharderror=sum(nxor) ! nharderror based on input llrs dmin=sum(nxor*abs(llr)) - ntype=1+nosd + ntype=1+i return endif enddo diff --git a/lib/fst4/get_fst4_bitmetrics.f90 b/lib/fst4/get_fst4_bitmetrics.f90 index f34cb9322..e245db18c 100644 --- a/lib/fst4/get_fst4_bitmetrics.f90 +++ b/lib/fst4/get_fst4_bitmetrics.f90 @@ -1,4 +1,4 @@ -subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,badsync) +subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,nsync_qual,badsync) include 'fst4_params.f90' complex cd(0:NN*nss-1) From e3171d2142740850b85dc88736ff559b0b44d7e4 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Sat, 22 Aug 2020 09:42:34 -0500 Subject: [PATCH 049/426] Fix argument list in call to fet_fst4_bitmetrics.f90 --- lib/fst4_decode.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index 8a116bbbf..444553618 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -338,7 +338,8 @@ contains cframe=c2(is0:is0+160*nss-1) bitmetrics=0 call timer('bitmetrc',0) - call get_fst4_bitmetrics(cframe,nss,hmod,nblock,nhicoh,bitmetrics,s4,badsync) + call get_fst4_bitmetrics(cframe,nss,hmod,nblock,nhicoh,bitmetrics, & + s4,nsync_qual,badsync) call timer('bitmetrc',1) if(badsync) cycle From ad0540a02785c8dfe223a7d299e5b400a36ae785 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Mon, 24 Aug 2020 10:17:45 -0500 Subject: [PATCH 050/426] Speed up decoder by eliminating some complex multiples in sequence detection loop. Add timer calls for doppler spread calculation and sequence detection loop. --- lib/fst4/get_fst4_bitmetrics.f90 | 13 ++++++++++--- lib/fst4_decode.f90 | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/fst4/get_fst4_bitmetrics.f90 b/lib/fst4/get_fst4_bitmetrics.f90 index e245db18c..69a649a04 100644 --- a/lib/fst4/get_fst4_bitmetrics.f90 +++ b/lib/fst4/get_fst4_bitmetrics.f90 @@ -1,5 +1,6 @@ subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,nsync_qual,badsync) + use timer_module, only: timer include 'fst4_params.f90' complex cd(0:NN*nss-1) complex cs(0:3,NN) @@ -84,6 +85,8 @@ subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,nsync_qual, return endif + + call timer('seqcorrs',0) bitmetrics=0.0 do nseq=1,nmax !Try coherent sequences of 1,2,3,4 or 1,2,4,8 symbols if(nseq.eq.1) nsym=1 @@ -100,11 +103,14 @@ subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,nsync_qual, s2=0 do i=0,nt-1 csum=0 - cterm=1 +! cterm=1 ! hmod.ne.1 + term=1 do j=0,nsym-1 ntone=mod(i/4**(nsym-1-j),4) - csum=csum+cs(graymap(ntone),ks+j)*cterm - cterm=cterm*conjg(cp(graymap(ntone))) + csum=csum+cs(graymap(ntone),ks+j)*term + term=-term +! csum=csum+cs(graymap(ntone),ks+j)*cterm ! hmod.ne.1 +! cterm=cterm*conjg(cp(graymap(ntone))) ! hmod.ne.1 enddo s2(i)=abs(csum) enddo @@ -122,6 +128,7 @@ subroutine get_fst4_bitmetrics(cd,nss,hmod,nmax,nhicoh,bitmetrics,s4,nsync_qual, enddo enddo enddo + call timer('seqcorrs',1) hbits=0 where(bitmetrics(:,1).ge.0) hbits=1 diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index 444553618..efe6a3798 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -455,10 +455,12 @@ contains endif inquire(file='plotspec',exist=ex) fmid=-999.0 + call timer('dopsprd ',0) if(ex) then call dopspread(itone,iwave,nsps,nmax,ndown,hmod, & isbest,fc_synced,fmid,w50) endif + call timer('dopsprd ',1) xsig=0 do i=1,NN xsig=xsig+s4(itone(i),i) From e232c32178e8310e4ed10d6d5c144d1069ba4e85 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Fri, 28 Aug 2020 09:22:22 -0500 Subject: [PATCH 051/426] Use 3rd order polynomial fit to estimate the noise baseline. The polynomial fit is done over 400 Hz bandwidth for T/R periods longer than 15s, and over approx. 600 Hz (10 times the signal bandwidth) for T/R period of 15s. --- CMakeLists.txt | 1 + lib/fst4/fst4_baseline.f90 | 48 ++++++++++++++++++++++++++++++++++++++ lib/fst4_decode.f90 | 48 +++++++++++++++++++++++--------------- 3 files changed, 78 insertions(+), 19 deletions(-) create mode 100644 lib/fst4/fst4_baseline.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 403774100..8080d98b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -621,6 +621,7 @@ set (wsjt_FSRCS lib/fst4/osd240_101.f90 lib/fst4/osd240_74.f90 lib/fst4/get_crc24.f90 + lib/fst4/fst4_baseline.f90 ) # temporary workaround for a gfortran v7.3 ICE on Fedora 27 64-bit diff --git a/lib/fst4/fst4_baseline.f90 b/lib/fst4/fst4_baseline.f90 new file mode 100644 index 000000000..32776651a --- /dev/null +++ b/lib/fst4/fst4_baseline.f90 @@ -0,0 +1,48 @@ +subroutine fst4_baseline(s,np,ia,ib,npct,sbase) + +! Fit baseline to spectrum (for FST4) +! Input: s(npts) Linear scale in power +! Output: sbase(npts) Baseline + + implicit real*8 (a-h,o-z) + real*4 s(np),sw(np) + real*4 sbase(np) + real*4 base + real*8 x(1000),y(1000),a(5) + data nseg/8/ + + do i=ia,ib + sw(i)=10.0*log10(s(i)) !Convert to dB scale + enddo + + nterms=3 + nlen=(ib-ia+1)/nseg !Length of test segment + i0=(ib-ia+1)/2 !Midpoint + k=0 + do n=1,nseg !Loop over all segments + ja=ia + (n-1)*nlen + jb=ja+nlen-1 + call pctile(sw(ja),nlen,npct,base) !Find lowest npct of points + do i=ja,jb + if(sw(i).le.base) then + if (k.lt.1000) k=k+1 !Save all "lower envelope" points + x(k)=i-i0 + y(k)=sw(i) + endif + enddo + enddo + kz=k + a=0. + call polyfit(x,y,y,kz,nterms,0,a,chisqr) !Fit a low-order polynomial + sbase=0.0 + do i=ia,ib + t=i-i0 + sbase(i)=a(1)+t*(a(2)+t*(a(3))) + 0.2 +! write(51,3051) i,sw(i),sbase(i) +!3051 format(i8,2f12.3) + enddo + + sbase=10**(sbase/10.0) + + return +end subroutine fst4_baseline diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index efe6a3798..d609df115 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -49,7 +49,7 @@ contains complex, allocatable :: cframe(:) complex, allocatable :: c_bigfft(:) !Complex waveform real llr(240),llrs(240,4) - real candidates(200,4) + real candidates(200,5) real bitmetrics(320,4) real s4(0:3,NN) real minsync @@ -254,14 +254,19 @@ contains nhicoh=1 nsyncoh=8 - fa=max(100,nint(nfqso+1.5*hmod*baud-ntol)) - fb=min(4800,nint(nfqso+1.5*hmod*baud+ntol)) - minsync=1.2 + if(iwspr.eq.1) then + fa=1400.0 + fb=1600.0 + else + fa=max(100,nint(nfqso+1.5*hmod*baud-ntol)) + fb=min(4800,nint(nfqso+1.5*hmod*baud+ntol)) + endif + minsync=1.20 if(ntrperiod.eq.15) minsync=1.15 ! Get first approximation of candidate frequencies call get_candidates_fst4(c_bigfft,nfft1,nsps,hmod,fs,fa,fb, & - minsync,ncand,candidates,base) + minsync,ncand,candidates) ndecodes=0 decodes=' ' @@ -317,7 +322,7 @@ contains enddo ncand=ic xsnr=0. - +!write(*,*) 'ncand ',ncand do icand=1,ncand sync=candidates(icand,2) fc_synced=candidates(icand,3) @@ -465,6 +470,7 @@ contains do i=1,NN xsig=xsig+s4(itone(i),i) enddo + base=candidates(icand,5) arg=600.0*(xsig/base)-1.0 if(arg.gt.0.0) then xsnr=10*log10(arg)-35.5-12.5*log10(nsps/8200.0) @@ -645,14 +651,15 @@ contains end subroutine fst4_downsample subroutine get_candidates_fst4(c_bigfft,nfft1,nsps,hmod,fs,fa,fb, & - minsync,ncand,candidates,base) + minsync,ncand,candidates) complex c_bigfft(0:nfft1/2) !Full length FFT of raw data integer hmod !Modulation index (submode) integer im(1) !For maxloc - real candidates(200,4) !Candidate list + real candidates(200,5) !Candidate list real, allocatable :: s(:) !Low resolution power spectrum real, allocatable :: s2(:) !CCF of s() with 4 tones + real, allocatable :: sbase(:) !noise baseline estimate real xdb(-3:3) !Model 4-tone CCF peaks real minsync data xdb/0.25,0.50,0.75,1.0,0.75,0.50,0.25/ @@ -668,17 +675,17 @@ contains signal_bw=4*(12000.0/nsps)*hmod analysis_bw=min(4800.0,fb)-max(100.0,fa) xnoise_bw=10.0*signal_bw !Is this a good compromise? - if(analysis_bw.gt.xnoise_bw) then - ina=ia - inb=ib - else - fcenter=(fa+fb)/2.0 !If noise_bw > analysis_bw, - fl = max(100.0,fcenter-xnoise_bw/2.)/df2 !we'll search over noise_bw + if(xnoise_bw .lt. 400.0) xnoise_bw=400.0 + if(analysis_bw.gt.xnoise_bw) then !Estimate noise baseline over analysis bw + ina=0.9*ia + inb=min(int(1.1*ib),nfft1/2) + else !Estimate noise baseline over noise bw + fcenter=(fa+fb)/2.0 + fl = max(100.0,fcenter-xnoise_bw/2.)/df2 fh = min(4800.0,fcenter+xnoise_bw/2.)/df2 ina=nint(fl) inb=nint(fh) endif - nnw=nint(48000.*nsps*2./fs) allocate (s(nnw)) s=0. !Compute low-resolution power spectrum @@ -692,12 +699,16 @@ contains ina=max(ina,1+3*hmod) !Don't run off the ends inb=min(inb,nnw-3*hmod) allocate (s2(nnw)) + allocate (sbase(nnw)) s2=0. do i=ina,inb !Compute CCF of s() and 4 tones s2(i)=s(i-hmod*3) + s(i-hmod) +s(i+hmod) +s(i+hmod*3) enddo - call pctile(s2(ina+hmod*3:inb-hmod*3),inb-ina+1-hmod*6,30,base) - s2=s2/base !Normalize wrt noise level + npct=30 + call fst4_baseline(s2,nnw,ina+hmod*3,inb-hmod*3,npct,sbase) + if(any(sbase(ina:inb).le.0.0)) return + s2(ina:inb)=s2(ina:inb)/sbase(ina:inb) !Normalize wrt noise level + ncand=0 candidates=0 if(ia.lt.3) ia=3 @@ -717,12 +728,11 @@ contains s2(k)=max(0.,s2(k)-0.9*pval*xdb(i)) endif enddo -! s2(max(1,iploc-2*hmod*3):min(nnw,iploc+2*hmod*3))=0.0 ncand=ncand+1 candidates(ncand,1)=df2*iploc !Candidate frequency candidates(ncand,2)=pval !Rough estimate of SNR + candidates(ncand,5)=sbase(iploc) enddo - return end subroutine get_candidates_fst4 From 8159478d51da4a7eafdb5d6f2d71536c994dbb66 Mon Sep 17 00:00:00 2001 From: K9AN Date: Fri, 28 Aug 2020 12:25:17 -0500 Subject: [PATCH 052/426] Remove an unused variable from fst4_decode --- lib/fst4_decode.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index d609df115..a3c2e2a98 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -57,7 +57,6 @@ contains integer itone(NN) integer hmod integer*1 apmask(240),cw(240) - integer*1 hbits(320) integer*1 message101(101),message74(74),message77(77) integer*1 rvec(77) integer apbits(240) From 6ca77f0245f21fa2d5c89812847f5a72f4b3acfa Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Fri, 11 Sep 2020 20:15:21 +0100 Subject: [PATCH 053/426] Restore deleted action --- widgets/mainwindow.ui | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index 6aa94e18b..a826b2fa2 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -3293,6 +3293,14 @@ Yellow when too low FST4W + + + true + + + QRA66 + + From 8e69f84bdd3472f74ce7abece09ebe6961672055 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 11 Sep 2020 18:39:24 -0400 Subject: [PATCH 054/426] Remove some obsolete, commented-out code. No real cghanges! --- lib/sync66.f90 | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/sync66.f90 b/lib/sync66.f90 index 3cd0188bb..bbb72c997 100644 --- a/lib/sync66.f90 +++ b/lib/sync66.f90 @@ -66,9 +66,6 @@ subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) sync_sig=0. ia=min(64,nint(ntol/df)) dt4=nsps/(NSTEP*12000.0) !duration of 1/4 symbol -! lag2=nint(0.5/dt4) -! if(nsps.ge.7680) lag2=nint(1.0/dt4) -! lag1=-lag2 jadd=11 if(nsps.ge.3600) jadd=7 @@ -78,7 +75,6 @@ subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) do i=-ia,ia x=s1(i0+2*mode66+i,:)-s1(i0+i,:) !Do the 2FSK demodulation -! do lag=lag1,lag2 do lag=-15,15 do k=1,22 n=4*NSTEP*(k-1) + 1 @@ -103,16 +99,6 @@ subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) xdt=jdt*tsym/4.0 snr1=maxval(sync_sig)/22.0 - -! do i=-64,64 -! write(62,3062) nfqso+i*df,sync_sig(i,jj) -!3062 format(2f12.3) -! enddo - -! do j=-15,15 -! write(63,3063) j,j*dt4,sync_sig(ii,j) -!3063 format(i5,2f12.3) -! enddo return end subroutine sync66 From 2057600f43c7fc5e05e51d646af2c6b4ad7045ae Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 25 Sep 2020 10:55:21 -0400 Subject: [PATCH 055/426] First functioning QRA65 encode and decode. --- lib/genqra64.f90 | 21 +++++---- lib/qra/qra66/qra66sim.f90 | 2 +- lib/qra66_decode.f90 | 11 ++--- lib/spec66.f90 | 15 +++++-- lib/sync66.f90 | 90 ++++++++++++++++++++------------------ 5 files changed, 75 insertions(+), 64 deletions(-) diff --git a/lib/genqra64.f90 b/lib/genqra64.f90 index babe7d692..fb0c19438 100644 --- a/lib/genqra64.f90 +++ b/lib/genqra64.f90 @@ -1,6 +1,6 @@ subroutine genqra64(msg0,ichk,msgsent,itone,itype) -! Encodes a QRA64 message to yield itone(1:84) +! Encodes a QRA64 message to yield itone(1:84) or a QRA65 msg, itone(1:85) use packjt character*22 msg0 @@ -8,15 +8,15 @@ subroutine genqra64(msg0,ichk,msgsent,itone,itype) character*22 msgsent !Message as it will be received integer itone(85) !QRA64 uses only 84 character*3 cok !' ' or 'OOO' - logical old_qra_sync integer dgen(13) integer sent(63) - integer b11(11) !Barker 11 code + integer isync(22) integer icos7(0:6) data icos7/2,5,6,0,4,1,3/ !Defines a 7x7 Costas array - data b11/1,1,1,0,0,0,1,0,0,1,0/ !Barker 11 definition + data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ save + print*,'ichk:',ichk if(msg0(1:1).eq.'@') then read(msg0(2:5),*,end=1,err=1) nfreq go to 2 @@ -44,18 +44,17 @@ subroutine genqra64(msg0,ichk,msgsent,itone,itype) if(ichk.eq.1) go to 999 !Return if checking only call qra64_enc(dgen,sent) !Encode using QRA64 - if(ichk.eq.66) then -! Experimental QRA66 (FST66?) mode - j=0 + if(ichk.eq.65) then +! Experimental QRA65 mode + j=1 k=0 do i=1,85 - if(mod(i,4).eq.1) then + if(i.eq.isync(j)) then j=j+1 !Index for next sync symbol - if(j.eq.12) j=1 - itone(i)=b11(j) !Insert a sync symbol + itone(i)=0 !Insert a sync symbol else k=k+1 - itone(i)=sent(k) + 2 + itone(i)=sent(k) + 1 endif enddo else diff --git a/lib/qra/qra66/qra66sim.f90 b/lib/qra/qra66/qra66sim.f90 index 1171542ad..335a37252 100644 --- a/lib/qra/qra66/qra66sim.f90 +++ b/lib/qra/qra66/qra66sim.f90 @@ -65,7 +65,7 @@ program qra66sim mode66=2**(ichar(csubmode) - ichar('A')) print*,csubmode,mode66 - ichk=66 !Flag sent to genqra64 + ichk=65 !Flag sent to genqra64 call genqra64(msg,ichk,msgsent,itone,itype) write(*,1001) itone 1001 format('Channel symbols:'/(20i3)) diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 index f175fb0ba..f0db7378e 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra66_decode.f90 @@ -115,6 +115,10 @@ contains endif naptype=maxaptype + call timer('sync66 ',0) + call sync66(iwave,ntrperiod*12000,mode66,nsps,nfqso,ntol,xdt,f0,snr1) + call timer('sync66 ',1) + ! Downsample to give complex data at 6000 S/s fac=2.0/nfft1 c0=fac*iwave(1:nfft1) @@ -122,21 +126,18 @@ contains 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 timer('sync66 ',0) - call sync66(iwave,ntrperiod*12000,mode66,nsps,nfqso,ntol,xdt,f0,snr1) - call timer('sync66 ',1) jpk=(xdt+0.5)*6000 - 384 !### Empirical ### if(ntrperiod.ge.60) jpk=(xdt+1.0)*6000 - 384 !### TBD ### if(jpk.lt.0) jpk=0 a=0. - a(1)=-(f0 + 2.0*mode66*baud) !Data tones start 2*mode66 bins higher + a(1)=-(f0 + mode66*baud) !Data tones start mode66 bins higher call twkfreq(c0,c0,ntrperiod*6000,6000.0,a) xdt=jpk/6000.0 - 0.5 LL=64*(mode66+2) NN=63 - call spec66(c0(jpk:),nsps/2,s3,LL,NN) !Compute the synchronized symbol spectra + call spec66(c0(jpk:),nsps/2,s3,LL,NN) !Compute synchronized symbol spectra do j=1,63 !Normalize to symbol baseline call pctile(s3(:,j),LL,40,base) diff --git a/lib/spec66.f90 b/lib/spec66.f90 index 0d049fddb..454f1c18d 100644 --- a/lib/spec66.f90 +++ b/lib/spec66.f90 @@ -6,12 +6,19 @@ subroutine spec66(c0,nsps,s3,LL,NN) complex cs(0:nsps-1) !Complex symbol spectrum real s3(LL,NN) !Synchronized symbol spectra real xbase0(LL),xbase(LL) !Work arrays + integer isync(22) !Indices of sync symbols + data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ fac=1.0/nsps - ja=-nsps - do j=1,NN - ja=ja+nsps - if(mod(ja/nsps,4).eq.0) ja=ja+nsps + j=0 + n=1 + do k=1,84 + if(k.eq.isync(n)) then + n=n+1 + cycle + endif + j=j+1 + ja=(k-1)*nsps jb=ja+nsps-1 cs=fac*c0(ja:jb) call four2a(cs,nsps,1,-1,1) !c2c FFT to frequency diff --git a/lib/sync66.f90 b/lib/sync66.f90 index bbb72c997..48ed279b4 100644 --- a/lib/sync66.f90 +++ b/lib/sync66.f90 @@ -1,36 +1,32 @@ subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) - parameter (NSTEP=4) !Quarter-symbol steps - integer*2 iwave(0:nmax-1) !Raw data - integer b11(11) !Barker 11 code - integer ijpk(2) !Indices i and j at peak of sync_sig - real, allocatable :: s1(:,:) !Symbol spectra - real, allocatable :: x(:) !Work array; demoduated 2FSK sync signal - real sync(4*85) !sync vector - real sync_sig(-64:64,-15:15) + parameter (NSTEP=4) !Quarter-symbol steps + integer*2 iwave(0:nmax-1) !Raw data + integer isync(22) !Indices of sync symbols + integer ijpk(2) !Indices i and j at peak of ccf + real, allocatable :: s1(:,:) !Symbol spectra, quarter-symbol steps + real sync(85) !sync vector + real ccf(-64:64,-15:15) complex, allocatable :: c0(:) !Complex spectrum of symbol - data b11/1,1,1,0,0,0,1,0,0,1,0/ !Barker 11 code + data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ data sync(1)/99.0/ save sync nfft=2*nsps df=12000.0/nfft istep=nsps/NSTEP - iz=5000.0/df !Uppermost frequency bin, at 5000 Hz + iz=5000.0/df !Uppermost frequency bin, at 5000 Hz txt=85.0*nsps/12000.0 - jz=(txt+1.0)*12000.0/istep !Number of quarter-symbol steps + jz=(txt+1.0)*12000.0/istep !Number of quarter-symbol steps if(nsps.ge.7680) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher + allocate(s1(iz,jz)) - allocate(x(jz)) - allocate(c0(0:nsps)) + allocate(c0(0:nfft-1)) if(sync(1).eq.99.0) then - sync=0. + sync=-22.0/63.0 !Sync OFF do k=1,22 - kk=k - if(kk.gt.11) kk=k-11 - i=4*NSTEP*(k-1) + 1 - sync(i)=2.0*b11(kk) - 1.0 + sync(isync(k))=1.0 !Sync ON enddo endif @@ -45,7 +41,7 @@ subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) k=k+1 c0(k)=fac*cmplx(xx,yy) enddo - c0(k+1:nfft/2)=0. + c0(k+1:)=0. call four2a(c0,nfft,1,-1,0) !r2c FFT do i=1,iz s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 @@ -58,14 +54,18 @@ subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) s1max=20.0 ! Apply AGC - do j=1,jz - x(j)=maxval(s1(i0-64:i0+192,j)) - if(x(j).gt.s1max) s1(i0-64:i0+192,j)=s1(i0-64:i0+192,j)*s1max/x(j) - enddo +! do j=1,jz +! smax=maxval(s1(i0-64:i0+192,j)) +! if(smax.gt.s1max) s1(i0-64:i0+192,j)=s1(i0-64:i0+192,j)*s1max/smax +! enddo - sync_sig=0. +! do i=1,iz +! write(60,3060) i,i*df,sum(s1(i,1:jz)) +!3060 format(i6,f10.3,e12.3) +! enddo + + ccf=0. ia=min(64,nint(ntol/df)) - dt4=nsps/(NSTEP*12000.0) !duration of 1/4 symbol jadd=11 if(nsps.ge.3600) jadd=7 @@ -74,31 +74,35 @@ subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) if(nsps.ge.41472) jadd=1 do i=-ia,ia - x=s1(i0+2*mode66+i,:)-s1(i0+i,:) !Do the 2FSK demodulation do lag=-15,15 - do k=1,22 - n=4*NSTEP*(k-1) + 1 + do k=1,85 + n=NSTEP*(k-1) + 1 j=n+lag+jadd - if(j.ge.1 .and. j.le.jz) sync_sig(i,lag)=sync_sig(i,lag) + sync(n)*x(j) + if(j.ge.1 .and. j.le.jz) then + ccf(i,lag)=ccf(i,lag) + sync(k)*s1(i0+i,j) + endif enddo enddo enddo - ijpk=maxloc(sync_sig) - ii=ijpk(1)-65 - jj=ijpk(2)-16 +! do i=-64,64 +! write(61,3061) i,ccf(i,jpk) +!3061 format(i5,e12.3) +! enddo +! do j=-15,15 +! write(62,3061) j,ccf(ipk,j) +! enddo -! Use peakup() here? - f0=nfqso + ii*df - jdt=jj - -! j0=0.5/dt4 -! if(nsps.ge.7680) j0=1.0/dt4 - - tsym=nsps/12000.0 - xdt=jdt*tsym/4.0 - - snr1=maxval(sync_sig)/22.0 + ijpk=maxloc(ccf) + ipk=ijpk(1)-65 + jpk=ijpk(2)-16 + dt4=nsps/(NSTEP*12000.0) !1/4 of symbol duration + if(nsps.ge.7680) j0=1.0/dt4 + f0=nfqso + ipk*df + xdt=jpk*dt4 + snr1=maxval(ccf)/22.0 +! write(*,3100) ipk,jpk,xdt,f0,snr1 +!3100 format(2i5,f7.2,2f10.2) return end subroutine sync66 From 6ebc7002880c4fa0539f138f8b06c72459745213 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 25 Sep 2020 12:21:57 -0400 Subject: [PATCH 056/426] Mostly(?) changed QRA66 to QRA65 on the Fortran side. --- CMakeLists.txt | 15 +++---- lib/decoder.f90 | 32 +++++++------- lib/genqra64.f90 | 1 - .../qra66sim.f90 => qra65/qra65sim.f90} | 17 ++++---- lib/{qra66_decode.f90 => qra65_decode.f90} | 42 +++++++++---------- lib/{spec66.f90 => spec_qra65.f90} | 4 +- lib/{sync66.f90 => sync_qra65.f90} | 11 ++--- 7 files changed, 59 insertions(+), 63 deletions(-) rename lib/qra/{qra66/qra66sim.f90 => qra65/qra65sim.f90} (93%) rename lib/{qra66_decode.f90 => qra65_decode.f90} (85%) rename lib/{spec66.f90 => spec_qra65.f90} (95%) rename lib/{sync66.f90 => sync_qra65.f90} (92%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8080d98b8..a14535141 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -380,7 +380,7 @@ set (wsjt_FSRCS lib/options.f90 lib/packjt.f90 lib/77bit/packjt77.f90 - lib/qra66_decode.f90 + lib/qra65_decode.f90 lib/readwav.f90 lib/timer_C_wrapper.f90 lib/timer_impl.f90 @@ -568,7 +568,7 @@ set (wsjt_FSRCS lib/softsym9w.f90 lib/shell.f90 lib/spec64.f90 - lib/spec66.f90 + lib/spec_qra65.f90 lib/spec9f.f90 lib/stdmsg.f90 lib/subtract65.f90 @@ -580,8 +580,8 @@ set (wsjt_FSRCS lib/symspec65.f90 lib/sync4.f90 lib/sync64.f90 - lib/sync66.f90 lib/sync65.f90 + lib/sync_qra65.f90 lib/ft4/getcandidates4.f90 lib/ft4/get_ft4_bitmetrics.f90 lib/ft8/sync8.f90 @@ -1335,11 +1335,8 @@ target_link_libraries (sumsim wsjt_fort wsjt_cxx) add_executable (qra64sim lib/qra/qra64/qra64sim.f90 wsjtx.rc) target_link_libraries (qra64sim wsjt_fort wsjt_cxx) -add_executable (qra66sim lib/qra/qra66/qra66sim.f90 wsjtx.rc) -target_link_libraries (qra66sim wsjt_fort wsjt_cxx) - -#add_executable (test_sync66 lib/test_sync66.f90 wsjtx.rc) -#target_link_libraries (test_sync66 wsjt_fort wsjt_cxx) +add_executable (qra65sim lib/qra/qra65/qra65sim.f90 wsjtx.rc) +target_link_libraries (qra65sim wsjt_fort wsjt_cxx) add_executable (jt49sim lib/jt49sim.f90 wsjtx.rc) target_link_libraries (jt49sim wsjt_fort wsjt_cxx) @@ -1543,7 +1540,7 @@ install (TARGETS jt9 wsprd fmtave fcal fmeasure if(WSJT_BUILD_UTILS) install (TARGETS ft8code jt65code qra64code qra64sim jt9code jt4code - msk144code fst4sim + msk144code fst4sim qra65sim RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime ) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index a6c84d66b..496729388 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -9,7 +9,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) use ft8_decode use ft4_decode use fst4_decode - use qra66_decode + use qra65_decode include 'jt9com.f90' include 'timer_common.inc' @@ -38,9 +38,9 @@ subroutine multimode_decoder(ss,id2,params,nfsample) integer :: decoded end type counting_fst4_decoder - type, extends(qra66_decoder) :: counting_qra66_decoder + type, extends(qra65_decoder) :: counting_qra65_decoder integer :: decoded - end type counting_qra66_decoder + end type counting_qra65_decoder real ss(184,NSMAX) logical baddata,newdat65,newdat9,single_decode,bVHF,bad0,newdat,ex @@ -59,7 +59,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) type(counting_ft8_decoder) :: my_ft8 type(counting_ft4_decoder) :: my_ft4 type(counting_fst4_decoder) :: my_fst4 - type(counting_qra66_decoder) :: my_qra66 + type(counting_qra65_decoder) :: my_qra65 !cast C character arrays to Fortran character strings datetime=transfer(params%datetime, datetime) @@ -75,7 +75,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) my_ft8%decoded = 0 my_ft4%decoded = 0 my_fst4%decoded = 0 - my_qra66%decoded = 0 + my_qra65%decoded = 0 ! For testing only: return Rx messages stored in a file as decodes inquire(file='rx_messages.txt',exist=ex) @@ -195,12 +195,12 @@ subroutine multimode_decoder(ss,id2,params,nfsample) endif if(params%nmode.eq.66) then -! We're in QRA66 mode - call timer('decqra66',0) - call my_qra66%decode(qra66_decoded,id2,params%nutc,params%ntr, & +! We're in QRA65 mode + call timer('decqra65',0) + call my_qra65%decode(qra65_decoded,id2,params%nutc,params%ntr, & params%nsubmode,params%nfqso,params%ntol,params%ndepth, & mycall,hiscall,hisgrid) - call timer('decqra66',1) + call timer('decqra65',1) go to 800 endif @@ -776,13 +776,13 @@ contains return end subroutine fst4_decoded - subroutine qra66_decoded (this,nutc,sync,nsnr,dt,freq,decoded,irc, & + subroutine qra65_decoded (this,nutc,sync,nsnr,dt,freq,decoded,irc, & qual,ntrperiod,fmid,w50) - use qra66_decode + use qra65_decode implicit none - class(qra66_decoder), intent(inout) :: this + class(qra65_decoder), intent(inout) :: this integer, intent(in) :: nutc real, intent(in) :: sync integer, intent(in) :: nsnr @@ -801,23 +801,23 @@ contains write(*,1001) nutc,nsnr,dt,nint(freq),decoded,mod(irc,100),navg 1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i2,i4) write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded -1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' QRA66') +1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' QRA65') else write(*,1003) nutc,nsnr,dt,nint(freq),decoded,mod(irc,100),navg 1003 format(i4.4,i4,f5.1,i5,' + ',1x,a37,1x,i2,i4) write(13,1004) nutc,nint(sync),nsnr,dt,freq,0,decoded -1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' QRA66') +1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' QRA65') endif call flush(6) call flush(13) select type(this) - type is (counting_qra66_decoder) + type is (counting_qra65_decoder) this%decoded = this%decoded + 1 end select return - end subroutine qra66_decoded + end subroutine qra65_decoded end subroutine multimode_decoder diff --git a/lib/genqra64.f90 b/lib/genqra64.f90 index fb0c19438..8426b8830 100644 --- a/lib/genqra64.f90 +++ b/lib/genqra64.f90 @@ -16,7 +16,6 @@ subroutine genqra64(msg0,ichk,msgsent,itone,itype) data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ save - print*,'ichk:',ichk if(msg0(1:1).eq.'@') then read(msg0(2:5),*,end=1,err=1) nfreq go to 2 diff --git a/lib/qra/qra66/qra66sim.f90 b/lib/qra/qra65/qra65sim.f90 similarity index 93% rename from lib/qra/qra66/qra66sim.f90 rename to lib/qra/qra65/qra65sim.f90 index 335a37252..299c95445 100644 --- a/lib/qra/qra66/qra66sim.f90 +++ b/lib/qra/qra65/qra65sim.f90 @@ -1,6 +1,6 @@ -program qra66sim +program qra65sim -! Generate simulated QRA66 data for testing the decoder. +! Generate simulated QRA65 data for testing the decoder. use wavhdr use packjt @@ -19,13 +19,13 @@ program qra66sim nargs=iargc() if(nargs.ne.8) then - print *, 'Usage: qra66sim "msg" A-E freq fDop DT TRp Nfiles SNR' - print *, 'Example: qra66sim "K1ABC W9XYZ EN37" A 1500 0.2 0.0 15 1 -10' + print *, 'Usage: qra65sim "msg" A-E freq fDop DT TRp Nfiles SNR' + print *, 'Example: qra65sim "K1ABC W9XYZ EN37" A 1500 0.2 0.0 15 1 -10' go to 999 endif call getarg(1,msg) call getarg(2,csubmode) - mode66=2**(ichar(csubmode)-ichar('A')) + mode65=2**(ichar(csubmode)-ichar('A')) call getarg(3,arg) read(arg,*) f0 call getarg(4,arg) @@ -62,8 +62,7 @@ program qra66sim dt=1.d0/fsample !Sample interval (s) twopi=8.d0*atan(1.d0) nsym=85 !Number of channel symbols - mode66=2**(ichar(csubmode) - ichar('A')) - print*,csubmode,mode66 + mode65=2**(ichar(csubmode) - ichar('A')) ichk=65 !Flag sent to genqra64 call genqra64(msg,ichk,msgsent,itone,itype) @@ -108,7 +107,7 @@ program qra66sim isym=i/nsps + 1 if(isym.gt.nsym) exit if(isym.ne.isym0) then - freq=f0 + itone(isym)*baud*mode66 + freq=f0 + itone(isym)*baud*mode65 dphi=twopi*freq*dt isym0=isym endif @@ -176,4 +175,4 @@ program qra66sim ! enddo enddo -999 end program qra66sim +999 end program qra65sim diff --git a/lib/qra66_decode.f90 b/lib/qra65_decode.f90 similarity index 85% rename from lib/qra66_decode.f90 rename to lib/qra65_decode.f90 index f0db7378e..1f4b0a1f2 100644 --- a/lib/qra66_decode.f90 +++ b/lib/qra65_decode.f90 @@ -1,17 +1,17 @@ -module qra66_decode +module qra65_decode - type :: qra66_decoder - procedure(qra66_decode_callback), pointer :: callback + type :: qra65_decoder + procedure(qra65_decode_callback), pointer :: callback contains procedure :: decode - end type qra66_decoder + end type qra65_decoder abstract interface - subroutine qra66_decode_callback (this,nutc,sync,nsnr,dt,freq, & + subroutine qra65_decode_callback (this,nutc,sync,nsnr,dt,freq, & decoded,nap,qual,ntrperiod,fmid,w50) - import qra66_decoder + import qra65_decoder implicit none - class(qra66_decoder), intent(inout) :: this + class(qra65_decoder), intent(inout) :: this integer, intent(in) :: nutc real, intent(in) :: sync integer, intent(in) :: nsnr @@ -23,7 +23,7 @@ module qra66_decode integer, intent(in) :: ntrperiod real, intent(in) :: fmid real, intent(in) :: w50 - end subroutine qra66_decode_callback + end subroutine qra65_decode_callback end interface contains @@ -35,8 +35,8 @@ contains use packjt use, intrinsic :: iso_c_binding parameter (NMAX=300*12000) !### Needs to be 300*12000 ### - class(qra66_decoder), intent(inout) :: this - procedure(qra66_decode_callback) :: callback + class(qra65_decoder), intent(inout) :: this + procedure(qra65_decode_callback) :: callback character(len=12) :: mycall, hiscall character(len=6) :: hisgrid character*37 decoded @@ -50,7 +50,7 @@ contains data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/,nsubmodez/-1/ save nc1z,nc2z,ng2z,maxaptypez,nsave,nsubmodez - mode66=2**nsubmode + mode65=2**nsubmode nfft1=ntrperiod*12000 nfft2=ntrperiod*6000 allocate (c0(0:nfft1-1)) @@ -58,8 +58,8 @@ contains if(nsubmode.ne.nsubmodez) then if(allocated(s3)) deallocate(s3) if(allocated(s3a)) deallocate(s3a) - allocate(s3(-64:64*mode66+63,63)) - allocate(s3a(-64:64*mode66+63,63)) + allocate(s3(-64:64*mode65+63,63)) + allocate(s3a(-64:64*mode65+63,63)) endif if(ntrperiod.eq.15) then @@ -115,9 +115,9 @@ contains endif naptype=maxaptype - call timer('sync66 ',0) - call sync66(iwave,ntrperiod*12000,mode66,nsps,nfqso,ntol,xdt,f0,snr1) - call timer('sync66 ',1) + call timer('sync_q65',0) + call sync_qra65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0,snr1) + call timer('sync_q65',1) ! Downsample to give complex data at 6000 S/s fac=2.0/nfft1 @@ -131,20 +131,20 @@ contains if(ntrperiod.ge.60) jpk=(xdt+1.0)*6000 - 384 !### TBD ### if(jpk.lt.0) jpk=0 a=0. - a(1)=-(f0 + mode66*baud) !Data tones start mode66 bins higher + a(1)=-(f0 + mode65*baud) !Data tones start mode65 bins higher call twkfreq(c0,c0,ntrperiod*6000,6000.0,a) xdt=jpk/6000.0 - 0.5 - LL=64*(mode66+2) + LL=64*(mode65+2) NN=63 - call spec66(c0(jpk:),nsps/2,s3,LL,NN) !Compute synchronized symbol spectra + call spec_qra65(c0(jpk:),nsps/2,s3,LL,NN) !Compute synchronized symbol spectra do j=1,63 !Normalize to symbol baseline call pctile(s3(:,j),LL,40,base) s3(:,j)=s3(:,j)/base enddo - LL2=64*(mode66+1)-1 + LL2=64*(mode65+1)-1 s3max=20.0 do j=1,63 !Apply AGC to suppress pings xx=maxval(s3(-64:LL2,j)) @@ -201,4 +201,4 @@ contains return end subroutine decode -end module qra66_decode +end module qra65_decode diff --git a/lib/spec66.f90 b/lib/spec_qra65.f90 similarity index 95% rename from lib/spec66.f90 rename to lib/spec_qra65.f90 index 454f1c18d..9f6af0b38 100644 --- a/lib/spec66.f90 +++ b/lib/spec_qra65.f90 @@ -1,4 +1,4 @@ -subroutine spec66(c0,nsps,s3,LL,NN) +subroutine spec_qra65(c0,nsps,s3,LL,NN) ! Compute synchronized symbol spectra. @@ -46,4 +46,4 @@ subroutine spec66(c0,nsps,s3,LL,NN) enddo return -end subroutine spec66 +end subroutine spec_qra65 diff --git a/lib/sync66.f90 b/lib/sync_qra65.f90 similarity index 92% rename from lib/sync66.f90 rename to lib/sync_qra65.f90 index 48ed279b4..8d3bc1013 100644 --- a/lib/sync66.f90 +++ b/lib/sync_qra65.f90 @@ -1,4 +1,4 @@ -subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) +subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) parameter (NSTEP=4) !Quarter-symbol steps integer*2 iwave(0:nmax-1) !Raw data @@ -7,7 +7,7 @@ subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) real, allocatable :: s1(:,:) !Symbol spectra, quarter-symbol steps real sync(85) !sync vector real ccf(-64:64,-15:15) - complex, allocatable :: c0(:) !Complex spectrum of symbol + complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ data sync(1)/99.0/ save sync @@ -72,6 +72,9 @@ subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) if(nsps.ge.7680) jadd=6 if(nsps.ge.16000) jadd=3 if(nsps.ge.41472) jadd=1 + dt4=nsps/(NSTEP*12000.0) !1/4 of symbol duration +! j0=0.5/dt4 +! if(nsps.ge.7680) j0=1.0/dt4 do i=-ia,ia do lag=-15,15 @@ -96,8 +99,6 @@ subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) ijpk=maxloc(ccf) ipk=ijpk(1)-65 jpk=ijpk(2)-16 - dt4=nsps/(NSTEP*12000.0) !1/4 of symbol duration - if(nsps.ge.7680) j0=1.0/dt4 f0=nfqso + ipk*df xdt=jpk*dt4 snr1=maxval(ccf)/22.0 @@ -105,4 +106,4 @@ subroutine sync66(iwave,nmax,mode66,nsps,nfqso,ntol,xdt,f0,snr1) !3100 format(2i5,f7.2,2f10.2) return -end subroutine sync66 +end subroutine sync_qra65 From 7fd2f1afe693d44b90e835d957b9fbf8d1f2da13 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 25 Sep 2020 13:20:46 -0400 Subject: [PATCH 057/426] Change QRA66 to QRA65, on the C++ side. --- lib/decoder.f90 | 2 +- widgets/mainwindow.cpp | 52 +++++++++++++++++++++--------------------- widgets/mainwindow.h | 4 ++-- widgets/mainwindow.ui | 6 ++--- widgets/plotter.cpp | 8 +++---- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 496729388..7a8e12275 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -194,7 +194,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) go to 800 endif - if(params%nmode.eq.66) then + if(params%nmode.eq.66) then !NB: JT65 = 65, QRA65 = 66. ! We're in QRA65 mode call timer('decqra65',0) call my_qra65%decode(qra65_decoded,id2,params%nutc,params%ntr, & diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 49aa52253..c2231eb5e 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -596,7 +596,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, ui->actionISCAT->setActionGroup(modeGroup); ui->actionMSK144->setActionGroup(modeGroup); ui->actionQRA64->setActionGroup(modeGroup); - ui->actionQRA66->setActionGroup(modeGroup); + ui->actionQRA65->setActionGroup(modeGroup); ui->actionFreqCal->setActionGroup(modeGroup); QActionGroup* saveGroup = new QActionGroup(this); @@ -1366,7 +1366,7 @@ void MainWindow::fixStop() } else if (m_mode=="QRA64"){ m_hsymStop=179; if(m_config.decode_at_52s()) m_hsymStop=186; - } else if (m_mode=="QRA66"){ + } else if (m_mode=="QRA65"){ m_hsymStop=48; if(m_TRperiod==30) m_hsymStop=96; if(m_TRperiod==60) m_hsymStop=196; @@ -2355,7 +2355,7 @@ void MainWindow::setup_status_bar (bool vhf) mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #66ff66}"); } else if ("QRA64" == m_mode) { mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #99ff33}"); - } else if ("QRA66" == m_mode) { + } else if ("QRA65" == m_mode) { mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #99ff33}"); } else if ("MSK144" == m_mode) { mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff6666}"); @@ -2557,7 +2557,7 @@ void MainWindow::on_actionCopyright_Notice_triggered() "notice prominently in your derivative work:\n\n" "\"The algorithms, source code, look-and-feel of WSJT-X and related " "programs, and protocol specifications for the modes FSK441, FST4, FT8, " - "JT4, JT6M, JT9, JT65, JTMS, QRA64, QRA66, ISCAT, MSK144 are Copyright (C) " + "JT4, JT6M, JT9, JT65, JTMS, QRA64, QRA65, ISCAT, MSK144 are Copyright (C) " "2001-2020 by one or more of the following authors: Joseph Taylor, " "K1JT; Bill Somerville, G4WJS; Steven Franke, K9AN; Nico Palermo, " "IV3NWV; Greg Beam, KI7MT; Michael Black, W9MDB; Edson Pereira, PY2SDR; " @@ -3079,8 +3079,8 @@ void MainWindow::decode() //decode() ui->actionEnable_AP_JT65->isChecked (); if(m_mode=="QRA64") dec_data.params.nmode=164; if(m_mode=="QRA64") dec_data.params.ntxmode=164; - if(m_mode=="QRA66") dec_data.params.nmode=66; - if(m_mode=="QRA66") dec_data.params.ntxmode=66; + if(m_mode=="QRA65") dec_data.params.nmode=66; + if(m_mode=="QRA65") dec_data.params.ntxmode=66; if(m_mode=="JT9+JT65") dec_data.params.nmode=9+65; // = 74 if(m_mode=="JT4") { dec_data.params.nmode=4; @@ -3403,7 +3403,7 @@ void MainWindow::readFromStdout() //readFromStdout //Right (Rx Frequency) window bool bDisplayRight=bAvgMsg; int audioFreq=decodedtext.frequencyOffset(); - if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="FST4" or m_mode=="QRA66") { + if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="FST4" or m_mode=="QRA65") { auto const& parts = decodedtext.string().remove("<").remove(">") .split (' ', SkipEmptyParts); if (parts.size() > 6) { @@ -3486,7 +3486,7 @@ void MainWindow::readFromStdout() //readFromStdout //### I think this is where we are preventing Hounds from spotting Fox ### if(m_mode!="FT8" or (SpecOp::HOUND != m_config.special_op_id())) { - if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="QRA64" or m_mode=="QRA66" + if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="QRA64" or m_mode=="QRA65" or m_mode=="JT4" or m_mode=="JT65" or m_mode=="JT9" or m_mode=="FST4") { auto_sequence (decodedtext, 25, 50); } @@ -3696,7 +3696,7 @@ void MainWindow::guiUpdate() if(m_modeTx=="JT9") txDuration=1.0 + 85.0*m_nsps/12000.0; // JT9 if(m_modeTx=="JT65") txDuration=1.0 + 126*4096/11025.0; // JT65 if(m_modeTx=="QRA64") txDuration=1.0 + 84*6912/12000.0; // QRA64 - if(m_modeTx=="QRA66") { // QRA66 + if(m_modeTx=="QRA65") { // QRA65 if(m_TRperiod==15) txDuration=0.5 + 85*1800/12000.0; if(m_TRperiod==30) txDuration=0.5 + 85*3600/12000.0; if(m_TRperiod==60) txDuration=1.0 + 85*7680/12000.0; @@ -3954,9 +3954,9 @@ void MainWindow::guiUpdate() &m_currentMessageType, 22, 22); if(m_modeTx=="QRA64") genqra64_(message, &ichk, msgsent, const_cast (itone), &m_currentMessageType, 22, 22); - if(m_modeTx=="QRA66") { - int ichk66=66; - genqra64_(message, &ichk66, msgsent, const_cast (itone), + if(m_modeTx=="QRA65") { + int ichk65=65; + genqra64_(message, &ichk65, msgsent, const_cast (itone), &m_currentMessageType, 22, 22); } if(m_modeTx=="WSPR") genwspr_(message, msgsent, const_cast (itone), @@ -4672,7 +4672,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; //Currently we do auto-sequencing only in FT4, FT8, MSK144, FST4, and QRA66 + return; //Currently we do auto-sequencing only in FT4, FT8, MSK144, FST4, and QRA65 } //Skip the rest if no decoded text extracted @@ -4780,7 +4780,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 != "QRA66" && m_mode!="FT8" && + m_mode != "QRA64" && m_mode != "QRA65" && m_mode!="FT8" && m_mode!="FT4" && m_mode!="FST4") { return; } @@ -6324,12 +6324,12 @@ void MainWindow::on_actionQRA64_triggered() statusChanged(); } -void MainWindow::on_actionQRA66_triggered() +void MainWindow::on_actionQRA65_triggered() { on_actionFST4_triggered(); - m_mode="QRA66"; - m_modeTx="QRA66"; - ui->actionQRA66->setChecked(true); + m_mode="QRA65"; + m_modeTx="QRA65"; + ui->actionQRA65->setChecked(true); switch_mode (Modes::QRA64); setup_status_bar (false); m_hsymStop=49; @@ -6392,7 +6392,7 @@ void MainWindow::on_actionMSK144_triggered() if("JT9_JT65"==m_mode) ui->actionJT9_JT65->setChecked(true); if("ISCAT"==m_mode) ui->actionISCAT->setChecked(true); if("QRA64"==m_mode) ui->actionQRA64->setChecked(true); - if("QRA66"==m_mode) ui->actionQRA66->setChecked(true); + if("QRA65"==m_mode) ui->actionQRA65->setChecked(true); if("WSPR"==m_mode) ui->actionWSPR->setChecked(true); if("Echo"==m_mode) ui->actionEcho->setChecked(true); if("FreqCal"==m_mode) ui->actionFreqCal->setChecked(true); @@ -7206,15 +7206,15 @@ void MainWindow::transmit (double snr) true, false, snr, m_TRperiod); } - if (m_modeTx == "QRA66") { + if (m_modeTx == "QRA65") { int nsps=1800; if(m_TRperiod==30) nsps=3600; if(m_TRperiod==60) nsps=7680; if(m_TRperiod==120) nsps=16000; if(m_TRperiod==300) nsps=41472; - int mode66=pow(2.0,double(m_nSubMode)); - toneSpacing=mode66*12000.0/nsps; - Q_EMIT sendMessage (m_mode, NUM_QRA66_SYMBOLS, + int mode65=pow(2.0,double(m_nSubMode)); + toneSpacing=mode65*12000.0/nsps; + Q_EMIT sendMessage (m_mode, NUM_QRA65_SYMBOLS, double(nsps), ui->TxFreqSpinBox->value () - m_XIT, toneSpacing, m_soundOutput, m_config.audio_output_channel (), true, false, snr, m_TRperiod); @@ -7475,9 +7475,9 @@ void::MainWindow::VHF_features_enabled(bool b) void MainWindow::on_sbTR_valueChanged(int value) { // if(!m_bFastMode and n>m_nSubMode) m_MinW=m_nSubMode; - if(m_bFastMode or m_mode=="FreqCal" or m_mode=="FST4" or m_mode=="FST4W" or m_mode=="QRA66") { + if(m_bFastMode or m_mode=="FreqCal" or m_mode=="FST4" or m_mode=="FST4W" or m_mode=="QRA65") { m_TRperiod = value; - if (m_mode == "FST4" or m_mode == "FST4W" or m_mode=="QRA66") { + if (m_mode == "FST4" or m_mode == "FST4W" or m_mode=="QRA65") { if (m_TRperiod < 60) { ui->decodedTextLabel->setText(" UTC dB DT Freq " + tr ("Message")); if (m_mode != "FST4W") { @@ -9157,7 +9157,7 @@ void MainWindow::set_mode (QString const& mode) else if ("JT9+JT65" == mode) on_actionJT9_JT65_triggered (); else if ("JT65" == mode) on_actionJT65_triggered (); else if ("QRA64" == mode) on_actionQRA64_triggered (); - else if ("QRA66" == mode) on_actionQRA66_triggered (); + else if ("QRA65" == mode) on_actionQRA65_triggered (); else if ("FreqCal" == mode) on_actionFreqCal_triggered (); else if ("ISCAT" == mode) on_actionISCAT_triggered (); else if ("MSK144" == mode) on_actionMSK144_triggered (); diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index 3660cd219..2e931ea3c 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -48,7 +48,7 @@ #define NUM_ISCAT_SYMBOLS 1291 //30*11025/256 #define NUM_MSK144_SYMBOLS 144 //s8 + d48 + s8 + d80 #define NUM_QRA64_SYMBOLS 84 //63 data + 21 sync -#define NUM_QRA66_SYMBOLS 85 //63 data + 22 sync +#define NUM_QRA65_SYMBOLS 85 //63 data + 22 sync #define NUM_FT8_SYMBOLS 79 #define NUM_FT4_SYMBOLS 105 #define NUM_FST4_SYMBOLS 160 //240/2 data + 5*8 sync @@ -300,7 +300,7 @@ private slots: void on_cbCQTx_toggled(bool b); void on_actionMSK144_triggered(); void on_actionQRA64_triggered(); - void on_actionQRA66_triggered(); + void on_actionQRA65_triggered(); void on_actionFreqCal_triggered(); void splash_done (); void on_measure_check_box_stateChanged (int); diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index a826b2fa2..33ffd38ca 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -2806,7 +2806,7 @@ Yellow when too low - + @@ -3293,12 +3293,12 @@ Yellow when too low FST4W - + true - QRA66 + QRA65 diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index ea53b7925..3879f0c27 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -472,7 +472,7 @@ void CPlotter::DrawOverlay() //DrawOverlay() if(m_nSubMode==4) bw=16*bw; //E } - if(m_mode=="QRA66") { //QRA66 + if(m_mode=="QRA65") { //QRA65 int h=int(pow(2.0,m_nSubMode)); int nsps=1800; if(m_TRperiod==30) nsps=3600; @@ -513,10 +513,10 @@ void CPlotter::DrawOverlay() //DrawOverlay() int yTxTop=12; int yRxBottom=yTxTop + 2*yh + 4; if(m_mode=="JT9" or m_mode=="JT65" or m_mode=="JT9+JT65" - or m_mode=="QRA64" or m_mode=="QRA66" or m_mode=="FT8" or m_mode=="FT4" + or m_mode=="QRA64" or m_mode=="QRA65" or m_mode=="FT8" or m_mode=="FT4" or m_mode.startsWith("FST4")) { - if(m_mode=="QRA64" or m_mode=="QRA66" or (m_mode=="JT65" and m_bVHF)) { + if(m_mode=="QRA64" or m_mode=="QRA65" or (m_mode=="JT65" and m_bVHF)) { painter0.setPen(penGreen); x1=XfromFreq(m_rxFreq-m_tol); x2=XfromFreq(m_rxFreq+m_tol); @@ -554,7 +554,7 @@ 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=="QRA66" or m_mode=="FT8" + m_mode.mid(0,4)=="WSPR" or m_mode=="QRA64" or m_mode=="QRA65" or m_mode=="FT8" or m_mode=="FT4" or m_mode.startsWith("FST4")) { painter0.setPen(penRed); x1=XfromFreq(m_txFreq); From 443aee5355b20ed7371af6950e451531df03b95a Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 25 Sep 2020 13:31:43 -0400 Subject: [PATCH 058/426] Restore AGC action in sync_qra65(). --- lib/sync_qra65.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/sync_qra65.f90 b/lib/sync_qra65.f90 index 8d3bc1013..09681262b 100644 --- a/lib/sync_qra65.f90 +++ b/lib/sync_qra65.f90 @@ -54,10 +54,10 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) s1max=20.0 ! Apply AGC -! do j=1,jz -! smax=maxval(s1(i0-64:i0+192,j)) -! if(smax.gt.s1max) s1(i0-64:i0+192,j)=s1(i0-64:i0+192,j)*s1max/smax -! enddo + do j=1,jz + smax=maxval(s1(i0-64:i0+192,j)) + if(smax.gt.s1max) s1(i0-64:i0+192,j)=s1(i0-64:i0+192,j)*s1max/smax + enddo ! do i=1,iz ! write(60,3060) i,i*df,sum(s1(i,1:jz)) From acd6253373765587813f7119efd003cb35dc75a7 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 25 Sep 2020 15:38:20 -0400 Subject: [PATCH 059/426] A few more fixups for QRA65. Not finished, yet! --- CMakeLists.txt | 3 +++ Modulator/Modulator.cpp | 2 +- displayWidgets.txt | 2 +- lib/sync_qra65.f90 | 1 + lib/{test_qra66.f90 => test_qra65.f90} | 11 +++++------ models/Modes.cpp | 3 ++- models/Modes.hpp | 1 + widgets/mainwindow.cpp | 11 ++++++----- 8 files changed, 20 insertions(+), 14 deletions(-) rename lib/{test_qra66.f90 => test_qra65.f90} (90%) diff --git a/CMakeLists.txt b/CMakeLists.txt index a14535141..712e176ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1338,6 +1338,9 @@ target_link_libraries (qra64sim wsjt_fort wsjt_cxx) add_executable (qra65sim lib/qra/qra65/qra65sim.f90 wsjtx.rc) target_link_libraries (qra65sim wsjt_fort wsjt_cxx) +add_executable (test_qra65 lib/test_qra65.f90 wsjtx.rc) +target_link_libraries (test_qra65 wsjt_fort wsjt_cxx) + add_executable (jt49sim lib/jt49sim.f90 wsjtx.rc) target_link_libraries (jt49sim wsjt_fort wsjt_cxx) diff --git a/Modulator/Modulator.cpp b/Modulator/Modulator.cpp index f391b1ffd..328b5b3ab 100644 --- a/Modulator/Modulator.cpp +++ b/Modulator/Modulator.cpp @@ -70,7 +70,7 @@ void Modulator::start (QString mode, unsigned symbolsLength, double framesPerSym m_bFastMode=fastMode; m_TRperiod=TRperiod; unsigned delay_ms=1000; - if(mode=="FT8" or (mode=="FST4" and m_nsps==720) or mode=="QRA66") delay_ms=500; //FT8, FST4-15, QRA66 + if(mode=="FT8" or (mode=="FST4" and m_nsps==720) or mode=="QRA65") delay_ms=500; //FT8, FST4-15, QRA65 if(mode=="FT4") delay_ms=300; //FT4 // noise generator parameters diff --git a/displayWidgets.txt b/displayWidgets.txt index 912b19f01..23e02480f 100644 --- a/displayWidgets.txt +++ b/displayWidgets.txt @@ -11,7 +11,7 @@ JT9+JT65 1110100000011110000100000000000010 JT65 1110100000001110000100000000000010 JT65/VHF 1111100100001101101011000100000000 QRA64 1111100101101101100000000010000000 -QRA66 1111110101101101000100000011000000 +QRA65 1111110101101101000100000011000000 ISCAT 1001110000000001100000000000000000 MSK144 1011111101000000000100010000000000 WSPR 0000000000000000010100000000000000 diff --git a/lib/sync_qra65.f90 b/lib/sync_qra65.f90 index 09681262b..1a7859cb1 100644 --- a/lib/sync_qra65.f90 +++ b/lib/sync_qra65.f90 @@ -73,6 +73,7 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) if(nsps.ge.16000) jadd=3 if(nsps.ge.41472) jadd=1 dt4=nsps/(NSTEP*12000.0) !1/4 of symbol duration +! print*,'DT range +/-',15*dt4 ! j0=0.5/dt4 ! if(nsps.ge.7680) j0=1.0/dt4 diff --git a/lib/test_qra66.f90 b/lib/test_qra65.f90 similarity index 90% rename from lib/test_qra66.f90 rename to lib/test_qra65.f90 index 8ff97829c..67af7a720 100644 --- a/lib/test_qra66.f90 +++ b/lib/test_qra65.f90 @@ -1,17 +1,16 @@ -program test_qra66 +program test_qra65 character*70 cmd1,cmd2,line character*22 msg character*8 arg integer nretcode(0:11) integer fDop - real fspread logical decok nargs=iargc() if(nargs.ne.7) then - print*,'Usage: test_qra66 "msg" ndepth freq DT fDop nfiles SNR' - print*,'Example: test_qra66 "K1ABC W9XYZ EN37" 3 1500 0.0 5 100 0' + print*,'Usage: test_qra65 "msg" ndepth freq DT fDop nfiles SNR' + print*,'Example: test_qra65 "K1ABC W9XYZ EN37" 3 1500 0.0 5 100 0' print*,' SNR = 0 to loop over all relevant SNRs' go to 999 endif @@ -31,7 +30,7 @@ program test_qra66 ! 1 2 3 4 5 6 ! 1234567890123456789012345678901234567890123456789012345678901234' - cmd1='qra66sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 100 -10 > junk0' + cmd1='qra65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 100 -10 > junk0' cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 1 *.wav > junk' write(cmd1(10:33),'(a)') '"'//msg//'"' @@ -96,7 +95,7 @@ program test_qra66 flush(12) enddo -999 end program test_qra66 +999 end program test_qra65 include 'sec0.f90' diff --git a/models/Modes.cpp b/models/Modes.cpp index 2de1f7c90..deaa2a0f6 100644 --- a/models/Modes.cpp +++ b/models/Modes.cpp @@ -26,7 +26,8 @@ namespace "FT8", "FT4", "FST4", - "FST4W" + "FST4W", + "QRA65" }; std::size_t constexpr mode_names_size = sizeof (mode_names) / sizeof (mode_names[0]); } diff --git a/models/Modes.hpp b/models/Modes.hpp index ac57ba4ad..2f3bf60f7 100644 --- a/models/Modes.hpp +++ b/models/Modes.hpp @@ -52,6 +52,7 @@ public: FT4, FST4, FST4W, + QRA65, MODES_END_SENTINAL_AND_COUNT // this must be last }; Q_ENUM (Mode) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index c2231eb5e..1cd40b1fe 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6330,16 +6330,17 @@ void MainWindow::on_actionQRA65_triggered() m_mode="QRA65"; m_modeTx="QRA65"; ui->actionQRA65->setChecked(true); - switch_mode (Modes::QRA64); - setup_status_bar (false); + switch_mode(Modes::QRA65); + setup_status_bar(true); m_hsymStop=49; ui->sbTR->values ({15, 30, 60, 120, 300}); on_sbTR_valueChanged (ui->sbTR->value()); - m_wideGraph->setMode(m_mode); + ui->sbSubmode->setMaximum(3); + ui->sbSubmode->setValue(m_nSubMode); m_wideGraph->setMode(m_mode); m_wideGraph->setModeTx(m_modeTx); m_wideGraph->setPeriod(m_TRperiod,6912); + m_wideGraph->setTol(ui->sbFtol->value()); // 0123456789012345678901234567890123 -//displayWidgets(nWidgets("1111100001001100000100000001000000")); displayWidgets(nWidgets("1111110101101101000100000011000000")); statusChanged();} @@ -7523,7 +7524,7 @@ void MainWindow::on_sbTR_FST4W_valueChanged(int value) QChar MainWindow::current_submode () const { QChar submode {0}; - if (m_mode.contains (QRegularExpression {R"(^(JT65|JT9|JT4|ISCAT|QRA64)$)"}) + if (m_mode.contains (QRegularExpression {R"(^(JT65|JT9|JT4|ISCAT|QRA64|QRA65)$)"}) && (m_config.enable_VHF_features () || "JT4" == m_mode || "ISCAT" == m_mode)) { submode = m_nSubMode + 65; From c3d8e4dd73fff65d559e38071d9a04639fb5b60c Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 26 Sep 2020 16:49:06 -0400 Subject: [PATCH 060/426] Working on QRA65, including simulator and test program. --- lib/qra/qra65/qra65sim.f90 | 4 +- lib/qra65_decode.f90 | 19 ++++--- lib/sync_qra65.f90 | 27 +++++----- lib/test_qra65.f90 | 101 ++++++++++++++++++++++++++----------- widgets/mainwindow.cpp | 11 +++- 5 files changed, 105 insertions(+), 57 deletions(-) diff --git a/lib/qra/qra65/qra65sim.f90 b/lib/qra/qra65/qra65sim.f90 index 299c95445..e91324d27 100644 --- a/lib/qra/qra65/qra65sim.f90 +++ b/lib/qra/qra65/qra65sim.f90 @@ -96,8 +96,8 @@ program qra65sim 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,f0,csubmode,xsnr,xdt,fspread,msgsent -1020 format(i4,i6,f7.1,2x,a1,2x,f5.1,f6.2,f6.1,1x,a22) + write(*,1020) ifile,ntrperiod,f0,csubmode,snrdb,xdt,fspread,msgsent +1020 format(i4,i6,f7.1,2x,a1,2x,f5.1,f6.2,f6.1,2x,a22) phi=0.d0 dphi=0.d0 k=(xdt+0.5)*12000 !Start audio at t=xdt+0.5 s (TR=15 and 30 s) diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index 1f4b0a1f2..0f04565f7 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -78,7 +78,7 @@ contains baud=12000.0/nsps df1=12000.0/nfft1 -! do i=1,NMAX +! do i=1,12000*ntrperiod ! write(61,3061) i/12000.0,iwave(i)/32767.0 !3061 format(2f12.6) ! enddo @@ -120,24 +120,27 @@ contains call timer('sync_q65',1) ! Downsample to give complex data at 6000 S/s + call timer('down_q65',0) fac=2.0/nfft1 c0=fac*iwave(1:nfft1) call four2a(c0,nfft1,1,-1,1) !Forward c2c FFT 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 - - jpk=(xdt+0.5)*6000 - 384 !### Empirical ### - if(ntrperiod.ge.60) jpk=(xdt+1.0)*6000 - 384 !### TBD ### - if(jpk.lt.0) jpk=0 a=0. a(1)=-(f0 + mode65*baud) !Data tones start mode65 bins higher call twkfreq(c0,c0,ntrperiod*6000,6000.0,a) + call timer('down_q65',1) + + jpk=(xdt+0.5)*6000 - 384 !### Empirical ### + if(ntrperiod.ge.60) jpk=(xdt+1.0)*6000 - 384 !### TBD ??? ### + if(jpk.lt.0) jpk=0 xdt=jpk/6000.0 - 0.5 - LL=64*(mode65+2) NN=63 - call spec_qra65(c0(jpk:),nsps/2,s3,LL,NN) !Compute synchronized symbol spectra + call timer('spec_q65',0) + call spec_qra65(c0(jpk:),nsps/2,s3,LL,NN) !Compute synced symbol spectra + call timer('spec_q65',1) do j=1,63 !Normalize to symbol baseline call pctile(s3(:,j),LL,40,base) @@ -188,7 +191,7 @@ contains irc,qual,ntrperiod,fmid,w50) else snr2=0. - nsnr=-25 + nsnr=-25 !### TEMPORARY? ### call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) diff --git a/lib/sync_qra65.f90 b/lib/sync_qra65.f90 index 1a7859cb1..21c155415 100644 --- a/lib/sync_qra65.f90 +++ b/lib/sync_qra65.f90 @@ -6,7 +6,7 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) integer ijpk(2) !Indices i and j at peak of ccf real, allocatable :: s1(:,:) !Symbol spectra, quarter-symbol steps real sync(85) !sync vector - real ccf(-64:64,-15:15) + real ccf(-64:64,-26:107) complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ data sync(1)/99.0/ @@ -64,24 +64,20 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) !3060 format(i6,f10.3,e12.3) ! enddo + dt4=nsps/(NSTEP*12000.0) !1/4 of symbol duration + j0=0.5/dt4 + if(nsps.ge.7680) j0=1.0/dt4 + ccf=0. ia=min(64,nint(ntol/df)) + lag1=-1.0/dt4 + lag2=4.0/dt4 + 0.9999 - jadd=11 - if(nsps.ge.3600) jadd=7 - if(nsps.ge.7680) jadd=6 - if(nsps.ge.16000) jadd=3 - if(nsps.ge.41472) jadd=1 - dt4=nsps/(NSTEP*12000.0) !1/4 of symbol duration -! print*,'DT range +/-',15*dt4 -! j0=0.5/dt4 -! if(nsps.ge.7680) j0=1.0/dt4 - do i=-ia,ia - do lag=-15,15 + do lag=lag1,lag2 do k=1,85 n=NSTEP*(k-1) + 1 - j=n+lag+jadd + j=n+lag+j0 if(j.ge.1 .and. j.le.jz) then ccf(i,lag)=ccf(i,lag) + sync(k)*s1(i0+i,j) endif @@ -93,13 +89,14 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) ! write(61,3061) i,ccf(i,jpk) !3061 format(i5,e12.3) ! enddo -! do j=-15,15 +! do j=lag1,lag2 ! write(62,3061) j,ccf(ipk,j) ! enddo ijpk=maxloc(ccf) ipk=ijpk(1)-65 - jpk=ijpk(2)-16 +! jpk=ijpk(2)-16 + jpk=ijpk(2)-27 f0=nfqso + ipk*df xdt=jpk*dt4 snr1=maxval(ccf)/22.0 diff --git a/lib/test_qra65.f90 b/lib/test_qra65.f90 index 67af7a720..2ebe08264 100644 --- a/lib/test_qra65.f90 +++ b/lib/test_qra65.f90 @@ -1,16 +1,15 @@ program test_qra65 - character*70 cmd1,cmd2,line + character*71 cmd1,cmd2,line character*22 msg character*8 arg integer nretcode(0:11) - integer fDop logical decok nargs=iargc() - if(nargs.ne.7) then - print*,'Usage: test_qra65 "msg" ndepth freq DT fDop nfiles SNR' - print*,'Example: test_qra65 "K1ABC W9XYZ EN37" 3 1500 0.0 5 100 0' + if(nargs.ne.8) then + print*,'Usage: test_qra65 "msg" ndepth freq DT fDop TRp nfiles SNR' + print*,'Example: test_qra65 "K1ABC W9XYZ EN37" 3 1500 0.0 5.0 60 100 -20' print*,' SNR = 0 to loop over all relevant SNRs' go to 999 endif @@ -24,32 +23,61 @@ program test_qra65 call getarg(5,arg) read(arg,*) fDop call getarg(6,arg) - read(arg,*) nfiles + read(arg,*) ntrperiod call getarg(7,arg) + read(arg,*) nfiles + call getarg(8,arg) read(arg,*) nsnr -! 1 2 3 4 5 6 -! 1234567890123456789012345678901234567890123456789012345678901234' - cmd1='qra65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 100 -10 > junk0' - cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 1 *.wav > junk' - - write(cmd1(10:33),'(a)') '"'//msg//'"' - write(cmd1(37:40),'(i4)') nf0 - write(cmd1(41:45),'(i5)') fDop - write(cmd1(46:50),'(f5.2)') dt - write(cmd1(51:55),'(i5)') nfiles - write(cmd2(32:32),'(i1)') ndepth - call system('rm -f *.wav') - - write(*,1000) (j,j=0,11) - write(12,1000) (j,j=0,11) -1000 format(/'SNR d Dop Sync Dec1 DecN Bad',i5,11i4,' tdec'/83('-')) - ia=-12 - ib=-30 + if(ntrperiod.eq.15) then + nsps=1800 + i50=-21 + else if(ntrperiod.eq.30) then + nsps=3600 + i50=-24 + else if(ntrperiod.eq.60) then + nsps=7680 + i50=-28 + else if(ntrperiod.eq.120) then + nsps=16000 + i50=-31 + else if(ntrperiod.eq.300) then + nsps=41472 + i50=-35 + else + stop 'Invalid TR period' + endif + ia=i50 + 8 + ib=i50 - 5 if(nsnr.ne.0) then ia=nsnr ib=nsnr endif + + baud=12000.0/nsps + tsym=1.0/baud + +! 1 2 3 4 5 6 7 +! 12345678901234567890123456789012345678901234567890123456789012345678901' + cmd1='qra65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 -10 > junk0' + cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 1 *.wav > junk' + + write(cmd1(10:33),'(a)') '"'//msg//'"' + write(cmd1(37:40),'(i4)') nf0 + write(cmd1(41:45),'(f5.1)') fDop + write(cmd1(46:50),'(f5.2)') dt + write(cmd1(51:54),'(i4)') ntrperiod + write(cmd1(55:59),'(i5)') nfiles + write(cmd2(11:13),'(i3)') ntrperiod + write(cmd2(33:33),'(i1)') ndepth + call system('rm -f *.wav') + + write(*,1000) (j,j=0,11) + write(12,1000) (j,j=0,11) +1000 format(/'SNR d Dop Sync Dec1 DecN Bad',i6,11i4,' tdec dtavg dtrms'/97('-')) + + dterr=3.0*tsym/4.0 + nferr=max(1,nint(0.5*baud)) do nsnr=ia,ib,-1 nsync=0 @@ -57,7 +85,9 @@ program test_qra65 nfalse=0 nretcode=0 navg=0 - write(cmd1(57:59),'(i3)') nsnr + sumxdt=0. + sqxdt=0. + write(cmd1(61:63),'(i3)') nsnr call system(cmd1) call sec0(0,tdec) call system(cmd2) @@ -65,10 +95,11 @@ program test_qra65 open(10,file='junk',status='unknown') n=0 do iline=1,9999 - read(10,'(a70)',end=10) line + read(10,'(a71)',end=10) line if(len(trim(line)).lt.60) cycle read(line(11:20),*) xdt,nf - if(abs(xdt-dt).lt.0.15 .and. abs(nf-nf0).lt.4) nsync=nsync+1 +! if(ntrperiod.eq.60) xdt=xdt-0.5 !### TEMPORARY ### + if(abs(xdt-dt).le.dterr .and. abs(nf-nf0).le.nferr) nsync=nsync+1 read(line(60:),*) irc,iavg if(irc.lt.0) cycle decok=index(line,'W9XYZ').gt.0 @@ -76,6 +107,8 @@ program test_qra65 i=irc if(i.le.11) then ndecodes=ndecodes + 1 + sumxdt=sumxdt + xdt + sqxdt=sqxdt + xdt*xdt navg=navg + 1 else i=mod(i,10) @@ -88,9 +121,17 @@ program test_qra65 endif enddo 10 close(10) - write(*,1100) nsnr,ndepth,fDop,nsync,ndecodes,navg,nfalse,nretcode,tdec/nfiles - write(12,1100) nsnr,ndepth,fDop,nsync,ndecodes,navg,nfalse,nretcode,tdec/nfiles -1100 format(i3,i2,i3,3i5,i4,i6,11i4,f6.2) + xdt_avg=0. + xdt_rms=0. + if(ndecodes.ge.2) then + xdt_avg=sumxdt/ndecodes + xdt_rms=sqxdt/(ndecodes-1) - xdt_avg*xdt_avg + endif + write(*,1100) nsnr,ndepth,fDop,nsync,ndecodes,navg,nfalse,nretcode, & + tdec/nfiles,xdt_avg,xdt_rms + write(12,1100) nsnr,ndepth,fDop,nsync,ndecodes,navg,nfalse,nretcode, & + tdec/nfiles,xdt_avg,xdt_rms +1100 format(i3,i2,f5.1,3i5,i4,i6,11i4,3f6.2) flush(6) flush(12) enddo diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 1cd40b1fe..df2117292 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6326,23 +6326,30 @@ void MainWindow::on_actionQRA64_triggered() void MainWindow::on_actionQRA65_triggered() { - on_actionFST4_triggered(); +// on_actionFST4_triggered(); m_mode="QRA65"; m_modeTx="QRA65"; ui->actionQRA65->setChecked(true); switch_mode(Modes::QRA65); setup_status_bar(true); + m_nsps=6912; //For symspec only + m_FFTSize = m_nsps / 2; + Q_EMIT FFTSize(m_FFTSize); m_hsymStop=49; ui->sbTR->values ({15, 30, 60, 120, 300}); on_sbTR_valueChanged (ui->sbTR->value()); ui->sbSubmode->setMaximum(3); ui->sbSubmode->setValue(m_nSubMode); m_wideGraph->setMode(m_mode); + m_wideGraph->setMode(m_mode); m_wideGraph->setModeTx(m_modeTx); m_wideGraph->setPeriod(m_TRperiod,6912); m_wideGraph->setTol(ui->sbFtol->value()); + m_wideGraph->setRxFreq(ui->RxFreqSpinBox->value()); + m_wideGraph->setTxFreq(ui->TxFreqSpinBox->value()); // 0123456789012345678901234567890123 displayWidgets(nWidgets("1111110101101101000100000011000000")); - statusChanged();} + statusChanged(); +} void MainWindow::on_actionISCAT_triggered() { From 5d1fb28d2b64cb761065c2339be947e3165a9664 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 3 Oct 2020 08:25:17 -0400 Subject: [PATCH 061/426] Working on test_qra6[45].f90. --- CMakeLists.txt | 3 + lib/test_qra64.f90 | 134 +++++++++++++++++++++++++++++++++++++++++++++ lib/test_qra65.f90 | 26 +++++---- 3 files changed, 152 insertions(+), 11 deletions(-) create mode 100644 lib/test_qra64.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 712e176ad..95a8cfb57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1338,6 +1338,9 @@ target_link_libraries (qra64sim wsjt_fort wsjt_cxx) add_executable (qra65sim lib/qra/qra65/qra65sim.f90 wsjtx.rc) target_link_libraries (qra65sim wsjt_fort wsjt_cxx) +add_executable (test_qra64 lib/test_qra64.f90 wsjtx.rc) +target_link_libraries (test_qra64 wsjt_fort wsjt_cxx) + add_executable (test_qra65 lib/test_qra65.f90 wsjtx.rc) target_link_libraries (test_qra65 wsjt_fort wsjt_cxx) diff --git a/lib/test_qra64.f90 b/lib/test_qra64.f90 new file mode 100644 index 000000000..751a9e9a4 --- /dev/null +++ b/lib/test_qra64.f90 @@ -0,0 +1,134 @@ +program test_qra64 + + character*71 cmd1,cmd2,line + character*22 msg + character*8 arg + character*1 csubmode + integer nretcode(0:11) + logical decok + + nargs=iargc() + if(nargs.ne.9) then + print*,'Usage: test_qra64 "msg" A-D depth freq DT fDop TRp nfiles SNR' + print*,'Example: test_qra64 "K1ABC W9XYZ EN37" A 3 1000 0.0 5.0 60 100 -20' + print*,' SNR = 0 to loop over all relevant SNRs' + go to 999 + endif + call getarg(1,msg) + call getarg(2,csubmode) + call getarg(3,arg) + read(arg,*) ndepth + call getarg(4,arg) + read(arg,*) nf0 + call getarg(5,arg) + read(arg,*) dt + call getarg(6,arg) + read(arg,*) fDop + call getarg(7,arg) + read(arg,*) ntrperiod + call getarg(8,arg) + read(arg,*) nfiles + call getarg(9,arg) + read(arg,*) nsnr + + nsps=7680 + i50=-28 + ia=-20 + ib=-33 + if(nsnr.ne.0) then + ia=nsnr + ib=nsnr + endif + + baud=12000.0/nsps + tsym=1.0/baud + +! 1 2 3 4 5 6 7 +! 12345678901234567890123456789012345678901234567890123456789012345678901' + cmd1='qra64sim "K1ABC W9XYZ EN37 " A 1 0.2 0.00 100 -20 > junk0' + + cmd2='jt9 -q -L 300 -H 3000 -f 1000 -d 3 -b A *.wav > junk' +! jt9 -q -L 300 -H 3000 -f 1000 -d 3 -b A *.w1000 unk + + write(cmd1(10:33),'(a)') '"'//msg//'"' + cmd1(35:35)=csubmode + write(cmd1(40:43),'(f4.1)') fDop + write(cmd1(44:48),'(f5.2)') dt + write(cmd1(49:53),'(i5)') nfiles + + write(cmd2(26:29),'(i4)') nf0 + write(cmd2(34:34),'(i1)') ndepth + cmd2(39:39)=csubmode + + call system('rm -f *.wav') + + write(*,1000) (j,j=0,11) + write(12,1000) (j,j=0,11) +1000 format(/'SNR d Dop Sync Dec1 DecN Bad',i6,11i4,' tdec dtavg dtrms'/97('-')) + + dterr=tsym/4.0 + nferr=max(1,nint(0.5*baud)) + + do nsnr=ia,ib,-1 + nsync=0 + ndecodes=0 + nfalse=0 + nretcode=0 + navg=0 + sumxdt=0. + sqxdt=0. + write(cmd1(55:57),'(i3)') nsnr + call system(cmd1) + call sec0(0,tdec) + call system(cmd2) + call sec0(1,tdec) + open(10,file='junk',status='unknown') + n=0 + do iline=1,9999 + read(10,'(a71)',end=10) line + if(index(line,' Date: Sat, 3 Oct 2020 11:02:18 -0400 Subject: [PATCH 062/426] Tweak test_qra64.f90 --- lib/test_qra64.f90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/test_qra64.f90 b/lib/test_qra64.f90 index 751a9e9a4..c16abde2f 100644 --- a/lib/test_qra64.f90 +++ b/lib/test_qra64.f90 @@ -33,7 +33,7 @@ program test_qra64 nsps=7680 i50=-28 - ia=-20 + ia=-24 ib=-33 if(nsnr.ne.0) then ia=nsnr @@ -48,7 +48,6 @@ program test_qra64 cmd1='qra64sim "K1ABC W9XYZ EN37 " A 1 0.2 0.00 100 -20 > junk0' cmd2='jt9 -q -L 300 -H 3000 -f 1000 -d 3 -b A *.wav > junk' -! jt9 -q -L 300 -H 3000 -f 1000 -d 3 -b A *.w1000 unk write(cmd1(10:33),'(a)') '"'//msg//'"' cmd1(35:35)=csubmode From 95ec019da3bca7add7b5cb1f18a75797c2290aa4 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 4 Oct 2020 12:29:18 -0400 Subject: [PATCH 063/426] Improvements to test_qra64.# Please enter the commit message for your changes. Lines starting --- lib/test_qra64.f90 | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/lib/test_qra64.f90 b/lib/test_qra64.f90 index c16abde2f..f816244a3 100644 --- a/lib/test_qra64.f90 +++ b/lib/test_qra64.f90 @@ -33,7 +33,7 @@ program test_qra64 nsps=7680 i50=-28 - ia=-24 + ia=-20 ib=-33 if(nsnr.ne.0) then ia=nsnr @@ -63,19 +63,17 @@ program test_qra64 write(*,1000) (j,j=0,11) write(12,1000) (j,j=0,11) -1000 format(/'SNR d Dop Sync Dec1 DecN Bad',i6,11i4,' tdec dtavg dtrms'/97('-')) +1000 format(/'SNR d Dop Sync Dec Bad',i6,11i4,' tdec'/80('-')) dterr=tsym/4.0 - nferr=max(1,nint(0.5*baud)) + nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) + ndecodes0=nfiles do nsnr=ia,ib,-1 nsync=0 ndecodes=0 nfalse=0 nretcode=0 - navg=0 - sumxdt=0. - sqxdt=0. write(cmd1(55:57),'(i3)') nsnr call system(cmd1) call sec0(0,tdec) @@ -89,17 +87,15 @@ program test_qra64 read(line(11:20),*) xdt,nf irc=-1 if(line(23:23).ne.' ') read(line(45:46),*) irc -! write(71,3071) dt,xdt,abs(xdt-dt),dterr,nf0,nf,abs(nf-nf0),nferr,irc -!3071 format(4f6.2,4i5,i8) - if(abs(xdt-dt).le.dterr .and. abs(nf-nf0).le.nferr) nsync=nsync+1 - if(irc.lt.0) cycle decok=index(line,'W9XYZ').gt.0 + if((abs(xdt-dt).le.dterr .and. abs(nf-nf0).le.nferr) .or. decok) then + nsync=nsync+1 + endif + if(irc.lt.0) cycle if(decok) then i=irc if(i.le.11) then ndecodes=ndecodes + 1 - sumxdt=sumxdt + xdt - sqxdt=sqxdt + xdt*xdt navg=navg + 1 else i=mod(i,10) @@ -110,22 +106,24 @@ program test_qra64 nfalse=nfalse + 1 print*,'False: ',line endif - enddo + enddo ! iline 10 close(10) - xdt_avg=0. - xdt_rms=0. - if(ndecodes.ge.2) then - xdt_avg=sumxdt/ndecodes - xdt_rms=sqxdt/(ndecodes-1) - xdt_avg*xdt_avg + write(*,1100) nsnr,ndepth,fDop,nsync,ndecodes,nfalse,nretcode, & + tdec/nfiles + write(12,1100) nsnr,ndepth,fDop,nsync,ndecodes,nfalse,nretcode, & + tdec/nfiles +1100 format(i3,i2,f5.1,2i5,i4,i6,11i4,f6.2) + if(ndecodes.lt.nfiles/2 .and. ndecodes0.ge.nfiles/2) then + snr_thresh=nsnr + float(nfiles/2 - ndecodes)/(ndecodes0-ndecodes) + write(13,1200) ndepth,fdop,csubmode,snr_thresh +1200 format(i1,f6.1,2x,a1,f7.1) + flush(13) endif - write(*,1100) nsnr,ndepth,fDop,nsync,ndecodes,navg,nfalse,nretcode, & - tdec/nfiles,xdt_avg,xdt_rms - write(12,1100) nsnr,ndepth,fDop,nsync,ndecodes,navg,nfalse,nretcode, & - tdec/nfiles,xdt_avg,xdt_rms -1100 format(i3,i2,f5.1,3i5,i4,i6,11i4,3f6.2) flush(6) flush(12) - enddo + if(ndecodes.eq.0) exit !Bail out if no decodes at this SNR + ndecodes0=ndecodes + enddo ! nsnr 999 end program test_qra64 From 20fb18c8ae1af9ed085efc9bb5824aaf03cab332 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 4 Oct 2020 15:22:45 -0400 Subject: [PATCH 064/426] Minor code cleanup. --- lib/sync64.f90 | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/sync64.f90 b/lib/sync64.f90 index d927af9c9..f6d438e32 100644 --- a/lib/sync64.f90 +++ b/lib/sync64.f90 @@ -5,14 +5,14 @@ subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & parameter (NMAX=60*12000) !Max size of raw data at 12000 Hz parameter (NSPS=3456) !Samples per symbol at 6000 Hz - parameter (NSPC=7*NSPS) !Samples per Costas array + parameter (NSPC=7*NSPS) !Samples per Costas waveform real s1(0:NSPC-1) !Power spectrum of Costas 1 real s2(0:NSPC-1) !Power spectrum of Costas 2 real s3(0:NSPC-1) !Power spectrum of Costas 3 real s0(0:NSPC-1) !Sum of s1+s2+s3 real s0a(0:NSPC-1) !Best synchromized spectrum (saved) real s0b(0:NSPC-1) !tmp - real a(5) + real a(5) !Parameters of Lorentzian fit integer icos7(0:6) !Costas 7x7 tones integer ipk0(1) complex cc(0:NSPC-1) !Costas waveform @@ -25,11 +25,12 @@ subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & save if(mode64.ne.mode64z) then +! Submode has changed, recompute the complex Costas waveform twopi=8.0*atan(1.0) dfgen=mode64*12000.0/6912.0 k=-1 phi=0. - do j=0,6 !Compute complex Costas waveform + do j=0,6 dphi=twopi*10.0*icos7(j)*dfgen/6000.0 do i=1,NSPS phi=phi + dphi @@ -42,7 +43,6 @@ subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & endif nfft3=NSPC - nh3=nfft3/2 df3=6000.0/nfft3 fa=max(nf1,nfqso-ntol) @@ -61,7 +61,7 @@ subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & smaxall=0. jpk=0 ja=0 - jb=(5.0+emedelay)*6000 + jb=(5.0+emedelay)*6000 !Bigger range than necessary? jstep=100 ipk=0 kpk=0 @@ -69,7 +69,7 @@ subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & if(mod(nadd,2).eq.0) nadd=nadd+1 !Make nadd odd nskip=max(49,nadd) - do j1=ja,jb,jstep + do j1=ja,jb,jstep !Loop over DT call timer('sync64_1',0) j2=j1 + 39*NSPS j3=j1 + 77*NSPS @@ -95,7 +95,7 @@ subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & s0(ia:ib)=s1(ia:ib) + s2(ia:ib) + s3(ia:ib) s0(:ia-1)=0. s0(ib+1:)=0. - if(nadd.ge.3) then + if(nadd.ge.3) then !Smooth the spectrum do ii=1,3 s0b(ia:ib)=s0(ia:ib) call smo(s0b(ia:ib),iz,s0(ia:ib),nadd) @@ -114,7 +114,7 @@ subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & f0=ip*df3 endif call timer('sync64_2',1) - enddo + enddo ! j1 (DT loop) s0a=s0a+2.0 ! write(17) ia,ib,s0a(ia:ib) !Save data for red curve @@ -141,7 +141,6 @@ subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & rewind 17 write(17,1110) 0.0,0.0 rewind 17 -! rewind 76 do i=2,iz-2*nskip-1,3 x=i z=(x-a(3))/(0.5*a(4)) @@ -155,11 +154,9 @@ subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & ss=(s0a(j-1)+s0a(j)+s0a(j+1))/3.0 if(ss.gt.slimit) write(17,1110) freq,ss 1110 format(3f10.3) -! write(76,1110) freq,ss,yfit enddo flush(17) close(17) -! flush(76) return end subroutine sync64 From 333fac1fae7778064113697c57a5bc7c6c5b8219 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 4 Oct 2020 15:32:08 -0400 Subject: [PATCH 065/426] Code cleanup. --- lib/lorentzian.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lorentzian.f90 b/lib/lorentzian.f90 index cd2257a75..f3f4e14ef 100644 --- a/lib/lorentzian.f90 +++ b/lib/lorentzian.f90 @@ -1,7 +1,7 @@ subroutine lorentzian(y,npts,a) ! Input: y(npts); assume x(i)=i, i=1,npts -! Output: a(5) +! Output: a(1:5) ! a(1) = baseline ! a(2) = amplitude ! a(3) = x0 From df034cf9b51ca622edbec81d12615fd72aec445b Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 4 Oct 2020 16:10:44 -0400 Subject: [PATCH 066/426] Don't set TxFreq and RxFreq defaults on QRA64 startup. --- widgets/mainwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index df2117292..2def05a2d 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6316,8 +6316,8 @@ void MainWindow::on_actionQRA64_triggered() ui->sbSubmode->setValue(m_nSubMode); ui->actionInclude_averaging->setVisible (false); ui->actionInclude_correlation->setVisible (false); - ui->RxFreqSpinBox->setValue(1000); - ui->TxFreqSpinBox->setValue(1000); +// ui->RxFreqSpinBox->setValue(1000); +// ui->TxFreqSpinBox->setValue(1000); QString fname {QDir::toNativeSeparators(m_config.temp_dir ().absoluteFilePath ("red.dat"))}; m_wideGraph->setRedFile(fname); displayWidgets(nWidgets("1111100100101101100000000010000000")); From c17acdb4474e45b958755c3ec9d54ac355740074 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 5 Oct 2020 09:48:44 -0400 Subject: [PATCH 067/426] Activate Fast and Normal decode options for QRA64. --- widgets/mainwindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 2def05a2d..60b3bf1ae 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6320,7 +6320,8 @@ void MainWindow::on_actionQRA64_triggered() // ui->TxFreqSpinBox->setValue(1000); QString fname {QDir::toNativeSeparators(m_config.temp_dir ().absoluteFilePath ("red.dat"))}; m_wideGraph->setRedFile(fname); - displayWidgets(nWidgets("1111100100101101100000000010000000")); +// 0123456789012345678901234567890123 + displayWidgets(nWidgets("1111100100101101100100000010000000")); statusChanged(); } From f38f355395886f03286e0f9ffc6b6a8b40649e6e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 5 Oct 2020 09:50:55 -0400 Subject: [PATCH 068/426] Many tweaks to QRA64 decoder to optimize processing of 6m ionoscatter signals. --- lib/qra64a.f90 | 69 +++++++++++++++++++++++++++---------------- lib/sync64.f90 | 14 +++++---- widgets/mainwindow.ui | 2 +- 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index 5d314f2b4..12198ad44 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -12,7 +12,7 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & logical ltext complex c00(0:720000) !Complex spectrum of dd() complex c0(0:720000) !Complex data for dd() - real a(3) + real a(3) !twkfreq params f,f1,f2 real dd(NMAX) !Raw data sampled at 12000 Hz real s3(LN) !Symbol spectra real s3a(LN) !Symbol spectra @@ -62,39 +62,49 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & npts2=npts/2 call timer('sync64 ',0) - call sync64(c00,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk0,sync, & - sync2,width) + call sync64(c00,nf1,nf2,nfqso,ntol,minsync,mode64,emedelay,dtx,f0, & + jpk0,sync,sync2,width) call timer('sync64 ',1) nfreq=nint(f0) - if(mode64.eq.1 .and. minsync.ge.0 .and. (sync-7.0).lt.minsync) go to 900 -! if((sync-3.4).lt.float(minsync) .or.width.gt.340.0) go to 900 - a=0. - a(1)=-f0 - call twkfreq(c00,c0,npts2,6000.0,a) + if(mode64.eq.1 .and. minsync.ne.-1 .and. (sync-7.0).lt.minsync) go to 900 irc=-99 s3lim=20. - itz=11 - if(mode64.eq.4) itz=9 - if(mode64.eq.2) itz=7 - if(mode64.eq.1) itz=5 - + ibwmax=11 + if(mode64.le.4) ibwmax=9 + ibwmin=0 + idtmax=5 + if(minsync.eq.-2) then + ibwmin=ibwmax + idtmax=3 + endif LL=64*(mode64+2) NN=63 napmin=99 - do itry0=1,5 - idt=itry0/2 - if(mod(itry0,2).eq.0) idt=-idt + ncall=0 + + do idf0=1,11 + idf=idf0/2 + if(mod(idf0,2).eq.0) idf=-idf + + a=0. + a(1)=-(f0+0.868*idf) + call twkfreq(c00,c0,npts2,6000.0,a) + + do idt0=1,idtmax + idt=idt0/2 + if(mod(idt0,2).eq.0) idt=-idt jpk=jpk0 + 750*idt call spec64(c0,jpk,s3a,LL,NN) call pctile(s3a,LL*NN,40,base) s3a=s3a/base where(s3a(1:LL*NN)>s3lim) s3a(1:LL*NN)=s3lim - do iter=itz,0,-2 - b90=1.728**iter + do ibw=ibwmax,ibwmin,-2 + b90=1.728**ibw if(b90.gt.230.0) cycle if(b90.lt.0.15*width) exit s3(1:LL*NN)=s3a(1:LL*NN) + ncall=ncall+1 call timer('qra64_de',0) call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & nFadingModel,dat4,snr2,irc) @@ -109,21 +119,27 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & napmin=nap(iirc) irckeep=irc dtxkeep=jpk/6000.0 - 1.0 - itry0keep=itry0 - iterkeep=iter + f0keep=-a(1) + idfkeep=idf + idtkeep=idt + ibwkeep=ibw endif enddo - if(irc.eq.0) exit - enddo + if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 + if(irc.eq.0) go to 100 + enddo ! idt (DT loop) + enddo ! idf (f0 loop) - if(napmin.ne.99) then +100 if(napmin.ne.99) then dat4=dat4x b90=b90x snr2=snr2x irc=irckeep dtx=dtxkeep - itry0=itry0keep - iter=iterkeep + f0=f0keep + idt=idtkeep + idf=idfkeep + ibw=ibwkeep endif 10 decoded=' ' @@ -140,6 +156,9 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & else snr2=0. endif + nfreq=nint(f0) + write(71,3071) idf,idt,ncall,irc,nsnr,dtx,nfreq,decoded +3071 format(5i5,f7.2,i6,2x,a22) 900 if(irc.lt.0) then sy=max(1.0,sync) diff --git a/lib/sync64.f90 b/lib/sync64.f90 index f6d438e32..e95814303 100644 --- a/lib/sync64.f90 +++ b/lib/sync64.f90 @@ -1,5 +1,5 @@ -subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & - sync2,width) +subroutine sync64(c0,nf1,nf2,nfqso,ntol,minsync,mode64,emedelay,dtx,f0, & + jpk,sync,sync2,width) use timer_module, only: timer @@ -61,11 +61,13 @@ subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & smaxall=0. jpk=0 ja=0 - jb=(5.0+emedelay)*6000 !Bigger range than necessary? +! jb=(5.0+emedelay)*6000 !Bigger range than necessary? + jb=(2.0+emedelay)*6000 !Bigger range than necessary? jstep=100 ipk=0 kpk=0 nadd=10*mode64 + if(minsync.eq.-2) nadd=10 !### if(mod(nadd,2).eq.0) nadd=nadd+1 !Make nadd odd nskip=max(49,nadd) @@ -96,7 +98,9 @@ subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & s0(:ia-1)=0. s0(ib+1:)=0. if(nadd.ge.3) then !Smooth the spectrum - do ii=1,3 + iiz=3 + if(minsync.eq.-2) iiz=1 + do ii=1,iiz !### Was ii=1,3 s0b(ia:ib)=s0(ia:ib) call smo(s0b(ia:ib),iz,s0(ia:ib),nadd) enddo @@ -117,8 +121,6 @@ subroutine sync64(c0,nf1,nf2,nfqso,ntol,mode64,emedelay,dtx,f0,jpk,sync, & enddo ! j1 (DT loop) s0a=s0a+2.0 -! write(17) ia,ib,s0a(ia:ib) !Save data for red curve -! close(17) nskip=50 call lorentzian(s0a(ia+nskip:ib-nskip),iz-2*nskip,a) diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index 33ffd38ca..f65b50480 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -799,7 +799,7 @@ QPushButton[state="ok"] { Sync - -1 + -2 10 From 69f7cea5e65270bf9f6efdc26868a4561ebea233 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 6 Oct 2020 10:29:03 -0400 Subject: [PATCH 069/426] Formatting adjustments to qra64a.f90; updates to test_qra6[45].f90. --- lib/qra64a.f90 | 84 +++++++++++++++++++++++----------------------- lib/test_qra64.f90 | 2 -- lib/test_qra65.f90 | 41 +++++++++++----------- 3 files changed, 64 insertions(+), 63 deletions(-) diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index 12198ad44..f880b6ab0 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -84,50 +84,50 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & ncall=0 do idf0=1,11 - idf=idf0/2 - if(mod(idf0,2).eq.0) idf=-idf + idf=idf0/2 + if(mod(idf0,2).eq.0) idf=-idf - a=0. - a(1)=-(f0+0.868*idf) - call twkfreq(c00,c0,npts2,6000.0,a) + a=0. + a(1)=-(f0+0.868*idf) + call twkfreq(c00,c0,npts2,6000.0,a) - do idt0=1,idtmax - idt=idt0/2 - if(mod(idt0,2).eq.0) idt=-idt - jpk=jpk0 + 750*idt - call spec64(c0,jpk,s3a,LL,NN) - call pctile(s3a,LL*NN,40,base) - s3a=s3a/base - where(s3a(1:LL*NN)>s3lim) s3a(1:LL*NN)=s3lim - do ibw=ibwmax,ibwmin,-2 - b90=1.728**ibw - if(b90.gt.230.0) cycle - if(b90.lt.0.15*width) exit - s3(1:LL*NN)=s3a(1:LL*NN) - ncall=ncall+1 - call timer('qra64_de',0) - call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & - nFadingModel,dat4,snr2,irc) - call timer('qra64_de',1) - if(irc.eq.0) go to 10 - if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) - iirc=max(0,min(irc,11)) - if(irc.gt.0 .and. nap(iirc).lt.napmin) then - dat4x=dat4 - b90x=b90 - snr2x=snr2 - napmin=nap(iirc) - irckeep=irc - dtxkeep=jpk/6000.0 - 1.0 - f0keep=-a(1) - idfkeep=idf - idtkeep=idt - ibwkeep=ibw - endif - enddo - if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 - if(irc.eq.0) go to 100 - enddo ! idt (DT loop) + do idt0=1,idtmax + idt=idt0/2 + if(mod(idt0,2).eq.0) idt=-idt + jpk=jpk0 + 750*idt + call spec64(c0,jpk,s3a,LL,NN) + call pctile(s3a,LL*NN,40,base) + s3a=s3a/base + where(s3a(1:LL*NN)>s3lim) s3a(1:LL*NN)=s3lim + do ibw=ibwmax,ibwmin,-2 + b90=1.728**ibw + if(b90.gt.230.0) cycle + if(b90.lt.0.15*width) exit + s3(1:LL*NN)=s3a(1:LL*NN) + ncall=ncall+1 + call timer('qra64_de',0) + call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & + nFadingModel,dat4,snr2,irc) + call timer('qra64_de',1) + if(irc.eq.0) go to 10 + if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) + iirc=max(0,min(irc,11)) + if(irc.gt.0 .and. nap(iirc).lt.napmin) then + dat4x=dat4 + b90x=b90 + snr2x=snr2 + napmin=nap(iirc) + irckeep=irc + dtxkeep=jpk/6000.0 - 1.0 + f0keep=-a(1) + idfkeep=idf + idtkeep=idt + ibwkeep=ibw + endif + enddo ! ibw (b90 loop) + if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 + if(irc.eq.0) go to 100 + enddo ! idt (DT loop) enddo ! idf (f0 loop) 100 if(napmin.ne.99) then diff --git a/lib/test_qra64.f90 b/lib/test_qra64.f90 index f816244a3..760c87452 100644 --- a/lib/test_qra64.f90 +++ b/lib/test_qra64.f90 @@ -96,10 +96,8 @@ program test_qra64 i=irc if(i.le.11) then ndecodes=ndecodes + 1 - navg=navg + 1 else i=mod(i,10) - navg=navg + 1 endif nretcode(i)=nretcode(i) + 1 else diff --git a/lib/test_qra65.f90 b/lib/test_qra65.f90 index 30e2c0d9f..76056cef8 100644 --- a/lib/test_qra65.f90 +++ b/lib/test_qra65.f90 @@ -80,8 +80,9 @@ program test_qra65 write(12,1000) (j,j=0,11) 1000 format(/'SNR d Dop Sync Dec1 DecN Bad',i6,11i4,' tdec dtavg dtrms'/97('-')) - dterr=3.0*tsym/4.0 - nferr=max(1,nint(0.5*baud)) + dterr=tsym/4.0 + nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) + ndecodes0=nfiles do nsnr=ia,ib,-1 nsync=0 @@ -89,8 +90,6 @@ program test_qra65 nfalse=0 nretcode=0 navg=0 - sumxdt=0. - sqxdt=0. write(cmd1(61:63),'(i3)') nsnr call system(cmd1) call sec0(0,tdec) @@ -100,19 +99,20 @@ program test_qra65 n=0 do iline=1,9999 read(10,'(a71)',end=10) line - if(len(trim(line)).lt.60) cycle + if(index(line,' Date: Tue, 6 Oct 2020 10:45:30 -0400 Subject: [PATCH 070/426] Add tentative 6m frequencies for QRA64 and QRA65. --- models/FrequencyList.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/models/FrequencyList.cpp b/models/FrequencyList.cpp index ec3c185ff..07b4d5a49 100644 --- a/models/FrequencyList.cpp +++ b/models/FrequencyList.cpp @@ -262,6 +262,8 @@ namespace {28180000, Modes::FT4, IARURegions::ALL}, {50200000, Modes::Echo, IARURegions::ALL}, + {50270000, Modes::QRA64, IARURegions::ALL}, + {50270000, Modes::QRA65, IARURegions::ALL}, {50276000, Modes::JT65, IARURegions::R2}, {50276000, Modes::JT65, IARURegions::R3}, {50380000, Modes::MSK144, IARURegions::R1}, From db1a24f1d4ccdec5cd7da89bf8fe52e6af99c334 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 6 Oct 2020 11:07:08 -0400 Subject: [PATCH 071/426] Update WideGraph parameters when entering QRA64 mode. --- widgets/mainwindow.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 60b3bf1ae..ed47afbfb 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6320,6 +6320,13 @@ void MainWindow::on_actionQRA64_triggered() // ui->TxFreqSpinBox->setValue(1000); QString fname {QDir::toNativeSeparators(m_config.temp_dir ().absoluteFilePath ("red.dat"))}; m_wideGraph->setRedFile(fname); + m_wideGraph->setMode(m_mode); + m_wideGraph->setModeTx(m_modeTx); + m_wideGraph->setPeriod(m_TRperiod,6912); + m_wideGraph->setTxFreq(ui->TxFreqSpinBox->value()); + m_wideGraph->setRxFreq(ui->RxFreqSpinBox->value()); + m_wideGraph->setTol(ui->sbFtol->value()); + switch_mode (Modes::FST4); // 0123456789012345678901234567890123 displayWidgets(nWidgets("1111100100101101100100000010000000")); statusChanged(); From 6e75a62811c75a00d607426f6169ad559b791bb0 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 6 Oct 2020 18:38:18 +0100 Subject: [PATCH 072/426] Correct mode switching for QRA64 and QRA65 --- widgets/mainwindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index ed47afbfb..ff9be4ec4 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6326,7 +6326,7 @@ void MainWindow::on_actionQRA64_triggered() m_wideGraph->setTxFreq(ui->TxFreqSpinBox->value()); m_wideGraph->setRxFreq(ui->RxFreqSpinBox->value()); m_wideGraph->setTol(ui->sbFtol->value()); - switch_mode (Modes::FST4); + switch_mode (Modes::QRA64); // 0123456789012345678901234567890123 displayWidgets(nWidgets("1111100100101101100100000010000000")); statusChanged(); @@ -6354,6 +6354,7 @@ void MainWindow::on_actionQRA65_triggered() m_wideGraph->setTol(ui->sbFtol->value()); m_wideGraph->setRxFreq(ui->RxFreqSpinBox->value()); m_wideGraph->setTxFreq(ui->TxFreqSpinBox->value()); + switch_mode (Modes::QRA65); // 0123456789012345678901234567890123 displayWidgets(nWidgets("1111110101101101000100000011000000")); statusChanged(); From bb8e6ea64ae36641da3426cb1c4afea7bf86a7ba Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 6 Oct 2020 19:55:43 +0100 Subject: [PATCH 073/426] Missed merge from develop branch conflict resolved --- widgets/mainwindow.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index b30b2043b..d211cbc1a 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3046,27 +3046,6 @@ void MainWindow::decode() //decode() dec_data.params.nutc=dec_data.params.nutc/100; } if(dec_data.params.nagain==0 && dec_data.params.newdat==1 && (!m_diskData)) { -<<<<<<< HEAD - qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000; - int imin=ms/60000; - int ihr=imin/60; - imin=imin % 60; - if(m_TRperiod>=60) imin=imin - (imin % (int(m_TRperiod)/60)); - dec_data.params.nutc=100*ihr + imin; - if(m_TRperiod < 60) { - qint64 ms=1000.0*(3.5-m_TRperiod); - if(m_mode=="FST4") ms=1000.0*(6.0-m_TRperiod); - //Adjust for FT8 early decode: - if(m_mode=="FT8" and m_ihsym==m_earlyDecode and !m_diskData) ms+=(m_hsymStop-m_earlyDecode)*288; - if(m_mode=="FT8" and m_ihsym==m_earlyDecode2 and !m_diskData) ms+=(m_hsymStop-m_earlyDecode2)*288; - QDateTime t=QDateTime::currentDateTimeUtc().addMSecs(ms); - ihr=t.toString("hh").toInt(); - imin=t.toString("mm").toInt(); - int isec=t.toString("ss").toInt(); - isec=isec - fmod(double(isec),m_TRperiod); - dec_data.params.nutc=10000*ihr + 100*imin + isec; - } -======= auto t_start = qt_truncate_date_time_to (QDateTime::currentDateTimeUtc (), m_TRperiod * 1.e3); auto t = t_start.time (); dec_data.params.nutc = t.hour () * 100 + t.minute (); @@ -3074,7 +3053,6 @@ void MainWindow::decode() //decode() { dec_data.params.nutc = dec_data.params.nutc * 100 + t.second (); } ->>>>>>> develop } if(m_nPick==1 and !m_diskData) { From 5e23f88f7e9a0a793ec9c7b93ed732a4c374d5c2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 7 Oct 2020 16:04:00 -0400 Subject: [PATCH 074/426] Save a working temporary state for QRA64/QRA65 decoders. --- CMakeLists.txt | 1 + lib/qra64a.f90 | 88 +++----------------------------------- lib/qra_loops.f90 | 105 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 82 deletions(-) create mode 100644 lib/qra_loops.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index d179f6da7..4d1246489 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -552,6 +552,7 @@ set (wsjt_FSRCS lib/prog_args.f90 lib/ps4.f90 lib/qra64a.f90 + lib/qra_loops.f90 lib/refspectrum.f90 lib/savec2.f90 lib/sec0.f90 diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index f880b6ab0..953a4789b 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -10,16 +10,10 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & character*6 mycall,hiscall,hisgrid_6 character*4 hisgrid logical ltext - complex c00(0:720000) !Complex spectrum of dd() - complex c0(0:720000) !Complex data for dd() - real a(3) !twkfreq params f,f1,f2 + complex c00(0:720000) !Analytic signal for dd() real dd(NMAX) !Raw data sampled at 12000 Hz real s3(LN) !Symbol spectra - real s3a(LN) !Symbol spectra integer dat4(12) !Decoded message (as 12 integers) - integer dat4x(12) - integer nap(0:11) - data nap/0,2,3,2,3,4,2,3,6,4,6,6/ data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/ save @@ -59,7 +53,6 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & naptype=maxaptype call ana64(dd,npts,c00) - npts2=npts/2 call timer('sync64 ',0) call sync64(c00,nf1,nf2,nfqso,ntol,minsync,mode64,emedelay,dtx,f0, & @@ -68,81 +61,12 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & nfreq=nint(f0) if(mode64.eq.1 .and. minsync.ne.-1 .and. (sync-7.0).lt.minsync) go to 900 - irc=-99 - s3lim=20. - ibwmax=11 - if(mode64.le.4) ibwmax=9 - ibwmin=0 - idtmax=5 - if(minsync.eq.-2) then - ibwmin=ibwmax - idtmax=3 - endif - LL=64*(mode64+2) - NN=63 - napmin=99 - ncall=0 - - do idf0=1,11 - idf=idf0/2 - if(mod(idf0,2).eq.0) idf=-idf + call timer('qraloops',0) + call qra_loops(c00,npts/2,64,mode64,nsubmode,nFadingModel,minsync, & + ndepth,nc1,nc2,ng2,naptype,jpk0,dtx,f0,width,snr2,s3,irc,dat4) + call timer('qraloops',1) - a=0. - a(1)=-(f0+0.868*idf) - call twkfreq(c00,c0,npts2,6000.0,a) - - do idt0=1,idtmax - idt=idt0/2 - if(mod(idt0,2).eq.0) idt=-idt - jpk=jpk0 + 750*idt - call spec64(c0,jpk,s3a,LL,NN) - call pctile(s3a,LL*NN,40,base) - s3a=s3a/base - where(s3a(1:LL*NN)>s3lim) s3a(1:LL*NN)=s3lim - do ibw=ibwmax,ibwmin,-2 - b90=1.728**ibw - if(b90.gt.230.0) cycle - if(b90.lt.0.15*width) exit - s3(1:LL*NN)=s3a(1:LL*NN) - ncall=ncall+1 - call timer('qra64_de',0) - call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & - nFadingModel,dat4,snr2,irc) - call timer('qra64_de',1) - if(irc.eq.0) go to 10 - if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) - iirc=max(0,min(irc,11)) - if(irc.gt.0 .and. nap(iirc).lt.napmin) then - dat4x=dat4 - b90x=b90 - snr2x=snr2 - napmin=nap(iirc) - irckeep=irc - dtxkeep=jpk/6000.0 - 1.0 - f0keep=-a(1) - idfkeep=idf - idtkeep=idt - ibwkeep=ibw - endif - enddo ! ibw (b90 loop) - if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 - if(irc.eq.0) go to 100 - enddo ! idt (DT loop) - enddo ! idf (f0 loop) - -100 if(napmin.ne.99) then - dat4=dat4x - b90=b90x - snr2=snr2x - irc=irckeep - dtx=dtxkeep - f0=f0keep - idt=idtkeep - idf=idfkeep - ibw=ibwkeep - endif -10 decoded=' ' - + decoded=' ' if(irc.ge.0) then call unpackmsg(dat4,decoded) !Unpack the user message call fmtmsg(decoded,iz) diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 new file mode 100644 index 000000000..e82b708dd --- /dev/null +++ b/lib/qra_loops.f90 @@ -0,0 +1,105 @@ +subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & + ndepth,nc1,nc2,ng2,naptype,jpk0,dtx,f0,width,snr2,s3,irc,dat4) + + use timer_module, only: timer + parameter (LN=1152*63) + complex c00(0:720000) !Analytic representation of dd(), 6000 Hz + complex c0(0:720000) !Ditto, with freq shift + real a(3) !twkfreq params f,f1,f2 + real s3(LN),s3a(LN) !Symbol spectra + integer dat4(12),dat4x(12) !Decoded message (as 12 integers) + integer nap(0:11) !AP return codes + data nap/0,2,3,2,3,4,2,3,6,4,6,6/ +! save + + irc=-99 + s3lim=20. + ibwmax=11 + if(mode64.le.4) ibwmax=9 + ibwmin=0 + idtmax=5 + if(minsync.eq.-2) then + ibwmin=ibwmax + idtmax=3 + endif + LL=64*(mode64+2) + NN=63 + napmin=99 + ncall=0 + + do idf0=1,11 + idf=idf0/2 + if(mod(idf0,2).eq.0) idf=-idf + a=0. + a(1)=-(f0+0.868*idf) + call twkfreq(c00,c0,npts2,6000.0,a) + do idt0=1,idtmax + idt=idt0/2 + if(mod(idt0,2).eq.0) idt=-idt + jpk=jpk0 + 750*idt + if(mode.eq.64) then + call spec64(c0,jpk,s3a,LL,NN) + else + if(jpk.lt.0) jpk=0 + call timer('spec_q65',0) + call spec_qra65(c0(jpk:),nsps2,s3,LL,NN) !Get synced symbol spectra + call timer('spec_q65',1) +! do j=1,63 !Normalize to symbol baseline +! call pctile(s3(:,j),LL,40,base) +! s3(:,j)=s3(:,j)/base +! enddo +! LL2=64*(mode65+1)-1 +! s3max=20.0 +! do j=1,63 !Apply AGC to suppress pings +! xx=maxval(s3(-64:LL2,j)) +! if(xx.gt.s3max) s3(-64:LL2,j)=s3(-64:LL2,j)*s3max/xx +! enddo + endif + call pctile(s3a,LL*NN,40,base) + s3a=s3a/base + where(s3a(1:LL*NN)>s3lim) s3a(1:LL*NN)=s3lim + do ibw=ibwmax,ibwmin,-2 + b90=1.728**ibw + if(b90.gt.230.0) cycle + if(b90.lt.0.15*width) exit + s3(1:LL*NN)=s3a(1:LL*NN) + ncall=ncall+1 + call timer('qra64_de',0) + call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & + nFadingModel,dat4,snr2,irc) + call timer('qra64_de',1) + if(irc.eq.0) go to 200 + if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) + iirc=max(0,min(irc,11)) + if(irc.gt.0 .and. nap(iirc).lt.napmin) then + dat4x=dat4 + b90x=b90 + snr2x=snr2 + napmin=nap(iirc) + irckeep=irc + dtxkeep=jpk/6000.0 - 1.0 + f0keep=-a(1) + idfkeep=idf + idtkeep=idt + ibwkeep=ibw + endif + enddo ! ibw (b90 loop) + if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 + if(irc.eq.0) go to 100 + enddo ! idt (DT loop) + enddo ! idf (f0 loop) + +100 if(napmin.ne.99) then + dat4=dat4x + b90=b90x + snr2=snr2x + irc=irckeep + dtx=dtxkeep + f0=f0keep + idt=idtkeep + idf=idfkeep + ibw=ibwkeep + endif + +200 return +end subroutine qra_loops From c5502cda05fcb37c67ad9886725f182087447021 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 8 Oct 2020 16:48:11 -0400 Subject: [PATCH 075/426] QRA65 now decodes using qra_loops() -- the same inner loops as QRA64. Very effective! --- CMakeLists.txt | 5 +- lib/ana64.f90 | 5 +- lib/qra64a.f90 | 2 - lib/qra65_decode.f90 | 139 +++++++++++++++---------------------------- lib/qra_loops.f90 | 31 +++------- lib/spec64.f90 | 67 ++++++++++++++++----- 6 files changed, 112 insertions(+), 137 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d1246489..8ff8f0ebf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -570,7 +570,6 @@ set (wsjt_FSRCS lib/softsym9w.f90 lib/shell.f90 lib/spec64.f90 - lib/spec_qra65.f90 lib/spec9f.f90 lib/stdmsg.f90 lib/subtract65.f90 @@ -1317,7 +1316,7 @@ if (${OPENMP_FOUND} OR APPLE) endif (APPLE) if (WIN32) set_target_properties (jt9 PROPERTIES - LINK_FLAGS -Wl,--stack,16777216 + LINK_FLAGS -Wl,--stack,28000000 ) endif () target_link_libraries (jt9 wsjt_fort_omp wsjt_cxx fort_qt) @@ -1454,7 +1453,7 @@ else () ) if (WIN32) set_target_properties (wsjtx PROPERTIES - LINK_FLAGS -Wl,--stack,16777216 + LINK_FLAGS -Wl,--stack,28000000 ) endif () endif () diff --git a/lib/ana64.f90 b/lib/ana64.f90 index 5681ee176..faebb84e5 100644 --- a/lib/ana64.f90 +++ b/lib/ana64.f90 @@ -3,13 +3,12 @@ subroutine ana64(dd,npts,c0) use timer_module, only: timer parameter (NMAX=60*12000) !Max size of raw data at 12000 Hz - parameter (NSPS=3456) !Samples per symbol at 6000 Hz - parameter (NSPC=7*NSPS) !Samples per Costas array real dd(NMAX) !Raw data complex c0(0:720000) !Complex spectrum of dd() save - nfft1=672000 +! nfft1=672000 + nfft1=720000 nfft2=nfft1/2 df1=12000.0/nfft1 fac=2.0/nfft1 diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index 953a4789b..e5da55afd 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -81,8 +81,6 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & snr2=0. endif nfreq=nint(f0) - write(71,3071) idf,idt,ncall,irc,nsnr,dtx,nfreq,decoded -3071 format(5i5,f7.2,i6,2x,a22) 900 if(irc.lt.0) then sy=max(1.0,sync) diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index 0f04565f7..d377a6d11 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -41,18 +41,20 @@ contains character(len=6) :: hisgrid character*37 decoded integer*2 iwave(NMAX) !Raw data + real dd(NMAX) !Raw data integer dat4(12) logical lapdx,ltext + complex, allocatable :: c00(:) !Analytic signal, 6000 S/s complex, allocatable :: c0(:) !Analytic signal, 6000 S/s real, allocatable, save :: s3(:,:) !Symbol spectra real, allocatable, save :: s3a(:,:) !Symbol spectra for avg messages - real a(5) data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/,nsubmodez/-1/ save nc1z,nc2z,ng2z,maxaptypez,nsave,nsubmodez mode65=2**nsubmode nfft1=ntrperiod*12000 nfft2=ntrperiod*6000 + allocate (c00(0:nfft1-1)) allocate (c0(0:nfft1-1)) if(nsubmode.ne.nsubmodez) then @@ -74,106 +76,61 @@ contains nsps=41472 else stop 'Invalid TR period' - endif - baud=12000.0/nsps - df1=12000.0/nfft1 - -! do i=1,12000*ntrperiod -! write(61,3061) i/12000.0,iwave(i)/32767.0 -!3061 format(2f12.6) -! enddo - - this%callback => callback - - if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning + endif + npts=ntrperiod*12000 + baud=12000.0/nsps + df1=12000.0/nfft1 + this%callback => callback + if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning ! 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) - b90=20.0 !8 to 25 is OK; not very critical - nFadingModel=1 + call packcall(mycall(1:6),nc1,ltext) + call packcall(hiscall(1:6),nc2,ltext) + call packgrid(hisgrid(1:4),ng2,ltext) + b90=20.0 !8 to 25 is OK; not very critical + nFadingModel=1 ! AP control could be done differently, but this works well: - maxaptype=0 - if(ndepth.eq.2) maxaptype=3 - if(ndepth.eq.3) maxaptype=5 + maxaptype=0 + if(ndepth.eq.2) maxaptype=3 + if(ndepth.eq.3) 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 - s3a=0. - nsave=0 - endif - naptype=maxaptype + 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 + s3a=0. + nsave=0 + endif + naptype=maxaptype - call timer('sync_q65',0) - call sync_qra65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0,snr1) - call timer('sync_q65',1) + call timer('sync_q65',0) + call sync_qra65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0,snr1) + call timer('sync_q65',1) -! Downsample to give complex data at 6000 S/s - call timer('down_q65',0) - fac=2.0/nfft1 - c0=fac*iwave(1:nfft1) - call four2a(c0,nfft1,1,-1,1) !Forward c2c FFT - 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 - a=0. - a(1)=-(f0 + mode65*baud) !Data tones start mode65 bins higher - call twkfreq(c0,c0,ntrperiod*6000,6000.0,a) - call timer('down_q65',1) + jpk0=(xdt+1.0)*6000 !### + if(jpk0.lt.0) jpk0=0 - jpk=(xdt+0.5)*6000 - 384 !### Empirical ### - if(ntrperiod.ge.60) jpk=(xdt+1.0)*6000 - 384 !### TBD ??? ### - if(jpk.lt.0) jpk=0 - xdt=jpk/6000.0 - 0.5 - LL=64*(mode65+2) - NN=63 - call timer('spec_q65',0) - call spec_qra65(c0(jpk:),nsps/2,s3,LL,NN) !Compute synced symbol spectra - call timer('spec_q65',1) + fac=1.0/32767.0 + dd=fac*iwave +! npts=648000 + minsync=-2 + nmode=65 - do j=1,63 !Normalize to symbol baseline - call pctile(s3(:,j),LL,40,base) - s3(:,j)=s3(:,j)/base - enddo + call ana64(dd,npts,c00) - LL2=64*(mode65+1)-1 - s3max=20.0 - do j=1,63 !Apply AGC to suppress pings - xx=maxval(s3(-64:LL2,j)) - if(xx.gt.s3max) s3(-64:LL2,j)=s3(-64:LL2,j)*s3max/xx - enddo - -! Call Nico's QRA64 decoder - call timer('qra64_de',0) - call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & - nFadingModel,dat4,snr2,irc) - call timer('qra64_de',1) - - if(irc.lt.0) then -! No luck so far. Try for an average decode. - call timer('qra64_av',0) - s3a=s3a+s3 - nsave=nsave+1 - if(nsave.ge.2) then - call qra64_dec(s3a,nc1,nc2,ng2,naptype,0,nSubmode,b90, & - nFadingModel,dat4,snr2,irc) - if(irc.ge.0) irc=100*nsave + irc - endif - call timer('qra64_av',1) - endif - snr2=snr2 + db(6912.0/nsps) - if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) + call timer('qraloops',0) + call qra_loops(c00,npts/2,nmode,mode65,nsubmode,nFadingModel,minsync, & + ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,s3,irc,dat4) + if(nmode.eq.65) xdt=xdt+0.4 !### Empirical WHY ??? ### + call timer('qraloops',1) decoded=' ' if(irc.ge.0) then diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index e82b708dd..0ea261047 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -1,5 +1,5 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & - ndepth,nc1,nc2,ng2,naptype,jpk0,dtx,f0,width,snr2,s3,irc,dat4) + ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,s3,irc,dat4) use timer_module, only: timer parameter (LN=1152*63) @@ -10,7 +10,6 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & integer dat4(12),dat4x(12) !Decoded message (as 12 integers) integer nap(0:11) !AP return codes data nap/0,2,3,2,3,4,2,3,6,4,6,6/ -! save irc=-99 s3lim=20. @@ -26,6 +25,8 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & NN=63 napmin=99 ncall=0 + nsps=3456 !QRA64 + if(mode.eq.65) nsps=3840 !QRA65 do idf0=1,11 idf=idf0/2 @@ -36,25 +37,9 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & do idt0=1,idtmax idt=idt0/2 if(mod(idt0,2).eq.0) idt=-idt - jpk=jpk0 + 750*idt - if(mode.eq.64) then - call spec64(c0,jpk,s3a,LL,NN) - else - if(jpk.lt.0) jpk=0 - call timer('spec_q65',0) - call spec_qra65(c0(jpk:),nsps2,s3,LL,NN) !Get synced symbol spectra - call timer('spec_q65',1) -! do j=1,63 !Normalize to symbol baseline -! call pctile(s3(:,j),LL,40,base) -! s3(:,j)=s3(:,j)/base -! enddo -! LL2=64*(mode65+1)-1 -! s3max=20.0 -! do j=1,63 !Apply AGC to suppress pings -! xx=maxval(s3(-64:LL2,j)) -! if(xx.gt.s3max) s3(-64:LL2,j)=s3(-64:LL2,j)*s3max/xx -! enddo - endif + jpk=jpk0 + 750*idt + if(jpk.lt.0) jpk=0 + call spec64(c0,nsps,mode,jpk,s3a,LL,NN) call pctile(s3a,LL*NN,40,base) s3a=s3a/base where(s3a(1:LL*NN)>s3lim) s3a(1:LL*NN)=s3lim @@ -77,7 +62,7 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & snr2x=snr2 napmin=nap(iirc) irckeep=irc - dtxkeep=jpk/6000.0 - 1.0 + xdtkeep=jpk/6000.0 - 1.0 f0keep=-a(1) idfkeep=idf idtkeep=idt @@ -94,7 +79,7 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & b90=b90x snr2=snr2x irc=irckeep - dtx=dtxkeep + xdt=xdtkeep f0=f0keep idt=idtkeep idf=idfkeep diff --git a/lib/spec64.f90 b/lib/spec64.f90 index 3404243c4..377e8dfcf 100644 --- a/lib/spec64.f90 +++ b/lib/spec64.f90 @@ -1,26 +1,50 @@ -subroutine spec64(c0,jpk,s3,LL,NN) +subroutine spec64(c0,nsps,mode,jpk,s3,LL,NN) - parameter (NSPS=3456) !Samples per symbol at 6000 Hz + parameter (MAXFFT=3840) complex c0(0:360000) !Complex spectrum of dd() - complex cs(0:NSPS-1) !Complex symbol spectrum + complex cs(0:MAXFFT-1) !Complex symbol spectrum real s3(LL,NN) !Synchronized symbol spectra real xbase0(LL),xbase(LL) + integer isync(22) !Indices of sync symbols + data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ nfft=nsps fac=1.0/nfft - do j=1,NN - jj=j+7 !Skip first Costas array - if(j.ge.33) jj=j+14 !Skip middle Costas array - ja=jpk + (jj-1)*nfft - jb=ja+nfft-1 - cs(0:nfft-1)=fac*c0(ja:jb) - call four2a(cs,nfft,1,-1,1) - do ii=1,LL - i=ii-65 - if(i.lt.0) i=i+nfft - s3(ii,j)=real(cs(i))**2 + aimag(cs(i))**2 + + if(mode.eq.64) then + do j=1,NN + jj=j+7 !Skip first Costas array + if(j.ge.33) jj=j+14 !Skip middle Costas array + ja=jpk + (jj-1)*nfft + jb=ja+nfft-1 + cs(0:nfft-1)=fac*c0(ja:jb) + call four2a(cs,nfft,1,-1,1) + do ii=1,LL + i=ii-65 + if(i.lt.0) i=i+nfft + s3(ii,j)=real(cs(i))**2 + aimag(cs(i))**2 + enddo enddo - enddo + else + j=0 + n=1 + do k=1,84 + if(k.eq.isync(n)) then + n=n+1 + cycle + endif + j=j+1 + ja=(k-1)*nsps + jpk + jb=ja+nsps-1 + cs(0:nfft-1)=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 + endif df=6000.0/nfft do i=1,LL @@ -38,5 +62,18 @@ subroutine spec64(c0,jpk,s3,LL,NN) s3(i,1:NN)=s3(i,1:NN)/(xbase(i)+0.001) !Apply frequency equalization enddo +! print*,'a',LL,NN,jpk +! df=6000.0/nfft +! do i=1,LL +! write(71,3071) i,i-65,i*df,(s3(i,j),j=1,4) +!3071 format(2i8,f10.3,4e12.3) +! enddo +! +! do j=1,NN +! write(72,3072) j,maxloc(s3(1:LL,j)),maxloc(s3(1:LL,j))-65 +!3072 format(3i8) +! enddo +! if(nfft.ne.-999) stop + return end subroutine spec64 From 8612398b020447be8a77b5d1d05653189fd122c9 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 9 Oct 2020 10:44:25 -0400 Subject: [PATCH 076/426] Minor tweaks to test_qra65. --- lib/test_qra65.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/test_qra65.f90 b/lib/test_qra65.f90 index 76056cef8..4a9881d9f 100644 --- a/lib/test_qra65.f90 +++ b/lib/test_qra65.f90 @@ -78,7 +78,7 @@ program test_qra65 write(*,1000) (j,j=0,11) write(12,1000) (j,j=0,11) -1000 format(/'SNR d Dop Sync Dec1 DecN Bad',i6,11i4,' tdec dtavg dtrms'/97('-')) +1000 format(/'SNR d Dop Sync Dec1 DecN Bad',i6,11i4,' tdec'/85('-')) dterr=tsym/4.0 nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) @@ -140,6 +140,7 @@ program test_qra65 endif flush(6) flush(12) + if(ndecodes.eq.0) exit !Bail out if no decodes at this SNR ndecodes0=ndecodes enddo ! nsnr From a4ba64a5fabeef7ccac0c00db11c61d8bf4bd467 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 9 Oct 2020 13:16:25 -0400 Subject: [PATCH 077/426] Code cleanup and documentation. --- lib/qra65_decode.f90 | 26 +++++++++++++++-------- lib/sync_qra65.f90 | 49 ++++++++++++++++++++------------------------ 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index d377a6d11..13ee12e1d 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -31,22 +31,32 @@ contains subroutine decode(this,callback,iwave,nutc,ntrperiod,nsubmode,nfqso, & ntol,ndepth,mycall,hiscall,hisgrid) +! Decodes QRA65 signals +! 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 (???) +! Output: sent to the callback routine for display to user + use timer_module, only: timer use packjt use, intrinsic :: iso_c_binding - parameter (NMAX=300*12000) !### Needs to be 300*12000 ### + parameter (NMAX=300*12000) !Max TRperiod is 300 s class(qra65_decoder), intent(inout) :: this procedure(qra65_decode_callback) :: callback - character(len=12) :: mycall, hiscall + character(len=12) :: mycall, hiscall !Used for AP decoding character(len=6) :: hisgrid - character*37 decoded + character*37 decoded !Decoded message integer*2 iwave(NMAX) !Raw data real dd(NMAX) !Raw data - integer dat4(12) - logical lapdx,ltext - complex, allocatable :: c00(:) !Analytic signal, 6000 S/s - complex, allocatable :: c0(:) !Analytic signal, 6000 S/s - real, allocatable, save :: s3(:,:) !Symbol spectra + integer dat4(12) !Decoded message as 12 6-bit integers + logical ltext + complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s + complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s + real, allocatable, save :: s3(:,:) !Synchronized symbol spectra real, allocatable, save :: s3a(:,:) !Symbol spectra for avg messages data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/,nsubmodez/-1/ save nc1z,nc2z,ng2z,maxaptypez,nsave,nsubmodez diff --git a/lib/sync_qra65.f90 b/lib/sync_qra65.f90 index 21c155415..c93aed626 100644 --- a/lib/sync_qra65.f90 +++ b/lib/sync_qra65.f90 @@ -1,19 +1,29 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) +! Look for the sync vector in a QRA65 signal. +! Input: iwave(0:nmax-1) Raw data +! mode65 Tone spacing 1 2 4 8 16 (A-E) +! nsps Samples per symbol at 12000 Sa/s +! nfqso Target frequency (Hz) +! ntol Search range around nfqso (Hz) +! Output: xdt Time offset from nominal (s) +! f0 Frequency of sync tone +! snr1 Relative SNR of sync signal + parameter (NSTEP=4) !Quarter-symbol steps integer*2 iwave(0:nmax-1) !Raw data integer isync(22) !Indices of sync symbols integer ijpk(2) !Indices i and j at peak of ccf real, allocatable :: s1(:,:) !Symbol spectra, quarter-symbol steps real sync(85) !sync vector - real ccf(-64:64,-26:107) + real ccf(-64:64,-26:107) !CCF(freq,time) complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ data sync(1)/99.0/ save sync nfft=2*nsps - df=12000.0/nfft + df=12000.0/nfft !Freq resolution = 0.5*baud istep=nsps/NSTEP iz=5000.0/df !Uppermost frequency bin, at 5000 Hz txt=85.0*nsps/12000.0 @@ -23,10 +33,10 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) allocate(s1(iz,jz)) allocate(c0(0:nfft-1)) - if(sync(1).eq.99.0) then - sync=-22.0/63.0 !Sync OFF + if(sync(1).eq.99.0) then !Generate the sync vector + sync=-22.0/63.0 !Sync tone OFF do k=1,22 - sync(isync(k))=1.0 !Sync ON + sync(isync(k))=1.0 !Sync tone ON enddo endif @@ -46,27 +56,23 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) do i=1,iz s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 enddo +! For large Doppler spreads, should we smooth the spectra here? enddo - i0=nint(nfqso/df) + i0=nint(nfqso/df) !Target QSO frequency call pctile(s1(i0-64:i0+192,1:jz),129*jz,40,base) - s1=s1/base - s1max=20.0 + s1=s1/base !Maybe should subtract 1.0 here? -! Apply AGC +! Apply fast AGC + s1max=20.0 !Empirical choice do j=1,jz smax=maxval(s1(i0-64:i0+192,j)) if(smax.gt.s1max) s1(i0-64:i0+192,j)=s1(i0-64:i0+192,j)*s1max/smax enddo -! do i=1,iz -! write(60,3060) i,i*df,sum(s1(i,1:jz)) -!3060 format(i6,f10.3,e12.3) -! enddo - - dt4=nsps/(NSTEP*12000.0) !1/4 of symbol duration + dt4=nsps/(NSTEP*12000.0) !1/4 of symbol duration j0=0.5/dt4 - if(nsps.ge.7680) j0=1.0/dt4 + if(nsps.ge.7680) j0=1.0/dt4 !Nominal index for start of signal ccf=0. ia=min(64,nint(ntol/df)) @@ -85,23 +91,12 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) enddo enddo -! do i=-64,64 -! write(61,3061) i,ccf(i,jpk) -!3061 format(i5,e12.3) -! enddo -! do j=lag1,lag2 -! write(62,3061) j,ccf(ipk,j) -! enddo - ijpk=maxloc(ccf) ipk=ijpk(1)-65 -! jpk=ijpk(2)-16 jpk=ijpk(2)-27 f0=nfqso + ipk*df xdt=jpk*dt4 snr1=maxval(ccf)/22.0 -! write(*,3100) ipk,jpk,xdt,f0,snr1 -!3100 format(2i5,f7.2,2f10.2) return end subroutine sync_qra65 From 538b0b91ab55463c90dcc8eb115b405f997e7e43 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 9 Oct 2020 14:12:34 -0400 Subject: [PATCH 078/426] Move s3, s3a arrays into qra_loops. --- lib/qra65_decode.f90 | 104 +++++++++++++++++++------------------------ lib/qra_loops.f90 | 2 +- 2 files changed, 46 insertions(+), 60 deletions(-) diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index 13ee12e1d..7778844bf 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -56,10 +56,8 @@ contains logical ltext complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s - real, allocatable, save :: s3(:,:) !Synchronized symbol spectra - real, allocatable, save :: s3a(:,:) !Symbol spectra for avg messages data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/,nsubmodez/-1/ - save nc1z,nc2z,ng2z,maxaptypez,nsave,nsubmodez + save nc1z,nc2z,ng2z,maxaptypez,nsubmodez mode65=2**nsubmode nfft1=ntrperiod*12000 @@ -67,13 +65,6 @@ contains allocate (c00(0:nfft1-1)) allocate (c0(0:nfft1-1)) - if(nsubmode.ne.nsubmodez) then - if(allocated(s3)) deallocate(s3) - if(allocated(s3a)) deallocate(s3a) - allocate(s3(-64:64*mode65+63,63)) - allocate(s3a(-64:64*mode65+63,63)) - endif - if(ntrperiod.eq.15) then nsps=1800 else if(ntrperiod.eq.30) then @@ -85,67 +76,62 @@ contains else if(ntrperiod.eq.300) then nsps=41472 else - stop 'Invalid TR period' - endif - npts=ntrperiod*12000 - baud=12000.0/nsps - df1=12000.0/nfft1 - this%callback => callback - if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning + stop 'Invalid TR period' + endif + npts=ntrperiod*12000 + baud=12000.0/nsps + df1=12000.0/nfft1 + this%callback => callback + if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning ! 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) - b90=20.0 !8 to 25 is OK; not very critical - nFadingModel=1 + call packcall(mycall(1:6),nc1,ltext) + call packcall(hiscall(1:6),nc2,ltext) + call packgrid(hisgrid(1:4),ng2,ltext) + b90=20.0 !8 to 25 is OK; not very critical + nFadingModel=1 ! AP control could be done differently, but this works well: - maxaptype=0 - if(ndepth.eq.2) maxaptype=3 - if(ndepth.eq.3) maxaptype=5 + maxaptype=0 + if(ndepth.eq.2) maxaptype=3 + if(ndepth.eq.3) 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 - s3a=0. - nsave=0 - endif - naptype=maxaptype + 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(s3dummy,nc1,nc2,ng2,naptype,1,nSubmode,b90, & + nFadingModel,dat4,snr2,irc) + enddo + nc1z=nc1 + nc2z=nc2 + ng2z=ng2 + maxaptypez=maxaptype + endif + naptype=maxaptype - call timer('sync_q65',0) - call sync_qra65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0,snr1) - call timer('sync_q65',1) + call timer('sync_q65',0) + call sync_qra65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0,snr1) + call timer('sync_q65',1) - jpk0=(xdt+1.0)*6000 !### - if(jpk0.lt.0) jpk0=0 + jpk0=(xdt+1.0)*6000 !### + if(jpk0.lt.0) jpk0=0 - fac=1.0/32767.0 - dd=fac*iwave -! npts=648000 - minsync=-2 - nmode=65 + fac=1.0/32767.0 + dd=fac*iwave + minsync=-2 + nmode=65 - call ana64(dd,npts,c00) - - call timer('qraloops',0) - call qra_loops(c00,npts/2,nmode,mode65,nsubmode,nFadingModel,minsync, & - ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,s3,irc,dat4) - if(nmode.eq.65) xdt=xdt+0.4 !### Empirical WHY ??? ### - call timer('qraloops',1) + call ana64(dd,npts,c00) + call timer('qraloops',0) + call qra_loops(c00,npts/2,nmode,mode65,nsubmode,nFadingModel,minsync, & + ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) + call timer('qraloops',1) + xdt=xdt+0.4 !### Empirical -- WHY ??? ### + snr2=snr2 + db(6912.0/nsps) decoded=' ' if(irc.ge.0) then - nsave=0 - s3a=0. call unpackmsg(dat4,decoded) !Unpack the user message call fmtmsg(decoded,iz) if(index(decoded,"000AAA ").ge.1) then diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index 0ea261047..4aa6c7b17 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -1,5 +1,5 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & - ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,s3,irc,dat4) + ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) use timer_module, only: timer parameter (LN=1152*63) From e82da4484cf608965aac7dbfc0d7ba8d5df7a84e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 9 Oct 2020 15:29:16 -0400 Subject: [PATCH 079/426] Code cleanup. --- lib/qra64a.f90 | 5 ++--- lib/qra_loops.f90 | 11 +++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index e5da55afd..b062928e1 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -12,7 +12,6 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & logical ltext complex c00(0:720000) !Analytic signal for dd() real dd(NMAX) !Raw data sampled at 12000 Hz - real s3(LN) !Symbol spectra integer dat4(12) !Decoded message (as 12 integers) data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/ save @@ -42,7 +41,7 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & 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, & + call qra64_dec(s3dummy,nc1,nc2,ng2,naptype,1,nSubmode,b90, & nFadingModel,dat4,snr2,irc) enddo nc1z=nc1 @@ -63,7 +62,7 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & call timer('qraloops',0) call qra_loops(c00,npts/2,64,mode64,nsubmode,nFadingModel,minsync, & - ndepth,nc1,nc2,ng2,naptype,jpk0,dtx,f0,width,snr2,s3,irc,dat4) + ndepth,nc1,nc2,ng2,naptype,jpk0,dtx,f0,width,snr2,irc,dat4) call timer('qraloops',1) decoded=' ' diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index 4aa6c7b17..445c7c93f 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -26,7 +26,7 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & napmin=99 ncall=0 nsps=3456 !QRA64 - if(mode.eq.65) nsps=3840 !QRA65 + if(mode.eq.65) nsps=3840 !QRA65 ### Is 3840 too big? ### do idf0=1,11 idf=idf0/2 @@ -39,15 +39,14 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & if(mod(idt0,2).eq.0) idt=-idt jpk=jpk0 + 750*idt if(jpk.lt.0) jpk=0 - call spec64(c0,nsps,mode,jpk,s3a,LL,NN) - call pctile(s3a,LL*NN,40,base) - s3a=s3a/base - where(s3a(1:LL*NN)>s3lim) s3a(1:LL*NN)=s3lim + call spec64(c0,nsps,mode,jpk,s3,LL,NN) + call pctile(s3,LL*NN,40,base) + s3=s3/base + where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim do ibw=ibwmax,ibwmin,-2 b90=1.728**ibw if(b90.gt.230.0) cycle if(b90.lt.0.15*width) exit - s3(1:LL*NN)=s3a(1:LL*NN) ncall=ncall+1 call timer('qra64_de',0) call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & From d5ce2def09abbe61aac4e678ba75cd059bfbd0fb Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 9 Oct 2020 17:04:06 -0400 Subject: [PATCH 080/426] Code cleanup. --- lib/qra_loops.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index 445c7c93f..d59bbe05f 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -6,7 +6,7 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & complex c00(0:720000) !Analytic representation of dd(), 6000 Hz complex c0(0:720000) !Ditto, with freq shift real a(3) !twkfreq params f,f1,f2 - real s3(LN),s3a(LN) !Symbol spectra + real s3(LN) !Symbol spectra integer dat4(12),dat4x(12) !Decoded message (as 12 integers) integer nap(0:11) !AP return codes data nap/0,2,3,2,3,4,2,3,6,4,6,6/ From ad70cdeb8a1e8409de694058111e18ab0476137d Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 13 Oct 2020 13:49:09 -0400 Subject: [PATCH 081/426] More work on sync tests, etc., of QRA6[45]. --- lib/qra/qra64/qra64sim.f90 | 41 +++++++++++++++++++++++++++++++----- lib/qra/qra65/qra65sim.f90 | 42 +++++++++++++++++++++++++++++-------- lib/qra65_decode.f90 | 5 +++-- lib/qra_loops.f90 | 7 +++++-- lib/sync_qra65.f90 | 43 +++++++++++++++++++------------------- 5 files changed, 99 insertions(+), 39 deletions(-) diff --git a/lib/qra/qra64/qra64sim.f90 b/lib/qra/qra64/qra64sim.f90 index 278c1797d..2360ef7da 100644 --- a/lib/qra/qra64/qra64sim.f90 +++ b/lib/qra/qra64/qra64sim.f90 @@ -14,14 +14,18 @@ program qra64sim complex cdat(NMAX) !Generated complex waveform complex cspread(0:NFFT-1) !Complex amplitude for Rayleigh fading complex z + complex c00(0:720000) !Analytic signal for dat() real*8 f0,dt,twopi,phi,dphi,baud,fsample,freq - character msg*22,fname*11,csubmode*1,arg*12 + character msg*22,fname*11,csubmode*1,arg*12,cd*1 character msgsent*22 + logical lsync + data lsync/.false./ nargs=iargc() - if(nargs.ne. 7) then - print *, 'Usage: qra64sim "msg" A-E Nsigs fDop DT Nfiles SNR' - print *, 'Example qra64sim "K1ABC W9XYZ EN37" A 10 0.2 0.0 1 0' + if(nargs.ne.8) then + print*,'Usage: qra64sim "msg" A-E Nsigs fDop DT Nfiles Sync SNR' + print*,'Example qra64sim "K1ABC W9XYZ EN37" A 10 0.2 0.0 1 T -26' + print*,'Sync = T to include sync test.' go to 999 endif call getarg(1,msg) @@ -36,8 +40,10 @@ program qra64sim call getarg(6,arg) read(arg,*) nfiles call getarg(7,arg) + if(arg(1:1).eq.'T' .or. arg(1:1).eq.'1') lsync=.true. + call getarg(8,arg) read(arg,*) snrdb - + if(mode64.ge.8) nsigs=1 rms=100. fsample=12000.d0 !Sample rate (Hz) @@ -54,6 +60,7 @@ program qra64sim write(*,1000) 1000 format('File Sig Freq A-E S/N DT Dop Message'/60('-')) + nsync=0 do ifile=1,nfiles !Loop over requested number of files write(fname,1002) ifile !Output filename 1002 format('000000_',i4.4) @@ -165,6 +172,30 @@ program qra64sim 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) + + if(lsync) then + cd=' ' + if(ifile.eq.nfiles) cd='d' + nf1=200 + nf2=3000 + nfqso=nint(f0) + ntol=100 + minsync=0 + emedelay=0.0 + call ana64(dat,npts,c00) + call sync64(c00,nf1,nf2,nfqso,ntol,minsync,mode64,emedelay,xdt2,f02, & + jpk0,sync,sync2,width) + terr=1.01/(8.0*baud) + ferr=1.01*mode64*baud + if(abs(xdt2-xdt).lt.terr .and. abs(f02-f0).lt.ferr) nsync=nsync+1 + open(40,file='sync64.out',status='unknown',position='append') + write(40,1030) ifile,64,csubmode,snrdb,fspread,xdt2-xdt,f02-f0, & + width,sync,sync2,nsync,cd +1030 format(i4,i3,1x,a1,2f7.1,f7.2,4f8.1,i5,1x,a1) + close(40) + endif enddo + if(lsync) write(*,1040) snrdb,nfiles,nsync +1040 format('SNR:',f6.1,' nfiles:',i5,' nsynced:',i5) 999 end program qra64sim diff --git a/lib/qra/qra65/qra65sim.f90 b/lib/qra/qra65/qra65sim.f90 index e91324d27..62ec63f57 100644 --- a/lib/qra/qra65/qra65sim.f90 +++ b/lib/qra/qra65/qra65sim.f90 @@ -14,13 +14,16 @@ program qra65sim complex cspread(0:NMAX-1) !Complex amplitude for Rayleigh fading complex z real*8 f0,dt,twopi,phi,dphi,baud,fsample,freq - character msg*22,fname*17,csubmode*1,arg*12 + character msg*22,fname*17,csubmode*1,arg*12,cd*1 character msgsent*22 - + logical lsync + data lsync/.false./ + nargs=iargc() - if(nargs.ne.8) then - print *, 'Usage: qra65sim "msg" A-E freq fDop DT TRp Nfiles SNR' - print *, 'Example: qra65sim "K1ABC W9XYZ EN37" A 1500 0.2 0.0 15 1 -10' + if(nargs.ne.9) then + print *, 'Usage: qra65sim "msg" A-E freq fDop DT TRp Nfiles Sync SNR' + print *, 'Example: qra65sim "K1ABC W9XYZ EN37" A 1500 0.0 0.0 60 1 T -26' + print*,'Sync = T to include sync test.' go to 999 endif call getarg(1,msg) @@ -37,8 +40,15 @@ program qra65sim call getarg(7,arg) read(arg,*) nfiles call getarg(8,arg) + if(arg(1:1).eq.'T' .or. arg(1:1).eq.'1') lsync=.true. + call getarg(9,arg) read(arg,*) snrdb + if(nfiles.lt.0) then + nfiles=-nfiles + lsync=.true. + endif + if(ntrperiod.eq.15) then nsps=1800 else if(ntrperiod.eq.30) then @@ -75,6 +85,7 @@ program qra65sim write(*,1000) 1000 format('File TR Freq Mode S/N DT Dop Message'/60('-')) + nsync=0 do ifile=1,nfiles !Loop over requested number of files if(ntrperiod.lt.60) then write(fname,1002) ifile !Output filename @@ -169,10 +180,23 @@ program qra65sim write(10) h,iwave(1:npts) !Save the .wav file close(10) -! do i=1,NMAX -! write(15,3020) i/12000.0,iwave(i) -!3020 format(f10.6,i8) -! enddo + if(lsync) then + cd=' ' + if(ifile.eq.nfiles) cd='d' + nfqso=nint(f0) + ntol=100 + call sync_qra65(iwave,npts,mode65,nsps,nfqso,ntol,xdt2,f02,snr2) + terr=1.01/(8.0*baud) + ferr=1.01*mode65*baud + if(abs(xdt2-xdt).lt.terr .and. abs(f02-f0).lt.ferr) nsync=nsync+1 + open(40,file='sync65.out',status='unknown',position='append') + write(40,1030) ifile,65,csubmode,snrdb,fspread,xdt2-xdt,f02-f0, & + snr2,nsync,cd +1030 format(i4,i3,1x,a1,2f7.1,f7.2,2f8.1,i5,1x,a1) + close(40) + endif enddo + if(lsync) write(*,1040) snrdb,nfiles,nsync +1040 format('SNR:',f6.1,' nfiles:',i5,' nsynced:',i5) 999 end program qra65sim diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index 7778844bf..c5ab8521c 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -93,8 +93,9 @@ contains ! AP control could be done differently, but this works well: maxaptype=0 - if(ndepth.eq.2) maxaptype=3 - if(ndepth.eq.3) maxaptype=5 +! if(ndepth.eq.2) maxaptype=3 +! if(ndepth.eq.3) maxaptype=5 + if(ndepth.ge.2) maxaptype=5 !### if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & maxaptype.ne.maxaptypez) then diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index d59bbe05f..59b9458b1 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -69,7 +69,6 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & endif enddo ! ibw (b90 loop) if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 - if(irc.eq.0) go to 100 enddo ! idt (DT loop) enddo ! idf (f0 loop) @@ -85,5 +84,9 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & ibw=ibwkeep endif -200 return +200 continue + write(53,3053) idt,idf,ibw,irc,b90,xdt,f0,snr2 +3053 format(4i5,f7.1,f7.2,2f7.1) + + return end subroutine qra_loops diff --git a/lib/sync_qra65.f90 b/lib/sync_qra65.f90 index c93aed626..b18aa8870 100644 --- a/lib/sync_qra65.f90 +++ b/lib/sync_qra65.f90 @@ -1,6 +1,8 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) -! Look for the sync vector in a QRA65 signal. +! Detect and align with the QRA65 sync vector, returning time and frequency +! offsets and SNR estimate. + ! Input: iwave(0:nmax-1) Raw data ! mode65 Tone spacing 1 2 4 8 16 (A-E) ! nsps Samples per symbol at 12000 Sa/s @@ -10,7 +12,7 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) ! f0 Frequency of sync tone ! snr1 Relative SNR of sync signal - parameter (NSTEP=4) !Quarter-symbol steps + parameter (NSTEP=8) !Step size nsps/NSTEP integer*2 iwave(0:nmax-1) !Raw data integer isync(22) !Indices of sync symbols integer ijpk(2) !Indices i and j at peak of ccf @@ -28,7 +30,7 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) iz=5000.0/df !Uppermost frequency bin, at 5000 Hz txt=85.0*nsps/12000.0 jz=(txt+1.0)*12000.0/istep !Number of quarter-symbol steps - if(nsps.ge.7680) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher + if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher allocate(s1(iz,jz)) allocate(c0(0:nfft-1)) @@ -41,11 +43,11 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) endif fac=1/32767.0 - do j=1,jz !Compute symbol spectra at quarter-symbol steps + do j=1,jz !Compute symbol spectra at step size ia=(j-1)*istep ib=ia+nsps-1 k=-1 - do i=ia,ib,2 + do i=ia,ib,2 !Load iwave data into complex array c0, for r2c FFT xx=iwave(i) yy=iwave(i+1) k=k+1 @@ -57,11 +59,12 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 enddo ! For large Doppler spreads, should we smooth the spectra here? + call smo121(s1(1:iz,j),iz) enddo i0=nint(nfqso/df) !Target QSO frequency call pctile(s1(i0-64:i0+192,1:jz),129*jz,40,base) - s1=s1/base !Maybe should subtract 1.0 here? + s1=s1/base - 1.0 ! Apply fast AGC s1max=20.0 !Empirical choice @@ -70,24 +73,22 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) if(smax.gt.s1max) s1(i0-64:i0+192,j)=s1(i0-64:i0+192,j)*s1max/smax enddo - dt4=nsps/(NSTEP*12000.0) !1/4 of symbol duration - j0=0.5/dt4 - if(nsps.ge.7680) j0=1.0/dt4 !Nominal index for start of signal + dtstep=nsps/(NSTEP*12000.0) !Step size in seconds + j0=0.5/dtstep + if(nsps.ge.6192) j0=1.0/dtstep !Nominal index for start of signal ccf=0. ia=min(64,nint(ntol/df)) - lag1=-1.0/dt4 - lag2=4.0/dt4 + 0.9999 + lag1=-1.0/dtstep + lag2=4.0/dtstep + 0.9999 - do i=-ia,ia - do lag=lag1,lag2 - do k=1,85 - n=NSTEP*(k-1) + 1 - j=n+lag+j0 - if(j.ge.1 .and. j.le.jz) then - ccf(i,lag)=ccf(i,lag) + sync(k)*s1(i0+i,j) - endif - enddo + do lag=lag1,lag2 + do k=1,85 + n=NSTEP*(k-1) + 1 + j=n+lag+j0 + if(j.ge.1 .and. j.le.jz) then + ccf(-ia:ia,lag)=ccf(-ia:ia,lag) + sync(k)*s1(i0-ia:i0+ia,j) + endif enddo enddo @@ -95,7 +96,7 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) ipk=ijpk(1)-65 jpk=ijpk(2)-27 f0=nfqso + ipk*df - xdt=jpk*dt4 + xdt=jpk*dtstep snr1=maxval(ccf)/22.0 return From 4bf5f23c544068c477ae217696db4e45186fea74 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 14 Oct 2020 11:49:27 -0400 Subject: [PATCH 082/426] Update QRA test programs. --- lib/qra64a.f90 | 16 ++++++++++++++++ lib/qra65_decode.f90 | 3 ++- lib/test_qra64.f90 | 4 ++-- lib/test_qra65.f90 | 8 ++++---- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index b062928e1..a8c532266 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -37,6 +37,7 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & nFadingModel=1 maxaptype=4 if(iand(ndepth,64).ne.0) maxaptype=5 + call qra_params(ndepth,maxaptype,minsync) if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & maxaptype.ne.maxaptypez) then do naptype=0,maxaptype @@ -93,3 +94,18 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & return end subroutine qra64a + +subroutine qra_params(ndepth,maxaptype,minsync) + +! If file qra_params is present in CWD, read decoding params from it. + + logical ex + inquire(file='qra_params',exist=ex) + if(ex) then + open(29,file='qra_params',status='old') + read(29,*) ndepth,maxaptype,minsync + close(29) + endif + + return +end subroutine qra_params diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index c5ab8521c..b9b9c1a7b 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -96,6 +96,8 @@ contains ! if(ndepth.eq.2) maxaptype=3 ! if(ndepth.eq.3) maxaptype=5 if(ndepth.ge.2) maxaptype=5 !### + minsync=-2 + call qra_params(ndepth,maxaptype,minsync) if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & maxaptype.ne.maxaptypez) then @@ -120,7 +122,6 @@ contains fac=1.0/32767.0 dd=fac*iwave - minsync=-2 nmode=65 call ana64(dd,npts,c00) diff --git a/lib/test_qra64.f90 b/lib/test_qra64.f90 index 760c87452..24f54c2d6 100644 --- a/lib/test_qra64.f90 +++ b/lib/test_qra64.f90 @@ -45,7 +45,7 @@ program test_qra64 ! 1 2 3 4 5 6 7 ! 12345678901234567890123456789012345678901234567890123456789012345678901' - cmd1='qra64sim "K1ABC W9XYZ EN37 " A 1 0.2 0.00 100 -20 > junk0' + cmd1='qra64sim "K1ABC W9XYZ EN37 " A 1 0.2 0.00 100 F -20 > junk0' cmd2='jt9 -q -L 300 -H 3000 -f 1000 -d 3 -b A *.wav > junk' @@ -74,7 +74,7 @@ program test_qra64 ndecodes=0 nfalse=0 nretcode=0 - write(cmd1(55:57),'(i3)') nsnr + write(cmd1(57:59),'(i3)') nsnr call system(cmd1) call sec0(0,tdec) call system(cmd2) diff --git a/lib/test_qra65.f90 b/lib/test_qra65.f90 index 4a9881d9f..21cc5caab 100644 --- a/lib/test_qra65.f90 +++ b/lib/test_qra65.f90 @@ -1,6 +1,6 @@ program test_qra65 - character*71 cmd1,cmd2,line + character*73 cmd1,cmd2,line character*22 msg character*8 arg character*1 csubmode @@ -60,8 +60,8 @@ program test_qra65 tsym=1.0/baud ! 1 2 3 4 5 6 7 -! 12345678901234567890123456789012345678901234567890123456789012345678901' - cmd1='qra65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 -10 > junk0' +! 1234567890123456789012345678901234567890123456789012345678901234567890123' + cmd1='qra65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 F -10 > junk0' cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A *.wav > junk' write(cmd1(10:33),'(a)') '"'//msg//'"' @@ -90,7 +90,7 @@ program test_qra65 nfalse=0 nretcode=0 navg=0 - write(cmd1(61:63),'(i3)') nsnr + write(cmd1(63:65),'(i3)') nsnr call system(cmd1) call sec0(0,tdec) call system(cmd2) From 2aa999ba44bcce36eff3a4ddeb4ab4d1bca326e7 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 16 Oct 2020 14:44:13 -0400 Subject: [PATCH 083/426] Improved version of qra_params() subroutine, for testing. --- lib/qra64a.f90 | 35 +++++++++++++++++++++++++---------- lib/qra65_decode.f90 | 6 +++--- lib/qra_loops.f90 | 11 ++++------- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index a8c532266..0691cea78 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -37,7 +37,7 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & nFadingModel=1 maxaptype=4 if(iand(ndepth,64).ne.0) maxaptype=5 - call qra_params(ndepth,maxaptype,minsync) + call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & maxaptype.ne.maxaptypez) then do naptype=0,maxaptype @@ -62,7 +62,7 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & if(mode64.eq.1 .and. minsync.ne.-1 .and. (sync-7.0).lt.minsync) go to 900 call timer('qraloops',0) - call qra_loops(c00,npts/2,64,mode64,nsubmode,nFadingModel,minsync, & + call qra_loops(c00,npts/2,64,mode64,nsubmode,nFadingModel, & ndepth,nc1,nc2,ng2,naptype,jpk0,dtx,f0,width,snr2,irc,dat4) call timer('qraloops',1) @@ -95,17 +95,32 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & return end subroutine qra64a -subroutine qra_params(ndepth,maxaptype,minsync) +subroutine qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) ! If file qra_params is present in CWD, read decoding params from it. - - logical ex - inquire(file='qra_params',exist=ex) - if(ex) then - open(29,file='qra_params',status='old') - read(29,*) ndepth,maxaptype,minsync - close(29) + + integer iparam(6) + logical first,ex + data iparam/3,5,11,5,9,9/ !Default values + data first/.true./ + save first,iparam + + if(first) then + inquire(file='qra_params',exist=ex) + if(ex) then + open(29,file='qra_params',status='old') + read(29,*) iparam + close(29) + endif + write(*,'(6i4)') iparam + first=.false. endif + ndepth=iparam(1) + maxaptype=iparam(2) + idf0max=iparam(3) + idt0max=iparam(4) + ibwmin=iparam(5) + ibwmax=iparam(6) return end subroutine qra_params diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index b9b9c1a7b..afde90c9d 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -97,7 +97,7 @@ contains ! if(ndepth.eq.3) maxaptype=5 if(ndepth.ge.2) maxaptype=5 !### minsync=-2 - call qra_params(ndepth,maxaptype,minsync) + call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & maxaptype.ne.maxaptypez) then @@ -127,7 +127,7 @@ contains call ana64(dd,npts,c00) call timer('qraloops',0) - call qra_loops(c00,npts/2,nmode,mode65,nsubmode,nFadingModel,minsync, & + call qra_loops(c00,npts/2,nmode,mode65,nsubmode,nFadingModel, & ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) call timer('qraloops',1) xdt=xdt+0.4 !### Empirical -- WHY ??? ### @@ -146,7 +146,7 @@ contains irc,qual,ntrperiod,fmid,w50) else snr2=0. - nsnr=-25 + nsnr=-30 !### TEMPORARY? ### call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index 59b9458b1..92908a2bc 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -1,4 +1,4 @@ -subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & +subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) use timer_module, only: timer @@ -15,12 +15,9 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel,minsync, & s3lim=20. ibwmax=11 if(mode64.le.4) ibwmax=9 - ibwmin=0 - idtmax=5 - if(minsync.eq.-2) then - ibwmin=ibwmax - idtmax=3 - endif + ibwmin=ibwmax + idtmax=3 + call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) LL=64*(mode64+2) NN=63 napmin=99 From 8542af30f9a8109b56798e93f4219d233b28d340 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 16 Oct 2020 18:42:31 -0400 Subject: [PATCH 084/426] Default ibwmin set to 0 in qra_params(). --- lib/qra64a.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index 0691cea78..ae810d044 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -101,7 +101,7 @@ subroutine qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) integer iparam(6) logical first,ex - data iparam/3,5,11,5,9,9/ !Default values + data iparam/3,5,11,5,0,9/ !Default values data first/.true./ save first,iparam From 3f68f075e80d204d5b3a602301ab38c0f0928176 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 17 Oct 2020 11:19:15 -0400 Subject: [PATCH 085/426] Tweak some QRA65 decoding ideas. --- lib/qra64a.f90 | 6 +++--- lib/qra65_decode.f90 | 2 +- lib/qra_loops.f90 | 32 ++++++++++++++++++++------------ lib/test_qra65.f90 | 2 +- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index ae810d044..dd678ce3e 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -37,7 +37,7 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & nFadingModel=1 maxaptype=4 if(iand(ndepth,64).ne.0) maxaptype=5 - call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) + call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax) if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & maxaptype.ne.maxaptypez) then do naptype=0,maxaptype @@ -101,7 +101,8 @@ subroutine qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) integer iparam(6) logical first,ex - data iparam/3,5,11,5,0,9/ !Default values +! data iparam/3,5,11,5,0,9/ !Maximum effort + data iparam/3,5,3,3,7,9/ !Default values data first/.true./ save first,iparam @@ -112,7 +113,6 @@ subroutine qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) read(29,*) iparam close(29) endif - write(*,'(6i4)') iparam first=.false. endif ndepth=iparam(1) diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index afde90c9d..d79275f53 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -97,7 +97,7 @@ contains ! if(ndepth.eq.3) maxaptype=5 if(ndepth.ge.2) maxaptype=5 !### minsync=-2 - call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) + call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax) if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & maxaptype.ne.maxaptypez) then diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index 92908a2bc..e366ed27d 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -1,8 +1,10 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) + use packjt use timer_module, only: timer parameter (LN=1152*63) + character*37 decoded complex c00(0:720000) !Analytic representation of dd(), 6000 Hz complex c0(0:720000) !Ditto, with freq shift real a(3) !twkfreq params f,f1,f2 @@ -17,7 +19,7 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & if(mode64.le.4) ibwmax=9 ibwmin=ibwmax idtmax=3 - call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) + call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax) LL=64*(mode64+2) NN=63 napmin=99 @@ -25,16 +27,16 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & nsps=3456 !QRA64 if(mode.eq.65) nsps=3840 !QRA65 ### Is 3840 too big? ### - do idf0=1,11 - idf=idf0/2 - if(mod(idf0,2).eq.0) idf=-idf + do idf=1,idfmax + ndf=idfn/2 + if(mod(idf,2).eq.0) ndf=-ndf a=0. - a(1)=-(f0+0.868*idf) + a(1)=-(f0+0.868*ndf) call twkfreq(c00,c0,npts2,6000.0,a) - do idt0=1,idtmax - idt=idt0/2 - if(mod(idt0,2).eq.0) idt=-idt - jpk=jpk0 + 750*idt + do idt=1,idtmax + ndt=idt/2 + if(mod(idt,2).eq.0) ndt=-ndt + jpk=jpk0 + 750*ndt if(jpk.lt.0) jpk=0 call spec64(c0,nsps,mode,jpk,s3,LL,NN) call pctile(s3,LL*NN,40,base) @@ -82,8 +84,14 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & endif 200 continue - write(53,3053) idt,idf,ibw,irc,b90,xdt,f0,snr2 -3053 format(4i5,f7.1,f7.2,2f7.1) - +!### For tests only: + if(irc.ge.0) then + open(53,file='fort.53',status='unknown',position='append') + call unpackmsg(dat4,decoded) !Unpack the user message + write(53,3053) idf,idt,ibw,b90,xdt,f0,snr2,irc,decoded(1:22) +3053 format(3i5,f7.1,f7.2,2f7.1,i5,2x,a22) + close(53) + endif +!### return end subroutine qra_loops diff --git a/lib/test_qra65.f90 b/lib/test_qra65.f90 index 21cc5caab..5b881d6ba 100644 --- a/lib/test_qra65.f90 +++ b/lib/test_qra65.f90 @@ -102,7 +102,7 @@ program test_qra65 if(index(line,' Date: Sat, 17 Oct 2020 13:16:46 -0400 Subject: [PATCH 086/426] Allocate some big arrays on the heap. --- CMakeLists.txt | 2 +- lib/qra65_decode.f90 | 3 ++- lib/spec_qra65.f90 | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c88b61e10..05cf0ae6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1382,7 +1382,7 @@ if (${OPENMP_FOUND} OR APPLE) endif (APPLE) if (WIN32) set_target_properties (jt9 PROPERTIES - LINK_FLAGS -Wl,--stack,28000000 + LINK_FLAGS -Wl,--stack,16777216 ) endif () target_link_libraries (jt9 wsjt_fort_omp wsjt_cxx fort_qt) diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index d79275f53..beb174527 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -51,7 +51,7 @@ contains character(len=6) :: hisgrid character*37 decoded !Decoded message integer*2 iwave(NMAX) !Raw data - real dd(NMAX) !Raw data + real, allocatable :: dd(:) !Raw data integer dat4(12) !Decoded message as 12 6-bit integers logical ltext complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s @@ -62,6 +62,7 @@ contains mode65=2**nsubmode nfft1=ntrperiod*12000 nfft2=ntrperiod*6000 + allocate(dd(NMAX)) allocate (c00(0:nfft1-1)) allocate (c0(0:nfft1-1)) diff --git a/lib/spec_qra65.f90 b/lib/spec_qra65.f90 index 9f6af0b38..15e8fd092 100644 --- a/lib/spec_qra65.f90 +++ b/lib/spec_qra65.f90 @@ -3,12 +3,13 @@ subroutine spec_qra65(c0,nsps,s3,LL,NN) ! Compute synchronized symbol spectra. complex c0(0:85*nsps-1) !Synchronized complex data at 6000 S/s - complex cs(0:nsps-1) !Complex symbol spectrum + complex, allocatable :: cs(:) !Complex symbol spectrum real s3(LL,NN) !Synchronized symbol spectra real xbase0(LL),xbase(LL) !Work arrays integer isync(22) !Indices of sync symbols data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ + allocate(cs(0:nsps-1)) fac=1.0/nsps j=0 n=1 From 3187be38489816092c76cb5819479a41dba8c417 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 19 Oct 2020 11:29:48 -0400 Subject: [PATCH 087/426] Improved qra_loops(). --- lib/qra_loops.f90 | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index e366ed27d..73eeb20f7 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -26,6 +26,9 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & ncall=0 nsps=3456 !QRA64 if(mode.eq.65) nsps=3840 !QRA65 ### Is 3840 too big? ### + maxdist=5 + if(ndepth.eq.2) maxdist=10 + if(ndepth.eq.3) maxdist=30 do idf=1,idfmax ndf=idfn/2 @@ -36,13 +39,15 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & do idt=1,idtmax ndt=idt/2 if(mod(idt,2).eq.0) ndt=-ndt - jpk=jpk0 + 750*ndt + jpk=jpk0 + 240*ndt !240/6000 = 0.04 s = tsym/32 if(jpk.lt.0) jpk=0 call spec64(c0,nsps,mode,jpk,s3,LL,NN) call pctile(s3,LL*NN,40,base) s3=s3/base where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim do ibw=ibwmax,ibwmin,-2 + ndist=ndf**2 + ndt**2 + ((ibwmax-ibw)/2)**2 + if(ndist.gt.maxdist) cycle b90=1.728**ibw if(b90.gt.230.0) cycle if(b90.lt.0.15*width) exit @@ -65,9 +70,11 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & idfkeep=idf idtkeep=idt ibwkeep=ibw + ndistx=ndist + go to 100 !### endif enddo ! ibw (b90 loop) - if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 +!### if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 enddo ! idt (DT loop) enddo ! idf (f0 loop) @@ -81,6 +88,7 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & idt=idtkeep idf=idfkeep ibw=ibwkeep + ndist=ndistx endif 200 continue @@ -88,8 +96,8 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & if(irc.ge.0) then open(53,file='fort.53',status='unknown',position='append') call unpackmsg(dat4,decoded) !Unpack the user message - write(53,3053) idf,idt,ibw,b90,xdt,f0,snr2,irc,decoded(1:22) -3053 format(3i5,f7.1,f7.2,2f7.1,i5,2x,a22) + write(53,3053) idf,idt,ibw,b90,xdt,f0,snr2,ndist,irc,decoded(1:22) +3053 format(3i5,f7.1,f7.2,2f7.1,2i5,2x,a22) close(53) endif !### From df69562a9fc1cb7e75354684f8f24feff68e91ce Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 19 Oct 2020 15:24:32 -0400 Subject: [PATCH 088/426] More tweaks to QRA65 decoder. --- lib/qra65_decode.f90 | 32 +++++++++++++++----------------- lib/qra_loops.f90 | 9 ++++++--- lib/sync_qra65.f90 | 17 ++++++++++++++++- 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index beb174527..e7ff43626 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -118,21 +118,20 @@ contains call sync_qra65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0,snr1) call timer('sync_q65',1) - jpk0=(xdt+1.0)*6000 !### - if(jpk0.lt.0) jpk0=0 - - fac=1.0/32767.0 - dd=fac*iwave - nmode=65 - - call ana64(dd,npts,c00) - - call timer('qraloops',0) - call qra_loops(c00,npts/2,nmode,mode65,nsubmode,nFadingModel, & - ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) - call timer('qraloops',1) - xdt=xdt+0.4 !### Empirical -- WHY ??? ### - snr2=snr2 + db(6912.0/nsps) + irc=-1 + if(snr1.ge.2.7) then + jpk0=(xdt+1.0)*6000 !### + if(jpk0.lt.0) jpk0=0 + fac=1.0/32767.0 + dd=fac*iwave + nmode=65 + call ana64(dd,npts,c00) + call timer('qraloops',0) + call qra_loops(c00,npts/2,nmode,mode65,nsubmode,nFadingModel, & + ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) + call timer('qraloops',1) + snr2=snr2 + db(6912.0/nsps) + endif decoded=' ' if(irc.ge.0) then call unpackmsg(dat4,decoded) !Unpack the user message @@ -146,8 +145,7 @@ contains call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) else - snr2=0. - nsnr=-30 + nsnr=db(snr1) - 32.0 !### TEMPORARY? ### call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index 73eeb20f7..f2d1e30e5 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -31,17 +31,19 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & if(ndepth.eq.3) maxdist=30 do idf=1,idfmax - ndf=idfn/2 + ndf=idf/2 if(mod(idf,2).eq.0) ndf=-ndf a=0. - a(1)=-(f0+0.868*ndf) + a(1)=-(f0+0.4*ndf) call twkfreq(c00,c0,npts2,6000.0,a) do idt=1,idtmax ndt=idt/2 if(mod(idt,2).eq.0) ndt=-ndt jpk=jpk0 + 240*ndt !240/6000 = 0.04 s = tsym/32 if(jpk.lt.0) jpk=0 + call timer('spec64 ',0) call spec64(c0,nsps,mode,jpk,s3,LL,NN) + call timer('spec64 ',1) call pctile(s3,LL*NN,40,base) s3=s3/base where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim @@ -91,7 +93,8 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & ndist=ndistx endif -200 continue +200 if(mode.eq.65) xdt=xdt+0.4 !### Empirical -- WHY ??? ### + !### For tests only: if(irc.ge.0) then open(53,file='fort.53',status='unknown',position='append') diff --git a/lib/sync_qra65.f90 b/lib/sync_qra65.f90 index b18aa8870..401efcc3c 100644 --- a/lib/sync_qra65.f90 +++ b/lib/sync_qra65.f90 @@ -97,7 +97,22 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) jpk=ijpk(2)-27 f0=nfqso + ipk*df xdt=jpk*dtstep - snr1=maxval(ccf)/22.0 + sq=0. + nsq=0 + do j=lag1,lag2 + if(abs(j-jpk).gt.6) then + sq=sq + ccf(ipk,j)**2 + nsq=nsq+1 + endif + enddo + rms=sqrt(sq/nsq) + snr1=ccf(ipk,jpk)/rms + +! do j=lag1,lag2 +! write(55,3055) j,j*dtstep,ccf(ipk,j)/rms +!3055 format(i5,f8.3,f10.3) +! enddo + return end subroutine sync_qra65 From 35d85744264eec87335c7ca894cc663089c64b5e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 19 Oct 2020 17:27:11 -0400 Subject: [PATCH 089/426] Basic message averaging implemented for QRA65. --- lib/qra_loops.f90 | 131 +++++++++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 54 deletions(-) diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index f2d1e30e5..797367c76 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -9,9 +9,12 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & complex c0(0:720000) !Ditto, with freq shift real a(3) !twkfreq params f,f1,f2 real s3(LN) !Symbol spectra + real s3a(LN) !Saved symbol spectra + real s3avg(LN) !Averaged symbol spectra integer dat4(12),dat4x(12) !Decoded message (as 12 integers) integer nap(0:11) !AP return codes - data nap/0,2,3,2,3,4,2,3,6,4,6,6/ + data nap/0,2,3,2,3,4,2,3,6,4,6,6/,nsave/0/ + save nsave,s3avg irc=-99 s3lim=20. @@ -30,56 +33,72 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & if(ndepth.eq.2) maxdist=10 if(ndepth.eq.3) maxdist=30 - do idf=1,idfmax - ndf=idf/2 - if(mod(idf,2).eq.0) ndf=-ndf - a=0. - a(1)=-(f0+0.4*ndf) - call twkfreq(c00,c0,npts2,6000.0,a) - do idt=1,idtmax - ndt=idt/2 - if(mod(idt,2).eq.0) ndt=-ndt - jpk=jpk0 + 240*ndt !240/6000 = 0.04 s = tsym/32 - if(jpk.lt.0) jpk=0 - call timer('spec64 ',0) - call spec64(c0,nsps,mode,jpk,s3,LL,NN) - call timer('spec64 ',1) - call pctile(s3,LL*NN,40,base) - s3=s3/base - where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim - do ibw=ibwmax,ibwmin,-2 - ndist=ndf**2 + ndt**2 + ((ibwmax-ibw)/2)**2 - if(ndist.gt.maxdist) cycle - b90=1.728**ibw - if(b90.gt.230.0) cycle - if(b90.lt.0.15*width) exit - ncall=ncall+1 - call timer('qra64_de',0) - call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & - nFadingModel,dat4,snr2,irc) - call timer('qra64_de',1) - if(irc.eq.0) go to 200 - if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) - iirc=max(0,min(irc,11)) - if(irc.gt.0 .and. nap(iirc).lt.napmin) then - dat4x=dat4 - b90x=b90 - snr2x=snr2 - napmin=nap(iirc) - irckeep=irc - xdtkeep=jpk/6000.0 - 1.0 - f0keep=-a(1) - idfkeep=idf - idtkeep=idt - ibwkeep=ibw - ndistx=ndist - go to 100 !### + do iavg=0,1 + if(iavg.eq.1) then + idfmax=1 + idtmax=1 + endif + do idf=1,idfmax + ndf=idf/2 + if(mod(idf,2).eq.0) ndf=-ndf + a=0. + a(1)=-(f0+0.4*ndf) + call twkfreq(c00,c0,npts2,6000.0,a) + do idt=1,idtmax + ndt=idt/2 + if(iavg.eq.0) then + if(mod(idt,2).eq.0) ndt=-ndt + jpk=jpk0 + 240*ndt !240/6000 = 0.04 s = tsym/32 + if(jpk.lt.0) jpk=0 + call timer('spec64 ',0) + call spec64(c0,nsps,mode,jpk,s3,LL,NN) + call timer('spec64 ',1) + call pctile(s3,LL*NN,40,base) + s3=s3/base + where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim + if(iavg.eq.0 .and. idf.eq.1 .and. idt.eq.1) s3a(1:LL*NN)=s3(1:LL*NN) + else + s3(1:LL*NN)=s3avg(1:LL*NN) endif - enddo ! ibw (b90 loop) -!### if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 - enddo ! idt (DT loop) - enddo ! idf (f0 loop) - + do ibw=ibwmax,ibwmin,-2 + ndist=ndf**2 + ndt**2 + ((ibwmax-ibw)/2)**2 + if(ndist.gt.maxdist) cycle + b90=1.728**ibw + if(b90.gt.230.0) cycle + if(b90.lt.0.15*width) exit + ncall=ncall+1 + call timer('qra64_de',0) + call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & + nFadingModel,dat4,snr2,irc) + call timer('qra64_de',1) + if(irc.eq.0) go to 200 + if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) + iirc=max(0,min(irc,11)) + if(irc.gt.0 .and. nap(iirc).lt.napmin) then + dat4x=dat4 + b90x=b90 + snr2x=snr2 + napmin=nap(iirc) + irckeep=irc + xdtkeep=jpk/6000.0 - 1.0 + f0keep=-a(1) + idfkeep=idf + idtkeep=idt + ibwkeep=ibw + ndistx=ndist + go to 100 !### + endif + enddo ! ibw (b90 loop) + !### if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 + enddo ! idt (DT loop) + enddo ! idf (f0 loop) + if(iavg.eq.0 .and. abs(jpk0-4320).le.1300) then + s3avg(1:LL*NN)=s3avg(1:LL*NN)+s3a(1:LL*NN) + nsave=nsave+1 + endif + if(iavg.eq.0 .and. nsave.lt.2) exit + enddo ! iavg + 100 if(napmin.ne.99) then dat4=dat4x b90=b90x @@ -95,14 +114,18 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & 200 if(mode.eq.65) xdt=xdt+0.4 !### Empirical -- WHY ??? ### -!### For tests only: if(irc.ge.0) then + navg=nsave + if(iavg.eq.0) navg=0 + !### For tests only: open(53,file='fort.53',status='unknown',position='append') call unpackmsg(dat4,decoded) !Unpack the user message - write(53,3053) idf,idt,ibw,b90,xdt,f0,snr2,ndist,irc,decoded(1:22) -3053 format(3i5,f7.1,f7.2,2f7.1,2i5,2x,a22) + write(53,3053) idf,idt,ibw,b90,xdt,f0,snr2,ndist,irc,navg,decoded(1:22) +3053 format(3i5,f7.1,f7.2,2f7.1,3i4,2x,a22) close(53) + !### + nsave=0 + s3avg=0. endif -!### return end subroutine qra_loops From ef3b158d431a2c6652637b625fd4962d06095061 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 19 Oct 2020 18:35:50 -0400 Subject: [PATCH 090/426] QRA65 message averaging assumes a fixed DT close to zero. --- lib/qra_loops.f90 | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index 797367c76..3c9c2f1c3 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -9,7 +9,6 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & complex c0(0:720000) !Ditto, with freq shift real a(3) !twkfreq params f,f1,f2 real s3(LN) !Symbol spectra - real s3a(LN) !Saved symbol spectra real s3avg(LN) !Averaged symbol spectra integer dat4(12),dat4x(12) !Decoded message (as 12 integers) integer nap(0:11) !AP return codes @@ -56,7 +55,7 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & call pctile(s3,LL*NN,40,base) s3=s3/base where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim - if(iavg.eq.0 .and. idf.eq.1 .and. idt.eq.1) s3a(1:LL*NN)=s3(1:LL*NN) +! if(iavg.eq.0 .and. idf.eq.1 .and. idt.eq.1) s3a(1:LL*NN)=s3(1:LL*NN) else s3(1:LL*NN)=s3avg(1:LL*NN) endif @@ -92,8 +91,18 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & !### if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 enddo ! idt (DT loop) enddo ! idf (f0 loop) - if(iavg.eq.0 .and. abs(jpk0-4320).le.1300) then - s3avg(1:LL*NN)=s3avg(1:LL*NN)+s3a(1:LL*NN) +! if(iavg.eq.0 .and. abs(jpk0-4320).le.1300) then + if(iavg.eq.0) then + a=0. + a(1)=-f0 + call twkfreq(c00,c0,npts2,6000.0,a) +! jpk=4320 + jpk=4080 + call spec64(c0,nsps,mode,jpk,s3,LL,NN) + call pctile(s3,LL*NN,40,base) + s3=s3/base + where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim + s3avg(1:LL*NN)=s3avg(1:LL*NN)+s3(1:LL*NN) nsave=nsave+1 endif if(iavg.eq.0 .and. nsave.lt.2) exit From a17a5193241c26c7c0137a0454c58bfb48557594 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 19 Oct 2020 18:49:52 -0400 Subject: [PATCH 091/426] Send QRA65 navg to the callback routine. --- lib/qra_loops.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index 3c9c2f1c3..0679e4d88 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -135,6 +135,7 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & !### nsave=0 s3avg=0. + irc=irc + 100*navg endif return end subroutine qra_loops From 7fe18a0a8ccfd1362817c17d5653acce211692a9 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 19 Oct 2020 18:56:54 -0400 Subject: [PATCH 092/426] Reset the default values in qra_params.f90. --- lib/qra64a.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index dd678ce3e..1a9728a8f 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -101,8 +101,8 @@ subroutine qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) integer iparam(6) logical first,ex -! data iparam/3,5,11,5,0,9/ !Maximum effort - data iparam/3,5,3,3,7,9/ !Default values +! data iparam/3,5,3,11,0,9/ !Maximum effort + data iparam/2,5,3,11,3,9/ !Default values data first/.true./ save first,iparam From 0ce65566117ee0f44bdf31ce58fa7e86486993d7 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 20 Oct 2020 22:05:43 -0400 Subject: [PATCH 093/426] Slight reduction in snr1 limit after sync_qra65(). --- lib/qra65_decode.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index e7ff43626..ea021e4cb 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -119,7 +119,7 @@ contains call timer('sync_q65',1) irc=-1 - if(snr1.ge.2.7) then + if(snr1.ge.2.5) then jpk0=(xdt+1.0)*6000 !### if(jpk0.lt.0) jpk0=0 fac=1.0/32767.0 From b68ccee2fae3952f6184abc432b77d6c4feb29d6 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 22 Oct 2020 14:05:20 -0400 Subject: [PATCH 094/426] Correct a flaw in assigning bin numbers for in sync64(), fixing QRA65 submodes B C D E. --- lib/qra_loops.f90 | 4 ++-- lib/spec64.f90 | 15 +++++++++------ lib/sync_qra65.f90 | 11 +++++++++-- widgets/mainwindow.cpp | 2 +- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index 0679e4d88..d6e2072f1 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -50,7 +50,7 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & jpk=jpk0 + 240*ndt !240/6000 = 0.04 s = tsym/32 if(jpk.lt.0) jpk=0 call timer('spec64 ',0) - call spec64(c0,nsps,mode,jpk,s3,LL,NN) + call spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) call timer('spec64 ',1) call pctile(s3,LL*NN,40,base) s3=s3/base @@ -98,7 +98,7 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & call twkfreq(c00,c0,npts2,6000.0,a) ! jpk=4320 jpk=4080 - call spec64(c0,nsps,mode,jpk,s3,LL,NN) + call spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) call pctile(s3,LL*NN,40,base) s3=s3/base where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim diff --git a/lib/spec64.f90 b/lib/spec64.f90 index 377e8dfcf..b6cc10c34 100644 --- a/lib/spec64.f90 +++ b/lib/spec64.f90 @@ -1,10 +1,11 @@ -subroutine spec64(c0,nsps,mode,jpk,s3,LL,NN) +subroutine spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) parameter (MAXFFT=3840) complex c0(0:360000) !Complex spectrum of dd() complex cs(0:MAXFFT-1) !Complex symbol spectrum real s3(LL,NN) !Synchronized symbol spectra real xbase0(LL),xbase(LL) +! integer ipk1(1) integer isync(22) !Indices of sync symbols data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ @@ -39,7 +40,7 @@ subroutine spec64(c0,nsps,mode,jpk,s3,LL,NN) cs(0:nfft-1)=fac*c0(ja:jb) call four2a(cs,nsps,1,-1,1) !c2c FFT to frequency do ii=1,LL - i=ii-65 + i=ii-65+mode64 !mode64 = 1 2 4 8 16 for QRA65 A B C D E if(i.lt.0) i=i+nsps s3(ii,j)=real(cs(i))**2 + aimag(cs(i))**2 enddo @@ -62,16 +63,18 @@ subroutine spec64(c0,nsps,mode,jpk,s3,LL,NN) s3(i,1:NN)=s3(i,1:NN)/(xbase(i)+0.001) !Apply frequency equalization enddo -! print*,'a',LL,NN,jpk +! print*,'a',LL,NN,jpk,mode,mode64 ! df=6000.0/nfft ! do i=1,LL ! write(71,3071) i,i-65,i*df,(s3(i,j),j=1,4) !3071 format(2i8,f10.3,4e12.3) ! enddo -! + ! do j=1,NN -! write(72,3072) j,maxloc(s3(1:LL,j)),maxloc(s3(1:LL,j))-65 -!3072 format(3i8) +! ipk1=maxloc(s3(1:LL,j)) +! m=ipk1(1)-65 +! write(72,3072) j,m,m/2,m/4,m/8 +!3072 format(5i8) ! enddo ! if(nfft.ne.-999) stop diff --git a/lib/sync_qra65.f90 b/lib/sync_qra65.f90 index 401efcc3c..c58241dbc 100644 --- a/lib/sync_qra65.f90 +++ b/lib/sync_qra65.f90 @@ -107,12 +107,19 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) endif enddo rms=sqrt(sq/nsq) - snr1=ccf(ipk,jpk)/rms + smax=ccf(ipk,jpk) + snr1=smax/rms ! do j=lag1,lag2 ! write(55,3055) j,j*dtstep,ccf(ipk,j)/rms !3055 format(i5,f8.3,f10.3) ! enddo - + +! ncall=ncall+1 +! do i=-ia,ia +! write(56,3056) ncall,i*df,ncall+0.3*ccf(i,0)/rms +!3056 format(i6,f7.2,f10.3) +! enddo + return end subroutine sync_qra65 diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index d211cbc1a..cd0152889 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6403,7 +6403,7 @@ void MainWindow::on_actionQRA65_triggered() m_hsymStop=49; ui->sbTR->values ({15, 30, 60, 120, 300}); on_sbTR_valueChanged (ui->sbTR->value()); - ui->sbSubmode->setMaximum(3); + ui->sbSubmode->setMaximum(4); ui->sbSubmode->setValue(m_nSubMode); m_wideGraph->setMode(m_mode); m_wideGraph->setMode(m_mode); m_wideGraph->setModeTx(m_modeTx); From 687d60019eef8151bd2fbb03bc49918d1317e4c6 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 22 Oct 2020 15:56:20 -0400 Subject: [PATCH 095/426] Working toward functionality for sequence lengths other than 60 s. --- lib/ana64.f90 | 11 ++++------- lib/qra64a.f90 | 3 ++- lib/qra65_decode.f90 | 2 +- lib/qra_loops.f90 | 6 ++---- lib/sync_qra65.f90 | 11 +++++------ 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/lib/ana64.f90 b/lib/ana64.f90 index faebb84e5..1f3b54820 100644 --- a/lib/ana64.f90 +++ b/lib/ana64.f90 @@ -2,20 +2,17 @@ subroutine ana64(dd,npts,c0) use timer_module, only: timer - parameter (NMAX=60*12000) !Max size of raw data at 12000 Hz - real dd(NMAX) !Raw data - complex c0(0:720000) !Complex spectrum of dd() + real dd(npts) !Raw data at 12000 Hz + complex c0(0:npts-1) !Complex data at 6000 Hz save -! nfft1=672000 - nfft1=720000 + nfft1=npts nfft2=nfft1/2 df1=12000.0/nfft1 fac=2.0/nfft1 c0(0:npts-1)=fac*dd(1:npts) - c0(npts:nfft1)=0. call four2a(c0,nfft1,1,-1,1) !Forward c2c FFT - c0(nfft2/2+1:nfft2)=0. + c0(nfft2/2+1:nfft2-1)=0. c0(0)=0.5*c0(0) call four2a(c0,nfft2,1,1,1) !Inverse c2c FFT; c0 is analytic sig diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index 1a9728a8f..45e44d8cf 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -61,8 +61,9 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & nfreq=nint(f0) if(mode64.eq.1 .and. minsync.ne.-1 .and. (sync-7.0).lt.minsync) go to 900 + nsps=6912 call timer('qraloops',0) - call qra_loops(c00,npts/2,64,mode64,nsubmode,nFadingModel, & + call qra_loops(c00,npts/2,nsps,64,mode64,nsubmode,nFadingModel, & ndepth,nc1,nc2,ng2,naptype,jpk0,dtx,f0,width,snr2,irc,dat4) call timer('qraloops',1) diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index ea021e4cb..4762e8419 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -127,7 +127,7 @@ contains nmode=65 call ana64(dd,npts,c00) call timer('qraloops',0) - call qra_loops(c00,npts/2,nmode,mode65,nsubmode,nFadingModel, & + call qra_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode,nFadingModel, & ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) call timer('qraloops',1) snr2=snr2 + db(6912.0/nsps) diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index d6e2072f1..21dbc863d 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -1,4 +1,4 @@ -subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & +subroutine qra_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) use packjt @@ -26,8 +26,6 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & NN=63 napmin=99 ncall=0 - nsps=3456 !QRA64 - if(mode.eq.65) nsps=3840 !QRA65 ### Is 3840 too big? ### maxdist=5 if(ndepth.eq.2) maxdist=10 if(ndepth.eq.3) maxdist=30 @@ -121,7 +119,7 @@ subroutine qra_loops(c00,npts2,mode,mode64,nsubmode,nFadingModel, & ndist=ndistx endif -200 if(mode.eq.65) xdt=xdt+0.4 !### Empirical -- WHY ??? ### +200 if(mode.eq.65 .and. nsps.eq.7680/2) xdt=xdt+0.4 !### Empirical -- WHY ??? ### if(irc.ge.0) then navg=nsave diff --git a/lib/sync_qra65.f90 b/lib/sync_qra65.f90 index c58241dbc..3b608f20a 100644 --- a/lib/sync_qra65.f90 +++ b/lib/sync_qra65.f90 @@ -18,7 +18,7 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) integer ijpk(2) !Indices i and j at peak of ccf real, allocatable :: s1(:,:) !Symbol spectra, quarter-symbol steps real sync(85) !sync vector - real ccf(-64:64,-26:107) !CCF(freq,time) + real ccf(-64:64,-53:214) !CCF(freq,time) complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ data sync(1)/99.0/ @@ -94,10 +94,10 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) ijpk=maxloc(ccf) ipk=ijpk(1)-65 - jpk=ijpk(2)-27 + jpk=ijpk(2)-54 f0=nfqso + ipk*df xdt=jpk*dtstep - + sq=0. nsq=0 do j=lag1,lag2 @@ -115,10 +115,9 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) !3055 format(i5,f8.3,f10.3) ! enddo -! ncall=ncall+1 ! do i=-ia,ia -! write(56,3056) ncall,i*df,ncall+0.3*ccf(i,0)/rms -!3056 format(i6,f7.2,f10.3) +! write(56,3056) i*df,ccf(i,0)/rms +!3056 format(2f10.3) ! enddo return From 37e5489080e57f33a5aa78075f3d38f1535ac9dd Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 22 Oct 2020 19:07:21 -0400 Subject: [PATCH 096/426] QRA65 submodes A-E now basically OK for 15, 30, 60, 120 s sequences; 300 s is NG. --- lib/qra65_decode.f90 | 3 ++- lib/spec64.f90 | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/qra65_decode.f90 b/lib/qra65_decode.f90 index 4762e8419..6c6cf29ae 100644 --- a/lib/qra65_decode.f90 +++ b/lib/qra65_decode.f90 @@ -120,7 +120,8 @@ contains irc=-1 if(snr1.ge.2.5) then - jpk0=(xdt+1.0)*6000 !### + jpk0=(xdt+1.0)*6000 !### + if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !### if(jpk0.lt.0) jpk0=0 fac=1.0/32767.0 dd=fac*iwave diff --git a/lib/spec64.f90 b/lib/spec64.f90 index b6cc10c34..9a62434ee 100644 --- a/lib/spec64.f90 +++ b/lib/spec64.f90 @@ -1,7 +1,8 @@ subroutine spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) - parameter (MAXFFT=3840) - complex c0(0:360000) !Complex spectrum of dd() + parameter (MAXFFT=20736) +!### Fix this: + complex c0(0:1800000-1) !Complex spectrum of dd() complex cs(0:MAXFFT-1) !Complex symbol spectrum real s3(LL,NN) !Synchronized symbol spectra real xbase0(LL),xbase(LL) @@ -76,7 +77,6 @@ subroutine spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) ! write(72,3072) j,m,m/2,m/4,m/8 !3072 format(5i8) ! enddo -! if(nfft.ne.-999) stop return end subroutine spec64 From 90fb84e43ed144de741922ac398e483797272baf Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 22 Oct 2020 19:20:56 -0400 Subject: [PATCH 097/426] All QRA65 submodes and sequence lengths 15 - 300 s are now basically functional. --- lib/qra_loops.f90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index 21dbc863d..a835ed5e9 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -5,8 +5,8 @@ subroutine qra_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & use timer_module, only: timer parameter (LN=1152*63) character*37 decoded - complex c00(0:720000) !Analytic representation of dd(), 6000 Hz - complex c0(0:720000) !Ditto, with freq shift + complex c00(0:npts2-1) !Analytic representation of dd(), 6000 Hz + complex ,allocatable :: c0(:) !Ditto, with freq shift real a(3) !twkfreq params f,f1,f2 real s3(LN) !Symbol spectra real s3avg(LN) !Averaged symbol spectra @@ -15,6 +15,7 @@ subroutine qra_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & data nap/0,2,3,2,3,4,2,3,6,4,6,6/,nsave/0/ save nsave,s3avg + allocate(c0(0:npts2-1)) irc=-99 s3lim=20. ibwmax=11 From 9b452e8f993f20932dbef8b81ade6071ceacd332 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 25 Oct 2020 13:58:18 -0400 Subject: [PATCH 098/426] Change mode name QRA65 to Q65 everywhere, supposedly. --- CMakeLists.txt | 14 ++--- Modulator/Modulator.cpp | 3 +- lib/decoder.f90 | 42 +++++++-------- lib/{qra65_decode.f90 => q65_decode.f90} | 28 +++++----- lib/q65params.f90 | 20 ++++++++ lib/qra/qra65/{qra65sim.f90 => q65sim.f90} | 12 ++--- lib/qra64a.f90 | 13 ++--- lib/qra_loops.f90 | 10 ++-- lib/spec64.f90 | 2 +- lib/{sync_qra65.f90 => sync_q65.f90} | 6 +-- lib/{test_qra65.f90 => test_q65.f90} | 60 ++++++++++++---------- models/FrequencyList.cpp | 2 +- models/Modes.cpp | 2 +- models/Modes.hpp | 2 +- widgets/mainwindow.cpp | 50 +++++++++--------- widgets/mainwindow.h | 4 +- widgets/mainwindow.ui | 6 +-- widgets/plotter.cpp | 8 +-- 18 files changed, 154 insertions(+), 130 deletions(-) rename lib/{qra65_decode.f90 => q65_decode.f90} (89%) create mode 100644 lib/q65params.f90 rename lib/qra/qra65/{qra65sim.f90 => q65sim.f90} (94%) rename lib/{sync_qra65.f90 => sync_q65.f90} (95%) rename lib/{test_qra65.f90 => test_q65.f90} (66%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05cf0ae6a..1ac4ad4f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -324,7 +324,7 @@ set (wsjt_FSRCS lib/options.f90 lib/packjt.f90 lib/77bit/packjt77.f90 - lib/qra65_decode.f90 + lib/q65_decode.f90 lib/readwav.f90 lib/timer_C_wrapper.f90 lib/timer_impl.f90 @@ -525,7 +525,7 @@ set (wsjt_FSRCS lib/sync4.f90 lib/sync64.f90 lib/sync65.f90 - lib/sync_qra65.f90 + lib/sync_q65.f90 lib/ft4/getcandidates4.f90 lib/ft4/get_ft4_bitmetrics.f90 lib/ft8/sync8.f90 @@ -1114,14 +1114,14 @@ target_link_libraries (sumsim wsjt_fort wsjt_cxx) add_executable (qra64sim lib/qra/qra64/qra64sim.f90) target_link_libraries (qra64sim wsjt_fort wsjt_cxx) -add_executable (qra65sim lib/qra/qra65/qra65sim.f90) -target_link_libraries (qra65sim wsjt_fort wsjt_cxx) +add_executable (q65sim lib/qra/qra65/q65sim.f90) +target_link_libraries (q65sim wsjt_fort wsjt_cxx) add_executable (test_qra64 lib/test_qra64.f90) target_link_libraries (test_qra64 wsjt_fort wsjt_cxx) -add_executable (test_qra65 lib/test_qra65.f90) -target_link_libraries (test_qra65 wsjt_fort wsjt_cxx) +add_executable (test_q65 lib/test_q65.f90) +target_link_libraries (test_q65 wsjt_fort wsjt_cxx) add_executable (jt49sim lib/jt49sim.f90) target_link_libraries (jt49sim wsjt_fort wsjt_cxx) @@ -1545,7 +1545,7 @@ install (TARGETS jt9 wsprd fmtave fcal fmeasure if(WSJT_BUILD_UTILS) install (TARGETS ft8code jt65code qra64code qra64sim jt9code jt4code - msk144code fst4sim qra65sim + msk144code fst4sim q65sim RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime ) diff --git a/Modulator/Modulator.cpp b/Modulator/Modulator.cpp index df096131b..32645bec2 100644 --- a/Modulator/Modulator.cpp +++ b/Modulator/Modulator.cpp @@ -70,7 +70,8 @@ void Modulator::start (QString mode, unsigned symbolsLength, double framesPerSym m_bFastMode=fastMode; m_TRperiod=TRperiod; unsigned delay_ms=1000; - if(mode=="FT8" or (mode=="FST4" and m_nsps==720) or mode=="QRA65") delay_ms=500; //FT8, FST4-15, QRA65 + if(mode=="FT8" or (mode=="FST4" and m_nsps==720)) delay_ms=500; //FT8, FST4-15 + if(mode=="Q65" and m_nsps<=3600) delay_ms=500; //Q65-15 and Q65-30 if(mode=="FT4") delay_ms=300; //FT4 // noise generator parameters diff --git a/lib/decoder.f90 b/lib/decoder.f90 index f70d07108..911352277 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -9,7 +9,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) use ft8_decode use ft4_decode use fst4_decode - use qra65_decode + use q65_decode include 'jt9com.f90' include 'timer_common.inc' @@ -38,9 +38,9 @@ subroutine multimode_decoder(ss,id2,params,nfsample) integer :: decoded end type counting_fst4_decoder - type, extends(qra65_decoder) :: counting_qra65_decoder + type, extends(q65_decoder) :: counting_q65_decoder integer :: decoded - end type counting_qra65_decoder + end type counting_q65_decoder real ss(184,NSMAX) logical baddata,newdat65,newdat9,single_decode,bVHF,bad0,newdat,ex @@ -59,7 +59,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) type(counting_ft8_decoder) :: my_ft8 type(counting_ft4_decoder) :: my_ft4 type(counting_fst4_decoder) :: my_fst4 - type(counting_qra65_decoder) :: my_qra65 + type(counting_q65_decoder) :: my_q65 rms=sqrt(dot_product(float(id2(1:180000)), & float(id2(1:180000)))/180000.0) @@ -79,7 +79,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) my_ft8%decoded = 0 my_ft4%decoded = 0 my_fst4%decoded = 0 - my_qra65%decoded = 0 + my_q65%decoded = 0 ! For testing only: return Rx messages stored in a file as decodes inquire(file='rx_messages.txt',exist=ex) @@ -198,13 +198,13 @@ subroutine multimode_decoder(ss,id2,params,nfsample) go to 800 endif - if(params%nmode.eq.66) then !NB: JT65 = 65, QRA65 = 66. -! We're in QRA65 mode - call timer('decqra65',0) - call my_qra65%decode(qra65_decoded,id2,params%nutc,params%ntr, & + if(params%nmode.eq.66) then !NB: JT65 = 65, Q65 = 66. +! We're in Q65 mode + call timer('dec_q65 ',0) + call my_q65%decode(q65_decoded,id2,params%nutc,params%ntr, & params%nsubmode,params%nfqso,params%ntol,params%ndepth, & mycall,hiscall,hisgrid) - call timer('decqra65',1) + call timer('dec_q65 ',1) go to 800 endif @@ -213,13 +213,13 @@ subroutine multimode_decoder(ss,id2,params,nfsample) ndepth=iand(params%ndepth,3) iwspr=0 params%nsubmode=0 - call timer('dec240 ',0) + call timer('dec_fst4',0) call my_fst4%decode(fst4_decoded,id2,params%nutc, & params%nQSOProgress,params%nfa,params%nfb, & params%nfqso,ndepth,params%ntr,params%nexp_decode, & params%ntol,params%emedelay,logical(params%nagain), & logical(params%lapcqonly),mycall,hiscall,iwspr) - call timer('dec240 ',1) + call timer('dec_fst4',1) go to 800 endif @@ -227,13 +227,13 @@ subroutine multimode_decoder(ss,id2,params,nfsample) ! We're in FST4W mode ndepth=iand(params%ndepth,3) iwspr=1 - call timer('dec240 ',0) + call timer('dec_fst4',0) call my_fst4%decode(fst4_decoded,id2,params%nutc, & params%nQSOProgress,params%nfa,params%nfb, & params%nfqso,ndepth,params%ntr,params%nexp_decode, & params%ntol,params%emedelay,logical(params%nagain), & logical(params%lapcqonly),mycall,hiscall,iwspr) - call timer('dec240 ',1) + call timer('dec_fst4',1) go to 800 endif @@ -776,13 +776,13 @@ contains return end subroutine fst4_decoded - subroutine qra65_decoded (this,nutc,sync,nsnr,dt,freq,decoded,irc, & + subroutine q65_decoded (this,nutc,sync,nsnr,dt,freq,decoded,irc, & qual,ntrperiod,fmid,w50) - use qra65_decode + use q65_decode implicit none - class(qra65_decoder), intent(inout) :: this + class(q65_decoder), intent(inout) :: this integer, intent(in) :: nutc real, intent(in) :: sync integer, intent(in) :: nsnr @@ -801,23 +801,23 @@ contains write(*,1001) nutc,nsnr,dt,nint(freq),decoded,mod(irc,100),navg 1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i2,i4) write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded -1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' QRA65') +1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') else write(*,1003) nutc,nsnr,dt,nint(freq),decoded,mod(irc,100),navg 1003 format(i4.4,i4,f5.1,i5,' + ',1x,a37,1x,i2,i4) write(13,1004) nutc,nint(sync),nsnr,dt,freq,0,decoded -1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' QRA65') +1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') endif call flush(6) call flush(13) select type(this) - type is (counting_qra65_decoder) + type is (counting_q65_decoder) this%decoded = this%decoded + 1 end select return - end subroutine qra65_decoded + end subroutine q65_decoded end subroutine multimode_decoder diff --git a/lib/qra65_decode.f90 b/lib/q65_decode.f90 similarity index 89% rename from lib/qra65_decode.f90 rename to lib/q65_decode.f90 index 6c6cf29ae..8b522ce2e 100644 --- a/lib/qra65_decode.f90 +++ b/lib/q65_decode.f90 @@ -1,17 +1,17 @@ -module qra65_decode +module q65_decode - type :: qra65_decoder - procedure(qra65_decode_callback), pointer :: callback + type :: q65_decoder + procedure(q65_decode_callback), pointer :: callback contains procedure :: decode - end type qra65_decoder + end type q65_decoder abstract interface - subroutine qra65_decode_callback (this,nutc,sync,nsnr,dt,freq, & + subroutine q65_decode_callback (this,nutc,sync,nsnr,dt,freq, & decoded,nap,qual,ntrperiod,fmid,w50) - import qra65_decoder + import q65_decoder implicit none - class(qra65_decoder), intent(inout) :: this + class(q65_decoder), intent(inout) :: this integer, intent(in) :: nutc real, intent(in) :: sync integer, intent(in) :: nsnr @@ -23,7 +23,7 @@ module qra65_decode integer, intent(in) :: ntrperiod real, intent(in) :: fmid real, intent(in) :: w50 - end subroutine qra65_decode_callback + end subroutine q65_decode_callback end interface contains @@ -31,7 +31,7 @@ contains subroutine decode(this,callback,iwave,nutc,ntrperiod,nsubmode,nfqso, & ntol,ndepth,mycall,hiscall,hisgrid) -! Decodes QRA65 signals +! Decodes Q65 signals ! Input: iwave Raw data, i*2 ! nutc UTC for time-tagging the decode ! ntrperiod T/R sequence length (s) @@ -45,8 +45,8 @@ contains use packjt use, intrinsic :: iso_c_binding parameter (NMAX=300*12000) !Max TRperiod is 300 s - class(qra65_decoder), intent(inout) :: this - procedure(qra65_decode_callback) :: callback + class(q65_decoder), intent(inout) :: this + procedure(q65_decode_callback) :: callback character(len=12) :: mycall, hiscall !Used for AP decoding character(len=6) :: hisgrid character*37 decoded !Decoded message @@ -98,7 +98,7 @@ contains ! if(ndepth.eq.3) maxaptype=5 if(ndepth.ge.2) maxaptype=5 !### minsync=-2 - call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax) + call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & maxaptype.ne.maxaptypez) then @@ -115,7 +115,7 @@ contains naptype=maxaptype call timer('sync_q65',0) - call sync_qra65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0,snr1) + call sync_q65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0,snr1) call timer('sync_q65',1) irc=-1 @@ -159,4 +159,4 @@ contains return end subroutine decode -end module qra65_decode +end module q65_decode diff --git a/lib/q65params.f90 b/lib/q65params.f90 new file mode 100644 index 000000000..c519cc94e --- /dev/null +++ b/lib/q65params.f90 @@ -0,0 +1,20 @@ +program q65params + + integer ntrp(5) + integer nsps(5) + data ntrp/15,30,60,120,300/ + data nsps/1800,3600,7200,15680,40960/ + + write(*,1000) +1000 format('T/R tsym baud BW TxT SNR'/39('-')) + do i=1,5 + baud=12000.0/nsps(i) + bw=65.0*baud + tsym=1.0/baud + txt=85.0*tsym + snr=-27.0 + 10.0*log10(7200.0/nsps(i)) + write(*,1010) ntrp(i),tsym,baud,bw,txt,snr +1010 format(i3,2f7.3,3f7.1) + enddo + +end program q65params diff --git a/lib/qra/qra65/qra65sim.f90 b/lib/qra/qra65/q65sim.f90 similarity index 94% rename from lib/qra/qra65/qra65sim.f90 rename to lib/qra/qra65/q65sim.f90 index 62ec63f57..03696a0ae 100644 --- a/lib/qra/qra65/qra65sim.f90 +++ b/lib/qra/qra65/q65sim.f90 @@ -1,6 +1,6 @@ -program qra65sim +program q65sim -! Generate simulated QRA65 data for testing the decoder. +! Generate simulated Q65 data for testing the decoder. use wavhdr use packjt @@ -21,8 +21,8 @@ program qra65sim nargs=iargc() if(nargs.ne.9) then - print *, 'Usage: qra65sim "msg" A-E freq fDop DT TRp Nfiles Sync SNR' - print *, 'Example: qra65sim "K1ABC W9XYZ EN37" A 1500 0.0 0.0 60 1 T -26' + print *, 'Usage: q65sim "msg" A-E freq fDop DT TRp Nfiles Sync SNR' + print *, 'Example: q65sim "K1ABC W9XYZ EN37" A 1500 0.0 0.0 60 1 T -26' print*,'Sync = T to include sync test.' go to 999 endif @@ -185,7 +185,7 @@ program qra65sim if(ifile.eq.nfiles) cd='d' nfqso=nint(f0) ntol=100 - call sync_qra65(iwave,npts,mode65,nsps,nfqso,ntol,xdt2,f02,snr2) + call sync_q65(iwave,npts,mode65,nsps,nfqso,ntol,xdt2,f02,snr2) terr=1.01/(8.0*baud) ferr=1.01*mode65*baud if(abs(xdt2-xdt).lt.terr .and. abs(f02-f0).lt.ferr) nsync=nsync+1 @@ -199,4 +199,4 @@ program qra65sim if(lsync) write(*,1040) snrdb,nfiles,nsync 1040 format('SNR:',f6.1,' nfiles:',i5,' nsynced:',i5) -999 end program qra65sim +999 end program q65sim diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index 45e44d8cf..c0d501fd7 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -37,7 +37,7 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & nFadingModel=1 maxaptype=4 if(iand(ndepth,64).ne.0) maxaptype=5 - call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax) + call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & maxaptype.ne.maxaptypez) then do naptype=0,maxaptype @@ -96,14 +96,14 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & return end subroutine qra64a -subroutine qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) +subroutine qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist) ! If file qra_params is present in CWD, read decoding params from it. - integer iparam(6) + integer iparam(7) logical first,ex -! data iparam/3,5,3,11,0,9/ !Maximum effort - data iparam/2,5,3,11,3,9/ !Default values +! data iparam/3,5,3,11,0,9,30/ !Maximum effort + data iparam/2,5,3,11,3,9,10/ !Default values data first/.true./ save first,iparam @@ -122,6 +122,7 @@ subroutine qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax) idt0max=iparam(4) ibwmin=iparam(5) ibwmax=iparam(6) - + maxdist=iparam(7) + return end subroutine qra_params diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index a835ed5e9..c2155aeba 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -22,14 +22,11 @@ subroutine qra_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & if(mode64.le.4) ibwmax=9 ibwmin=ibwmax idtmax=3 - call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax) + call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) LL=64*(mode64+2) NN=63 napmin=99 ncall=0 - maxdist=5 - if(ndepth.eq.2) maxdist=10 - if(ndepth.eq.3) maxdist=30 do iavg=0,1 if(iavg.eq.1) then @@ -95,8 +92,9 @@ subroutine qra_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & a=0. a(1)=-f0 call twkfreq(c00,c0,npts2,6000.0,a) -! jpk=4320 - jpk=4080 + jpk=3000 !### These definitions need work ### +! if(nsps.ge.3600) jpk=4080 !### + if(nsps.ge.3600) jpk=6000 !### call spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) call pctile(s3,LL*NN,40,base) s3=s3/base diff --git a/lib/spec64.f90 b/lib/spec64.f90 index 9a62434ee..0bf30e679 100644 --- a/lib/spec64.f90 +++ b/lib/spec64.f90 @@ -41,7 +41,7 @@ subroutine spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) cs(0:nfft-1)=fac*c0(ja:jb) call four2a(cs,nsps,1,-1,1) !c2c FFT to frequency do ii=1,LL - i=ii-65+mode64 !mode64 = 1 2 4 8 16 for QRA65 A B C D E + i=ii-65+mode64 !mode64 = 1 2 4 8 16 for Q65 A B C D E if(i.lt.0) i=i+nsps s3(ii,j)=real(cs(i))**2 + aimag(cs(i))**2 enddo diff --git a/lib/sync_qra65.f90 b/lib/sync_q65.f90 similarity index 95% rename from lib/sync_qra65.f90 rename to lib/sync_q65.f90 index 3b608f20a..8df6da47f 100644 --- a/lib/sync_qra65.f90 +++ b/lib/sync_q65.f90 @@ -1,6 +1,6 @@ -subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) +subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) -! Detect and align with the QRA65 sync vector, returning time and frequency +! Detect and align with the Q65 sync vector, returning time and frequency ! offsets and SNR estimate. ! Input: iwave(0:nmax-1) Raw data @@ -121,4 +121,4 @@ subroutine sync_qra65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) ! enddo return -end subroutine sync_qra65 +end subroutine sync_q65 diff --git a/lib/test_qra65.f90 b/lib/test_q65.f90 similarity index 66% rename from lib/test_qra65.f90 rename to lib/test_q65.f90 index 5b881d6ba..c56fa0bab 100644 --- a/lib/test_qra65.f90 +++ b/lib/test_q65.f90 @@ -1,4 +1,4 @@ -program test_qra65 +program test_q65 character*73 cmd1,cmd2,line character*22 msg @@ -9,8 +9,8 @@ program test_qra65 nargs=iargc() if(nargs.ne.9) then - print*,'Usage: test_qra65 "msg" A-D depth freq DT fDop TRp nfiles SNR' - print*,'Example: test_qra65 "K1ABC W9XYZ EN37" A 3 1500 0.0 5.0 60 100 -20' + print*,'Usage: test_q65 "msg" A-D depth freq DT fDop TRp nfiles SNR' + print*,'Example: test_q65 "K1ABC W9XYZ EN37" A 3 1500 0.0 5.0 60 100 -20' print*,' SNR = 0 to loop over all relevant SNRs' go to 999 endif @@ -61,7 +61,7 @@ program test_qra65 ! 1 2 3 4 5 6 7 ! 1234567890123456789012345678901234567890123456789012345678901234567890123' - cmd1='qra65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 F -10 > junk0' + cmd1='q65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 F -10 > junk0' cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A *.wav > junk' write(cmd1(10:33),'(a)') '"'//msg//'"' @@ -71,25 +71,32 @@ program test_qra65 write(cmd1(46:50),'(f5.2)') dt write(cmd1(51:54),'(i4)') ntrperiod write(cmd1(55:59),'(i5)') nfiles + write(cmd2(11:13),'(i3)') ntrperiod write(cmd2(33:33),'(i1)') ndepth cmd2(38:38)=csubmode call system('rm -f *.wav') - write(*,1000) (j,j=0,11) - write(12,1000) (j,j=0,11) -1000 format(/'SNR d Dop Sync Dec1 DecN Bad',i6,11i4,' tdec'/85('-')) + call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist) + write(*,1000) ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist + write(12,1000) ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist +1000 format(/'Depth:',i2,' AP:',i2,' df:',i3,' dt:',i3,' bw1:',i3,' bw2:',i3, & + ' dist:',i3) + + write(*,1010) (j,j=0,11) + write(12,1010) (j,j=0,11) +1010 format('SNR d Dop Sync DecN Dec1 Bad',i6,11i4,' tdec'/85('-')) dterr=tsym/4.0 nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) - ndecodes0=nfiles + ndec10=nfiles do nsnr=ia,ib,-1 nsync=0 - ndecodes=0 + ndec1=0 nfalse=0 nretcode=0 - navg=0 + ndecn=0 write(cmd1(63:65),'(i3)') nsnr call system(cmd1) call sec0(0,tdec) @@ -107,18 +114,15 @@ program test_qra65 nsync=nsync+1 endif irc=-1 - if(line(23:23).ne.' ') read(line(60:),*) irc,iavg + iavg=0 + i0=23 + if(ntrperiod.le.30) i0=25 + if(line(i0:i0).ne.' ') read(line(60:),*) irc,iavg if(irc.lt.0) cycle if(decok) then - i=irc - if(i.le.11) then - ndecodes=ndecodes + 1 - navg=navg + 1 - else - i=mod(i,10) - navg=navg + 1 - endif - nretcode(i)=nretcode(i) + 1 + ndecn=ndecn + 1 + if(iavg.le.1) ndec1=ndec1 + 1 + nretcode(irc)=nretcode(irc) + 1 else nfalse=nfalse + 1 print*,'False: ',line @@ -127,24 +131,24 @@ program test_qra65 10 close(10) xdt_avg=0. xdt_rms=0. - write(*,1100) nsnr,ndepth,fDop,nsync,ndecodes,navg,nfalse,nretcode, & + write(*,1100) nsnr,ndepth,fDop,nsync,ndecn,ndec1,nfalse,nretcode, & tdec/nfiles - write(12,1100) nsnr,ndepth,fDop,nsync,ndecodes,navg,nfalse,nretcode, & + write(12,1100) nsnr,ndepth,fDop,nsync,ndecn,ndec1,nfalse,nretcode, & tdec/nfiles 1100 format(i3,i2,f5.1,3i5,i4,i6,11i4,f6.2) - if(ndecodes.lt.nfiles/2 .and. ndecodes0.ge.nfiles/2) then - snr_thresh=nsnr + float(nfiles/2 - ndecodes)/(ndecodes0-ndecodes) + if(ndec1.lt.nfiles/2 .and. ndec10.ge.nfiles/2) then + snr_thresh=nsnr + float(nfiles/2 - ndec1)/(ndec10-ndec1) write(13,1200) ndepth,fdop,csubmode,snr_thresh 1200 format(i1,f6.1,2x,a1,f7.1) flush(13) endif flush(6) flush(12) - if(ndecodes.eq.0) exit !Bail out if no decodes at this SNR - ndecodes0=ndecodes + if(ndec1.eq.0 .and. ndecn.eq.0) exit !Bail out if no decodes at this SNR + ndec10=ndec1 enddo ! nsnr -999 end program test_qra65 +999 end program test_q65 - include 'sec0.f90' +include 'sec0.f90' diff --git a/models/FrequencyList.cpp b/models/FrequencyList.cpp index bb2d1738f..2662e24ff 100644 --- a/models/FrequencyList.cpp +++ b/models/FrequencyList.cpp @@ -263,7 +263,7 @@ namespace {50200000, Modes::Echo, IARURegions::ALL}, {50270000, Modes::QRA64, IARURegions::ALL}, - {50270000, Modes::QRA65, IARURegions::ALL}, + {50270000, Modes::Q65, IARURegions::ALL}, {50276000, Modes::JT65, IARURegions::R2}, {50276000, Modes::JT65, IARURegions::R3}, {50380000, Modes::MSK144, IARURegions::R1}, diff --git a/models/Modes.cpp b/models/Modes.cpp index deaa2a0f6..0d3c544d4 100644 --- a/models/Modes.cpp +++ b/models/Modes.cpp @@ -27,7 +27,7 @@ namespace "FT4", "FST4", "FST4W", - "QRA65" + "Q65" }; std::size_t constexpr mode_names_size = sizeof (mode_names) / sizeof (mode_names[0]); } diff --git a/models/Modes.hpp b/models/Modes.hpp index 2f3bf60f7..132ca8657 100644 --- a/models/Modes.hpp +++ b/models/Modes.hpp @@ -52,7 +52,7 @@ public: FT4, FST4, FST4W, - QRA65, + Q65, MODES_END_SENTINAL_AND_COUNT // this must be last }; Q_ENUM (Mode) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index cd0152889..f835e9f31 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -602,7 +602,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, ui->actionISCAT->setActionGroup(modeGroup); ui->actionMSK144->setActionGroup(modeGroup); ui->actionQRA64->setActionGroup(modeGroup); - ui->actionQRA65->setActionGroup(modeGroup); + ui->actionQ65->setActionGroup(modeGroup); ui->actionFreqCal->setActionGroup(modeGroup); QActionGroup* saveGroup = new QActionGroup(this); @@ -1382,7 +1382,7 @@ void MainWindow::fixStop() } else if (m_mode=="QRA64"){ m_hsymStop=179; if(m_config.decode_at_52s()) m_hsymStop=186; - } else if (m_mode=="QRA65"){ + } else if (m_mode=="Q65"){ m_hsymStop=48; if(m_TRperiod==30) m_hsymStop=96; if(m_TRperiod==60) m_hsymStop=196; @@ -2379,7 +2379,7 @@ void MainWindow::setup_status_bar (bool vhf) mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #66ff66}"); } else if ("QRA64" == m_mode) { mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #99ff33}"); - } else if ("QRA65" == m_mode) { + } else if ("Q65" == m_mode) { mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #99ff33}"); } else if ("MSK144" == m_mode) { mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff6666}"); @@ -2581,7 +2581,7 @@ void MainWindow::on_actionCopyright_Notice_triggered() "notice prominently in your derivative work:\n\n" "\"The algorithms, source code, look-and-feel of WSJT-X and related " "programs, and protocol specifications for the modes FSK441, FST4, FT8, " - "JT4, JT6M, JT9, JT65, JTMS, QRA64, QRA65, ISCAT, MSK144 are Copyright (C) " + "JT4, JT6M, JT9, JT65, JTMS, QRA64, Q65, ISCAT, MSK144 are Copyright (C) " "2001-2020 by one or more of the following authors: Joseph Taylor, " "K1JT; Bill Somerville, G4WJS; Steven Franke, K9AN; Nico Palermo, " "IV3NWV; Greg Beam, KI7MT; Michael Black, W9MDB; Edson Pereira, PY2SDR; " @@ -3110,8 +3110,8 @@ void MainWindow::decode() //decode() ui->actionEnable_AP_JT65->isChecked (); if(m_mode=="QRA64") dec_data.params.nmode=164; if(m_mode=="QRA64") dec_data.params.ntxmode=164; - if(m_mode=="QRA65") dec_data.params.nmode=66; - if(m_mode=="QRA65") dec_data.params.ntxmode=66; + if(m_mode=="Q65") dec_data.params.nmode=66; + if(m_mode=="Q65") dec_data.params.ntxmode=66; if(m_mode=="JT9+JT65") dec_data.params.nmode=9+65; // = 74 if(m_mode=="JT4") { dec_data.params.nmode=4; @@ -3434,7 +3434,7 @@ void MainWindow::readFromStdout() //readFromStdout //Right (Rx Frequency) window bool bDisplayRight=bAvgMsg; int audioFreq=decodedtext.frequencyOffset(); - if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="FST4" or m_mode=="QRA65") { + if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="FST4" or m_mode=="Q65") { auto const& parts = decodedtext.string().remove("<").remove(">") .split (' ', SkipEmptyParts); if (parts.size() > 6) { @@ -3517,7 +3517,7 @@ void MainWindow::readFromStdout() //readFromStdout //### I think this is where we are preventing Hounds from spotting Fox ### if(m_mode!="FT8" or (SpecOp::HOUND != m_config.special_op_id())) { - if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="QRA64" or m_mode=="QRA65" + if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="QRA64" or m_mode=="Q65" or m_mode=="JT4" or m_mode=="JT65" or m_mode=="JT9" or m_mode=="FST4") { auto_sequence (decodedtext, 25, 50); } @@ -3727,7 +3727,7 @@ void MainWindow::guiUpdate() if(m_modeTx=="JT9") txDuration=1.0 + 85.0*m_nsps/12000.0; // JT9 if(m_modeTx=="JT65") txDuration=1.0 + 126*4096/11025.0; // JT65 if(m_modeTx=="QRA64") txDuration=1.0 + 84*6912/12000.0; // QRA64 - if(m_modeTx=="QRA65") { // QRA65 + if(m_modeTx=="Q65") { // Q65 if(m_TRperiod==15) txDuration=0.5 + 85*1800/12000.0; if(m_TRperiod==30) txDuration=0.5 + 85*3600/12000.0; if(m_TRperiod==60) txDuration=1.0 + 85*7680/12000.0; @@ -3985,7 +3985,7 @@ void MainWindow::guiUpdate() &m_currentMessageType, 22, 22); if(m_modeTx=="QRA64") genqra64_(message, &ichk, msgsent, const_cast (itone), &m_currentMessageType, 22, 22); - if(m_modeTx=="QRA65") { + if(m_modeTx=="Q65") { int ichk65=65; genqra64_(message, &ichk65, msgsent, const_cast (itone), &m_currentMessageType, 22, 22); @@ -4703,7 +4703,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; //Currently we do auto-sequencing only in FT4, FT8, MSK144, FST4, and QRA65 + return; //Currently we do auto-sequencing only in FT4, FT8, MSK144, FST4, and Q65 } //Skip the rest if no decoded text extracted @@ -4811,7 +4811,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 != "QRA65" && m_mode!="FT8" && + m_mode != "QRA64" && m_mode != "Q65" && m_mode!="FT8" && m_mode!="FT4" && m_mode!="FST4") { return; } @@ -6389,13 +6389,13 @@ void MainWindow::on_actionQRA64_triggered() statusChanged(); } -void MainWindow::on_actionQRA65_triggered() +void MainWindow::on_actionQ65_triggered() { // on_actionFST4_triggered(); - m_mode="QRA65"; - m_modeTx="QRA65"; - ui->actionQRA65->setChecked(true); - switch_mode(Modes::QRA65); + m_mode="Q65"; + m_modeTx="Q65"; + ui->actionQ65->setChecked(true); + switch_mode(Modes::Q65); setup_status_bar(true); m_nsps=6912; //For symspec only m_FFTSize = m_nsps / 2; @@ -6411,7 +6411,7 @@ void MainWindow::on_actionQRA65_triggered() m_wideGraph->setTol(ui->sbFtol->value()); m_wideGraph->setRxFreq(ui->RxFreqSpinBox->value()); m_wideGraph->setTxFreq(ui->TxFreqSpinBox->value()); - switch_mode (Modes::QRA65); + switch_mode (Modes::Q65); // 012345678901234567890123456789012345 displayWidgets(nWidgets("111111010110110100010000001100000000")); statusChanged(); @@ -6464,7 +6464,7 @@ void MainWindow::on_actionMSK144_triggered() if("JT9_JT65"==m_mode) ui->actionJT9_JT65->setChecked(true); if("ISCAT"==m_mode) ui->actionISCAT->setChecked(true); if("QRA64"==m_mode) ui->actionQRA64->setChecked(true); - if("QRA65"==m_mode) ui->actionQRA65->setChecked(true); + if("Q65"==m_mode) ui->actionQ65->setChecked(true); if("WSPR"==m_mode) ui->actionWSPR->setChecked(true); if("Echo"==m_mode) ui->actionEcho->setChecked(true); if("FreqCal"==m_mode) ui->actionFreqCal->setChecked(true); @@ -7312,7 +7312,7 @@ void MainWindow::transmit (double snr) true, false, snr, m_TRperiod); } - if (m_modeTx == "QRA65") { + if (m_modeTx == "Q65") { int nsps=1800; if(m_TRperiod==30) nsps=3600; if(m_TRperiod==60) nsps=7680; @@ -7320,7 +7320,7 @@ void MainWindow::transmit (double snr) if(m_TRperiod==300) nsps=41472; int mode65=pow(2.0,double(m_nSubMode)); toneSpacing=mode65*12000.0/nsps; - Q_EMIT sendMessage (m_mode, NUM_QRA65_SYMBOLS, + Q_EMIT sendMessage (m_mode, NUM_Q65_SYMBOLS, double(nsps), ui->TxFreqSpinBox->value () - m_XIT, toneSpacing, m_soundOutput, m_config.audio_output_channel (), true, false, snr, m_TRperiod); @@ -7581,9 +7581,9 @@ void::MainWindow::VHF_features_enabled(bool b) void MainWindow::on_sbTR_valueChanged(int value) { // if(!m_bFastMode and n>m_nSubMode) m_MinW=m_nSubMode; - if(m_bFastMode or m_mode=="FreqCal" or m_mode=="FST4" or m_mode=="FST4W" or m_mode=="QRA65") { + if(m_bFastMode or m_mode=="FreqCal" or m_mode=="FST4" or m_mode=="FST4W" or m_mode=="Q65") { m_TRperiod = value; - if (m_mode == "FST4" || m_mode == "FST4W" || m_mode=="QRA65") + if (m_mode == "FST4" || m_mode == "FST4W" || m_mode=="Q65") { if (m_TRperiod < 60) { @@ -7627,7 +7627,7 @@ void MainWindow::on_sbTR_FST4W_valueChanged(int value) QChar MainWindow::current_submode () const { QChar submode {0}; - if (m_mode.contains (QRegularExpression {R"(^(JT65|JT9|JT4|ISCAT|QRA64|QRA65)$)"}) + if (m_mode.contains (QRegularExpression {R"(^(JT65|JT9|JT4|ISCAT|QRA64|Q65)$)"}) && (m_config.enable_VHF_features () || "JT4" == m_mode || "ISCAT" == m_mode)) { submode = m_nSubMode + 65; @@ -9256,7 +9256,7 @@ void MainWindow::set_mode (QString const& mode) else if ("JT9+JT65" == mode) on_actionJT9_JT65_triggered (); else if ("JT65" == mode) on_actionJT65_triggered (); else if ("QRA64" == mode) on_actionQRA64_triggered (); - else if ("QRA65" == mode) on_actionQRA65_triggered (); + else if ("Q65" == mode) on_actionQ65_triggered (); else if ("FreqCal" == mode) on_actionFreqCal_triggered (); else if ("ISCAT" == mode) on_actionISCAT_triggered (); else if ("MSK144" == mode) on_actionMSK144_triggered (); diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index 3a111e0c4..4066af831 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -48,7 +48,7 @@ #define NUM_ISCAT_SYMBOLS 1291 //30*11025/256 #define NUM_MSK144_SYMBOLS 144 //s8 + d48 + s8 + d80 #define NUM_QRA64_SYMBOLS 84 //63 data + 21 sync -#define NUM_QRA65_SYMBOLS 85 //63 data + 22 sync +#define NUM_Q65_SYMBOLS 85 //63 data + 22 sync #define NUM_FT8_SYMBOLS 79 #define NUM_FT4_SYMBOLS 105 #define NUM_FST4_SYMBOLS 160 //240/2 data + 5*8 sync @@ -301,7 +301,7 @@ private slots: void on_cbCQTx_toggled(bool b); void on_actionMSK144_triggered(); void on_actionQRA64_triggered(); - void on_actionQRA65_triggered(); + void on_actionQ65_triggered(); void on_actionFreqCal_triggered(); void splash_done (); void on_measure_check_box_stateChanged (int); diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index 0b7adff9b..ff7986403 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -2895,7 +2895,7 @@ list. The list can be maintained in Settings (F2). - + @@ -3383,12 +3383,12 @@ list. The list can be maintained in Settings (F2). FST4W - + true - QRA65 + Q65 diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 44ee92291..888ae8619 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -471,7 +471,7 @@ void CPlotter::DrawOverlay() //DrawOverlay() if(m_nSubMode==4) bw=16*bw; //E } - if(m_mode=="QRA65") { //QRA65 + if(m_mode=="Q65") { //Q65 int h=int(pow(2.0,m_nSubMode)); int nsps=1800; if(m_TRperiod==30) nsps=3600; @@ -512,7 +512,7 @@ void CPlotter::DrawOverlay() //DrawOverlay() int yTxTop=12; int yRxBottom=yTxTop + 2*yh + 4; if(m_mode=="JT9" or m_mode=="JT65" or m_mode=="JT9+JT65" - or m_mode=="QRA64" or m_mode=="QRA65" or m_mode=="FT8" or m_mode=="FT4" + or m_mode=="QRA64" or m_mode=="Q65" or m_mode=="FT8" or m_mode=="FT4" or m_mode.startsWith("FST4")) { if(m_mode=="FST4" and !m_bSingleDecode) { @@ -524,7 +524,7 @@ void CPlotter::DrawOverlay() //DrawOverlay() painter0.drawLine(x2,25,x2-5,20); } - if(m_mode=="QRA64" or m_mode=="QRA65" or (m_mode=="JT65" and m_bVHF)) { + if(m_mode=="QRA64" or m_mode=="Q65" or (m_mode=="JT65" and m_bVHF)) { painter0.setPen(penGreen); x1=XfromFreq(m_rxFreq-m_tol); x2=XfromFreq(m_rxFreq+m_tol); @@ -562,7 +562,7 @@ 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=="QRA65" or m_mode=="FT8" + m_mode.mid(0,4)=="WSPR" or m_mode=="QRA64" or m_mode=="Q65" or m_mode=="FT8" or m_mode=="FT4" or m_mode.startsWith("FST4")) { painter0.setPen(penRed); x1=XfromFreq(m_txFreq); From 7a22e6a2e1a219a5d2ff908f9fd1366c170bbaac Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 25 Oct 2020 14:10:38 -0400 Subject: [PATCH 099/426] Change the Q65 value of nsps from 7680 to 7200. --- lib/q65_decode.f90 | 2 +- lib/qra/qra65/q65sim.f90 | 2 +- lib/qra_loops.f90 | 2 +- lib/test_q65.f90 | 2 +- lib/test_qra64.f90 | 2 +- widgets/mainwindow.cpp | 4 ++-- widgets/plotter.cpp | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 8b522ce2e..d1f874649 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -71,7 +71,7 @@ contains else if(ntrperiod.eq.30) then nsps=3600 else if(ntrperiod.eq.60) then - nsps=7680 + nsps=7200 else if(ntrperiod.eq.120) then nsps=16000 else if(ntrperiod.eq.300) then diff --git a/lib/qra/qra65/q65sim.f90 b/lib/qra/qra65/q65sim.f90 index 03696a0ae..8841b92cd 100644 --- a/lib/qra/qra65/q65sim.f90 +++ b/lib/qra/qra65/q65sim.f90 @@ -54,7 +54,7 @@ program q65sim else if(ntrperiod.eq.30) then nsps=3600 else if(ntrperiod.eq.60) then - nsps=7680 + nsps=7200 else if(ntrperiod.eq.120) then nsps=16000 else if(ntrperiod.eq.300) then diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index c2155aeba..de58263de 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -118,7 +118,7 @@ subroutine qra_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & ndist=ndistx endif -200 if(mode.eq.65 .and. nsps.eq.7680/2) xdt=xdt+0.4 !### Empirical -- WHY ??? ### +200 if(mode.eq.65 .and. nsps.eq.7200/2) xdt=xdt+0.4 !### Empirical -- WHY ??? ### if(irc.ge.0) then navg=nsave diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index c56fa0bab..ead15b013 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -38,7 +38,7 @@ program test_q65 nsps=3600 i50=-24 else if(ntrperiod.eq.60) then - nsps=7680 + nsps=7200 i50=-28 else if(ntrperiod.eq.120) then nsps=16000 diff --git a/lib/test_qra64.f90 b/lib/test_qra64.f90 index 24f54c2d6..632dbdf66 100644 --- a/lib/test_qra64.f90 +++ b/lib/test_qra64.f90 @@ -31,7 +31,7 @@ program test_qra64 call getarg(9,arg) read(arg,*) nsnr - nsps=7680 + nsps=6192 i50=-28 ia=-20 ib=-33 diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index f835e9f31..231e9efa9 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3730,7 +3730,7 @@ void MainWindow::guiUpdate() if(m_modeTx=="Q65") { // Q65 if(m_TRperiod==15) txDuration=0.5 + 85*1800/12000.0; if(m_TRperiod==30) txDuration=0.5 + 85*3600/12000.0; - if(m_TRperiod==60) txDuration=1.0 + 85*7680/12000.0; + if(m_TRperiod==60) txDuration=1.0 + 85*7200/12000.0; if(m_TRperiod==120) txDuration=1.0 + 85*16000/12000.0; if(m_TRperiod==300) txDuration=1.0 + 85*41472/12000.0; } @@ -7315,7 +7315,7 @@ void MainWindow::transmit (double snr) if (m_modeTx == "Q65") { int nsps=1800; if(m_TRperiod==30) nsps=3600; - if(m_TRperiod==60) nsps=7680; + if(m_TRperiod==60) nsps=7200; if(m_TRperiod==120) nsps=16000; if(m_TRperiod==300) nsps=41472; int mode65=pow(2.0,double(m_nSubMode)); diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 888ae8619..b813978b5 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -475,7 +475,7 @@ void CPlotter::DrawOverlay() //DrawOverlay() int h=int(pow(2.0,m_nSubMode)); int nsps=1800; if(m_TRperiod==30) nsps=3600; - if(m_TRperiod==60) nsps=7680; + if(m_TRperiod==60) nsps=7200; if(m_TRperiod==120) nsps=16000; if(m_TRperiod==300) nsps=41472; float baud=12000.0/nsps; From ef2c63af29e06ecc533a03c3bba062c8005276c5 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 25 Oct 2020 15:44:52 -0400 Subject: [PATCH 100/426] Enable some changes for Q65 submodes F, G, H. (They won't decode without additions to the core Q65 decoder.) --- lib/pctile.f90 | 2 +- lib/qra_loops.f90 | 3 +-- lib/spec64.f90 | 4 ++-- widgets/mainwindow.cpp | 3 ++- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/pctile.f90 b/lib/pctile.f90 index 7a039b05a..3bf2ffcf8 100644 --- a/lib/pctile.f90 +++ b/lib/pctile.f90 @@ -1,6 +1,6 @@ subroutine pctile(x,npts,npct,xpct) - parameter (NMAX=128*1024) + parameter (NMAX=256*1024) real*4 x(npts) real*4 tmp(NMAX) diff --git a/lib/qra_loops.f90 b/lib/qra_loops.f90 index de58263de..cebd7036a 100644 --- a/lib/qra_loops.f90 +++ b/lib/qra_loops.f90 @@ -3,7 +3,7 @@ subroutine qra_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & use packjt use timer_module, only: timer - parameter (LN=1152*63) + parameter (LN=2176*63) !LN=LL*NN; LL = 64*(mode64+2) character*37 decoded complex c00(0:npts2-1) !Analytic representation of dd(), 6000 Hz complex ,allocatable :: c0(:) !Ditto, with freq shift @@ -51,7 +51,6 @@ subroutine qra_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & call pctile(s3,LL*NN,40,base) s3=s3/base where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim -! if(iavg.eq.0 .and. idf.eq.1 .and. idt.eq.1) s3a(1:LL*NN)=s3(1:LL*NN) else s3(1:LL*NN)=s3avg(1:LL*NN) endif diff --git a/lib/spec64.f90 b/lib/spec64.f90 index 0bf30e679..fe518d747 100644 --- a/lib/spec64.f90 +++ b/lib/spec64.f90 @@ -74,8 +74,8 @@ subroutine spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) ! do j=1,NN ! ipk1=maxloc(s3(1:LL,j)) ! m=ipk1(1)-65 -! write(72,3072) j,m,m/2,m/4,m/8 -!3072 format(5i8) +! write(72,3072) j,m,m/2,m/4,m/8,m/16,m/32,m/64 +!3072 format(8i7) ! enddo return diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 231e9efa9..dbb950df7 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6403,7 +6403,8 @@ void MainWindow::on_actionQ65_triggered() m_hsymStop=49; ui->sbTR->values ({15, 30, 60, 120, 300}); on_sbTR_valueChanged (ui->sbTR->value()); - ui->sbSubmode->setMaximum(4); +//### ui->sbSubmode->setMaximum(4); + ui->sbSubmode->setMaximum(7); ui->sbSubmode->setValue(m_nSubMode); m_wideGraph->setMode(m_mode); m_wideGraph->setMode(m_mode); m_wideGraph->setModeTx(m_modeTx); From 1f06fd65fc33bbecc618cfd7ae7bc3ecd6b055f6 Mon Sep 17 00:00:00 2001 From: Nico Palermo/IV3NWV Date: Mon, 26 Oct 2020 01:10:53 +0100 Subject: [PATCH 101/426] Initial q65 distrib --- lib/qra/q65/Makefile.Win | 33 + lib/qra/q65/build.sh | 2 + lib/qra/q65/ebnovalues.txt | 17 + lib/qra/q65/fadengauss.c | 302 ++++++ lib/qra/q65/fadenlorentz.c | 304 ++++++ lib/qra/q65/normrnd.c | 82 ++ lib/qra/q65/normrnd.h | 51 + lib/qra/q65/npfwht.c | 216 +++++ lib/qra/q65/npfwht.h | 45 + lib/qra/q65/pdmath.c | 385 ++++++++ lib/qra/q65/pdmath.h | 85 ++ lib/qra/q65/q65.c | 795 +++++++++++++++ lib/qra/q65/q65.h | 103 ++ lib/qra/q65/q65.sln | 20 + lib/qra/q65/q65.vcproj | 255 +++++ lib/qra/q65/q65test.c | 910 ++++++++++++++++++ lib/qra/q65/qra15_65_64_irr_e23.c | 557 +++++++++++ lib/qra/q65/qra15_65_64_irr_e23.h | 41 + lib/qra/q65/qra65.c | 795 +++++++++++++++ lib/qra/q65/qra65.h | 101 ++ lib/qra/q65/qracodes.c | 474 +++++++++ lib/qra/q65/qracodes.h | 80 ++ .../q65/wer-ff-qra15_65_64_irr_e23-ap00.txt | 19 + 23 files changed, 5672 insertions(+) create mode 100644 lib/qra/q65/Makefile.Win create mode 100644 lib/qra/q65/build.sh create mode 100644 lib/qra/q65/ebnovalues.txt create mode 100644 lib/qra/q65/fadengauss.c create mode 100644 lib/qra/q65/fadenlorentz.c create mode 100644 lib/qra/q65/normrnd.c create mode 100644 lib/qra/q65/normrnd.h create mode 100644 lib/qra/q65/npfwht.c create mode 100644 lib/qra/q65/npfwht.h create mode 100644 lib/qra/q65/pdmath.c create mode 100644 lib/qra/q65/pdmath.h create mode 100644 lib/qra/q65/q65.c create mode 100644 lib/qra/q65/q65.h create mode 100644 lib/qra/q65/q65.sln create mode 100644 lib/qra/q65/q65.vcproj create mode 100644 lib/qra/q65/q65test.c create mode 100644 lib/qra/q65/qra15_65_64_irr_e23.c create mode 100644 lib/qra/q65/qra15_65_64_irr_e23.h create mode 100644 lib/qra/q65/qra65.c create mode 100644 lib/qra/q65/qra65.h create mode 100644 lib/qra/q65/qracodes.c create mode 100644 lib/qra/q65/qracodes.h create mode 100644 lib/qra/q65/wer-ff-qra15_65_64_irr_e23-ap00.txt diff --git a/lib/qra/q65/Makefile.Win b/lib/qra/q65/Makefile.Win new file mode 100644 index 000000000..cedf2ef17 --- /dev/null +++ b/lib/qra/q65/Makefile.Win @@ -0,0 +1,33 @@ +CC = gcc +CFLAGS = -O2 -Wall -I. -D_WIN32 + +# Default rules +%.o: %.c + ${CC} ${CFLAGS} -c $< +%.o: %.f + ${FC} ${FFLAGS} -c $< +%.o: %.F + ${FC} ${FFLAGS} -c $< +%.o: %.f90 + ${FC} ${FFLAGS} -c $< +%.o: %.F90 + ${FC} ${FFLAGS} -c $< + +all: libq65.a q65.exe + +OBJS1 = normrnd.o npfwht.o pdmath.o qra15_65_64_irr_e23.o \ + q65.o + +libq65.a: $(OBJS1) + ar cr libq65.a $(OBJS1) + ranlib libq65.a + +OBJS2 = q65test.o + +q65.exe: $(OBJS2) + ${CC} -o q65.exe $(OBJS2) libq65.a -lm + +.PHONY : clean + +clean: + $(RM) *.o libq65.a q65.exe diff --git a/lib/qra/q65/build.sh b/lib/qra/q65/build.sh new file mode 100644 index 000000000..3d7d76f1d --- /dev/null +++ b/lib/qra/q65/build.sh @@ -0,0 +1,2 @@ +gcc -Wall -march=native -pthread -O3 *.c -lpthread -lm -o q65 + diff --git a/lib/qra/q65/ebnovalues.txt b/lib/qra/q65/ebnovalues.txt new file mode 100644 index 000000000..06a3f585e --- /dev/null +++ b/lib/qra/q65/ebnovalues.txt @@ -0,0 +1,17 @@ +# Eb/No Values to be used during the Q65 codec simulation +# Each line of this file indicates the Eb/No value to be simulated (in dB) +# and the number of errors that should be detected by the decoder +# +# Be careful that the simulation takes a long time to complete +# if the number of errors is large for the specified Eb/No +# (this is particularly true if AP decoding is used) +# +-30 100 +0.5 1000 +1.0 1000 +1.5 1000 +2.0 1000 +2.5 1000 +3.0 1000 +3.5 1000 +4.0 1000 \ No newline at end of file diff --git a/lib/qra/q65/fadengauss.c b/lib/qra/q65/fadengauss.c new file mode 100644 index 000000000..f6ca27253 --- /dev/null +++ b/lib/qra/q65/fadengauss.c @@ -0,0 +1,302 @@ +// Gaussian energy fading tables for QRA64 +static const int glen_tab_gauss[64] = { + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 6, + 6, 6, 7, 7, 8, 8, 9, 10, + 10, 11, 12, 13, 14, 15, 17, 18, + 19, 21, 23, 25, 27, 29, 32, 34, + 37, 41, 44, 48, 52, 57, 62, 65 +}; +static const float ggauss1[2] = { +0.0296f, 0.9101f +}; +static const float ggauss2[2] = { +0.0350f, 0.8954f +}; +static const float ggauss3[2] = { +0.0411f, 0.8787f +}; +static const float ggauss4[2] = { +0.0483f, 0.8598f +}; +static const float ggauss5[2] = { +0.0566f, 0.8387f +}; +static const float ggauss6[2] = { +0.0660f, 0.8154f +}; +static const float ggauss7[2] = { +0.0767f, 0.7898f +}; +static const float ggauss8[2] = { +0.0886f, 0.7621f +}; +static const float ggauss9[2] = { +0.1017f, 0.7325f +}; +static const float ggauss10[2] = { +0.1159f, 0.7012f +}; +static const float ggauss11[2] = { +0.1310f, 0.6687f +}; +static const float ggauss12[2] = { +0.1465f, 0.6352f +}; +static const float ggauss13[2] = { +0.1621f, 0.6013f +}; +static const float ggauss14[2] = { +0.1771f, 0.5674f +}; +static const float ggauss15[2] = { +0.1911f, 0.5339f +}; +static const float ggauss16[2] = { +0.2034f, 0.5010f +}; +static const float ggauss17[3] = { +0.0299f, 0.2135f, 0.4690f +}; +static const float ggauss18[3] = { +0.0369f, 0.2212f, 0.4383f +}; +static const float ggauss19[3] = { +0.0454f, 0.2263f, 0.4088f +}; +static const float ggauss20[3] = { +0.0552f, 0.2286f, 0.3806f +}; +static const float ggauss21[3] = { +0.0658f, 0.2284f, 0.3539f +}; +static const float ggauss22[3] = { +0.0766f, 0.2258f, 0.3287f +}; +static const float ggauss23[3] = { +0.0869f, 0.2212f, 0.3049f +}; +static const float ggauss24[3] = { +0.0962f, 0.2148f, 0.2826f +}; +static const float ggauss25[4] = { +0.0351f, 0.1041f, 0.2071f, 0.2616f +}; +static const float ggauss26[4] = { +0.0429f, 0.1102f, 0.1984f, 0.2420f +}; +static const float ggauss27[4] = { +0.0508f, 0.1145f, 0.1890f, 0.2237f +}; +static const float ggauss28[4] = { +0.0582f, 0.1169f, 0.1791f, 0.2067f +}; +static const float ggauss29[5] = { +0.0289f, 0.0648f, 0.1176f, 0.1689f, 0.1908f +}; +static const float ggauss30[5] = { +0.0351f, 0.0703f, 0.1168f, 0.1588f, 0.1760f +}; +static const float ggauss31[5] = { +0.0411f, 0.0745f, 0.1146f, 0.1488f, 0.1623f +}; +static const float ggauss32[6] = { +0.0246f, 0.0466f, 0.0773f, 0.1115f, 0.1390f, 0.1497f +}; +static const float ggauss33[6] = { +0.0297f, 0.0512f, 0.0788f, 0.1075f, 0.1295f, 0.1379f +}; +static const float ggauss34[6] = { +0.0345f, 0.0549f, 0.0791f, 0.1029f, 0.1205f, 0.1270f +}; +static const float ggauss35[7] = { +0.0240f, 0.0387f, 0.0575f, 0.0784f, 0.0979f, 0.1118f, 0.1169f +}; +static const float ggauss36[7] = { +0.0281f, 0.0422f, 0.0590f, 0.0767f, 0.0926f, 0.1037f, 0.1076f +}; +static const float ggauss37[8] = { +0.0212f, 0.0318f, 0.0449f, 0.0596f, 0.0744f, 0.0872f, 0.0960f, 0.0991f +}; +static const float ggauss38[8] = { +0.0247f, 0.0348f, 0.0467f, 0.0593f, 0.0716f, 0.0819f, 0.0887f, 0.0911f +}; +static const float ggauss39[9] = { +0.0199f, 0.0278f, 0.0372f, 0.0476f, 0.0584f, 0.0684f, 0.0766f, 0.0819f, +0.0838f +}; +static const float ggauss40[10] = { +0.0166f, 0.0228f, 0.0303f, 0.0388f, 0.0478f, 0.0568f, 0.0649f, 0.0714f, +0.0756f, 0.0771f +}; +static const float ggauss41[10] = { +0.0193f, 0.0254f, 0.0322f, 0.0397f, 0.0474f, 0.0548f, 0.0613f, 0.0664f, +0.0697f, 0.0709f +}; +static const float ggauss42[11] = { +0.0168f, 0.0217f, 0.0273f, 0.0335f, 0.0399f, 0.0464f, 0.0524f, 0.0576f, +0.0617f, 0.0643f, 0.0651f +}; +static const float ggauss43[12] = { +0.0151f, 0.0191f, 0.0237f, 0.0288f, 0.0342f, 0.0396f, 0.0449f, 0.0498f, +0.0540f, 0.0572f, 0.0592f, 0.0599f +}; +static const float ggauss44[13] = { +0.0138f, 0.0171f, 0.0210f, 0.0252f, 0.0297f, 0.0343f, 0.0388f, 0.0432f, +0.0471f, 0.0504f, 0.0529f, 0.0545f, 0.0550f +}; +static const float ggauss45[14] = { +0.0128f, 0.0157f, 0.0189f, 0.0224f, 0.0261f, 0.0300f, 0.0339f, 0.0377f, +0.0412f, 0.0444f, 0.0470f, 0.0489f, 0.0501f, 0.0505f +}; +static const float ggauss46[15] = { +0.0121f, 0.0146f, 0.0173f, 0.0202f, 0.0234f, 0.0266f, 0.0299f, 0.0332f, +0.0363f, 0.0391f, 0.0416f, 0.0437f, 0.0452f, 0.0461f, 0.0464f +}; +static const float ggauss47[17] = { +0.0097f, 0.0116f, 0.0138f, 0.0161f, 0.0186f, 0.0212f, 0.0239f, 0.0267f, +0.0294f, 0.0321f, 0.0346f, 0.0369f, 0.0389f, 0.0405f, 0.0417f, 0.0424f, +0.0427f +}; +static const float ggauss48[18] = { +0.0096f, 0.0113f, 0.0131f, 0.0151f, 0.0172f, 0.0194f, 0.0217f, 0.0241f, +0.0264f, 0.0287f, 0.0308f, 0.0329f, 0.0347f, 0.0362f, 0.0375f, 0.0384f, +0.0390f, 0.0392f +}; +static const float ggauss49[19] = { +0.0095f, 0.0110f, 0.0126f, 0.0143f, 0.0161f, 0.0180f, 0.0199f, 0.0219f, +0.0239f, 0.0258f, 0.0277f, 0.0294f, 0.0310f, 0.0325f, 0.0337f, 0.0347f, +0.0354f, 0.0358f, 0.0360f +}; +static const float ggauss50[21] = { +0.0083f, 0.0095f, 0.0108f, 0.0122f, 0.0136f, 0.0152f, 0.0168f, 0.0184f, +0.0201f, 0.0217f, 0.0234f, 0.0250f, 0.0265f, 0.0279f, 0.0292f, 0.0303f, +0.0313f, 0.0320f, 0.0326f, 0.0329f, 0.0330f +}; +static const float ggauss51[23] = { +0.0074f, 0.0084f, 0.0095f, 0.0106f, 0.0118f, 0.0131f, 0.0144f, 0.0157f, +0.0171f, 0.0185f, 0.0199f, 0.0213f, 0.0227f, 0.0240f, 0.0252f, 0.0263f, +0.0273f, 0.0282f, 0.0290f, 0.0296f, 0.0300f, 0.0303f, 0.0303f +}; +static const float ggauss52[25] = { +0.0068f, 0.0076f, 0.0085f, 0.0094f, 0.0104f, 0.0115f, 0.0126f, 0.0137f, +0.0149f, 0.0160f, 0.0172f, 0.0184f, 0.0196f, 0.0207f, 0.0218f, 0.0228f, +0.0238f, 0.0247f, 0.0255f, 0.0262f, 0.0268f, 0.0273f, 0.0276f, 0.0278f, +0.0279f +}; +static const float ggauss53[27] = { +0.0063f, 0.0070f, 0.0078f, 0.0086f, 0.0094f, 0.0103f, 0.0112f, 0.0121f, +0.0131f, 0.0141f, 0.0151f, 0.0161f, 0.0170f, 0.0180f, 0.0190f, 0.0199f, +0.0208f, 0.0216f, 0.0224f, 0.0231f, 0.0237f, 0.0243f, 0.0247f, 0.0251f, +0.0254f, 0.0255f, 0.0256f +}; +static const float ggauss54[29] = { +0.0060f, 0.0066f, 0.0072f, 0.0079f, 0.0086f, 0.0093f, 0.0101f, 0.0109f, +0.0117f, 0.0125f, 0.0133f, 0.0142f, 0.0150f, 0.0159f, 0.0167f, 0.0175f, +0.0183f, 0.0190f, 0.0197f, 0.0204f, 0.0210f, 0.0216f, 0.0221f, 0.0225f, +0.0228f, 0.0231f, 0.0233f, 0.0234f, 0.0235f +}; +static const float ggauss55[32] = { +0.0053f, 0.0058f, 0.0063f, 0.0068f, 0.0074f, 0.0080f, 0.0086f, 0.0093f, +0.0099f, 0.0106f, 0.0113f, 0.0120f, 0.0127f, 0.0134f, 0.0141f, 0.0148f, +0.0155f, 0.0162f, 0.0168f, 0.0174f, 0.0180f, 0.0186f, 0.0191f, 0.0196f, +0.0201f, 0.0204f, 0.0208f, 0.0211f, 0.0213f, 0.0214f, 0.0215f, 0.0216f +}; +static const float ggauss56[34] = { +0.0052f, 0.0056f, 0.0060f, 0.0065f, 0.0070f, 0.0075f, 0.0080f, 0.0086f, +0.0091f, 0.0097f, 0.0103f, 0.0109f, 0.0115f, 0.0121f, 0.0127f, 0.0133f, +0.0138f, 0.0144f, 0.0150f, 0.0155f, 0.0161f, 0.0166f, 0.0170f, 0.0175f, +0.0179f, 0.0183f, 0.0186f, 0.0189f, 0.0192f, 0.0194f, 0.0196f, 0.0197f, +0.0198f, 0.0198f +}; +static const float ggauss57[37] = { +0.0047f, 0.0051f, 0.0055f, 0.0058f, 0.0063f, 0.0067f, 0.0071f, 0.0076f, +0.0080f, 0.0085f, 0.0090f, 0.0095f, 0.0100f, 0.0105f, 0.0110f, 0.0115f, +0.0120f, 0.0125f, 0.0130f, 0.0134f, 0.0139f, 0.0144f, 0.0148f, 0.0152f, +0.0156f, 0.0160f, 0.0164f, 0.0167f, 0.0170f, 0.0173f, 0.0175f, 0.0177f, +0.0179f, 0.0180f, 0.0181f, 0.0181f, 0.0182f +}; +static const float ggauss58[41] = { +0.0041f, 0.0044f, 0.0047f, 0.0050f, 0.0054f, 0.0057f, 0.0060f, 0.0064f, +0.0068f, 0.0072f, 0.0076f, 0.0080f, 0.0084f, 0.0088f, 0.0092f, 0.0096f, +0.0101f, 0.0105f, 0.0109f, 0.0113f, 0.0117f, 0.0121f, 0.0125f, 0.0129f, +0.0133f, 0.0137f, 0.0140f, 0.0144f, 0.0147f, 0.0150f, 0.0153f, 0.0155f, +0.0158f, 0.0160f, 0.0162f, 0.0163f, 0.0164f, 0.0165f, 0.0166f, 0.0167f, +0.0167f +}; +static const float ggauss59[44] = { +0.0039f, 0.0042f, 0.0044f, 0.0047f, 0.0050f, 0.0053f, 0.0056f, 0.0059f, +0.0062f, 0.0065f, 0.0068f, 0.0072f, 0.0075f, 0.0079f, 0.0082f, 0.0086f, +0.0089f, 0.0093f, 0.0096f, 0.0100f, 0.0104f, 0.0107f, 0.0110f, 0.0114f, +0.0117f, 0.0120f, 0.0124f, 0.0127f, 0.0130f, 0.0132f, 0.0135f, 0.0138f, +0.0140f, 0.0142f, 0.0144f, 0.0146f, 0.0148f, 0.0149f, 0.0150f, 0.0151f, +0.0152f, 0.0153f, 0.0153f, 0.0153f +}; +static const float ggauss60[48] = { +0.0036f, 0.0038f, 0.0040f, 0.0042f, 0.0044f, 0.0047f, 0.0049f, 0.0052f, +0.0055f, 0.0057f, 0.0060f, 0.0063f, 0.0066f, 0.0068f, 0.0071f, 0.0074f, +0.0077f, 0.0080f, 0.0083f, 0.0086f, 0.0089f, 0.0092f, 0.0095f, 0.0098f, +0.0101f, 0.0104f, 0.0107f, 0.0109f, 0.0112f, 0.0115f, 0.0117f, 0.0120f, +0.0122f, 0.0124f, 0.0126f, 0.0128f, 0.0130f, 0.0132f, 0.0134f, 0.0135f, +0.0136f, 0.0137f, 0.0138f, 0.0139f, 0.0140f, 0.0140f, 0.0140f, 0.0140f +}; +static const float ggauss61[52] = { +0.0033f, 0.0035f, 0.0037f, 0.0039f, 0.0041f, 0.0043f, 0.0045f, 0.0047f, +0.0049f, 0.0051f, 0.0053f, 0.0056f, 0.0058f, 0.0060f, 0.0063f, 0.0065f, +0.0068f, 0.0070f, 0.0073f, 0.0075f, 0.0078f, 0.0080f, 0.0083f, 0.0085f, +0.0088f, 0.0090f, 0.0093f, 0.0095f, 0.0098f, 0.0100f, 0.0102f, 0.0105f, +0.0107f, 0.0109f, 0.0111f, 0.0113f, 0.0115f, 0.0116f, 0.0118f, 0.0120f, +0.0121f, 0.0122f, 0.0124f, 0.0125f, 0.0126f, 0.0126f, 0.0127f, 0.0128f, +0.0128f, 0.0129f, 0.0129f, 0.0129f +}; +static const float ggauss62[57] = { +0.0030f, 0.0031f, 0.0033f, 0.0034f, 0.0036f, 0.0038f, 0.0039f, 0.0041f, +0.0043f, 0.0045f, 0.0047f, 0.0048f, 0.0050f, 0.0052f, 0.0054f, 0.0056f, +0.0058f, 0.0060f, 0.0063f, 0.0065f, 0.0067f, 0.0069f, 0.0071f, 0.0073f, +0.0075f, 0.0077f, 0.0080f, 0.0082f, 0.0084f, 0.0086f, 0.0088f, 0.0090f, +0.0092f, 0.0094f, 0.0096f, 0.0097f, 0.0099f, 0.0101f, 0.0103f, 0.0104f, +0.0106f, 0.0107f, 0.0108f, 0.0110f, 0.0111f, 0.0112f, 0.0113f, 0.0114f, +0.0115f, 0.0116f, 0.0116f, 0.0117f, 0.0117f, 0.0118f, 0.0118f, 0.0118f, +0.0118f +}; +static const float ggauss63[62] = { +0.0027f, 0.0029f, 0.0030f, 0.0031f, 0.0032f, 0.0034f, 0.0035f, 0.0037f, +0.0038f, 0.0040f, 0.0041f, 0.0043f, 0.0045f, 0.0046f, 0.0048f, 0.0049f, +0.0051f, 0.0053f, 0.0055f, 0.0056f, 0.0058f, 0.0060f, 0.0062f, 0.0063f, +0.0065f, 0.0067f, 0.0069f, 0.0071f, 0.0072f, 0.0074f, 0.0076f, 0.0078f, +0.0079f, 0.0081f, 0.0083f, 0.0084f, 0.0086f, 0.0088f, 0.0089f, 0.0091f, +0.0092f, 0.0094f, 0.0095f, 0.0096f, 0.0098f, 0.0099f, 0.0100f, 0.0101f, +0.0102f, 0.0103f, 0.0104f, 0.0105f, 0.0105f, 0.0106f, 0.0107f, 0.0107f, +0.0108f, 0.0108f, 0.0108f, 0.0108f, 0.0109f, 0.0109f +}; +static const float ggauss64[65] = { +0.0028f, 0.0029f, 0.0030f, 0.0031f, 0.0032f, 0.0034f, 0.0035f, 0.0036f, +0.0037f, 0.0039f, 0.0040f, 0.0041f, 0.0043f, 0.0044f, 0.0046f, 0.0047f, +0.0048f, 0.0050f, 0.0051f, 0.0053f, 0.0054f, 0.0056f, 0.0057f, 0.0059f, +0.0060f, 0.0062f, 0.0063f, 0.0065f, 0.0066f, 0.0068f, 0.0069f, 0.0071f, +0.0072f, 0.0074f, 0.0075f, 0.0077f, 0.0078f, 0.0079f, 0.0081f, 0.0082f, +0.0083f, 0.0084f, 0.0086f, 0.0087f, 0.0088f, 0.0089f, 0.0090f, 0.0091f, +0.0092f, 0.0093f, 0.0094f, 0.0094f, 0.0095f, 0.0096f, 0.0097f, 0.0097f, +0.0098f, 0.0098f, 0.0099f, 0.0099f, 0.0099f, 0.0099f, 0.0100f, 0.0100f, +0.0100f +}; +static const float *gptr_tab_gauss[64] = { +ggauss1, ggauss2, ggauss3, ggauss4, +ggauss5, ggauss6, ggauss7, ggauss8, +ggauss9, ggauss10, ggauss11, ggauss12, +ggauss13, ggauss14, ggauss15, ggauss16, +ggauss17, ggauss18, ggauss19, ggauss20, +ggauss21, ggauss22, ggauss23, ggauss24, +ggauss25, ggauss26, ggauss27, ggauss28, +ggauss29, ggauss30, ggauss31, ggauss32, +ggauss33, ggauss34, ggauss35, ggauss36, +ggauss37, ggauss38, ggauss39, ggauss40, +ggauss41, ggauss42, ggauss43, ggauss44, +ggauss45, ggauss46, ggauss47, ggauss48, +ggauss49, ggauss50, ggauss51, ggauss52, +ggauss53, ggauss54, ggauss55, ggauss56, +ggauss57, ggauss58, ggauss59, ggauss60, +ggauss61, ggauss62, ggauss63, ggauss64 +}; diff --git a/lib/qra/q65/fadenlorentz.c b/lib/qra/q65/fadenlorentz.c new file mode 100644 index 000000000..22329f65e --- /dev/null +++ b/lib/qra/q65/fadenlorentz.c @@ -0,0 +1,304 @@ +// Lorentz energy fading tables for QRA64 +static const int glen_tab_lorentz[64] = { + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 6, 6, + 7, 7, 7, 8, 8, 9, 10, 10, + 11, 12, 13, 14, 15, 16, 17, 19, + 20, 22, 23, 25, 27, 30, 32, 35, + 38, 41, 45, 49, 53, 57, 62, 65 +}; +static const float glorentz1[2] = { +0.0214f, 0.9107f +}; +static const float glorentz2[2] = { +0.0244f, 0.9030f +}; +static const float glorentz3[2] = { +0.0280f, 0.8950f +}; +static const float glorentz4[2] = { +0.0314f, 0.8865f +}; +static const float glorentz5[2] = { +0.0349f, 0.8773f +}; +static const float glorentz6[2] = { +0.0388f, 0.8675f +}; +static const float glorentz7[2] = { +0.0426f, 0.8571f +}; +static const float glorentz8[2] = { +0.0463f, 0.8459f +}; +static const float glorentz9[2] = { +0.0500f, 0.8339f +}; +static const float glorentz10[2] = { +0.0538f, 0.8210f +}; +static const float glorentz11[2] = { +0.0579f, 0.8074f +}; +static const float glorentz12[2] = { +0.0622f, 0.7930f +}; +static const float glorentz13[2] = { +0.0668f, 0.7777f +}; +static const float glorentz14[2] = { +0.0715f, 0.7616f +}; +static const float glorentz15[3] = { +0.0196f, 0.0765f, 0.7445f +}; +static const float glorentz16[3] = { +0.0210f, 0.0816f, 0.7267f +}; +static const float glorentz17[3] = { +0.0226f, 0.0870f, 0.7080f +}; +static const float glorentz18[3] = { +0.0242f, 0.0925f, 0.6885f +}; +static const float glorentz19[3] = { +0.0259f, 0.0981f, 0.6682f +}; +static const float glorentz20[3] = { +0.0277f, 0.1039f, 0.6472f +}; +static const float glorentz21[3] = { +0.0296f, 0.1097f, 0.6255f +}; +static const float glorentz22[4] = { +0.0143f, 0.0316f, 0.1155f, 0.6031f +}; +static const float glorentz23[4] = { +0.0153f, 0.0337f, 0.1213f, 0.5803f +}; +static const float glorentz24[4] = { +0.0163f, 0.0358f, 0.1270f, 0.5570f +}; +static const float glorentz25[4] = { +0.0174f, 0.0381f, 0.1325f, 0.5333f +}; +static const float glorentz26[4] = { +0.0186f, 0.0405f, 0.1378f, 0.5095f +}; +static const float glorentz27[5] = { +0.0113f, 0.0198f, 0.0429f, 0.1428f, 0.4855f +}; +static const float glorentz28[5] = { +0.0120f, 0.0211f, 0.0455f, 0.1473f, 0.4615f +}; +static const float glorentz29[5] = { +0.0129f, 0.0225f, 0.0481f, 0.1514f, 0.4376f +}; +static const float glorentz30[5] = { +0.0137f, 0.0239f, 0.0508f, 0.1549f, 0.4140f +}; +static const float glorentz31[6] = { +0.0095f, 0.0147f, 0.0254f, 0.0536f, 0.1578f, 0.3907f +}; +static const float glorentz32[6] = { +0.0101f, 0.0156f, 0.0270f, 0.0564f, 0.1600f, 0.3680f +}; +static const float glorentz33[7] = { +0.0076f, 0.0109f, 0.0167f, 0.0287f, 0.0592f, 0.1614f, 0.3458f +}; +static const float glorentz34[7] = { +0.0081f, 0.0116f, 0.0178f, 0.0305f, 0.0621f, 0.1620f, 0.3243f +}; +static const float glorentz35[7] = { +0.0087f, 0.0124f, 0.0190f, 0.0324f, 0.0649f, 0.1618f, 0.3035f +}; +static const float glorentz36[8] = { +0.0069f, 0.0093f, 0.0133f, 0.0203f, 0.0343f, 0.0676f, 0.1607f, 0.2836f +}; +static const float glorentz37[8] = { +0.0074f, 0.0100f, 0.0142f, 0.0216f, 0.0362f, 0.0702f, 0.1588f, 0.2645f +}; +static const float glorentz38[9] = { +0.0061f, 0.0080f, 0.0107f, 0.0152f, 0.0230f, 0.0382f, 0.0726f, 0.1561f, +0.2464f +}; +static const float glorentz39[10] = { +0.0052f, 0.0066f, 0.0086f, 0.0115f, 0.0162f, 0.0244f, 0.0402f, 0.0747f, +0.1526f, 0.2291f +}; +static const float glorentz40[10] = { +0.0056f, 0.0071f, 0.0092f, 0.0123f, 0.0173f, 0.0259f, 0.0422f, 0.0766f, +0.1484f, 0.2128f +}; +static const float glorentz41[11] = { +0.0049f, 0.0061f, 0.0076f, 0.0098f, 0.0132f, 0.0184f, 0.0274f, 0.0441f, +0.0780f, 0.1437f, 0.1975f +}; +static const float glorentz42[12] = { +0.0044f, 0.0053f, 0.0065f, 0.0082f, 0.0106f, 0.0141f, 0.0196f, 0.0290f, +0.0460f, 0.0791f, 0.1384f, 0.1831f +}; +static const float glorentz43[13] = { +0.0040f, 0.0048f, 0.0057f, 0.0070f, 0.0088f, 0.0113f, 0.0150f, 0.0209f, +0.0305f, 0.0477f, 0.0797f, 0.1327f, 0.1695f +}; +static const float glorentz44[14] = { +0.0037f, 0.0043f, 0.0051f, 0.0062f, 0.0075f, 0.0094f, 0.0121f, 0.0160f, +0.0221f, 0.0321f, 0.0493f, 0.0799f, 0.1267f, 0.1568f +}; +static const float glorentz45[15] = { +0.0035f, 0.0040f, 0.0047f, 0.0055f, 0.0066f, 0.0081f, 0.0101f, 0.0129f, +0.0171f, 0.0234f, 0.0335f, 0.0506f, 0.0795f, 0.1204f, 0.1450f +}; +static const float glorentz46[16] = { +0.0033f, 0.0037f, 0.0043f, 0.0050f, 0.0059f, 0.0071f, 0.0087f, 0.0108f, +0.0138f, 0.0181f, 0.0246f, 0.0349f, 0.0517f, 0.0786f, 0.1141f, 0.1340f +}; +static const float glorentz47[17] = { +0.0031f, 0.0035f, 0.0040f, 0.0046f, 0.0054f, 0.0064f, 0.0077f, 0.0093f, +0.0116f, 0.0147f, 0.0192f, 0.0259f, 0.0362f, 0.0525f, 0.0773f, 0.1076f, +0.1237f +}; +static const float glorentz48[19] = { +0.0027f, 0.0030f, 0.0034f, 0.0038f, 0.0043f, 0.0050f, 0.0058f, 0.0069f, +0.0082f, 0.0100f, 0.0123f, 0.0156f, 0.0203f, 0.0271f, 0.0374f, 0.0530f, +0.0755f, 0.1013f, 0.1141f +}; +static const float glorentz49[20] = { +0.0026f, 0.0029f, 0.0032f, 0.0036f, 0.0041f, 0.0047f, 0.0054f, 0.0063f, +0.0074f, 0.0088f, 0.0107f, 0.0131f, 0.0165f, 0.0213f, 0.0282f, 0.0383f, +0.0531f, 0.0734f, 0.0950f, 0.1053f +}; +static const float glorentz50[22] = { +0.0023f, 0.0025f, 0.0028f, 0.0031f, 0.0035f, 0.0039f, 0.0044f, 0.0050f, +0.0058f, 0.0067f, 0.0079f, 0.0094f, 0.0114f, 0.0139f, 0.0175f, 0.0223f, +0.0292f, 0.0391f, 0.0529f, 0.0709f, 0.0889f, 0.0971f +}; +static const float glorentz51[23] = { +0.0023f, 0.0025f, 0.0027f, 0.0030f, 0.0034f, 0.0037f, 0.0042f, 0.0048f, +0.0054f, 0.0062f, 0.0072f, 0.0085f, 0.0100f, 0.0121f, 0.0148f, 0.0184f, +0.0233f, 0.0301f, 0.0396f, 0.0524f, 0.0681f, 0.0829f, 0.0894f +}; +static const float glorentz52[25] = { +0.0021f, 0.0023f, 0.0025f, 0.0027f, 0.0030f, 0.0033f, 0.0036f, 0.0040f, +0.0045f, 0.0051f, 0.0058f, 0.0067f, 0.0077f, 0.0090f, 0.0107f, 0.0128f, +0.0156f, 0.0192f, 0.0242f, 0.0308f, 0.0398f, 0.0515f, 0.0650f, 0.0772f, +0.0824f +}; +static const float glorentz53[27] = { +0.0019f, 0.0021f, 0.0022f, 0.0024f, 0.0027f, 0.0029f, 0.0032f, 0.0035f, +0.0039f, 0.0044f, 0.0049f, 0.0055f, 0.0062f, 0.0072f, 0.0083f, 0.0096f, +0.0113f, 0.0135f, 0.0164f, 0.0201f, 0.0249f, 0.0314f, 0.0398f, 0.0502f, +0.0619f, 0.0718f, 0.0759f +}; +static const float glorentz54[30] = { +0.0017f, 0.0018f, 0.0019f, 0.0021f, 0.0022f, 0.0024f, 0.0026f, 0.0029f, +0.0031f, 0.0034f, 0.0038f, 0.0042f, 0.0047f, 0.0052f, 0.0059f, 0.0067f, +0.0076f, 0.0088f, 0.0102f, 0.0120f, 0.0143f, 0.0171f, 0.0208f, 0.0256f, +0.0317f, 0.0395f, 0.0488f, 0.0586f, 0.0666f, 0.0698f +}; +static const float glorentz55[32] = { +0.0016f, 0.0017f, 0.0018f, 0.0019f, 0.0021f, 0.0022f, 0.0024f, 0.0026f, +0.0028f, 0.0031f, 0.0034f, 0.0037f, 0.0041f, 0.0045f, 0.0050f, 0.0056f, +0.0063f, 0.0071f, 0.0081f, 0.0094f, 0.0108f, 0.0127f, 0.0149f, 0.0178f, +0.0214f, 0.0261f, 0.0318f, 0.0389f, 0.0470f, 0.0553f, 0.0618f, 0.0643f +}; +static const float glorentz56[35] = { +0.0014f, 0.0015f, 0.0016f, 0.0017f, 0.0018f, 0.0020f, 0.0021f, 0.0023f, +0.0024f, 0.0026f, 0.0028f, 0.0031f, 0.0033f, 0.0036f, 0.0040f, 0.0044f, +0.0049f, 0.0054f, 0.0060f, 0.0067f, 0.0076f, 0.0087f, 0.0099f, 0.0114f, +0.0133f, 0.0156f, 0.0184f, 0.0220f, 0.0264f, 0.0318f, 0.0381f, 0.0451f, +0.0520f, 0.0572f, 0.0591f +}; +static const float glorentz57[38] = { +0.0013f, 0.0014f, 0.0015f, 0.0016f, 0.0017f, 0.0018f, 0.0019f, 0.0020f, +0.0021f, 0.0023f, 0.0024f, 0.0026f, 0.0028f, 0.0031f, 0.0033f, 0.0036f, +0.0039f, 0.0043f, 0.0047f, 0.0052f, 0.0058f, 0.0064f, 0.0072f, 0.0081f, +0.0092f, 0.0104f, 0.0120f, 0.0139f, 0.0162f, 0.0190f, 0.0224f, 0.0265f, +0.0315f, 0.0371f, 0.0431f, 0.0487f, 0.0529f, 0.0544f +}; +static const float glorentz58[41] = { +0.0012f, 0.0013f, 0.0014f, 0.0014f, 0.0015f, 0.0016f, 0.0017f, 0.0018f, +0.0019f, 0.0020f, 0.0022f, 0.0023f, 0.0025f, 0.0026f, 0.0028f, 0.0030f, +0.0033f, 0.0036f, 0.0039f, 0.0042f, 0.0046f, 0.0050f, 0.0056f, 0.0061f, +0.0068f, 0.0076f, 0.0086f, 0.0097f, 0.0110f, 0.0125f, 0.0144f, 0.0167f, +0.0194f, 0.0226f, 0.0265f, 0.0309f, 0.0359f, 0.0409f, 0.0455f, 0.0488f, +0.0500f +}; +static const float glorentz59[45] = { +0.0011f, 0.0012f, 0.0012f, 0.0013f, 0.0013f, 0.0014f, 0.0015f, 0.0016f, +0.0016f, 0.0017f, 0.0018f, 0.0019f, 0.0021f, 0.0022f, 0.0023f, 0.0025f, +0.0026f, 0.0028f, 0.0030f, 0.0033f, 0.0035f, 0.0038f, 0.0041f, 0.0045f, +0.0049f, 0.0054f, 0.0059f, 0.0065f, 0.0072f, 0.0081f, 0.0090f, 0.0102f, +0.0115f, 0.0130f, 0.0149f, 0.0171f, 0.0197f, 0.0227f, 0.0263f, 0.0302f, +0.0345f, 0.0387f, 0.0425f, 0.0451f, 0.0460f +}; +static const float glorentz60[49] = { +0.0010f, 0.0011f, 0.0011f, 0.0012f, 0.0012f, 0.0013f, 0.0013f, 0.0014f, +0.0014f, 0.0015f, 0.0016f, 0.0017f, 0.0018f, 0.0019f, 0.0020f, 0.0021f, +0.0022f, 0.0024f, 0.0025f, 0.0027f, 0.0028f, 0.0030f, 0.0033f, 0.0035f, +0.0038f, 0.0041f, 0.0044f, 0.0048f, 0.0052f, 0.0057f, 0.0063f, 0.0069f, +0.0077f, 0.0085f, 0.0095f, 0.0106f, 0.0119f, 0.0135f, 0.0153f, 0.0174f, +0.0199f, 0.0227f, 0.0259f, 0.0293f, 0.0330f, 0.0365f, 0.0395f, 0.0415f, +0.0423f +}; +static const float glorentz61[53] = { +0.0009f, 0.0010f, 0.0010f, 0.0011f, 0.0011f, 0.0011f, 0.0012f, 0.0012f, +0.0013f, 0.0014f, 0.0014f, 0.0015f, 0.0016f, 0.0016f, 0.0017f, 0.0018f, +0.0019f, 0.0020f, 0.0021f, 0.0023f, 0.0024f, 0.0025f, 0.0027f, 0.0029f, +0.0031f, 0.0033f, 0.0035f, 0.0038f, 0.0041f, 0.0044f, 0.0047f, 0.0051f, +0.0056f, 0.0061f, 0.0067f, 0.0073f, 0.0081f, 0.0089f, 0.0099f, 0.0110f, +0.0124f, 0.0139f, 0.0156f, 0.0176f, 0.0199f, 0.0225f, 0.0253f, 0.0283f, +0.0314f, 0.0343f, 0.0367f, 0.0383f, 0.0389f +}; +static const float glorentz62[57] = { +0.0009f, 0.0009f, 0.0009f, 0.0010f, 0.0010f, 0.0011f, 0.0011f, 0.0011f, +0.0012f, 0.0012f, 0.0013f, 0.0013f, 0.0014f, 0.0015f, 0.0015f, 0.0016f, +0.0017f, 0.0018f, 0.0019f, 0.0020f, 0.0021f, 0.0022f, 0.0023f, 0.0024f, +0.0026f, 0.0027f, 0.0029f, 0.0031f, 0.0033f, 0.0035f, 0.0038f, 0.0040f, +0.0043f, 0.0047f, 0.0050f, 0.0055f, 0.0059f, 0.0064f, 0.0070f, 0.0077f, +0.0085f, 0.0093f, 0.0103f, 0.0114f, 0.0127f, 0.0142f, 0.0158f, 0.0177f, +0.0198f, 0.0221f, 0.0246f, 0.0272f, 0.0297f, 0.0321f, 0.0340f, 0.0353f, +0.0357f +}; +static const float glorentz63[62] = { +0.0008f, 0.0008f, 0.0009f, 0.0009f, 0.0009f, 0.0010f, 0.0010f, 0.0010f, +0.0011f, 0.0011f, 0.0011f, 0.0012f, 0.0012f, 0.0013f, 0.0013f, 0.0014f, +0.0015f, 0.0015f, 0.0016f, 0.0017f, 0.0017f, 0.0018f, 0.0019f, 0.0020f, +0.0021f, 0.0022f, 0.0023f, 0.0025f, 0.0026f, 0.0028f, 0.0029f, 0.0031f, +0.0033f, 0.0035f, 0.0038f, 0.0040f, 0.0043f, 0.0046f, 0.0050f, 0.0053f, +0.0058f, 0.0062f, 0.0068f, 0.0074f, 0.0081f, 0.0088f, 0.0097f, 0.0106f, +0.0117f, 0.0130f, 0.0144f, 0.0159f, 0.0176f, 0.0195f, 0.0216f, 0.0237f, +0.0259f, 0.0280f, 0.0299f, 0.0315f, 0.0325f, 0.0328f +}; +static const float glorentz64[65] = { +0.0008f, 0.0008f, 0.0008f, 0.0009f, 0.0009f, 0.0009f, 0.0010f, 0.0010f, +0.0010f, 0.0011f, 0.0011f, 0.0012f, 0.0012f, 0.0012f, 0.0013f, 0.0013f, +0.0014f, 0.0014f, 0.0015f, 0.0016f, 0.0016f, 0.0017f, 0.0018f, 0.0019f, +0.0020f, 0.0021f, 0.0022f, 0.0023f, 0.0024f, 0.0025f, 0.0027f, 0.0028f, +0.0030f, 0.0031f, 0.0033f, 0.0035f, 0.0038f, 0.0040f, 0.0043f, 0.0046f, +0.0049f, 0.0052f, 0.0056f, 0.0061f, 0.0066f, 0.0071f, 0.0077f, 0.0084f, +0.0091f, 0.0100f, 0.0109f, 0.0120f, 0.0132f, 0.0145f, 0.0159f, 0.0175f, +0.0192f, 0.0209f, 0.0228f, 0.0246f, 0.0264f, 0.0279f, 0.0291f, 0.0299f, +0.0301f +}; +static const float *gptr_tab_lorentz[64] = { +glorentz1, glorentz2, glorentz3, glorentz4, +glorentz5, glorentz6, glorentz7, glorentz8, +glorentz9, glorentz10, glorentz11, glorentz12, +glorentz13, glorentz14, glorentz15, glorentz16, +glorentz17, glorentz18, glorentz19, glorentz20, +glorentz21, glorentz22, glorentz23, glorentz24, +glorentz25, glorentz26, glorentz27, glorentz28, +glorentz29, glorentz30, glorentz31, glorentz32, +glorentz33, glorentz34, glorentz35, glorentz36, +glorentz37, glorentz38, glorentz39, glorentz40, +glorentz41, glorentz42, glorentz43, glorentz44, +glorentz45, glorentz46, glorentz47, glorentz48, +glorentz49, glorentz50, glorentz51, glorentz52, +glorentz53, glorentz54, glorentz55, glorentz56, +glorentz57, glorentz58, glorentz59, glorentz60, +glorentz61, glorentz62, glorentz63, glorentz64 +}; diff --git a/lib/qra/q65/normrnd.c b/lib/qra/q65/normrnd.c new file mode 100644 index 000000000..90abfa425 --- /dev/null +++ b/lib/qra/q65/normrnd.c @@ -0,0 +1,82 @@ +// normrnd.c +// functions to generate gaussian distributed numbers +// +// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy +// +// Credits to Andrea Montefusco - IW0HDV for his help on adapting the sources +// to OSs other than MS Windows +// +// ------------------------------------------------------------------------------ +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + + +#include "normrnd.h" + +#if _WIN32 // note the underscore: without it, it's not msdn official! + // Windows (x64 and x86) + #include // required only for GetTickCount(...) + #define K_RAND_MAX UINT_MAX +#elif _SVID_SOURCE || _XOPEN_SOURCE || __unix__ || (defined (__APPLE__) && defined(__MACH__)) /* POSIX or Unix or Apple */ + #include + #define rand_s(x) (*x)=(unsigned int)lrand48() // returns unsigned integers in the range 0..0x7FFFFFFF + #define K_RAND_MAX 0x7FFFFFFF // that's the max number + // generated by lrand48 +#else + #error "No good quality PRNG found" +#endif + + +// use MS rand_s(...) function +void normrnd_s(float *dst, int nitems, float mean, float stdev) +{ + unsigned int r; + float phi=0, u=0; + int set = 0; + + while (nitems--) + if (set==1) { + *dst++ = (float)sin(phi)*u*stdev+mean; + set = 0; + } + else { + rand_s((unsigned int*)&r); phi = (M_2PI/(1.0f+K_RAND_MAX))*r; + rand_s((unsigned int*)&r); u = (float)sqrt(-2.0f* log( (1.0f/(1.0f+K_RAND_MAX))*(1.0f+r) ) ); + *dst++ = (float)cos(phi)*u*stdev+mean; + set=1; + } +} + +/* NOT USED +// use MS rand() function +void normrnd(float *dst, int nitems, float mean, float stdev) +{ + float phi=0, u=0; + int set = 0; + + while (nitems--) + if (set==1) { + *dst++ = (float)sin(phi)*u*stdev+mean; + set = 0; + } + else { + phi = (M_2PI/(1.0f+RAND_MAX))*rand(); + u = (float)sqrt(-2.0f* log( (1.0f/(1.0f+RAND_MAX))*(1.0f+rand()) ) ); + *dst++ = (float)cos(phi)*u*stdev+mean; + set=1; + } +} +*/ diff --git a/lib/qra/q65/normrnd.h b/lib/qra/q65/normrnd.h new file mode 100644 index 000000000..dd4b65bbe --- /dev/null +++ b/lib/qra/q65/normrnd.h @@ -0,0 +1,51 @@ +// normrnd.h +// Functions to generate gaussian distributed numbers +// +// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy +// ------------------------------------------------------------------------------ +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + +#ifndef _normrnd_h_ +#define _normrnd_h_ + +#define _CRT_RAND_S +#include + +#define _USE_MATH_DEFINES +#include +#define M_2PI (2.0f*(float)M_PI) + +#ifdef __cplusplus +extern "C" { +#endif + +void normrnd_s(float *dst, int nitems, float mean, float stdev); +// generate a random array of numbers with a gaussian distribution of given mean and stdev +// use MS rand_s(...) function + +/* not used +void normrnd(float *dst, int nitems, float mean, float stdev); +// generate a random array of numbers with a gaussian distribution of given mean and stdev +// use MS rand() function +*/ + +#ifdef __cplusplus +} +#endif + +#endif // _normrnd_h_ + diff --git a/lib/qra/q65/npfwht.c b/lib/qra/q65/npfwht.c new file mode 100644 index 000000000..5732ce913 --- /dev/null +++ b/lib/qra/q65/npfwht.c @@ -0,0 +1,216 @@ +// npfwht.c +// Basic implementation of the Fast Walsh-Hadamard Transforms +// +// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy +// ------------------------------------------------------------------------------ +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (repeat and accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + +#include "npfwht.h" + +#define WHBFY(dst,src,base,offs,dist) { dst[base+offs]=src[base+offs]+src[base+offs+dist]; dst[base+offs+dist]=src[base+offs]-src[base+offs+dist]; } + +typedef void (*pnp_fwht)(float*,float*); + +static void np_fwht2(float *dst, float *src); + +static void np_fwht1(float *dst, float *src); +static void np_fwht2(float *dst, float *src); +static void np_fwht4(float *dst, float *src); +static void np_fwht8(float *dst, float *src); +static void np_fwht16(float *dst, float *src); +static void np_fwht32(float *dst, float *src); +static void np_fwht64(float *dst, float *src); + +static pnp_fwht np_fwht_tab[7] = { + np_fwht1, + np_fwht2, + np_fwht4, + np_fwht8, + np_fwht16, + np_fwht32, + np_fwht64 +}; + +void np_fwht(int nlogdim, float *dst, float *src) +{ + np_fwht_tab[nlogdim](dst,src); +} + +static void np_fwht1(float *dst, float *src) +{ + dst[0] = src[0]; +} + + +static void np_fwht2(float *dst, float *src) +{ + float t[2]; + + WHBFY(t,src,0,0,1); + dst[0]= t[0]; + dst[1]= t[1]; +} + +static void np_fwht4(float *dst, float *src) +{ + float t[4]; + + // group 1 + WHBFY(t,src,0,0,2); WHBFY(t,src,0,1,2); + // group 2 + WHBFY(dst,t,0,0,1); WHBFY(dst,t,2,0,1); +}; + + +static void np_fwht8(float *dst, float *src) +{ + float t[16]; + float *t1=t, *t2=t+8; + + // group 1 + WHBFY(t1,src,0,0,4); WHBFY(t1,src,0,1,4); WHBFY(t1,src,0,2,4); WHBFY(t1,src,0,3,4); + // group 2 + WHBFY(t2,t1,0,0,2); WHBFY(t2,t1,0,1,2); WHBFY(t2,t1,4,0,2); WHBFY(t2,t1,4,1,2); + // group 3 + WHBFY(dst,t2,0,0,1); WHBFY(dst,t2,2,0,1); WHBFY(dst,t2,4,0,1); WHBFY(dst,t2,6,0,1); +}; + + +static void np_fwht16(float *dst, float *src) +{ + float t[32]; + float *t1=t, *t2=t+16; + + // group 1 + WHBFY(t1,src,0,0,8); WHBFY(t1,src,0,1,8); WHBFY(t1,src,0,2,8); WHBFY(t1,src,0,3,8); + WHBFY(t1,src,0,4,8); WHBFY(t1,src,0,5,8); WHBFY(t1,src,0,6,8); WHBFY(t1,src,0,7,8); + // group 2 + WHBFY(t2,t1,0,0,4); WHBFY(t2,t1,0,1,4); WHBFY(t2,t1,0,2,4); WHBFY(t2,t1,0,3,4); + WHBFY(t2,t1,8,0,4); WHBFY(t2,t1,8,1,4); WHBFY(t2,t1,8,2,4); WHBFY(t2,t1,8,3,4); + // group 3 + WHBFY(t1,t2,0,0,2); WHBFY(t1,t2,0,1,2); WHBFY(t1,t2,4,0,2); WHBFY(t1,t2,4,1,2); + WHBFY(t1,t2,8,0,2); WHBFY(t1,t2,8,1,2); WHBFY(t1,t2,12,0,2); WHBFY(t1,t2,12,1,2); + // group 4 + WHBFY(dst,t1,0,0,1); WHBFY(dst,t1,2,0,1); WHBFY(dst,t1,4,0,1); WHBFY(dst,t1,6,0,1); + WHBFY(dst,t1,8,0,1); WHBFY(dst,t1,10,0,1); WHBFY(dst,t1,12,0,1); WHBFY(dst,t1,14,0,1); + +} + +static void np_fwht32(float *dst, float *src) +{ + float t[64]; + float *t1=t, *t2=t+32; + + // group 1 + WHBFY(t1,src,0,0,16); WHBFY(t1,src,0,1,16); WHBFY(t1,src,0,2,16); WHBFY(t1,src,0,3,16); + WHBFY(t1,src,0,4,16); WHBFY(t1,src,0,5,16); WHBFY(t1,src,0,6,16); WHBFY(t1,src,0,7,16); + WHBFY(t1,src,0,8,16); WHBFY(t1,src,0,9,16); WHBFY(t1,src,0,10,16); WHBFY(t1,src,0,11,16); + WHBFY(t1,src,0,12,16); WHBFY(t1,src,0,13,16); WHBFY(t1,src,0,14,16); WHBFY(t1,src,0,15,16); + + // group 2 + WHBFY(t2,t1,0,0,8); WHBFY(t2,t1,0,1,8); WHBFY(t2,t1,0,2,8); WHBFY(t2,t1,0,3,8); + WHBFY(t2,t1,0,4,8); WHBFY(t2,t1,0,5,8); WHBFY(t2,t1,0,6,8); WHBFY(t2,t1,0,7,8); + WHBFY(t2,t1,16,0,8); WHBFY(t2,t1,16,1,8); WHBFY(t2,t1,16,2,8); WHBFY(t2,t1,16,3,8); + WHBFY(t2,t1,16,4,8); WHBFY(t2,t1,16,5,8); WHBFY(t2,t1,16,6,8); WHBFY(t2,t1,16,7,8); + + // group 3 + WHBFY(t1,t2,0,0,4); WHBFY(t1,t2,0,1,4); WHBFY(t1,t2,0,2,4); WHBFY(t1,t2,0,3,4); + WHBFY(t1,t2,8,0,4); WHBFY(t1,t2,8,1,4); WHBFY(t1,t2,8,2,4); WHBFY(t1,t2,8,3,4); + WHBFY(t1,t2,16,0,4); WHBFY(t1,t2,16,1,4); WHBFY(t1,t2,16,2,4); WHBFY(t1,t2,16,3,4); + WHBFY(t1,t2,24,0,4); WHBFY(t1,t2,24,1,4); WHBFY(t1,t2,24,2,4); WHBFY(t1,t2,24,3,4); + + // group 4 + WHBFY(t2,t1,0,0,2); WHBFY(t2,t1,0,1,2); WHBFY(t2,t1,4,0,2); WHBFY(t2,t1,4,1,2); + WHBFY(t2,t1,8,0,2); WHBFY(t2,t1,8,1,2); WHBFY(t2,t1,12,0,2); WHBFY(t2,t1,12,1,2); + WHBFY(t2,t1,16,0,2); WHBFY(t2,t1,16,1,2); WHBFY(t2,t1,20,0,2); WHBFY(t2,t1,20,1,2); + WHBFY(t2,t1,24,0,2); WHBFY(t2,t1,24,1,2); WHBFY(t2,t1,28,0,2); WHBFY(t2,t1,28,1,2); + + // group 5 + WHBFY(dst,t2,0,0,1); WHBFY(dst,t2,2,0,1); WHBFY(dst,t2,4,0,1); WHBFY(dst,t2,6,0,1); + WHBFY(dst,t2,8,0,1); WHBFY(dst,t2,10,0,1); WHBFY(dst,t2,12,0,1); WHBFY(dst,t2,14,0,1); + WHBFY(dst,t2,16,0,1); WHBFY(dst,t2,18,0,1); WHBFY(dst,t2,20,0,1); WHBFY(dst,t2,22,0,1); + WHBFY(dst,t2,24,0,1); WHBFY(dst,t2,26,0,1); WHBFY(dst,t2,28,0,1); WHBFY(dst,t2,30,0,1); + +} + +static void np_fwht64(float *dst, float *src) +{ + float t[128]; + float *t1=t, *t2=t+64; + + + // group 1 + WHBFY(t1,src,0,0,32); WHBFY(t1,src,0,1,32); WHBFY(t1,src,0,2,32); WHBFY(t1,src,0,3,32); + WHBFY(t1,src,0,4,32); WHBFY(t1,src,0,5,32); WHBFY(t1,src,0,6,32); WHBFY(t1,src,0,7,32); + WHBFY(t1,src,0,8,32); WHBFY(t1,src,0,9,32); WHBFY(t1,src,0,10,32); WHBFY(t1,src,0,11,32); + WHBFY(t1,src,0,12,32); WHBFY(t1,src,0,13,32); WHBFY(t1,src,0,14,32); WHBFY(t1,src,0,15,32); + WHBFY(t1,src,0,16,32); WHBFY(t1,src,0,17,32); WHBFY(t1,src,0,18,32); WHBFY(t1,src,0,19,32); + WHBFY(t1,src,0,20,32); WHBFY(t1,src,0,21,32); WHBFY(t1,src,0,22,32); WHBFY(t1,src,0,23,32); + WHBFY(t1,src,0,24,32); WHBFY(t1,src,0,25,32); WHBFY(t1,src,0,26,32); WHBFY(t1,src,0,27,32); + WHBFY(t1,src,0,28,32); WHBFY(t1,src,0,29,32); WHBFY(t1,src,0,30,32); WHBFY(t1,src,0,31,32); + + // group 2 + WHBFY(t2,t1,0,0,16); WHBFY(t2,t1,0,1,16); WHBFY(t2,t1,0,2,16); WHBFY(t2,t1,0,3,16); + WHBFY(t2,t1,0,4,16); WHBFY(t2,t1,0,5,16); WHBFY(t2,t1,0,6,16); WHBFY(t2,t1,0,7,16); + WHBFY(t2,t1,0,8,16); WHBFY(t2,t1,0,9,16); WHBFY(t2,t1,0,10,16); WHBFY(t2,t1,0,11,16); + WHBFY(t2,t1,0,12,16); WHBFY(t2,t1,0,13,16); WHBFY(t2,t1,0,14,16); WHBFY(t2,t1,0,15,16); + + WHBFY(t2,t1,32,0,16); WHBFY(t2,t1,32,1,16); WHBFY(t2,t1,32,2,16); WHBFY(t2,t1,32,3,16); + WHBFY(t2,t1,32,4,16); WHBFY(t2,t1,32,5,16); WHBFY(t2,t1,32,6,16); WHBFY(t2,t1,32,7,16); + WHBFY(t2,t1,32,8,16); WHBFY(t2,t1,32,9,16); WHBFY(t2,t1,32,10,16); WHBFY(t2,t1,32,11,16); + WHBFY(t2,t1,32,12,16); WHBFY(t2,t1,32,13,16); WHBFY(t2,t1,32,14,16); WHBFY(t2,t1,32,15,16); + + // group 3 + WHBFY(t1,t2,0,0,8); WHBFY(t1,t2,0,1,8); WHBFY(t1,t2,0,2,8); WHBFY(t1,t2,0,3,8); + WHBFY(t1,t2,0,4,8); WHBFY(t1,t2,0,5,8); WHBFY(t1,t2,0,6,8); WHBFY(t1,t2,0,7,8); + WHBFY(t1,t2,16,0,8); WHBFY(t1,t2,16,1,8); WHBFY(t1,t2,16,2,8); WHBFY(t1,t2,16,3,8); + WHBFY(t1,t2,16,4,8); WHBFY(t1,t2,16,5,8); WHBFY(t1,t2,16,6,8); WHBFY(t1,t2,16,7,8); + WHBFY(t1,t2,32,0,8); WHBFY(t1,t2,32,1,8); WHBFY(t1,t2,32,2,8); WHBFY(t1,t2,32,3,8); + WHBFY(t1,t2,32,4,8); WHBFY(t1,t2,32,5,8); WHBFY(t1,t2,32,6,8); WHBFY(t1,t2,32,7,8); + WHBFY(t1,t2,48,0,8); WHBFY(t1,t2,48,1,8); WHBFY(t1,t2,48,2,8); WHBFY(t1,t2,48,3,8); + WHBFY(t1,t2,48,4,8); WHBFY(t1,t2,48,5,8); WHBFY(t1,t2,48,6,8); WHBFY(t1,t2,48,7,8); + + // group 4 + WHBFY(t2,t1,0,0,4); WHBFY(t2,t1,0,1,4); WHBFY(t2,t1,0,2,4); WHBFY(t2,t1,0,3,4); + WHBFY(t2,t1,8,0,4); WHBFY(t2,t1,8,1,4); WHBFY(t2,t1,8,2,4); WHBFY(t2,t1,8,3,4); + WHBFY(t2,t1,16,0,4); WHBFY(t2,t1,16,1,4); WHBFY(t2,t1,16,2,4); WHBFY(t2,t1,16,3,4); + WHBFY(t2,t1,24,0,4); WHBFY(t2,t1,24,1,4); WHBFY(t2,t1,24,2,4); WHBFY(t2,t1,24,3,4); + WHBFY(t2,t1,32,0,4); WHBFY(t2,t1,32,1,4); WHBFY(t2,t1,32,2,4); WHBFY(t2,t1,32,3,4); + WHBFY(t2,t1,40,0,4); WHBFY(t2,t1,40,1,4); WHBFY(t2,t1,40,2,4); WHBFY(t2,t1,40,3,4); + WHBFY(t2,t1,48,0,4); WHBFY(t2,t1,48,1,4); WHBFY(t2,t1,48,2,4); WHBFY(t2,t1,48,3,4); + WHBFY(t2,t1,56,0,4); WHBFY(t2,t1,56,1,4); WHBFY(t2,t1,56,2,4); WHBFY(t2,t1,56,3,4); + + // group 5 + WHBFY(t1,t2,0,0,2); WHBFY(t1,t2,0,1,2); WHBFY(t1,t2,4,0,2); WHBFY(t1,t2,4,1,2); + WHBFY(t1,t2,8,0,2); WHBFY(t1,t2,8,1,2); WHBFY(t1,t2,12,0,2); WHBFY(t1,t2,12,1,2); + WHBFY(t1,t2,16,0,2); WHBFY(t1,t2,16,1,2); WHBFY(t1,t2,20,0,2); WHBFY(t1,t2,20,1,2); + WHBFY(t1,t2,24,0,2); WHBFY(t1,t2,24,1,2); WHBFY(t1,t2,28,0,2); WHBFY(t1,t2,28,1,2); + WHBFY(t1,t2,32,0,2); WHBFY(t1,t2,32,1,2); WHBFY(t1,t2,36,0,2); WHBFY(t1,t2,36,1,2); + WHBFY(t1,t2,40,0,2); WHBFY(t1,t2,40,1,2); WHBFY(t1,t2,44,0,2); WHBFY(t1,t2,44,1,2); + WHBFY(t1,t2,48,0,2); WHBFY(t1,t2,48,1,2); WHBFY(t1,t2,52,0,2); WHBFY(t1,t2,52,1,2); + WHBFY(t1,t2,56,0,2); WHBFY(t1,t2,56,1,2); WHBFY(t1,t2,60,0,2); WHBFY(t1,t2,60,1,2); + + // group 6 + WHBFY(dst,t1,0,0,1); WHBFY(dst,t1,2,0,1); WHBFY(dst,t1,4,0,1); WHBFY(dst,t1,6,0,1); + WHBFY(dst,t1,8,0,1); WHBFY(dst,t1,10,0,1); WHBFY(dst,t1,12,0,1); WHBFY(dst,t1,14,0,1); + WHBFY(dst,t1,16,0,1); WHBFY(dst,t1,18,0,1); WHBFY(dst,t1,20,0,1); WHBFY(dst,t1,22,0,1); + WHBFY(dst,t1,24,0,1); WHBFY(dst,t1,26,0,1); WHBFY(dst,t1,28,0,1); WHBFY(dst,t1,30,0,1); + WHBFY(dst,t1,32,0,1); WHBFY(dst,t1,34,0,1); WHBFY(dst,t1,36,0,1); WHBFY(dst,t1,38,0,1); + WHBFY(dst,t1,40,0,1); WHBFY(dst,t1,42,0,1); WHBFY(dst,t1,44,0,1); WHBFY(dst,t1,46,0,1); + WHBFY(dst,t1,48,0,1); WHBFY(dst,t1,50,0,1); WHBFY(dst,t1,52,0,1); WHBFY(dst,t1,54,0,1); + WHBFY(dst,t1,56,0,1); WHBFY(dst,t1,58,0,1); WHBFY(dst,t1,60,0,1); WHBFY(dst,t1,62,0,1); +} \ No newline at end of file diff --git a/lib/qra/q65/npfwht.h b/lib/qra/q65/npfwht.h new file mode 100644 index 000000000..9452e2077 --- /dev/null +++ b/lib/qra/q65/npfwht.h @@ -0,0 +1,45 @@ +// np_fwht.h +// Basic implementation of the Fast Walsh-Hadamard Transforms +// +// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy +// ------------------------------------------------------------------------------ +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (repeat and accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + +#ifndef _npfwht_h_ +#define _npfwht_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +void np_fwht(int nlogdim, float *dst, float *src); +// Compute the Walsh-Hadamard transform of the given data up to a +// 64-dimensional transform +// +// Input parameters: +// nlogdim: log2 of the transform size. Must be in the range [0..6] +// src : pointer to the input data buffer. +// dst : pointer to the output data buffer. +// +// src and dst must point to preallocated data buffers of size 2^nlogdim*sizeof(float) +// src and dst buffers can overlap + +#ifdef __cplusplus +} +#endif + +#endif // _npfwht_ diff --git a/lib/qra/q65/pdmath.c b/lib/qra/q65/pdmath.c new file mode 100644 index 000000000..47ecab917 --- /dev/null +++ b/lib/qra/q65/pdmath.c @@ -0,0 +1,385 @@ +// pdmath.c +// Elementary math on probability distributions +// +// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy +// ------------------------------------------------------------------------------ +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + +#include "pdmath.h" + +typedef const float *ppd_uniform; +typedef void (*ppd_imul)(float*,const float*); +typedef float (*ppd_norm)(float*); + +// define vector size in function of its logarithm in base 2 +static const int pd_log2dim[7] = { + 1,2,4,8,16,32,64 +}; + +// define uniform distributions of given size +static const float pd_uniform1[1] = { + 1. +}; +static const float pd_uniform2[2] = { + 1./2., 1./2. +}; +static const float pd_uniform4[4] = { + 1./4., 1./4.,1./4., 1./4. +}; +static const float pd_uniform8[8] = { + 1./8., 1./8.,1./8., 1./8.,1./8., 1./8.,1./8., 1./8. +}; +static const float pd_uniform16[16] = { + 1./16., 1./16., 1./16., 1./16.,1./16., 1./16.,1./16., 1./16., + 1./16., 1./16., 1./16., 1./16.,1./16., 1./16.,1./16., 1./16. +}; +static const float pd_uniform32[32] = { + 1./32., 1./32., 1./32., 1./32.,1./32., 1./32.,1./32., 1./32., + 1./32., 1./32., 1./32., 1./32.,1./32., 1./32.,1./32., 1./32., + 1./32., 1./32., 1./32., 1./32.,1./32., 1./32.,1./32., 1./32., + 1./32., 1./32., 1./32., 1./32.,1./32., 1./32.,1./32., 1./32. +}; +static const float pd_uniform64[64] = { + 1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64., + 1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64., + 1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64., + 1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64., + 1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64., + 1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64., + 1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64., + 1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64. + +}; + +static const ppd_uniform pd_uniform_tab[7] = { + pd_uniform1, + pd_uniform2, + pd_uniform4, + pd_uniform8, + pd_uniform16, + pd_uniform32, + pd_uniform64 +}; + +// returns a pointer to the uniform distribution of the given logsize +const float *pd_uniform(int nlogdim) +{ + return pd_uniform_tab[nlogdim]; +} + +// in-place multiplication functions +// compute dst = dst*src for any element of the distrib + +static void pd_imul1(float *dst, const float *src) +{ + dst[0] *= src[0]; +} + +static void pd_imul2(float *dst, const float *src) +{ + dst[0] *= src[0]; dst[1] *= src[1]; +} +static void pd_imul4(float *dst, const float *src) +{ + dst[0] *= src[0]; dst[1] *= src[1]; + dst[2] *= src[2]; dst[3] *= src[3]; +} +static void pd_imul8(float *dst, const float *src) +{ + dst[0] *= src[0]; dst[1] *= src[1]; dst[2] *= src[2]; dst[3] *= src[3]; + dst[4] *= src[4]; dst[5] *= src[5]; dst[6] *= src[6]; dst[7] *= src[7]; +} +static void pd_imul16(float *dst, const float *src) +{ + dst[0] *= src[0]; dst[1] *= src[1]; dst[2] *= src[2]; dst[3] *= src[3]; + dst[4] *= src[4]; dst[5] *= src[5]; dst[6] *= src[6]; dst[7] *= src[7]; + dst[8] *= src[8]; dst[9] *= src[9]; dst[10]*= src[10]; dst[11]*= src[11]; + dst[12]*= src[12]; dst[13]*= src[13]; dst[14]*= src[14]; dst[15]*= src[15]; +} +static void pd_imul32(float *dst, const float *src) +{ + pd_imul16(dst,src); + pd_imul16(dst+16,src+16); +} +static void pd_imul64(float *dst, const float *src) +{ + pd_imul16(dst, src); + pd_imul16(dst+16, src+16); + pd_imul16(dst+32, src+32); + pd_imul16(dst+48, src+48); +} + +static const ppd_imul pd_imul_tab[7] = { + pd_imul1, + pd_imul2, + pd_imul4, + pd_imul8, + pd_imul16, + pd_imul32, + pd_imul64 +}; + +// in place multiplication +// compute dst = dst*src for any element of the distrib give their log2 size +// arguments must be pointers to array of floats of the given size +void pd_imul(float *dst, const float *src, int nlogdim) +{ + pd_imul_tab[nlogdim](dst,src); +} + +static float pd_norm1(float *ppd) +{ + float t = ppd[0]; + ppd[0] = 1.f; + return t; +} + +static float pd_norm2(float *ppd) +{ + float t,to; + + t =ppd[0]; t +=ppd[1]; + + if (t<=0) { + pd_init(ppd,pd_uniform(1),pd_log2dim[1]); + return t; + } + + to = t; + t = 1.f/t; + ppd[0] *=t; ppd[1] *=t; + return to; + +} + +static float pd_norm4(float *ppd) +{ + float t,to; + + t =ppd[0]; t +=ppd[1]; t +=ppd[2]; t +=ppd[3]; + + if (t<=0) { + pd_init(ppd,pd_uniform(2),pd_log2dim[2]); + return t; + } + + to = t; + t = 1.f/t; + ppd[0] *=t; ppd[1] *=t; ppd[2] *=t; ppd[3] *=t; + return to; +} + +static float pd_norm8(float *ppd) +{ + float t,to; + + t =ppd[0]; t +=ppd[1]; t +=ppd[2]; t +=ppd[3]; + t +=ppd[4]; t +=ppd[5]; t +=ppd[6]; t +=ppd[7]; + + if (t<=0) { + pd_init(ppd,pd_uniform(3),pd_log2dim[3]); + return t; + } + + to = t; + t = 1.f/t; + ppd[0] *=t; ppd[1] *=t; ppd[2] *=t; ppd[3] *=t; + ppd[4] *=t; ppd[5] *=t; ppd[6] *=t; ppd[7] *=t; + return to; +} +static float pd_norm16(float *ppd) +{ + float t,to; + + t =ppd[0]; t +=ppd[1]; t +=ppd[2]; t +=ppd[3]; + t +=ppd[4]; t +=ppd[5]; t +=ppd[6]; t +=ppd[7]; + t +=ppd[8]; t +=ppd[9]; t +=ppd[10]; t +=ppd[11]; + t +=ppd[12]; t +=ppd[13]; t +=ppd[14]; t +=ppd[15]; + + if (t<=0) { + pd_init(ppd,pd_uniform(4),pd_log2dim[4]); + return t; + } + + to = t; + t = 1.f/t; + ppd[0] *=t; ppd[1] *=t; ppd[2] *=t; ppd[3] *=t; + ppd[4] *=t; ppd[5] *=t; ppd[6] *=t; ppd[7] *=t; + ppd[8] *=t; ppd[9] *=t; ppd[10] *=t; ppd[11] *=t; + ppd[12] *=t; ppd[13] *=t; ppd[14] *=t; ppd[15] *=t; + + return to; +} +static float pd_norm32(float *ppd) +{ + float t,to; + + t =ppd[0]; t +=ppd[1]; t +=ppd[2]; t +=ppd[3]; + t +=ppd[4]; t +=ppd[5]; t +=ppd[6]; t +=ppd[7]; + t +=ppd[8]; t +=ppd[9]; t +=ppd[10]; t +=ppd[11]; + t +=ppd[12]; t +=ppd[13]; t +=ppd[14]; t +=ppd[15]; + t +=ppd[16]; t +=ppd[17]; t +=ppd[18]; t +=ppd[19]; + t +=ppd[20]; t +=ppd[21]; t +=ppd[22]; t +=ppd[23]; + t +=ppd[24]; t +=ppd[25]; t +=ppd[26]; t +=ppd[27]; + t +=ppd[28]; t +=ppd[29]; t +=ppd[30]; t +=ppd[31]; + + if (t<=0) { + pd_init(ppd,pd_uniform(5),pd_log2dim[5]); + return t; + } + + to = t; + t = 1.f/t; + ppd[0] *=t; ppd[1] *=t; ppd[2] *=t; ppd[3] *=t; + ppd[4] *=t; ppd[5] *=t; ppd[6] *=t; ppd[7] *=t; + ppd[8] *=t; ppd[9] *=t; ppd[10] *=t; ppd[11] *=t; + ppd[12] *=t; ppd[13] *=t; ppd[14] *=t; ppd[15] *=t; + ppd[16] *=t; ppd[17] *=t; ppd[18] *=t; ppd[19] *=t; + ppd[20] *=t; ppd[21] *=t; ppd[22] *=t; ppd[23] *=t; + ppd[24] *=t; ppd[25] *=t; ppd[26] *=t; ppd[27] *=t; + ppd[28] *=t; ppd[29] *=t; ppd[30] *=t; ppd[31] *=t; + + return to; +} + +static float pd_norm64(float *ppd) +{ + float t,to; + + t =ppd[0]; t +=ppd[1]; t +=ppd[2]; t +=ppd[3]; + t +=ppd[4]; t +=ppd[5]; t +=ppd[6]; t +=ppd[7]; + t +=ppd[8]; t +=ppd[9]; t +=ppd[10]; t +=ppd[11]; + t +=ppd[12]; t +=ppd[13]; t +=ppd[14]; t +=ppd[15]; + t +=ppd[16]; t +=ppd[17]; t +=ppd[18]; t +=ppd[19]; + t +=ppd[20]; t +=ppd[21]; t +=ppd[22]; t +=ppd[23]; + t +=ppd[24]; t +=ppd[25]; t +=ppd[26]; t +=ppd[27]; + t +=ppd[28]; t +=ppd[29]; t +=ppd[30]; t +=ppd[31]; + + t +=ppd[32]; t +=ppd[33]; t +=ppd[34]; t +=ppd[35]; + t +=ppd[36]; t +=ppd[37]; t +=ppd[38]; t +=ppd[39]; + t +=ppd[40]; t +=ppd[41]; t +=ppd[42]; t +=ppd[43]; + t +=ppd[44]; t +=ppd[45]; t +=ppd[46]; t +=ppd[47]; + t +=ppd[48]; t +=ppd[49]; t +=ppd[50]; t +=ppd[51]; + t +=ppd[52]; t +=ppd[53]; t +=ppd[54]; t +=ppd[55]; + t +=ppd[56]; t +=ppd[57]; t +=ppd[58]; t +=ppd[59]; + t +=ppd[60]; t +=ppd[61]; t +=ppd[62]; t +=ppd[63]; + + if (t<=0) { + pd_init(ppd,pd_uniform(6),pd_log2dim[6]); + return t; + } + + to = t; + t = 1.0f/t; + ppd[0] *=t; ppd[1] *=t; ppd[2] *=t; ppd[3] *=t; + ppd[4] *=t; ppd[5] *=t; ppd[6] *=t; ppd[7] *=t; + ppd[8] *=t; ppd[9] *=t; ppd[10] *=t; ppd[11] *=t; + ppd[12] *=t; ppd[13] *=t; ppd[14] *=t; ppd[15] *=t; + ppd[16] *=t; ppd[17] *=t; ppd[18] *=t; ppd[19] *=t; + ppd[20] *=t; ppd[21] *=t; ppd[22] *=t; ppd[23] *=t; + ppd[24] *=t; ppd[25] *=t; ppd[26] *=t; ppd[27] *=t; + ppd[28] *=t; ppd[29] *=t; ppd[30] *=t; ppd[31] *=t; + + ppd[32] *=t; ppd[33] *=t; ppd[34] *=t; ppd[35] *=t; + ppd[36] *=t; ppd[37] *=t; ppd[38] *=t; ppd[39] *=t; + ppd[40] *=t; ppd[41] *=t; ppd[42] *=t; ppd[43] *=t; + ppd[44] *=t; ppd[45] *=t; ppd[46] *=t; ppd[47] *=t; + ppd[48] *=t; ppd[49] *=t; ppd[50] *=t; ppd[51] *=t; + ppd[52] *=t; ppd[53] *=t; ppd[54] *=t; ppd[55] *=t; + ppd[56] *=t; ppd[57] *=t; ppd[58] *=t; ppd[59] *=t; + ppd[60] *=t; ppd[61] *=t; ppd[62] *=t; ppd[63] *=t; + + return to; +} + + +static const ppd_norm pd_norm_tab[7] = { + pd_norm1, + pd_norm2, + pd_norm4, + pd_norm8, + pd_norm16, + pd_norm32, + pd_norm64 +}; + +float pd_norm(float *pd, int nlogdim) +{ + return pd_norm_tab[nlogdim](pd); +} + +void pd_memset(float *dst, const float *src, int ndim, int nitems) +{ + int size = PD_SIZE(ndim); + while(nitems--) { + memcpy(dst,src,size); + dst +=ndim; + } +} + +void pd_fwdperm(float *dst, float *src, const int *perm, int ndim) +{ + // TODO: non-loop implementation + while (ndim--) + dst[ndim] = src[perm[ndim]]; +} + +void pd_bwdperm(float *dst, float *src, const int *perm, int ndim) +{ + // TODO: non-loop implementation + while (ndim--) + dst[perm[ndim]] = src[ndim]; +} + +float pd_max(float *src, int ndim) +{ + // TODO: faster implementation + + float cmax=0; // we assume that prob distributions are always positive + float cval; + + while (ndim--) { + cval = src[ndim]; + if (cval>=cmax) { + cmax = cval; + } + } + + return cmax; +} + +int pd_argmax(float *pmax, float *src, int ndim) +{ + // TODO: faster implementation + + float cmax=0; // we assume that prob distributions are always positive + float cval; + int idxmax=-1; // indicates that all pd elements are <0 + + while (ndim--) { + cval = src[ndim]; + if (cval>=cmax) { + cmax = cval; + idxmax = ndim; + } + } + + if (pmax) + *pmax = cmax; + + return idxmax; +} diff --git a/lib/qra/q65/pdmath.h b/lib/qra/q65/pdmath.h new file mode 100644 index 000000000..bbd1210c4 --- /dev/null +++ b/lib/qra/q65/pdmath.h @@ -0,0 +1,85 @@ +// pdmath.h +// Elementary math on probability distributions +// +// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy +// ------------------------------------------------------------------------------ +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (repeat and accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + + +#ifndef _pdmath_h_ +#define _pdmath_h_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PD_NDIM(nlogdim) ((1<<(nlogdim)) +#define PD_SIZE(ndim) ((ndim)*sizeof(float)) +#define PD_ROWADDR(fp,ndim,idx) (fp+((ndim)*(idx))) + +const float *pd_uniform(int nlogdim); +// Returns a pointer to a (constant) uniform distribution of the given log2 size + +#define pd_init(dst,src,ndim) memcpy(dst,src,PD_SIZE(ndim)) +// Distribution copy + +void pd_memset(float *dst, const float *src, int ndim, int nitems); +// Copy the distribution pointed by src to the array of distributions dst +// src is a pointer to the input distribution (a vector of size ndim) +// dst is a pointer to a linear array of distributions (a vector of size ndim*nitems) + +void pd_imul(float *dst, const float *src, int nlogdim); +// In place multiplication +// Compute dst = dst*src for any element of the distrib give their log2 size +// src and dst arguments must be pointers to array of floats of the given size + +float pd_norm(float *pd, int nlogdim); +// In place normalizazion +// Normalizes the input vector so that the sum of its components are one +// pd must be a pointer to an array of floats of the given size. +// If the norm of the input vector is non-positive the vector components +// are replaced with a uniform distribution +// Returns the norm of the distribution prior to the normalization + +void pd_fwdperm(float *dst, float *src, const int *perm, int ndim); +// Forward permutation of a distribution +// Computes dst[k] = src[perm[k]] for every element in the distribution +// perm must be a pointer to an array of integers of length ndim + +void pd_bwdperm(float *dst, float *src, const int *perm, int ndim); +// Backward permutation of a distribution +// Computes dst[perm[k]] = src[k] for every element in the distribution +// perm must be a pointer to an array of integers of length ndim + +float pd_max(float *src, int ndim); +// Return the maximum of the elements of the given distribution +// Assumes that the input vector is a probability distribution and that each element in the +// distribution is non negative + +int pd_argmax(float *pmax, float *src, int ndim); +// Return the index of the maximum element of the given distribution +// The maximum is stored in the variable pointed by pmax if pmax is not null +// Same note of pd_max applies. +// Return -1 if all the elements in the distribution are negative + +#ifdef __cplusplus +} +#endif + +#endif // _pdmath_h_ diff --git a/lib/qra/q65/q65.c b/lib/qra/q65/q65.c new file mode 100644 index 000000000..856d8d7a7 --- /dev/null +++ b/lib/qra/q65/q65.c @@ -0,0 +1,795 @@ +// q65.c +// q65 modes encoding/decoding functions +// +// (c) 2020 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy +// ------------------------------------------------------------------------------ +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + +#include +#include +#include + +#include "q65.h" +#include "pdmath.h" + + +static int _q65_crc6(int *x, int sz); +static void _q65_crc12(int *y, int *x, int sz); + + +int q65_init(q65_codec_ds *pCodec, const qracode *pqracode) +{ + // Eb/No value for which we optimize the decoder metric (AWGN/Rayleigh cases) + const float EbNodBMetric = 2.8f; + const float EbNoMetric = (float)pow(10,EbNodBMetric/10); + + float R; // code effective rate (after puncturing) + int nm; // bits per symbol + + if (!pCodec) + return -1; // why do you called me? + + if (!pqracode) + return -2; // invalid qra code + + if (pqracode->M!=64) + return -3; // q65 supports only codes over GF(64) + + pCodec->pQraCode = pqracode; + + // allocate buffers used by encoding/decoding functions + pCodec->x = (int*)malloc(pqracode->K*sizeof(int)); + pCodec->y = (int*)malloc(pqracode->N*sizeof(int)); + pCodec->qra_v2cmsg = (float*)malloc(pqracode->NMSG*pqracode->M*sizeof(float)); + pCodec->qra_c2vmsg = (float*)malloc(pqracode->NMSG*pqracode->M*sizeof(float)); + pCodec->ix = (float*)malloc(pqracode->N*pqracode->M*sizeof(float)); + pCodec->ex = (float*)malloc(pqracode->N*pqracode->M*sizeof(float)); + + if (pCodec->x== NULL || + pCodec->y== NULL || + pCodec->qra_v2cmsg== NULL || + pCodec->qra_c2vmsg== NULL || + pCodec->ix== NULL || + pCodec->ex== NULL) { + q65_free(pCodec); + return -4; // out of memory + } + + // compute and store the AWGN/Rayleigh Es/No ratio for which we optimize + // the decoder metric + nm = _q65_get_bits_per_symbol(pqracode); + R = _q65_get_code_rate(pqracode); + pCodec->decoderEsNoMetric = 1.0f*nm*R*EbNoMetric; + + return 1; +} + +void q65_free(q65_codec_ds *pCodec) +{ + if (!pCodec) + return; + + // free internal buffers + if (pCodec->x!=NULL) + free(pCodec->x); + + if (pCodec->y!=NULL) + free(pCodec->y); + + if (pCodec->qra_v2cmsg!=NULL) + free(pCodec->qra_v2cmsg); + + if (pCodec->qra_c2vmsg!=NULL) + free(pCodec->qra_c2vmsg); + + if (pCodec->ix!=NULL) + free(pCodec->ix); + + if (pCodec->ex!=NULL) + free(pCodec->ex); + + pCodec->pQraCode = NULL; + pCodec->x = NULL; + pCodec->y = NULL; + pCodec->qra_v2cmsg = NULL; + pCodec->qra_c2vmsg = NULL; + pCodec->qra_v2cmsg = NULL; + pCodec->ix = NULL; + pCodec->ex = NULL; + + return; +} + +int q65_encode(const q65_codec_ds *pCodec, int *pOutputCodeword, const int *pInputMsg) +{ + const qracode *pQraCode; + int *px; + int *py; + int nK; + int nN; + + if (!pCodec) + return -1; // which codec? + + pQraCode = pCodec->pQraCode; + px = pCodec->x; + py = pCodec->y; + nK = _q65_get_message_length(pQraCode); + nN = _q65_get_codeword_length(pQraCode); + + // copy the information symbols into the internal buffer + memcpy(px,pInputMsg,nK*sizeof(int)); + + // compute and append the appropriate CRC if required + switch (pQraCode->type) { + case QRATYPE_NORMAL: + break; + case QRATYPE_CRC: + case QRATYPE_CRCPUNCTURED: + px[nK] = _q65_crc6(px,nK); + break; + case QRATYPE_CRCPUNCTURED2: + _q65_crc12(px+nK,px,nK); + break; + default: + return -2; // code type not supported + } + + // encode with the given qra code + qra_encode(pQraCode,py,px); + + // puncture the CRC symbols as required + // and copy the result to the destination buffer + switch (pQraCode->type) { + case QRATYPE_NORMAL: + case QRATYPE_CRC: + // no puncturing + memcpy(pOutputCodeword,py,nN*sizeof(int)); + break; + case QRATYPE_CRCPUNCTURED: + // strip the single CRC symbol from the encoded codeword + memcpy(pOutputCodeword,py,nK*sizeof(int)); // copy the systematic symbols + memcpy(pOutputCodeword+nK,py+nK+1,(nN-nK)*sizeof(int)); // copy the check symbols skipping the CRC symbol + break; + case QRATYPE_CRCPUNCTURED2: + // strip the 2 CRC symbols from the encoded codeword + memcpy(pOutputCodeword,py,nK*sizeof(int)); // copy the systematic symbols + memcpy(pOutputCodeword+nK,py+nK+2,(nN-nK)*sizeof(int)); // copy the check symbols skipping the two CRC symbols + break; + default: + return -2; // code type unsupported + } + + return 1; // ok +} + +int q65_intrinsics(q65_codec_ds *pCodec, float *pIntrinsics, const float *pInputEnergies) +{ + // compute observations intrinsics probabilities + // for the AWGN/Rayleigh channels + + // NOTE: + // A true Rayleigh channel metric would require that the channel gains were known + // for each symbol in the codeword. Such gains cannot be estimated reliably when + // the Es/No ratio is small. Therefore we compute intrinsic probabilities assuming + // that, on average, these channel gains are unitary. + // In general it is even difficult to estimate the Es/No ratio for the AWGN channel + // Therefore we always compute the intrinsic probabilities assuming that the Es/No + // ratio is known and equal to the constant decoderEsNoMetric. This assumption will + // generate the true intrinsic probabilities only when the actual Eb/No ratio is + // equal to this constant. As in all the other cases the probabilities are evaluated + // with a wrong scaling constant we can expect that the decoder performance at different + // Es/No will be worse. Anyway, since the EsNoMetric constant has been chosen so that the + // decoder error rate is about 50%, we obtain almost optimal error rates down to + // any useful Es/No ratio. + + const qracode *pQraCode; + int nN, nBits; + float EsNoMetric; + + if (pCodec==NULL) + return -1; // which codec? + + pQraCode = pCodec->pQraCode; + nN = _q65_get_codeword_length(pQraCode); + nBits = pQraCode->m; + + EsNoMetric = pCodec->decoderEsNoMetric; + qra_mfskbesselmetric(pIntrinsics,pInputEnergies,nBits,nN,EsNoMetric); + + return 1; // success +} + +int q65_esnodb(const q65_codec_ds *pCodec, float *pEsNodB, const int *ydec, const float *pInputEnergies) +{ + // compute average Es/No for the AWGN/Rayleigh channel cases + + int k,j; + float sigplusnoise=0; + float noise=0; + int nN, nM; + const float *pIn = pInputEnergies; + const int *py = ydec; + float EsNodB; + + nN = q65_get_codeword_length(pCodec); + nM = q65_get_alphabet_size(pCodec); + + for (k=0;k4) + return Q65_DECODE_INVPARAMS; // invalid submode + + // As the symbol duration in q65 is longer than in QRA64 the fading tables continue + // to be valid if the B90 parameter is scaled by the actual symbol rate + // Compute index to most appropriate weighting function coefficients + hidx = (int)(logf(B90*TS_Q65/TS_QRA64)/logf(1.09f) - 0.499f); + +// if (hidx<0 || hidx > 64) +// // index of weighting function out of range +// // B90 out of range +// return q65_DECODE_INVPARAMS; + + // Unlike in QRA64 we accept any B90, anyway limiting it to + // the extreme cases (0.9 to 210 Hz approx.) + if (hidx<0) + hidx = 0; + else + if (hidx > 64) + hidx=64; + + // select the appropriate weighting fading coefficients array + if (fadingModel==0) { // gaussian fading model + // point to gaussian energy weighting taps + hlen = glen_tab_gauss[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun) + hptr = gptr_tab_gauss[hidx]; // pointer to the first (L+1)/2 coefficients of w fun + } + else if (fadingModel==1) { + // point to lorentzian energy weighting taps + hlen = glen_tab_lorentz[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun) + hptr = gptr_tab_lorentz[hidx]; // pointer to the first (L+1)/2 coefficients of w fun + } + else + return Q65_DECODE_INVPARAMS; // invalid fading model + + // compute (euristically) the optimal decoder metric accordingly the given spread amount + // We assume that the decoder 50% decoding threshold is: + // Es/No(dB) = Es/No(AWGN)(dB) + 8*log(B90)/log(240)(dB) + // that's to say, at the maximum Doppler spread bandwidth (240 Hz for QRA64) + // there's a ~8 dB Es/No degradation over the AWGN case + fTemp = 8.0f*logf(B90)/logf(240.0f); // assumed Es/No degradation for the given fading bandwidth + EsNoMetric = pCodec->decoderEsNoMetric*powf(10.0f,fTemp/10.0f); + + nM = q65_get_alphabet_size(pCodec); + nN = q65_get_codeword_length(pCodec); + nBinsPerTone = 1<ffNoiseVar = fNoiseVar; + pCodec->ffEsNoMetric = EsNoMetric; + pCodec->nBinsPerTone = nBinsPerTone; + pCodec->nBinsPerSymbol = nBinsPerSymbol; + pCodec->nWeights = hlen; + weight = pCodec->ffWeight; + + // compute the fast fading weights accordingly to the Es/No ratio + // for which we compute the exact intrinsics probabilities + for (k=0;kmaxlogp) // keep track of the max + maxlogp = fTemp; + pCurIx[k]=fTemp; + + pCurBin += nBinsPerTone; // next tone + } + + // exponentiate and accumulate the normalization constant + sumix = 0.0f; + for (k=0;knBinsPerTone; + nBinsPerSymbol = pCodec->nBinsPerSymbol; + nWeights = pCodec->nWeights; + ffNoiseVar = pCodec->ffNoiseVar; + ffEsNoMetric = pCodec->ffEsNoMetric; + nTotWeights = 2*nWeights-1; + + // compute symbols energy (noise included) summing the + // energies pertaining to the decoded symbols in the codeword + + EsPlusWNo = 0.0f; + pCurSym = pInputEnergies + nM; // point to first central bin of first symbol tone + for (n=0;npQraCode; + ix = pCodec->ix; + ex = pCodec->ex; + + nK = _q65_get_message_length(pQraCode); + nN = _q65_get_codeword_length(pQraCode); + nM = pQraCode->M; + nBits = pQraCode->m; + + px = pCodec->x; + py = pCodec->y; + + // Depuncture intrinsics observations as required by the code type + switch (pQraCode->type) { + case QRATYPE_CRCPUNCTURED: + memcpy(ix,pIntrinsics,nK*nM*sizeof(float)); // information symbols + pd_init(PD_ROWADDR(ix,nM,nK),pd_uniform(nBits),nM); // crc + memcpy(ix+(nK+1)*nM,pIntrinsics+nK*nM,(nN-nK)*nM*sizeof(float)); // parity checks + break; + case QRATYPE_CRCPUNCTURED2: + memcpy(ix,pIntrinsics,nK*nM*sizeof(float)); // information symbols + pd_init(PD_ROWADDR(ix,nM,nK),pd_uniform(nBits),nM); // crc + pd_init(PD_ROWADDR(ix,nM,nK+1),pd_uniform(nBits),nM); // crc + memcpy(ix+(nK+2)*nM,pIntrinsics+nK*nM,(nN-nK)*nM*sizeof(float)); // parity checks + break; + case QRATYPE_NORMAL: + case QRATYPE_CRC: + default: + // no puncturing + memcpy(ix,pIntrinsics,nN*nM*sizeof(float)); // as they are + } + + // mask the intrinsics with the available a priori knowledge + if (pAPMask!=NULL) + _q65_mask(pQraCode,ix,pAPMask,pAPSymbols); + + + // Compute the extrinsic symbols probabilities with the message-passing algorithm + // Stop if the extrinsics information does not converges to unity + // within the given number of iterations + rc = qra_extrinsic( pQraCode, + ex, + ix, + 100, + pCodec->qra_v2cmsg, + pCodec->qra_c2vmsg); + + if (rc<0) + // failed to converge to a solution + return Q65_DECODE_FAILED; + + // decode the information symbols (punctured information symbols included) + qra_mapdecode(pQraCode,px,ex,ix); + + // verify CRC match + + switch (pQraCode->type) { + case QRATYPE_CRC: + case QRATYPE_CRCPUNCTURED: + crc6=_q65_crc6(px,nK); // compute crc-6 + if (crc6!=px[nK]) + return Q65_DECODE_CRCMISMATCH; // crc doesn't match + break; + case QRATYPE_CRCPUNCTURED2: + _q65_crc12(crc12, px,nK); // compute crc-12 + if (crc12[0]!=px[nK] || + crc12[1]!=px[nK+1]) + return Q65_DECODE_CRCMISMATCH; // crc doesn't match + break; + case QRATYPE_NORMAL: + default: + // nothing to check + break; + } + + // copy the decoded msg to the user buffer (excluding punctured symbols) + if (pDecodedMsg) + memcpy(pDecodedMsg,px,nK*sizeof(int)); + + if (pDecodedCodeword==NULL) // user is not interested in it + return rc; // return the number of iterations required to decode + + // crc matches therefore we can reconstruct the transmitted codeword + // reencoding the information available in px... + + qra_encode(pQraCode, py, px); + + // ...and strip the punctured symbols from the codeword + switch (pQraCode->type) { + case QRATYPE_CRCPUNCTURED: + memcpy(pDecodedCodeword,py,nK*sizeof(int)); + memcpy(pDecodedCodeword+nK,py+nK+1,(nN-nK)*sizeof(int)); // puncture crc-6 symbol + break; + case QRATYPE_CRCPUNCTURED2: + memcpy(pDecodedCodeword,py,nK*sizeof(int)); + memcpy(pDecodedCodeword+nK,py+nK+2,(nN-nK)*sizeof(int)); // puncture crc-12 symbols + break; + case QRATYPE_CRC: + case QRATYPE_NORMAL: + default: + memcpy(pDecodedCodeword,py,nN*sizeof(int)); // no puncturing + } + + return rc; // return the number of iterations required to decode + +} + + + + +// helper functions ------------------------------------------------------------- + +int _q65_get_message_length(const qracode *pCode) +{ + // return the actual information message length (in symbols) + // excluding any punctured symbol + + int nMsgLength; + + switch (pCode->type) { + case QRATYPE_NORMAL: + nMsgLength = pCode->K; + break; + case QRATYPE_CRC: + case QRATYPE_CRCPUNCTURED: + // one information symbol of the underlying qra code is reserved for CRC + nMsgLength = pCode->K-1; + break; + case QRATYPE_CRCPUNCTURED2: + // two code information symbols are reserved for CRC + nMsgLength = pCode->K-2; + break; + default: + nMsgLength = -1; + } + + return nMsgLength; +} + +int _q65_get_codeword_length(const qracode *pCode) +{ + // return the actual codeword length (in symbols) + // excluding any punctured symbol + + int nCwLength; + + switch (pCode->type) { + case QRATYPE_NORMAL: + case QRATYPE_CRC: + // no puncturing + nCwLength = pCode->N; + break; + case QRATYPE_CRCPUNCTURED: + // the CRC symbol is punctured + nCwLength = pCode->N-1; + break; + case QRATYPE_CRCPUNCTURED2: + // the two CRC symbols are punctured + nCwLength = pCode->N-2; + break; + default: + nCwLength = -1; + } + + return nCwLength; +} + +float _q65_get_code_rate(const qracode *pCode) +{ + return 1.0f*_q65_get_message_length(pCode)/_q65_get_codeword_length(pCode); +} + +int _q65_get_alphabet_size(const qracode *pCode) +{ + return pCode->M; +} +int _q65_get_bits_per_symbol(const qracode *pCode) +{ + return pCode->m; +} +static void _q65_mask(const qracode *pcode, float *ix, const int *mask, const int *x) +{ + // mask intrinsic information ix with available a priori knowledge + + int k,kk, smask; + const int nM=pcode->M; + const int nm=pcode->m; + int nK; + + // Exclude from masking the symbols which have been punctured. + // nK is the length of the mask and x arrays, which do + // not include any punctured symbol + nK = _q65_get_message_length(pcode); + + // for each symbol set to zero the probability + // of the values which are not allowed by + // the a priori information + + for (k=0;k>1) ^ CRC6_GEN_POL; + else + sr = (sr>>1); + t>>=1; + } + } + + return sr; +} + +static void _q65_crc12(int *y, int *x, int sz) +{ + int k,j,t,sr = 0; + for (k=0;k>1) ^ CRC12_GEN_POL; + else + sr = (sr>>1); + t>>=1; + } + } + + y[0] = sr&0x3F; + y[1] = (sr>>6); +} + + diff --git a/lib/qra/q65/q65.h b/lib/qra/q65/q65.h new file mode 100644 index 000000000..04b5d8365 --- /dev/null +++ b/lib/qra/q65/q65.h @@ -0,0 +1,103 @@ +// q65.h +// Q65 modes encoding/decoding functions +// +// (c) 2020 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy +// ------------------------------------------------------------------------------ +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + +#ifndef _q65_h +#define _q65_h + +#include "qracodes.h" + +// Error codes returned by q65_decode(...) +#define Q65_DECODE_INVPARAMS -1 +#define Q65_DECODE_FAILED -2 +#define Q65_DECODE_CRCMISMATCH -3 + +// maximum number of weights for the fast-fading metric evaluation +#define Q65_FASTFADING_MAXWEIGTHS 65 + +typedef struct { + const qracode *pQraCode; // qra code to be used by the codec + float decoderEsNoMetric; // value for which we optimize the decoder metric + int *x; // codec input + int *y; // codec output + float *qra_v2cmsg; // decoder v->c messages + float *qra_c2vmsg; // decoder c->v messages + float *ix; // decoder intrinsic information + float *ex; // decoder extrinsic information + // variables used to compute the intrinsics in the fast-fading case + int nBinsPerTone; + int nBinsPerSymbol; + float ffNoiseVar; + float ffEsNoMetric; + int nWeights; + float ffWeight[Q65_FASTFADING_MAXWEIGTHS]; +} q65_codec_ds; + +int q65_init(q65_codec_ds *pCodec, const qracode *pQraCode); +void q65_free(q65_codec_ds *pCodec); + +int q65_encode(const q65_codec_ds *pCodec, int *pOutputCodeword, const int *pInputMsg); + +int q65_intrinsics(q65_codec_ds *pCodec, float *pIntrinsics, const float *pInputEnergies); + +int q65_intrinsics_fastfading(q65_codec_ds *pCodec, + float *pIntrinsics, // intrinsic symbol probabilities output + const float *pInputEnergies, // received energies input + const int submode, // submode idx (0=A ... 4=E) + const float B90, // spread bandwidth (90% fractional energy) + const int fadingModel); // 0=Gaussian 1=Lorentzian fade model + + +int q65_decode(q65_codec_ds *pCodec, + int* pDecodedCodeword, + int *pDecodedMsg, + const float *pIntrinsics, + const int *pAPMask, + const int *pAPSymbols); + +int q65_esnodb(const q65_codec_ds *pCodec, + float *pEsNodB, + const int *ydec, + const float *pInputEnergies); + +int q65_esnodb_fastfading( + const q65_codec_ds *pCodec, + float *pEsNodB, + const int *ydec, + const float *pInputEnergies); + + +// helper functions +#define q65_get_message_length(pCodec) _q65_get_message_length((pCodec)->pQraCode) +#define q65_get_codeword_length(pCodec) _q65_get_codeword_length((pCodec)->pQraCode) +#define q65_get_code_rate(pCodec) _q65_get_code_rate((pCodec)->pQraCode) +#define q65_get_alphabet_size(pCodec) _q65_get_alphabet_size((pCodec)->pQraCode) +#define q65_get_bits_per_symbol(pCodec) _q65_get_bits_per_symbol((pCodec)->pQraCode) + + +// internally used but made public for the above defines +int _q65_get_message_length(const qracode *pCode); +int _q65_get_codeword_length(const qracode *pCode); +float _q65_get_code_rate(const qracode *pCode); +void _q65_mask(const qracode *pcode, float *ix, const int *mask, const int *x); +int _q65_get_alphabet_size(const qracode *pCode); +int _q65_get_bits_per_symbol(const qracode *pCode); + +#endif // _qra65_h \ No newline at end of file diff --git a/lib/qra/q65/q65.sln b/lib/qra/q65/q65.sln new file mode 100644 index 000000000..1ac03a6ae --- /dev/null +++ b/lib/qra/q65/q65.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "q65", "q65.vcproj", "{933A58F6-199B-4723-ACFE-3013E6DD9D0A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {933A58F6-199B-4723-ACFE-3013E6DD9D0A}.Debug|Win32.ActiveCfg = Debug|Win32 + {933A58F6-199B-4723-ACFE-3013E6DD9D0A}.Debug|Win32.Build.0 = Debug|Win32 + {933A58F6-199B-4723-ACFE-3013E6DD9D0A}.Release|Win32.ActiveCfg = Release|Win32 + {933A58F6-199B-4723-ACFE-3013E6DD9D0A}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/lib/qra/q65/q65.vcproj b/lib/qra/q65/q65.vcproj new file mode 100644 index 000000000..36b3235e1 --- /dev/null +++ b/lib/qra/q65/q65.vcproj @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/qra/q65/q65test.c b/lib/qra/q65/q65test.c new file mode 100644 index 000000000..e90792803 --- /dev/null +++ b/lib/qra/q65/q65test.c @@ -0,0 +1,910 @@ +// q65test.c +// Word Error Rate test example for the Q65 mode +// Multi-threaded simulator version + +// (c) 2020 - Nico Palermo, IV3NWV +// +// +// ------------------------------------------------------------------------------ +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. +// +// Dependencies: +// q65test.c - this file +// normrnd.c/.h - random gaussian number generator +// npfwht.c/.h - Fast Walsh-Hadamard Transforms +// pdmath.c/.h - Elementary math on probability distributions +// qra15_65_64_irr_e23.c/.h - Tables for the QRA(15,65) irregular RA code used by Q65 +// qracodes.c/.h - QRA codes encoding/decoding functions +// fadengauss.c - fading coefficients tables for gaussian shaped fast fading channels +// fadenlorenz.c - fading coefficients tables for lorenzian shaped fast fading channels +// +// ------------------------------------------------------------------------------- +// +// This is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . +// +// ------------------------------------------------------------------------------ + +// OS dependent defines and includes -------------------------------------------- + +#if _WIN32 // note the underscore: without it, it's not msdn official! + // Windows (x64 and x86) + #define _CRT_SECURE_NO_WARNINGS // we don't need warnings for sprintf/fopen function usage + #include // required only for GetTickCount(...) + #include // _beginthread +#endif + +#if defined(__linux__) + +// remove unwanted macros +#define __cdecl + +// implements Windows API +#include + + unsigned int GetTickCount(void) { + struct timespec ts; + unsigned int theTick = 0U; + clock_gettime( CLOCK_REALTIME, &ts ); + theTick = ts.tv_nsec / 1000000; + theTick += ts.tv_sec * 1000; + return theTick; +} + +// Convert Windows millisecond sleep +// +// VOID WINAPI Sleep(_In_ DWORD dwMilliseconds); +// +// to Posix usleep (in microseconds) +// +// int usleep(useconds_t usec); +// +#include +#define Sleep(x) usleep(x*1000) + +#endif + +#if defined(__linux__) || ( defined(__MINGW32__) || defined (__MIGW64__) ) +#include +#endif + +#if __APPLE__ +#endif + +#include +#include + +#include "qracodes.h" // basic qra encoding/decoding functions +#include "normrnd.h" // gaussian numbers generator +#include "pdmath.h" // operations on probability distributions + +#include "qra15_65_64_irr_e23.h" // QRA code used by Q65 +#include "q65.h" + +#define Q65_TS 0.640f // Q65 symbol time interval in seconds +#define Q65_REFBW 2500.0f // reference bandwidth in Hz for SNR estimates + +// ----------------------------------------------------------------------------------- + +#define NTHREADS_MAX 160 // if you have some big enterprise hardware + +// channel types +#define CHANNEL_AWGN 0 +#define CHANNEL_RAYLEIGH 1 +#define CHANNEL_FASTFADING 2 + +// amount of a-priori information provided to the decoder +#define AP_NONE 0 +#define AP_MYCALL 1 +#define AP_HISCALL 2 +#define AP_BOTHCALL 3 +#define AP_FULL 4 +#define AP_LAST AP_FULL + +const char ap_str[AP_LAST+1][16] = { + "None", + "32 bit", + "32 bit", + "62 bit", + "78 bit", +}; +const char fnameout_sfx[AP_LAST+1][64] = { + "-ap00.txt", + "-ap32m.txt", + "-ap32h.txt", + "-ap62.txt", + "-ap78.txt" +}; + +const char fnameout_pfx[3][64] = { + "wer-awgn-", + "wer-rayl-", + "wer-ff-" +}; + +// AP masks are computed assuming that the source message has been packed in 13 symbols s[0]..[s12] +// in a little indian format, that's to say: + +// s[0] = {src5 src4 src3 src2 src1 src0} +// s[1] = {src11 src10 src9 src8 src7 src6} +// ... +// s[12]= {src78 src77 src76 src75 src74 src73} +// +// where srcj is the j-th bit of the source message. +// +// It is also assumed that the source message is as indicated by the protocol specification of wsjt-x +// structured messages. src78 should be always set to a value known by the decoder (and masked as an AP bit) +// With this convention the field i3 of the structured message is mapped to bits src77 src76 src75, +// that's to say to the 3rd,4th and 5th bit of s[12]. +// Therefore, if i3 is known in advance, since src78 is always known, +// the AP mask for s[12] is 0x3C (4 most significant bits of s[12] are known) + +const int ap_masks_q65[AP_LAST+1][13] = { +// AP0 Mask +{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +// Mask first(c28 r1) .... i3 src78 (AP32my MyCall ? ? StdMsg) +{ 0x3F, 0x3F, 0x3F, 0x3F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C}, +// Mask second(c28 r1) .... i3 src78 (AP32his ? HisCall ? StdMsg) +{ 0x00, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x3F, 0x3F, 0x3F, 0x0F, 0x00, 0x00, 0x3C}, +// Mask (c28 r1 c28 r1) ... i3 src78 (AP62 MyCall HisCall ? StdMsg) +{ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x0F, 0x00, 0x00, 0x3C}, +// Mask All (c28 r1 c28 r1 R g15 StdMsg src78) (AP78) +{ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F}, +}; + +int verbose = 0; + +void printword(char *msg, int *x, int size) +{ + int k; + printf("\n%s ",msg); + for (k=0;kchannel_type; + + rc = q65_init(&codec,pdata->pcode); + + if (rc<0) { + printf("error in qra65_init\n"); + goto term_thread; + } + + nK = q65_get_message_length(&codec); + nN = q65_get_codeword_length(&codec); + nM = q65_get_alphabet_size(&codec); + nm = q65_get_bits_per_symbol(&codec); + R = q65_get_code_rate(&codec); + + nSamples = nN*nM; + + x = (int*)malloc(nK*sizeof(int)); + xdec = (int*)malloc(nK*sizeof(int)); + y = (int*)malloc(nN*sizeof(int)); + ydec = (int*)malloc(nN*sizeof(int)); + rsquared = (float*)malloc(nSamples*sizeof(float)); + pIntrinsics = (float*)malloc(nSamples*sizeof(float)); + + // sets the AP mask to be used for this simulation + if (pdata->ap_index==AP_NONE) + apMask = NULL; // we simply avoid masking if ap-index specifies no AP + else + apMask = ap_masks_q65[pdata->ap_index]; + + // Channel simulation variables -------------------- + rp = (float*)malloc(nSamples*sizeof(float)); + rq = (float*)malloc(nSamples*sizeof(float)); + chp = (float*)malloc(nN*sizeof(float)); + chq = (float*)malloc(nN*sizeof(float)); + + EbNo = (float)powf(10,pdata->EbNodB/10); + EsNo = 1.0f*nm*R*EbNo; + Es = EsNo*No; + A = (float)sqrt(Es); + + // Generate a (meaningless) test message + for (k=0;kstop==0) { + + // Channel simulation -------------------------------------------- + // Generate AWGN noise + normrnd_s(rp,nSamples,0,sigma); + normrnd_s(rq,nSamples,0,sigma); + + if (channel_type == CHANNEL_AWGN) + // add symbol amplitudes + for (k=0;k0 && verbose==1) { + float EbNodBestimated; + float SNRdBestimated; + q65_esnodb(&codec, &EsNodBestimated, ydec,rsquared); + EbNodBestimated = EsNodBestimated -10.0f*log10f(R*nm); + SNRdBestimated = EsNodBestimated -10.0f*log10f(Q65_TS*Q65_REFBW); + printf("\nEstimated Eb/No=%5.1fdB SNR2500=%5.1fdB", + EbNodBestimated, + SNRdBestimated); + } + + nt = nt+1; + pdata->nt=nt; + pdata->nerrs=nerrs; + pdata->ncrcwrong = ncrcwrong; + } + +term_thread: + + free(x); + free(xdec); + free(y); + free(ydec); + free(rsquared); + free(pIntrinsics); + + free(rp); + free(rq); + free(chp); + free(chq); + + q65_free(&codec); + + // signal the calling thread we are quitting + pdata->done=1; + #if _WIN32 + _endthread(); + #endif +} + +void wer_test_thread_ff(wer_test_ds *pdata) +{ + // We don't do a realistic simulation of the fading-channel here + // If required give a look to the simulator used in the QRA64 mode. + // For the purpose of testing the formal correctness of the Q65 decoder + // fast-fadind routines here we simulate the channel as a Rayleigh channel + // with no frequency spread but use the q65....-fastfading routines + // to check that they produce correct results also in this case. + + const int submode = 2; // Assume that we are using the Q65C tone spacing + const float B90 = 4.0f; // Configure the Q65 fast-fading decoder for a the given freq. spread + const int fadingModel = 1; // Assume a lorenzian frequency spread + + int nt = 0; // transmitted codewords + int nerrs = 0; // total number of errors + int ncrcwrong = 0; // number of decodes with wrong crc + + q65_codec_ds codec; + + int rc, k; + int nK, nN, nM, nm, nSamples; + int *x, *y, *xdec, *ydec; + const int *apMask; + float R; + float *rsquared, *pIntrinsics; + float EsNodBestimated; + + int nBinsPerTone, nBinsPerSymbol; + + + // for channel simulation + const float No = 1.0f; // noise spectral density + const float sigma = sqrtf(No/2.0f); // std dev of I/Q noise components + const float sigmach = sqrtf(1/2.0f); // std dev of I/Q channel gains (Rayleigh channel) + float EbNo, EsNo, Es, A; + float *rp, *rq, *chp, *chq; + int channel_type = pdata->channel_type; + + rc = q65_init(&codec,pdata->pcode); + + if (rc<0) { + printf("error in q65_init\n"); + goto term_thread; + } + + nK = q65_get_message_length(&codec); + nN = q65_get_codeword_length(&codec); + nM = q65_get_alphabet_size(&codec); + nm = q65_get_bits_per_symbol(&codec); + R = q65_get_code_rate(&codec); + + + nBinsPerTone = 1<ap_index==AP_NONE) + apMask = NULL; // we simply avoid masking if ap-index specifies no AP + else + apMask = ap_masks_q65[pdata->ap_index]; + + + x = (int*)malloc(nK*sizeof(int)); + xdec = (int*)malloc(nK*sizeof(int)); + y = (int*)malloc(nN*sizeof(int)); + ydec = (int*)malloc(nN*sizeof(int)); + rsquared = (float*)malloc(nSamples*sizeof(float)); + pIntrinsics = (float*)malloc(nN*nM*sizeof(float)); + + // Channel simulation variables -------------------- + rp = (float*)malloc(nSamples*sizeof(float)); + rq = (float*)malloc(nSamples*sizeof(float)); + chp = (float*)malloc(nN*sizeof(float)); + chq = (float*)malloc(nN*sizeof(float)); + + EbNo = (float)powf(10,pdata->EbNodB/10); + EsNo = 1.0f*nm*R*EbNo; + Es = EsNo*No; + A = (float)sqrt(Es); + // ------------------------------------------------- + + // generate a test message + for (k=0;kstop==0) { + + // Channel simulation -------------------------------------------- + // generate AWGN noise + normrnd_s(rp,nSamples,0,sigma); + normrnd_s(rq,nSamples,0,sigma); + + + // Generate Rayleigh distributed symbol amplitudes + normrnd_s(chp,nN,0,sigmach); + normrnd_s(chq,nN,0,sigmach); + // Don't simulate a really frequency spreaded signal. + // Just place the tones in the appropriate central bins + // ot the received signal + for (k=0;k0 && verbose==1) { + float EbNodBestimated; + float SNRdBestimated; + // use the fastfading version + q65_esnodb_fastfading(&codec, &EsNodBestimated, ydec,rsquared); + EbNodBestimated = EsNodBestimated -10.0f*log10f(R*nm); + SNRdBestimated = EsNodBestimated -10.0f*log10f(Q65_TS*Q65_REFBW); + printf("\nEstimated Eb/No=%5.1fdB SNR2500=%5.1fdB", + EbNodBestimated, + SNRdBestimated); + } + + nt = nt+1; + pdata->nt=nt; + pdata->nerrs=nerrs; + pdata->ncrcwrong = ncrcwrong; + } + +term_thread: + + free(x); + free(xdec); + free(y); + free(ydec); + free(rsquared); + free(pIntrinsics); + + free(rp); + free(rq); + free(chp); + free(chq); + + q65_free(&codec); + + // signal the calling thread we are quitting + pdata->done=1; + #if _WIN32 + _endthread(); + #endif +} + + +#if defined(__linux__) || ( defined(__MINGW32__) || defined (__MIGW64__) ) + +void *wer_test_pthread_awgnrayl(void *p) +{ + wer_test_thread_awgnrayl((wer_test_ds *)p); + return 0; +} + +void *wer_test_pthread_ff(void *p) +{ + wer_test_thread_ff((wer_test_ds *)p); + return 0; +} + +#endif + + +int wer_test_proc(const qracode *pcode, int nthreads, int chtype, int ap_index, float *EbNodB, int *nerrstgt, int nitems) +{ + int k,j,nt,nerrs,nerrsu,ncrcwrong,nd; + int cini,cend; + char fnameout[128]; + FILE *fout; + wer_test_ds wt[NTHREADS_MAX]; + float pe,peu,avgt; + + if (nthreads>NTHREADS_MAX) { + printf("Error: nthreads should be <=%d\n",NTHREADS_MAX); + return -1; + } + + sprintf(fnameout,"%s%s%s", + fnameout_pfx[chtype], + pcode->name, + fnameout_sfx[ap_index]); + + fout = fopen(fnameout,"w"); + fprintf(fout,"#Code Name: %s\n",pcode->name); + fprintf(fout,"#ChannelType (0=AWGN,1=Rayleigh,2=Fast-Fading)\n#Eb/No (dB)\n#Transmitted Codewords\n#Errors\n#CRC Errors\n#Undetected\n#Avg dec. time (ms)\n#WER\n#UER\n"); + + printf("\nTesting the code %s\nSimulation data will be saved to %s\n", + pcode->name, + fnameout); + fflush (stdout); + + // init fixed thread parameters and preallocate buffers + for (j=0;j=nerrstgt[k]) { + for (j=0;j] [-t] [-c] [-a] [-f[-h]\n"); + printf("Options: \n"); + printf(" -q: code to simulate. 0=qra_15_65_64_irr_e23 (default)\n"); + printf(" -t : number of threads to be used for the simulation [1..24]\n"); + printf(" (default=8)\n"); + printf(" -c : channel_type. 0=AWGN 1=Rayleigh 2=Fast-Fading\n"); + printf(" (default=AWGN)\n"); + printf(" -a : amount of a-priori information provided to decoder. \n"); + printf(" 0= No a-priori (default)\n"); + printf(" 1= 32 bit (Mycall)\n"); + printf(" 2= 32 bit (Hiscall)\n"); + printf(" 3= 62 bit (Bothcalls\n"); + printf(" 4= 78 bit (full AP)\n"); + printf(" -v : verbose (output SNRs of decoded messages\n"); + + printf(" -f : name of the file containing the Eb/No values to be simulated\n"); + printf(" (default=ebnovalues.txt)\n"); + printf(" This file should contain lines in this format:\n"); + printf(" # Eb/No(dB) Target Errors\n"); + printf(" 0.1 5000\n"); + printf(" 0.6 5000\n"); + printf(" 1.1 1000\n"); + printf(" 1.6 1000\n"); + printf(" ...\n"); + printf(" (lines beginning with a # are treated as comments\n\n"); +} + +#define SIM_POINTS_MAX 20 + +int main(int argc, char* argv[]) +{ + + float EbNodB[SIM_POINTS_MAX]; + int nerrstgt[SIM_POINTS_MAX]; + FILE *fin; + + char fnamein[128]= "ebnovalues.txt"; + char buf[128]; + + int nitems = 0; + int code_idx = 0; + int nthreads = 8; + int ch_type = CHANNEL_AWGN; + int ap_index = AP_NONE; + + // parse command line + while(--argc) { + argv++; + if (strncmp(*argv,"-h",2)==0) { + syntax(); + return 0; + } + else + if (strncmp(*argv,"-q",2)==0) { + code_idx = (int)atoi((*argv)+2); + if (code_idx>7) { + printf("Invalid code index\n"); + syntax(); + return -1; + } + } + else + if (strncmp(*argv,"-t",2)==0) { + nthreads = (int)atoi((*argv)+2); + +// printf("nthreads = %d\n",nthreads); + + if (nthreads>NTHREADS_MAX) { + printf("Invalid number of threads\n"); + syntax(); + return -1; + } + } + else + if (strncmp(*argv,"-c",2)==0) { + ch_type = (int)atoi((*argv)+2); + if (ch_type>CHANNEL_FASTFADING) { + printf("Invalid channel type\n"); + syntax(); + return -1; + } + } + else + if (strncmp(*argv,"-a",2)==0) { + ap_index = (int)atoi((*argv)+2); + if (ap_index>AP_LAST) { + printf("Invalid a-priori information index\n"); + syntax(); + return -1; + } + } + else + if (strncmp(*argv,"-f",2)==0) { + strncpy(fnamein,(*argv)+2,127); + } + else + if (strncmp(*argv,"-h",2)==0) { + syntax(); + return -1; + } + else + if (strncmp(*argv,"-v",2)==0) + verbose = TRUE; + else { + printf("Invalid option\n"); + syntax(); + return -1; + } + } + + // parse points to be simulated from the input file + fin = fopen(fnamein,"r"); + if (!fin) { + printf("Can't open file: %s\n",fnamein); + syntax(); + return -1; + } + + while (fgets(buf,128,fin)!=0) + if (*buf=='#' || *buf=='\n' ) + continue; + else + if (nitems==SIM_POINTS_MAX) + break; + else + if (sscanf(buf,"%f %u",&EbNodB[nitems],&nerrstgt[nitems])!=2) { + printf("Invalid input file format\n"); + syntax(); + return -1; + } + else + nitems++; + + fclose(fin); + + if (nitems==0) { + printf("No Eb/No point specified in file %s\n",fnamein); + syntax(); + return -1; + } + + printf("\nQ65 Word Error Rate Simulator\n"); + printf("(c) 2016-2020, Nico Palermo - IV3NWV\n\n"); + + printf("Nthreads = %d\n",nthreads); + switch(ch_type) { + case CHANNEL_AWGN: + printf("Channel = AWGN\n"); + break; + case CHANNEL_RAYLEIGH: + printf("Channel = Rayleigh\n"); + break; + case CHANNEL_FASTFADING: + printf("Channel = Fast Fading\n"); + break; + } + printf("Codename = %s\n",codetotest[code_idx]->name); + printf("A-priori = %s\n",ap_str[ap_index]); + printf("Eb/No input file = %s\n\n",fnamein); + + wer_test_proc(codetotest[code_idx], nthreads, ch_type, ap_index, EbNodB, nerrstgt, nitems); + + printf("\n\n\n"); + return 0; +} + diff --git a/lib/qra/q65/qra15_65_64_irr_e23.c b/lib/qra/q65/qra15_65_64_irr_e23.c new file mode 100644 index 000000000..d56e057fd --- /dev/null +++ b/lib/qra/q65/qra15_65_64_irr_e23.c @@ -0,0 +1,557 @@ +// qra15_65_64_irr_e23.c +// Encoding/Decoding tables for Q-ary RA code (15,65) over GF(64) +// Code Name: qra15_65_64_irr_e23 +// (15,65) RA Code over GF(64) + +// (c) 2020 - Nico Palermo - IV3NWV - Microtelecom Srl, Italy + +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + +#include "qra15_65_64_irr_e23.h" + +// File generated by npiwnarsavehc.m + +#define qra_K 15 // number of information symbols +#define qra_N 65 // codeword length in symbols +#define qra_m 6 // bits/symbol +#define qra_M 64 // Symbol alphabet cardinality +#define qra_a 1 // grouping factor +#define qra_NC 50 // number of check symbols (N-K) + +// Defines used by the message passing decoder -------- + +#define qra_V 65 // number of variables in the code graph (N) +#define qra_C 116 // number of factors in the code graph (N +(N-K)+1) +#define qra_NMSG 216 // number of msgs in the code graph +#define qra_MAXVDEG 5 // maximum variable degree +#define qra_MAXCDEG 3 // maximum factor degree +#define qra_R 0.23077f // code rate (K/N) +#define CODE_NAME "qra15_65_64_irr_e23" // code name + +// table of the systematic symbols indexes in the accumulator chain +static const int qra_acc_input_idx[qra_NC+1] = { + 13, 1, 3, 4, 8, 12, 9, 14, 10, 5, + 0, 7, 1, 11, 8, 9, 12, 6, 3, 10, + 7, 5, 2, 13, 12, 4, 8, 0, 1, 11, + 2, 9, 14, 5, 6, 13, 7, 12, 11, 2, + 9, 0, 10, 4, 7, 14, 8, 11, 3, 6, + 10 +}; + +// table of the systematic symbols weight logarithms over GF(M) +static const int qra_acc_input_wlog[qra_NC+1] = { + 0, 14, 0, 0, 13, 37, 0, 27, 56, 62, + 29, 0, 52, 34, 62, 4, 3, 22, 25, 0, + 22, 0, 20, 10, 0, 43, 53, 60, 0, 0, + 0, 62, 0, 5, 0, 61, 36, 31, 61, 59, + 10, 0, 29, 39, 25, 18, 0, 14, 11, 50, + 17 +}; + +// table of the logarithms of the elements of GF(M) (log(0) never used) +static const int qra_log[qra_M] = { + -1, 0, 1, 6, 2, 12, 7, 26, 3, 32, + 13, 35, 8, 48, 27, 18, 4, 24, 33, 16, + 14, 52, 36, 54, 9, 45, 49, 38, 28, 41, + 19, 56, 5, 62, 25, 11, 34, 31, 17, 47, + 15, 23, 53, 51, 37, 44, 55, 40, 10, 61, + 46, 30, 50, 22, 39, 43, 29, 60, 42, 21, + 20, 59, 57, 58 +}; + +// table of GF(M) elements given their logarithm +static const int qra_exp[qra_M-1] = { + 1, 2, 4, 8, 16, 32, 3, 6, 12, 24, + 48, 35, 5, 10, 20, 40, 19, 38, 15, 30, + 60, 59, 53, 41, 17, 34, 7, 14, 28, 56, + 51, 37, 9, 18, 36, 11, 22, 44, 27, 54, + 47, 29, 58, 55, 45, 25, 50, 39, 13, 26, + 52, 43, 21, 42, 23, 46, 31, 62, 63, 61, + 57, 49, 33 +}; + +// table of the messages weight logarithms over GF(M) +static const int qra_msgw[qra_NMSG] = { + 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 14, 0, 0, 13, + 37, 0, 27, 56, 62, 29, 0, 52, 34, 62, + 4, 3, 22, 25, 0, 22, 0, 20, 10, 0, + 43, 53, 60, 0, 0, 0, 62, 0, 5, 0, + 61, 36, 31, 61, 59, 10, 0, 29, 39, 25, + 18, 0, 14, 11, 50, 17, 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, 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, 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, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; + +// table of the degrees of the variable nodes +static const int qra_vdeg[qra_V] = { + 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, + 5, 5, 5, 4, 4, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3 +}; + +// table of the degrees of the factor nodes +static const int qra_cdeg[qra_C] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 2 +}; + +// table (uncompressed) of the v->c message indexes (-1=unused entry) +static const int qra_v2cmidx[qra_V*qra_MAXVDEG] = { + 0, 75, 92, 106, -1, + 1, 66, 77, 93, -1, + 2, 87, 95, 104, -1, + 3, 67, 83, 113, -1, + 4, 68, 90, 108, -1, + 5, 74, 86, 98, -1, + 6, 82, 99, 114, -1, + 7, 76, 85, 101, 109, + 8, 69, 79, 91, 111, + 9, 71, 80, 96, 105, + 10, 73, 84, 107, 115, + 11, 78, 94, 103, 112, + 12, 70, 81, 89, 102, + 13, 65, 88, 100, -1, + 14, 72, 97, 110, -1, + 15, 116, 117, -1, -1, + 16, 118, 119, -1, -1, + 17, 120, 121, -1, -1, + 18, 122, 123, -1, -1, + 19, 124, 125, -1, -1, + 20, 126, 127, -1, -1, + 21, 128, 129, -1, -1, + 22, 130, 131, -1, -1, + 23, 132, 133, -1, -1, + 24, 134, 135, -1, -1, + 25, 136, 137, -1, -1, + 26, 138, 139, -1, -1, + 27, 140, 141, -1, -1, + 28, 142, 143, -1, -1, + 29, 144, 145, -1, -1, + 30, 146, 147, -1, -1, + 31, 148, 149, -1, -1, + 32, 150, 151, -1, -1, + 33, 152, 153, -1, -1, + 34, 154, 155, -1, -1, + 35, 156, 157, -1, -1, + 36, 158, 159, -1, -1, + 37, 160, 161, -1, -1, + 38, 162, 163, -1, -1, + 39, 164, 165, -1, -1, + 40, 166, 167, -1, -1, + 41, 168, 169, -1, -1, + 42, 170, 171, -1, -1, + 43, 172, 173, -1, -1, + 44, 174, 175, -1, -1, + 45, 176, 177, -1, -1, + 46, 178, 179, -1, -1, + 47, 180, 181, -1, -1, + 48, 182, 183, -1, -1, + 49, 184, 185, -1, -1, + 50, 186, 187, -1, -1, + 51, 188, 189, -1, -1, + 52, 190, 191, -1, -1, + 53, 192, 193, -1, -1, + 54, 194, 195, -1, -1, + 55, 196, 197, -1, -1, + 56, 198, 199, -1, -1, + 57, 200, 201, -1, -1, + 58, 202, 203, -1, -1, + 59, 204, 205, -1, -1, + 60, 206, 207, -1, -1, + 61, 208, 209, -1, -1, + 62, 210, 211, -1, -1, + 63, 212, 213, -1, -1, + 64, 214, 215, -1, -1 +}; + +// table (uncompressed) of the c->v message indexes (-1=unused entry) +static const int qra_c2vmidx[qra_C*qra_MAXCDEG] = { + 0, -1, -1, 1, -1, -1, 2, -1, -1, 3, -1, -1, + 4, -1, -1, 5, -1, -1, 6, -1, -1, 7, -1, -1, + 8, -1, -1, 9, -1, -1, 10, -1, -1, 11, -1, -1, + 12, -1, -1, 13, -1, -1, 14, -1, -1, 15, -1, -1, + 16, -1, -1, 17, -1, -1, 18, -1, -1, 19, -1, -1, + 20, -1, -1, 21, -1, -1, 22, -1, -1, 23, -1, -1, + 24, -1, -1, 25, -1, -1, 26, -1, -1, 27, -1, -1, + 28, -1, -1, 29, -1, -1, 30, -1, -1, 31, -1, -1, + 32, -1, -1, 33, -1, -1, 34, -1, -1, 35, -1, -1, + 36, -1, -1, 37, -1, -1, 38, -1, -1, 39, -1, -1, + 40, -1, -1, 41, -1, -1, 42, -1, -1, 43, -1, -1, + 44, -1, -1, 45, -1, -1, 46, -1, -1, 47, -1, -1, + 48, -1, -1, 49, -1, -1, 50, -1, -1, 51, -1, -1, + 52, -1, -1, 53, -1, -1, 54, -1, -1, 55, -1, -1, + 56, -1, -1, 57, -1, -1, 58, -1, -1, 59, -1, -1, + 60, -1, -1, 61, -1, -1, 62, -1, -1, 63, -1, -1, + 64, -1, -1, 65, 116, -1, 66, 117, 118, 67, 119, 120, + 68, 121, 122, 69, 123, 124, 70, 125, 126, 71, 127, 128, + 72, 129, 130, 73, 131, 132, 74, 133, 134, 75, 135, 136, + 76, 137, 138, 77, 139, 140, 78, 141, 142, 79, 143, 144, + 80, 145, 146, 81, 147, 148, 82, 149, 150, 83, 151, 152, + 84, 153, 154, 85, 155, 156, 86, 157, 158, 87, 159, 160, + 88, 161, 162, 89, 163, 164, 90, 165, 166, 91, 167, 168, + 92, 169, 170, 93, 171, 172, 94, 173, 174, 95, 175, 176, + 96, 177, 178, 97, 179, 180, 98, 181, 182, 99, 183, 184, +100, 185, 186, 101, 187, 188, 102, 189, 190, 103, 191, 192, +104, 193, 194, 105, 195, 196, 106, 197, 198, 107, 199, 200, +108, 201, 202, 109, 203, 204, 110, 205, 206, 111, 207, 208, +112, 209, 210, 113, 211, 212, 114, 213, 214, 115, 215, -1 +}; + +// permutation matrix to compute Prob(x*alfa^logw) +static const int qra_pmat[qra_M*qra_M] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 33, 1, 32, 2, 35, 3, 34, 4, 37, 5, 36, 6, 39, 7, 38, + 8, 41, 9, 40, 10, 43, 11, 42, 12, 45, 13, 44, 14, 47, 15, 46, + 16, 49, 17, 48, 18, 51, 19, 50, 20, 53, 21, 52, 22, 55, 23, 54, + 24, 57, 25, 56, 26, 59, 27, 58, 28, 61, 29, 60, 30, 63, 31, 62, + 0, 49, 33, 16, 1, 48, 32, 17, 2, 51, 35, 18, 3, 50, 34, 19, + 4, 53, 37, 20, 5, 52, 36, 21, 6, 55, 39, 22, 7, 54, 38, 23, + 8, 57, 41, 24, 9, 56, 40, 25, 10, 59, 43, 26, 11, 58, 42, 27, + 12, 61, 45, 28, 13, 60, 44, 29, 14, 63, 47, 30, 15, 62, 46, 31, + 0, 57, 49, 8, 33, 24, 16, 41, 1, 56, 48, 9, 32, 25, 17, 40, + 2, 59, 51, 10, 35, 26, 18, 43, 3, 58, 50, 11, 34, 27, 19, 42, + 4, 61, 53, 12, 37, 28, 20, 45, 5, 60, 52, 13, 36, 29, 21, 44, + 6, 63, 55, 14, 39, 30, 22, 47, 7, 62, 54, 15, 38, 31, 23, 46, + 0, 61, 57, 4, 49, 12, 8, 53, 33, 28, 24, 37, 16, 45, 41, 20, + 1, 60, 56, 5, 48, 13, 9, 52, 32, 29, 25, 36, 17, 44, 40, 21, + 2, 63, 59, 6, 51, 14, 10, 55, 35, 30, 26, 39, 18, 47, 43, 22, + 3, 62, 58, 7, 50, 15, 11, 54, 34, 31, 27, 38, 19, 46, 42, 23, + 0, 63, 61, 2, 57, 6, 4, 59, 49, 14, 12, 51, 8, 55, 53, 10, + 33, 30, 28, 35, 24, 39, 37, 26, 16, 47, 45, 18, 41, 22, 20, 43, + 1, 62, 60, 3, 56, 7, 5, 58, 48, 15, 13, 50, 9, 54, 52, 11, + 32, 31, 29, 34, 25, 38, 36, 27, 17, 46, 44, 19, 40, 23, 21, 42, + 0, 62, 63, 1, 61, 3, 2, 60, 57, 7, 6, 56, 4, 58, 59, 5, + 49, 15, 14, 48, 12, 50, 51, 13, 8, 54, 55, 9, 53, 11, 10, 52, + 33, 31, 30, 32, 28, 34, 35, 29, 24, 38, 39, 25, 37, 27, 26, 36, + 16, 46, 47, 17, 45, 19, 18, 44, 41, 23, 22, 40, 20, 42, 43, 21, + 0, 31, 62, 33, 63, 32, 1, 30, 61, 34, 3, 28, 2, 29, 60, 35, + 57, 38, 7, 24, 6, 25, 56, 39, 4, 27, 58, 37, 59, 36, 5, 26, + 49, 46, 15, 16, 14, 17, 48, 47, 12, 19, 50, 45, 51, 44, 13, 18, + 8, 23, 54, 41, 55, 40, 9, 22, 53, 42, 11, 20, 10, 21, 52, 43, + 0, 46, 31, 49, 62, 16, 33, 15, 63, 17, 32, 14, 1, 47, 30, 48, + 61, 19, 34, 12, 3, 45, 28, 50, 2, 44, 29, 51, 60, 18, 35, 13, + 57, 23, 38, 8, 7, 41, 24, 54, 6, 40, 25, 55, 56, 22, 39, 9, + 4, 42, 27, 53, 58, 20, 37, 11, 59, 21, 36, 10, 5, 43, 26, 52, + 0, 23, 46, 57, 31, 8, 49, 38, 62, 41, 16, 7, 33, 54, 15, 24, + 63, 40, 17, 6, 32, 55, 14, 25, 1, 22, 47, 56, 30, 9, 48, 39, + 61, 42, 19, 4, 34, 53, 12, 27, 3, 20, 45, 58, 28, 11, 50, 37, + 2, 21, 44, 59, 29, 10, 51, 36, 60, 43, 18, 5, 35, 52, 13, 26, + 0, 42, 23, 61, 46, 4, 57, 19, 31, 53, 8, 34, 49, 27, 38, 12, + 62, 20, 41, 3, 16, 58, 7, 45, 33, 11, 54, 28, 15, 37, 24, 50, + 63, 21, 40, 2, 17, 59, 6, 44, 32, 10, 55, 29, 14, 36, 25, 51, + 1, 43, 22, 60, 47, 5, 56, 18, 30, 52, 9, 35, 48, 26, 39, 13, + 0, 21, 42, 63, 23, 2, 61, 40, 46, 59, 4, 17, 57, 44, 19, 6, + 31, 10, 53, 32, 8, 29, 34, 55, 49, 36, 27, 14, 38, 51, 12, 25, + 62, 43, 20, 1, 41, 60, 3, 22, 16, 5, 58, 47, 7, 18, 45, 56, + 33, 52, 11, 30, 54, 35, 28, 9, 15, 26, 37, 48, 24, 13, 50, 39, + 0, 43, 21, 62, 42, 1, 63, 20, 23, 60, 2, 41, 61, 22, 40, 3, + 46, 5, 59, 16, 4, 47, 17, 58, 57, 18, 44, 7, 19, 56, 6, 45, + 31, 52, 10, 33, 53, 30, 32, 11, 8, 35, 29, 54, 34, 9, 55, 28, + 49, 26, 36, 15, 27, 48, 14, 37, 38, 13, 51, 24, 12, 39, 25, 50, + 0, 52, 43, 31, 21, 33, 62, 10, 42, 30, 1, 53, 63, 11, 20, 32, + 23, 35, 60, 8, 2, 54, 41, 29, 61, 9, 22, 34, 40, 28, 3, 55, + 46, 26, 5, 49, 59, 15, 16, 36, 4, 48, 47, 27, 17, 37, 58, 14, + 57, 13, 18, 38, 44, 24, 7, 51, 19, 39, 56, 12, 6, 50, 45, 25, + 0, 26, 52, 46, 43, 49, 31, 5, 21, 15, 33, 59, 62, 36, 10, 16, + 42, 48, 30, 4, 1, 27, 53, 47, 63, 37, 11, 17, 20, 14, 32, 58, + 23, 13, 35, 57, 60, 38, 8, 18, 2, 24, 54, 44, 41, 51, 29, 7, + 61, 39, 9, 19, 22, 12, 34, 56, 40, 50, 28, 6, 3, 25, 55, 45, + 0, 13, 26, 23, 52, 57, 46, 35, 43, 38, 49, 60, 31, 18, 5, 8, + 21, 24, 15, 2, 33, 44, 59, 54, 62, 51, 36, 41, 10, 7, 16, 29, + 42, 39, 48, 61, 30, 19, 4, 9, 1, 12, 27, 22, 53, 56, 47, 34, + 63, 50, 37, 40, 11, 6, 17, 28, 20, 25, 14, 3, 32, 45, 58, 55, + 0, 39, 13, 42, 26, 61, 23, 48, 52, 19, 57, 30, 46, 9, 35, 4, + 43, 12, 38, 1, 49, 22, 60, 27, 31, 56, 18, 53, 5, 34, 8, 47, + 21, 50, 24, 63, 15, 40, 2, 37, 33, 6, 44, 11, 59, 28, 54, 17, + 62, 25, 51, 20, 36, 3, 41, 14, 10, 45, 7, 32, 16, 55, 29, 58, + 0, 50, 39, 21, 13, 63, 42, 24, 26, 40, 61, 15, 23, 37, 48, 2, + 52, 6, 19, 33, 57, 11, 30, 44, 46, 28, 9, 59, 35, 17, 4, 54, + 43, 25, 12, 62, 38, 20, 1, 51, 49, 3, 22, 36, 60, 14, 27, 41, + 31, 45, 56, 10, 18, 32, 53, 7, 5, 55, 34, 16, 8, 58, 47, 29, + 0, 25, 50, 43, 39, 62, 21, 12, 13, 20, 63, 38, 42, 51, 24, 1, + 26, 3, 40, 49, 61, 36, 15, 22, 23, 14, 37, 60, 48, 41, 2, 27, + 52, 45, 6, 31, 19, 10, 33, 56, 57, 32, 11, 18, 30, 7, 44, 53, + 46, 55, 28, 5, 9, 16, 59, 34, 35, 58, 17, 8, 4, 29, 54, 47, + 0, 45, 25, 52, 50, 31, 43, 6, 39, 10, 62, 19, 21, 56, 12, 33, + 13, 32, 20, 57, 63, 18, 38, 11, 42, 7, 51, 30, 24, 53, 1, 44, + 26, 55, 3, 46, 40, 5, 49, 28, 61, 16, 36, 9, 15, 34, 22, 59, + 23, 58, 14, 35, 37, 8, 60, 17, 48, 29, 41, 4, 2, 47, 27, 54, + 0, 55, 45, 26, 25, 46, 52, 3, 50, 5, 31, 40, 43, 28, 6, 49, + 39, 16, 10, 61, 62, 9, 19, 36, 21, 34, 56, 15, 12, 59, 33, 22, + 13, 58, 32, 23, 20, 35, 57, 14, 63, 8, 18, 37, 38, 17, 11, 60, + 42, 29, 7, 48, 51, 4, 30, 41, 24, 47, 53, 2, 1, 54, 44, 27, + 0, 58, 55, 13, 45, 23, 26, 32, 25, 35, 46, 20, 52, 14, 3, 57, + 50, 8, 5, 63, 31, 37, 40, 18, 43, 17, 28, 38, 6, 60, 49, 11, + 39, 29, 16, 42, 10, 48, 61, 7, 62, 4, 9, 51, 19, 41, 36, 30, + 21, 47, 34, 24, 56, 2, 15, 53, 12, 54, 59, 1, 33, 27, 22, 44, + 0, 29, 58, 39, 55, 42, 13, 16, 45, 48, 23, 10, 26, 7, 32, 61, + 25, 4, 35, 62, 46, 51, 20, 9, 52, 41, 14, 19, 3, 30, 57, 36, + 50, 47, 8, 21, 5, 24, 63, 34, 31, 2, 37, 56, 40, 53, 18, 15, + 43, 54, 17, 12, 28, 1, 38, 59, 6, 27, 60, 33, 49, 44, 11, 22, + 0, 47, 29, 50, 58, 21, 39, 8, 55, 24, 42, 5, 13, 34, 16, 63, + 45, 2, 48, 31, 23, 56, 10, 37, 26, 53, 7, 40, 32, 15, 61, 18, + 25, 54, 4, 43, 35, 12, 62, 17, 46, 1, 51, 28, 20, 59, 9, 38, + 52, 27, 41, 6, 14, 33, 19, 60, 3, 44, 30, 49, 57, 22, 36, 11, + 0, 54, 47, 25, 29, 43, 50, 4, 58, 12, 21, 35, 39, 17, 8, 62, + 55, 1, 24, 46, 42, 28, 5, 51, 13, 59, 34, 20, 16, 38, 63, 9, + 45, 27, 2, 52, 48, 6, 31, 41, 23, 33, 56, 14, 10, 60, 37, 19, + 26, 44, 53, 3, 7, 49, 40, 30, 32, 22, 15, 57, 61, 11, 18, 36, + 0, 27, 54, 45, 47, 52, 25, 2, 29, 6, 43, 48, 50, 41, 4, 31, + 58, 33, 12, 23, 21, 14, 35, 56, 39, 60, 17, 10, 8, 19, 62, 37, + 55, 44, 1, 26, 24, 3, 46, 53, 42, 49, 28, 7, 5, 30, 51, 40, + 13, 22, 59, 32, 34, 57, 20, 15, 16, 11, 38, 61, 63, 36, 9, 18, + 0, 44, 27, 55, 54, 26, 45, 1, 47, 3, 52, 24, 25, 53, 2, 46, + 29, 49, 6, 42, 43, 7, 48, 28, 50, 30, 41, 5, 4, 40, 31, 51, + 58, 22, 33, 13, 12, 32, 23, 59, 21, 57, 14, 34, 35, 15, 56, 20, + 39, 11, 60, 16, 17, 61, 10, 38, 8, 36, 19, 63, 62, 18, 37, 9, + 0, 22, 44, 58, 27, 13, 55, 33, 54, 32, 26, 12, 45, 59, 1, 23, + 47, 57, 3, 21, 52, 34, 24, 14, 25, 15, 53, 35, 2, 20, 46, 56, + 29, 11, 49, 39, 6, 16, 42, 60, 43, 61, 7, 17, 48, 38, 28, 10, + 50, 36, 30, 8, 41, 63, 5, 19, 4, 18, 40, 62, 31, 9, 51, 37, + 0, 11, 22, 29, 44, 39, 58, 49, 27, 16, 13, 6, 55, 60, 33, 42, + 54, 61, 32, 43, 26, 17, 12, 7, 45, 38, 59, 48, 1, 10, 23, 28, + 47, 36, 57, 50, 3, 8, 21, 30, 52, 63, 34, 41, 24, 19, 14, 5, + 25, 18, 15, 4, 53, 62, 35, 40, 2, 9, 20, 31, 46, 37, 56, 51, + 0, 36, 11, 47, 22, 50, 29, 57, 44, 8, 39, 3, 58, 30, 49, 21, + 27, 63, 16, 52, 13, 41, 6, 34, 55, 19, 60, 24, 33, 5, 42, 14, + 54, 18, 61, 25, 32, 4, 43, 15, 26, 62, 17, 53, 12, 40, 7, 35, + 45, 9, 38, 2, 59, 31, 48, 20, 1, 37, 10, 46, 23, 51, 28, 56, + 0, 18, 36, 54, 11, 25, 47, 61, 22, 4, 50, 32, 29, 15, 57, 43, + 44, 62, 8, 26, 39, 53, 3, 17, 58, 40, 30, 12, 49, 35, 21, 7, + 27, 9, 63, 45, 16, 2, 52, 38, 13, 31, 41, 59, 6, 20, 34, 48, + 55, 37, 19, 1, 60, 46, 24, 10, 33, 51, 5, 23, 42, 56, 14, 28, + 0, 9, 18, 27, 36, 45, 54, 63, 11, 2, 25, 16, 47, 38, 61, 52, + 22, 31, 4, 13, 50, 59, 32, 41, 29, 20, 15, 6, 57, 48, 43, 34, + 44, 37, 62, 55, 8, 1, 26, 19, 39, 46, 53, 60, 3, 10, 17, 24, + 58, 51, 40, 33, 30, 23, 12, 5, 49, 56, 35, 42, 21, 28, 7, 14, + 0, 37, 9, 44, 18, 55, 27, 62, 36, 1, 45, 8, 54, 19, 63, 26, + 11, 46, 2, 39, 25, 60, 16, 53, 47, 10, 38, 3, 61, 24, 52, 17, + 22, 51, 31, 58, 4, 33, 13, 40, 50, 23, 59, 30, 32, 5, 41, 12, + 29, 56, 20, 49, 15, 42, 6, 35, 57, 28, 48, 21, 43, 14, 34, 7, + 0, 51, 37, 22, 9, 58, 44, 31, 18, 33, 55, 4, 27, 40, 62, 13, + 36, 23, 1, 50, 45, 30, 8, 59, 54, 5, 19, 32, 63, 12, 26, 41, + 11, 56, 46, 29, 2, 49, 39, 20, 25, 42, 60, 15, 16, 35, 53, 6, + 47, 28, 10, 57, 38, 21, 3, 48, 61, 14, 24, 43, 52, 7, 17, 34, + 0, 56, 51, 11, 37, 29, 22, 46, 9, 49, 58, 2, 44, 20, 31, 39, + 18, 42, 33, 25, 55, 15, 4, 60, 27, 35, 40, 16, 62, 6, 13, 53, + 36, 28, 23, 47, 1, 57, 50, 10, 45, 21, 30, 38, 8, 48, 59, 3, + 54, 14, 5, 61, 19, 43, 32, 24, 63, 7, 12, 52, 26, 34, 41, 17, + 0, 28, 56, 36, 51, 47, 11, 23, 37, 57, 29, 1, 22, 10, 46, 50, + 9, 21, 49, 45, 58, 38, 2, 30, 44, 48, 20, 8, 31, 3, 39, 59, + 18, 14, 42, 54, 33, 61, 25, 5, 55, 43, 15, 19, 4, 24, 60, 32, + 27, 7, 35, 63, 40, 52, 16, 12, 62, 34, 6, 26, 13, 17, 53, 41, + 0, 14, 28, 18, 56, 54, 36, 42, 51, 61, 47, 33, 11, 5, 23, 25, + 37, 43, 57, 55, 29, 19, 1, 15, 22, 24, 10, 4, 46, 32, 50, 60, + 9, 7, 21, 27, 49, 63, 45, 35, 58, 52, 38, 40, 2, 12, 30, 16, + 44, 34, 48, 62, 20, 26, 8, 6, 31, 17, 3, 13, 39, 41, 59, 53, + 0, 7, 14, 9, 28, 27, 18, 21, 56, 63, 54, 49, 36, 35, 42, 45, + 51, 52, 61, 58, 47, 40, 33, 38, 11, 12, 5, 2, 23, 16, 25, 30, + 37, 34, 43, 44, 57, 62, 55, 48, 29, 26, 19, 20, 1, 6, 15, 8, + 22, 17, 24, 31, 10, 13, 4, 3, 46, 41, 32, 39, 50, 53, 60, 59, + 0, 34, 7, 37, 14, 44, 9, 43, 28, 62, 27, 57, 18, 48, 21, 55, + 56, 26, 63, 29, 54, 20, 49, 19, 36, 6, 35, 1, 42, 8, 45, 15, + 51, 17, 52, 22, 61, 31, 58, 24, 47, 13, 40, 10, 33, 3, 38, 4, + 11, 41, 12, 46, 5, 39, 2, 32, 23, 53, 16, 50, 25, 59, 30, 60, + 0, 17, 34, 51, 7, 22, 37, 52, 14, 31, 44, 61, 9, 24, 43, 58, + 28, 13, 62, 47, 27, 10, 57, 40, 18, 3, 48, 33, 21, 4, 55, 38, + 56, 41, 26, 11, 63, 46, 29, 12, 54, 39, 20, 5, 49, 32, 19, 2, + 36, 53, 6, 23, 35, 50, 1, 16, 42, 59, 8, 25, 45, 60, 15, 30, + 0, 41, 17, 56, 34, 11, 51, 26, 7, 46, 22, 63, 37, 12, 52, 29, + 14, 39, 31, 54, 44, 5, 61, 20, 9, 32, 24, 49, 43, 2, 58, 19, + 28, 53, 13, 36, 62, 23, 47, 6, 27, 50, 10, 35, 57, 16, 40, 1, + 18, 59, 3, 42, 48, 25, 33, 8, 21, 60, 4, 45, 55, 30, 38, 15, + 0, 53, 41, 28, 17, 36, 56, 13, 34, 23, 11, 62, 51, 6, 26, 47, + 7, 50, 46, 27, 22, 35, 63, 10, 37, 16, 12, 57, 52, 1, 29, 40, + 14, 59, 39, 18, 31, 42, 54, 3, 44, 25, 5, 48, 61, 8, 20, 33, + 9, 60, 32, 21, 24, 45, 49, 4, 43, 30, 2, 55, 58, 15, 19, 38, + 0, 59, 53, 14, 41, 18, 28, 39, 17, 42, 36, 31, 56, 3, 13, 54, + 34, 25, 23, 44, 11, 48, 62, 5, 51, 8, 6, 61, 26, 33, 47, 20, + 7, 60, 50, 9, 46, 21, 27, 32, 22, 45, 35, 24, 63, 4, 10, 49, + 37, 30, 16, 43, 12, 55, 57, 2, 52, 15, 1, 58, 29, 38, 40, 19, + 0, 60, 59, 7, 53, 9, 14, 50, 41, 21, 18, 46, 28, 32, 39, 27, + 17, 45, 42, 22, 36, 24, 31, 35, 56, 4, 3, 63, 13, 49, 54, 10, + 34, 30, 25, 37, 23, 43, 44, 16, 11, 55, 48, 12, 62, 2, 5, 57, + 51, 15, 8, 52, 6, 58, 61, 1, 26, 38, 33, 29, 47, 19, 20, 40, + 0, 30, 60, 34, 59, 37, 7, 25, 53, 43, 9, 23, 14, 16, 50, 44, + 41, 55, 21, 11, 18, 12, 46, 48, 28, 2, 32, 62, 39, 57, 27, 5, + 17, 15, 45, 51, 42, 52, 22, 8, 36, 58, 24, 6, 31, 1, 35, 61, + 56, 38, 4, 26, 3, 29, 63, 33, 13, 19, 49, 47, 54, 40, 10, 20, + 0, 15, 30, 17, 60, 51, 34, 45, 59, 52, 37, 42, 7, 8, 25, 22, + 53, 58, 43, 36, 9, 6, 23, 24, 14, 1, 16, 31, 50, 61, 44, 35, + 41, 38, 55, 56, 21, 26, 11, 4, 18, 29, 12, 3, 46, 33, 48, 63, + 28, 19, 2, 13, 32, 47, 62, 49, 39, 40, 57, 54, 27, 20, 5, 10, + 0, 38, 15, 41, 30, 56, 17, 55, 60, 26, 51, 21, 34, 4, 45, 11, + 59, 29, 52, 18, 37, 3, 42, 12, 7, 33, 8, 46, 25, 63, 22, 48, + 53, 19, 58, 28, 43, 13, 36, 2, 9, 47, 6, 32, 23, 49, 24, 62, + 14, 40, 1, 39, 16, 54, 31, 57, 50, 20, 61, 27, 44, 10, 35, 5, + 0, 19, 38, 53, 15, 28, 41, 58, 30, 13, 56, 43, 17, 2, 55, 36, + 60, 47, 26, 9, 51, 32, 21, 6, 34, 49, 4, 23, 45, 62, 11, 24, + 59, 40, 29, 14, 52, 39, 18, 1, 37, 54, 3, 16, 42, 57, 12, 31, + 7, 20, 33, 50, 8, 27, 46, 61, 25, 10, 63, 44, 22, 5, 48, 35, + 0, 40, 19, 59, 38, 14, 53, 29, 15, 39, 28, 52, 41, 1, 58, 18, + 30, 54, 13, 37, 56, 16, 43, 3, 17, 57, 2, 42, 55, 31, 36, 12, + 60, 20, 47, 7, 26, 50, 9, 33, 51, 27, 32, 8, 21, 61, 6, 46, + 34, 10, 49, 25, 4, 44, 23, 63, 45, 5, 62, 22, 11, 35, 24, 48, + 0, 20, 40, 60, 19, 7, 59, 47, 38, 50, 14, 26, 53, 33, 29, 9, + 15, 27, 39, 51, 28, 8, 52, 32, 41, 61, 1, 21, 58, 46, 18, 6, + 30, 10, 54, 34, 13, 25, 37, 49, 56, 44, 16, 4, 43, 63, 3, 23, + 17, 5, 57, 45, 2, 22, 42, 62, 55, 35, 31, 11, 36, 48, 12, 24, + 0, 10, 20, 30, 40, 34, 60, 54, 19, 25, 7, 13, 59, 49, 47, 37, + 38, 44, 50, 56, 14, 4, 26, 16, 53, 63, 33, 43, 29, 23, 9, 3, + 15, 5, 27, 17, 39, 45, 51, 57, 28, 22, 8, 2, 52, 62, 32, 42, + 41, 35, 61, 55, 1, 11, 21, 31, 58, 48, 46, 36, 18, 24, 6, 12, + 0, 5, 10, 15, 20, 17, 30, 27, 40, 45, 34, 39, 60, 57, 54, 51, + 19, 22, 25, 28, 7, 2, 13, 8, 59, 62, 49, 52, 47, 42, 37, 32, + 38, 35, 44, 41, 50, 55, 56, 61, 14, 11, 4, 1, 26, 31, 16, 21, + 53, 48, 63, 58, 33, 36, 43, 46, 29, 24, 23, 18, 9, 12, 3, 6, + 0, 35, 5, 38, 10, 41, 15, 44, 20, 55, 17, 50, 30, 61, 27, 56, + 40, 11, 45, 14, 34, 1, 39, 4, 60, 31, 57, 26, 54, 21, 51, 16, + 19, 48, 22, 53, 25, 58, 28, 63, 7, 36, 2, 33, 13, 46, 8, 43, + 59, 24, 62, 29, 49, 18, 52, 23, 47, 12, 42, 9, 37, 6, 32, 3, + 0, 48, 35, 19, 5, 53, 38, 22, 10, 58, 41, 25, 15, 63, 44, 28, + 20, 36, 55, 7, 17, 33, 50, 2, 30, 46, 61, 13, 27, 43, 56, 8, + 40, 24, 11, 59, 45, 29, 14, 62, 34, 18, 1, 49, 39, 23, 4, 52, + 60, 12, 31, 47, 57, 9, 26, 42, 54, 6, 21, 37, 51, 3, 16, 32, + 0, 24, 48, 40, 35, 59, 19, 11, 5, 29, 53, 45, 38, 62, 22, 14, + 10, 18, 58, 34, 41, 49, 25, 1, 15, 23, 63, 39, 44, 52, 28, 4, + 20, 12, 36, 60, 55, 47, 7, 31, 17, 9, 33, 57, 50, 42, 2, 26, + 30, 6, 46, 54, 61, 37, 13, 21, 27, 3, 43, 51, 56, 32, 8, 16, + 0, 12, 24, 20, 48, 60, 40, 36, 35, 47, 59, 55, 19, 31, 11, 7, + 5, 9, 29, 17, 53, 57, 45, 33, 38, 42, 62, 50, 22, 26, 14, 2, + 10, 6, 18, 30, 58, 54, 34, 46, 41, 37, 49, 61, 25, 21, 1, 13, + 15, 3, 23, 27, 63, 51, 39, 43, 44, 32, 52, 56, 28, 16, 4, 8, + 0, 6, 12, 10, 24, 30, 20, 18, 48, 54, 60, 58, 40, 46, 36, 34, + 35, 37, 47, 41, 59, 61, 55, 49, 19, 21, 31, 25, 11, 13, 7, 1, + 5, 3, 9, 15, 29, 27, 17, 23, 53, 51, 57, 63, 45, 43, 33, 39, + 38, 32, 42, 44, 62, 56, 50, 52, 22, 16, 26, 28, 14, 8, 2, 4, + 0, 3, 6, 5, 12, 15, 10, 9, 24, 27, 30, 29, 20, 23, 18, 17, + 48, 51, 54, 53, 60, 63, 58, 57, 40, 43, 46, 45, 36, 39, 34, 33, + 35, 32, 37, 38, 47, 44, 41, 42, 59, 56, 61, 62, 55, 52, 49, 50, + 19, 16, 21, 22, 31, 28, 25, 26, 11, 8, 13, 14, 7, 4, 1, 2, + 0, 32, 3, 35, 6, 38, 5, 37, 12, 44, 15, 47, 10, 42, 9, 41, + 24, 56, 27, 59, 30, 62, 29, 61, 20, 52, 23, 55, 18, 50, 17, 49, + 48, 16, 51, 19, 54, 22, 53, 21, 60, 28, 63, 31, 58, 26, 57, 25, + 40, 8, 43, 11, 46, 14, 45, 13, 36, 4, 39, 7, 34, 2, 33, 1, + 0, 16, 32, 48, 3, 19, 35, 51, 6, 22, 38, 54, 5, 21, 37, 53, + 12, 28, 44, 60, 15, 31, 47, 63, 10, 26, 42, 58, 9, 25, 41, 57, + 24, 8, 56, 40, 27, 11, 59, 43, 30, 14, 62, 46, 29, 13, 61, 45, + 20, 4, 52, 36, 23, 7, 55, 39, 18, 2, 50, 34, 17, 1, 49, 33, + 0, 8, 16, 24, 32, 40, 48, 56, 3, 11, 19, 27, 35, 43, 51, 59, + 6, 14, 22, 30, 38, 46, 54, 62, 5, 13, 21, 29, 37, 45, 53, 61, + 12, 4, 28, 20, 44, 36, 60, 52, 15, 7, 31, 23, 47, 39, 63, 55, + 10, 2, 26, 18, 42, 34, 58, 50, 9, 1, 25, 17, 41, 33, 57, 49, + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, + 3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63, + 6, 2, 14, 10, 22, 18, 30, 26, 38, 34, 46, 42, 54, 50, 62, 58, + 5, 1, 13, 9, 21, 17, 29, 25, 37, 33, 45, 41, 53, 49, 61, 57, + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, + 3, 1, 7, 5, 11, 9, 15, 13, 19, 17, 23, 21, 27, 25, 31, 29, + 35, 33, 39, 37, 43, 41, 47, 45, 51, 49, 55, 53, 59, 57, 63, 61 +}; + +// SO array +static const int SO[qra_N-qra_K+1] = { + 14, 2, 4, 5, 9, 13, 10, 15, 11, 6, 1, 8, 2, 12, 9, 10, + 13, 7, 4, 11, 8, 6, 3, 14, 13, 5, 9, 1, 2, 12, 3, 10, + 15, 6, 7, 14, 8, 13, 12, 3, 10, 1, 11, 5, 8, 15, 9, 12, + 4, 7, 11 +}; + +// LOGWO array +static const int LOGWO[qra_N-qra_K+1] = { + 0, 14, 0, 0, 13, 37, 0, 27, 56, 62, 29, 0, 52, 34, 62, 4, + 3, 22, 25, 0, 22, 0, 20, 10, 0, 43, 53, 60, 0, 0, 0, 62, + 0, 5, 0, 61, 36, 31, 61, 59, 10, 0, 29, 39, 25, 18, 0, 14, + 11, 50, 17 +}; + +// repfact array +static const int repfact[qra_K] = { + 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 3, 3 +}; + +const qracode qra15_65_64_irr_e23 = { + qra_K, + qra_N, + qra_m, + qra_M, + qra_a, + qra_NC, + qra_V, + qra_C, + qra_NMSG, + qra_MAXVDEG, + qra_MAXCDEG, + QRATYPE_CRCPUNCTURED2, + qra_R, + CODE_NAME, + qra_acc_input_idx, + qra_acc_input_wlog, + qra_log, + qra_exp, + qra_msgw, + qra_vdeg, + qra_cdeg, + qra_v2cmidx, + qra_c2vmidx, + qra_pmat +}; +#undef qra_K +#undef qra_N +#undef qra_m +#undef qra_M +#undef qra_a +#undef qra_NC +#undef qra_V +#undef qra_C +#undef qra_NMSG +#undef qra_MAXVDEG +#undef qra_MAXCDEG +#undef qra_R +#undef CODE_NAME diff --git a/lib/qra/q65/qra15_65_64_irr_e23.h b/lib/qra/q65/qra15_65_64_irr_e23.h new file mode 100644 index 000000000..4e4f601c4 --- /dev/null +++ b/lib/qra/q65/qra15_65_64_irr_e23.h @@ -0,0 +1,41 @@ +// qra15_65_64_irr_e23.h +// Code tables and defines for Q-ary RA code (15,65) over GF(64) +// Code Name: qra15_65_64_irr_e23 +// (15,65) RA Code over GF(64) + +// (c) 2020 - Nico Palermo - IV3NWV - Microtelecom Srl, Italy + +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + +#ifndef _qra15_65_64_irr_e23_h +#define _qra15_65_64_irr_e23_h + +// File generated by npiwnarsavehc.m + +#include "qracodes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const qracode qra15_65_64_irr_e23; + +#ifdef __cplusplus +} +#endif + +#endif // _qra15_65_64_irr_e23_h diff --git a/lib/qra/q65/qra65.c b/lib/qra/q65/qra65.c new file mode 100644 index 000000000..efb3e94a0 --- /dev/null +++ b/lib/qra/q65/qra65.c @@ -0,0 +1,795 @@ +// qra65.c +// QRA65 modes encoding/decoding functions +// +// (c) 2020 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy +// ------------------------------------------------------------------------------ +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + +#include +#include +#include + +#include "qra65.h" +#include "pdmath.h" + + +static int _qra65_crc6(int *x, int sz); +static void _qra65_crc12(int *y, int *x, int sz); + + +int qra65_init(qra65_codec_ds *pCodec, const qracode *pqracode) +{ + // Eb/No value for which we optimize the decoder metric (AWGN/Rayleigh cases) + const float EbNodBMetric = 2.8f; + const float EbNoMetric = (float)pow(10,EbNodBMetric/10); + + float R; // code effective rate (after puncturing) + int nm; // bits per symbol + + if (!pCodec) + return -1; // why do you called me? + + if (!pqracode) + return -2; // invalid qra code + + if (pqracode->M!=64) + return -3; // QRA65 supports only codes over GF(64) + + pCodec->pQraCode = pqracode; + + // allocate buffers used by encoding/decoding functions + pCodec->x = (int*)malloc(pqracode->K*sizeof(int)); + pCodec->y = (int*)malloc(pqracode->N*sizeof(int)); + pCodec->qra_v2cmsg = (float*)malloc(pqracode->NMSG*pqracode->M*sizeof(float)); + pCodec->qra_c2vmsg = (float*)malloc(pqracode->NMSG*pqracode->M*sizeof(float)); + pCodec->ix = (float*)malloc(pqracode->N*pqracode->M*sizeof(float)); + pCodec->ex = (float*)malloc(pqracode->N*pqracode->M*sizeof(float)); + + if (pCodec->x== NULL || + pCodec->y== NULL || + pCodec->qra_v2cmsg== NULL || + pCodec->qra_c2vmsg== NULL || + pCodec->ix== NULL || + pCodec->ex== NULL) { + qra65_free(pCodec); + return -4; // out of memory + } + + // compute and store the AWGN/Rayleigh Es/No ratio for which we optimize + // the decoder metric + nm = _qra65_get_bits_per_symbol(pqracode); + R = _qra65_get_code_rate(pqracode); + pCodec->decoderEsNoMetric = 1.0f*nm*R*EbNoMetric; + + return 1; +} + +void qra65_free(qra65_codec_ds *pCodec) +{ + if (!pCodec) + return; + + // free internal buffers + if (pCodec->x!=NULL) + free(pCodec->x); + + if (pCodec->y!=NULL) + free(pCodec->y); + + if (pCodec->qra_v2cmsg!=NULL) + free(pCodec->qra_v2cmsg); + + if (pCodec->qra_c2vmsg!=NULL) + free(pCodec->qra_c2vmsg); + + if (pCodec->ix!=NULL) + free(pCodec->ix); + + if (pCodec->ex!=NULL) + free(pCodec->ex); + + pCodec->pQraCode = NULL; + pCodec->x = NULL; + pCodec->y = NULL; + pCodec->qra_v2cmsg = NULL; + pCodec->qra_c2vmsg = NULL; + pCodec->qra_v2cmsg = NULL; + pCodec->ix = NULL; + pCodec->ex = NULL; + + return; +} + +int qra65_encode(const qra65_codec_ds *pCodec, int *pOutputCodeword, const int *pInputMsg) +{ + const qracode *pQraCode; + int *px; + int *py; + int nK; + int nN; + + if (!pCodec) + return -1; // which codec? + + pQraCode = pCodec->pQraCode; + px = pCodec->x; + py = pCodec->y; + nK = _qra65_get_message_length(pQraCode); + nN = _qra65_get_codeword_length(pQraCode); + + // copy the information symbols into the internal buffer + memcpy(px,pInputMsg,nK*sizeof(int)); + + // compute and append the appropriate CRC if required + switch (pQraCode->type) { + case QRATYPE_NORMAL: + break; + case QRATYPE_CRC: + case QRATYPE_CRCPUNCTURED: + px[nK] = _qra65_crc6(px,nK); + break; + case QRATYPE_CRCPUNCTURED2: + _qra65_crc12(px+nK,px,nK); + break; + default: + return -2; // code type not supported + } + + // encode with the given qra code + qra_encode(pQraCode,py,px); + + // puncture the CRC symbols as required + // and copy the result to the destination buffer + switch (pQraCode->type) { + case QRATYPE_NORMAL: + case QRATYPE_CRC: + // no puncturing + memcpy(pOutputCodeword,py,nN*sizeof(int)); + break; + case QRATYPE_CRCPUNCTURED: + // strip the single CRC symbol from the encoded codeword + memcpy(pOutputCodeword,py,nK*sizeof(int)); // copy the systematic symbols + memcpy(pOutputCodeword+nK,py+nK+1,(nN-nK)*sizeof(int)); // copy the check symbols skipping the CRC symbol + break; + case QRATYPE_CRCPUNCTURED2: + // strip the 2 CRC symbols from the encoded codeword + memcpy(pOutputCodeword,py,nK*sizeof(int)); // copy the systematic symbols + memcpy(pOutputCodeword+nK,py+nK+2,(nN-nK)*sizeof(int)); // copy the check symbols skipping the two CRC symbols + break; + default: + return -2; // code type unsupported + } + + return 1; // ok +} + +int qra65_intrinsics(qra65_codec_ds *pCodec, float *pIntrinsics, const float *pInputEnergies) +{ + // compute observations intrinsics probabilities + // for the AWGN/Rayleigh channels + + // NOTE: + // A true Rayleigh channel metric would require that the channel gains were known + // for each symbol in the codeword. Such gains cannot be estimated reliably when + // the Es/No ratio is small. Therefore we compute intrinsic probabilities assuming + // that, on average, these channel gains are unitary. + // In general it is even difficult to estimate the Es/No ratio for the AWGN channel + // Therefore we always compute the intrinsic probabilities assuming that the Es/No + // ratio is known and equal to the constant decoderEsNoMetric. This assumption will + // generate the true intrinsic probabilities only when the actual Eb/No ratio is + // equal to this constant. As in all the other cases the probabilities are evaluated + // with a wrong scaling constant we can expect that the decoder performance at different + // Es/No will be worse. Anyway, since the EsNoMetric constant has been chosen so that the + // decoder error rate is about 50%, we obtain almost optimal error rates down to + // any useful Es/No ratio. + + const qracode *pQraCode; + int nN, nBits; + float EsNoMetric; + + if (pCodec==NULL) + return -1; // which codec? + + pQraCode = pCodec->pQraCode; + nN = _qra65_get_codeword_length(pQraCode); + nBits = pQraCode->m; + + EsNoMetric = pCodec->decoderEsNoMetric; + qra_mfskbesselmetric(pIntrinsics,pInputEnergies,nBits,nN,EsNoMetric); + + return 1; // success +} + +int qra65_esnodb(const qra65_codec_ds *pCodec, float *pEsNodB, const int *ydec, const float *pInputEnergies) +{ + // compute average Es/No for the AWGN/Rayleigh channel cases + + int k,j; + float sigplusnoise=0; + float noise=0; + int nN, nM; + const float *pIn = pInputEnergies; + const int *py = ydec; + float EsNodB; + + nN = qra65_get_codeword_length(pCodec); + nM = qra65_get_alphabet_size(pCodec); + + for (k=0;k4) + return QRA65_DECODE_INVPARAMS; // invalid submode + + // As the symbol duration in QRA65 is longer than in QRA64 the fading tables continue + // to be valid if the B90 parameter is scaled by the actual symbol rate + // Compute index to most appropriate weighting function coefficients + hidx = (int)(logf(B90*TS_QRA65/TS_QRA64)/logf(1.09f) - 0.499f); + +// if (hidx<0 || hidx > 64) +// // index of weighting function out of range +// // B90 out of range +// return QRA65_DECODE_INVPARAMS; + + // Unlike in QRA64 we accept any B90, anyway limiting it to + // the extreme cases (0.9 to 210 Hz approx.) + if (hidx<0) + hidx = 0; + else + if (hidx > 64) + hidx=64; + + // select the appropriate weighting fading coefficients array + if (fadingModel==0) { // gaussian fading model + // point to gaussian energy weighting taps + hlen = glen_tab_gauss[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun) + hptr = gptr_tab_gauss[hidx]; // pointer to the first (L+1)/2 coefficients of w fun + } + else if (fadingModel==1) { + // point to lorentzian energy weighting taps + hlen = glen_tab_lorentz[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun) + hptr = gptr_tab_lorentz[hidx]; // pointer to the first (L+1)/2 coefficients of w fun + } + else + return QRA65_DECODE_INVPARAMS; // invalid fading model + + // compute (euristically) the optimal decoder metric accordingly the given spread amount + // We assume that the decoder 50% decoding threshold is: + // Es/No(dB) = Es/No(AWGN)(dB) + 8*log(B90)/log(240)(dB) + // that's to say, at the maximum Doppler spread bandwidth (240 Hz for QRA64) + // there's a ~8 dB Es/No degradation over the AWGN case + fTemp = 8.0f*logf(B90)/logf(240.0f); // assumed Es/No degradation for the given fading bandwidth + EsNoMetric = pCodec->decoderEsNoMetric*powf(10.0f,fTemp/10.0f); + + nM = qra65_get_alphabet_size(pCodec); + nN = qra65_get_codeword_length(pCodec); + nBinsPerTone = 1<ffNoiseVar = fNoiseVar; + pCodec->ffEsNoMetric = EsNoMetric; + pCodec->nBinsPerTone = nBinsPerTone; + pCodec->nBinsPerSymbol = nBinsPerSymbol; + pCodec->nWeights = hlen; + weight = pCodec->ffWeight; + + // compute the fast fading weights accordingly to the Es/No ratio + // for which we compute the exact intrinsics probabilities + for (k=0;kmaxlogp) // keep track of the max + maxlogp = fTemp; + pCurIx[k]=fTemp; + + pCurBin += nBinsPerTone; // next tone + } + + // exponentiate and accumulate the normalization constant + sumix = 0.0f; + for (k=0;knBinsPerTone; + nBinsPerSymbol = pCodec->nBinsPerSymbol; + nWeights = pCodec->nWeights; + ffNoiseVar = pCodec->ffNoiseVar; + ffEsNoMetric = pCodec->ffEsNoMetric; + nTotWeights = 2*nWeights-1; + + // compute symbols energy (noise included) summing the + // energies pertaining to the decoded symbols in the codeword + + EsPlusWNo = 0.0f; + pCurSym = pInputEnergies + nM; // point to first central bin of first symbol tone + for (n=0;npQraCode; + ix = pCodec->ix; + ex = pCodec->ex; + + nK = _qra65_get_message_length(pQraCode); + nN = _qra65_get_codeword_length(pQraCode); + nM = pQraCode->M; + nBits = pQraCode->m; + + px = pCodec->x; + py = pCodec->y; + + // Depuncture intrinsics observations as required by the code type + switch (pQraCode->type) { + case QRATYPE_CRCPUNCTURED: + memcpy(ix,pIntrinsics,nK*nM*sizeof(float)); // information symbols + pd_init(PD_ROWADDR(ix,nM,nK),pd_uniform(nBits),nM); // crc + memcpy(ix+(nK+1)*nM,pIntrinsics+nK*nM,(nN-nK)*nM*sizeof(float)); // parity checks + break; + case QRATYPE_CRCPUNCTURED2: + memcpy(ix,pIntrinsics,nK*nM*sizeof(float)); // information symbols + pd_init(PD_ROWADDR(ix,nM,nK),pd_uniform(nBits),nM); // crc + pd_init(PD_ROWADDR(ix,nM,nK+1),pd_uniform(nBits),nM); // crc + memcpy(ix+(nK+2)*nM,pIntrinsics+nK*nM,(nN-nK)*nM*sizeof(float)); // parity checks + break; + case QRATYPE_NORMAL: + case QRATYPE_CRC: + default: + // no puncturing + memcpy(ix,pIntrinsics,nN*nM*sizeof(float)); // as they are + } + + // mask the intrinsics with the available a priori knowledge + if (pAPMask!=NULL) + _qra65_mask(pQraCode,ix,pAPMask,pAPSymbols); + + + // Compute the extrinsic symbols probabilities with the message-passing algorithm + // Stop if the extrinsics information does not converges to unity + // within the given number of iterations + rc = qra_extrinsic( pQraCode, + ex, + ix, + 100, + pCodec->qra_v2cmsg, + pCodec->qra_c2vmsg); + + if (rc<0) + // failed to converge to a solution + return QRA65_DECODE_FAILED; + + // decode the information symbols (punctured information symbols included) + qra_mapdecode(pQraCode,px,ex,ix); + + // verify CRC match + + switch (pQraCode->type) { + case QRATYPE_CRC: + case QRATYPE_CRCPUNCTURED: + crc6=_qra65_crc6(px,nK); // compute crc-6 + if (crc6!=px[nK]) + return QRA65_DECODE_CRCMISMATCH; // crc doesn't match + break; + case QRATYPE_CRCPUNCTURED2: + _qra65_crc12(crc12, px,nK); // compute crc-12 + if (crc12[0]!=px[nK] || + crc12[1]!=px[nK+1]) + return QRA65_DECODE_CRCMISMATCH; // crc doesn't match + break; + case QRATYPE_NORMAL: + default: + // nothing to check + break; + } + + // copy the decoded msg to the user buffer (excluding punctured symbols) + if (pDecodedMsg) + memcpy(pDecodedMsg,px,nK*sizeof(int)); + + if (pDecodedCodeword==NULL) // user is not interested in it + return rc; // return the number of iterations required to decode + + // crc matches therefore we can reconstruct the transmitted codeword + // reencoding the information available in px... + + qra_encode(pQraCode, py, px); + + // ...and strip the punctured symbols from the codeword + switch (pQraCode->type) { + case QRATYPE_CRCPUNCTURED: + memcpy(pDecodedCodeword,py,nK*sizeof(int)); + memcpy(pDecodedCodeword+nK,py+nK+1,(nN-nK)*sizeof(int)); // puncture crc-6 symbol + break; + case QRATYPE_CRCPUNCTURED2: + memcpy(pDecodedCodeword,py,nK*sizeof(int)); + memcpy(pDecodedCodeword+nK,py+nK+2,(nN-nK)*sizeof(int)); // puncture crc-12 symbols + break; + case QRATYPE_CRC: + case QRATYPE_NORMAL: + default: + memcpy(pDecodedCodeword,py,nN*sizeof(int)); // no puncturing + } + + return rc; // return the number of iterations required to decode + +} + + + + +// helper functions ------------------------------------------------------------- + +int _qra65_get_message_length(const qracode *pCode) +{ + // return the actual information message length (in symbols) + // excluding any punctured symbol + + int nMsgLength; + + switch (pCode->type) { + case QRATYPE_NORMAL: + nMsgLength = pCode->K; + break; + case QRATYPE_CRC: + case QRATYPE_CRCPUNCTURED: + // one information symbol of the underlying qra code is reserved for CRC + nMsgLength = pCode->K-1; + break; + case QRATYPE_CRCPUNCTURED2: + // two code information symbols are reserved for CRC + nMsgLength = pCode->K-2; + break; + default: + nMsgLength = -1; + } + + return nMsgLength; +} + +int _qra65_get_codeword_length(const qracode *pCode) +{ + // return the actual codeword length (in symbols) + // excluding any punctured symbol + + int nCwLength; + + switch (pCode->type) { + case QRATYPE_NORMAL: + case QRATYPE_CRC: + // no puncturing + nCwLength = pCode->N; + break; + case QRATYPE_CRCPUNCTURED: + // the CRC symbol is punctured + nCwLength = pCode->N-1; + break; + case QRATYPE_CRCPUNCTURED2: + // the two CRC symbols are punctured + nCwLength = pCode->N-2; + break; + default: + nCwLength = -1; + } + + return nCwLength; +} + +float _qra65_get_code_rate(const qracode *pCode) +{ + return 1.0f*_qra65_get_message_length(pCode)/_qra65_get_codeword_length(pCode); +} + +int _qra65_get_alphabet_size(const qracode *pCode) +{ + return pCode->M; +} +int _qra65_get_bits_per_symbol(const qracode *pCode) +{ + return pCode->m; +} +static void _qra65_mask(const qracode *pcode, float *ix, const int *mask, const int *x) +{ + // mask intrinsic information ix with available a priori knowledge + + int k,kk, smask; + const int nM=pcode->M; + const int nm=pcode->m; + int nK; + + // Exclude from masking the symbols which have been punctured. + // nK is the length of the mask and x arrays, which do + // not include any punctured symbol + nK = _qra65_get_message_length(pcode); + + // for each symbol set to zero the probability + // of the values which are not allowed by + // the a priori information + + for (k=0;k>1) ^ CRC6_GEN_POL; + else + sr = (sr>>1); + t>>=1; + } + } + + return sr; +} + +static void _qra65_crc12(int *y, int *x, int sz) +{ + int k,j,t,sr = 0; + for (k=0;k>1) ^ CRC12_GEN_POL; + else + sr = (sr>>1); + t>>=1; + } + } + + y[0] = sr&0x3F; + y[1] = (sr>>6); +} + + diff --git a/lib/qra/q65/qra65.h b/lib/qra/q65/qra65.h new file mode 100644 index 000000000..ffa383279 --- /dev/null +++ b/lib/qra/q65/qra65.h @@ -0,0 +1,101 @@ +// qra65.h +// QRA65 modes encoding/decoding functions +// +// (c) 2020 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy +// ------------------------------------------------------------------------------ +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + +#ifndef _qra65_h +#define _qra65_h + +#include "qracodes.h" + +// Error codes returned by qra65_decode(...) +#define QRA65_DECODE_INVPARAMS -1 +#define QRA65_DECODE_FAILED -2 +#define QRA65_DECODE_CRCMISMATCH -3 + +// maximum number of weights for the fast-fading metric evaluation +#define QRA65_FASTFADING_MAXWEIGTHS 65 + +typedef struct { + const qracode *pQraCode; // qra code to be used by the codec + float decoderEsNoMetric; // value for which we optimize the decoder metric + int *x; // codec input + int *y; // codec output + float *qra_v2cmsg; // decoder v->c messages + float *qra_c2vmsg; // decoder c->v messages + float *ix; // decoder intrinsic information + float *ex; // decoder extrinsic information + // variables used to compute the intrinsics in the fast-fading case + int nBinsPerTone; + int nBinsPerSymbol; + float ffNoiseVar; + float ffEsNoMetric; + int nWeights; + float ffWeight[QRA65_FASTFADING_MAXWEIGTHS]; +} qra65_codec_ds; + +int qra65_init(qra65_codec_ds *pCodec, const qracode *pQraCode); +void qra65_free(qra65_codec_ds *pCodec); + +int qra65_encode(const qra65_codec_ds *pCodec, int *pOutputCodeword, const int *pInputMsg); + +int qra65_intrinsics(qra65_codec_ds *pCodec, float *pIntrinsics, const float *pInputEnergies); + +int qra65_intrinsics_fastfading(qra65_codec_ds *pCodec, + float *pIntrinsics, // intrinsic symbol probabilities output + const float *pInputEnergies, // received energies input + const int submode, // submode idx (0=A ... 4=E) + const float B90, // spread bandwidth (90% fractional energy) + const int fadingModel); // 0=Gaussian 1=Lorentzian fade model + + +int qra65_decode(qra65_codec_ds *pCodec, + int* pDecodedCodeword, + int *pDecodedMsg, + const float *pIntrinsics, + const int *pAPMask, + const int *pAPSymbols); + +int qra65_esnodb(const qra65_codec_ds *pCodec, + float *pEsNodB, + const int *ydec, + const float *pInputEnergies); + +int qra65_esnodb_fastfading( + const qra65_codec_ds *pCodec, + float *pEsNodB, + const int *ydec, + const float *pInputEnergies); + + +#define qra65_get_message_length(pCodec) _qra65_get_message_length((pCodec)->pQraCode) +#define qra65_get_codeword_length(pCodec) _qra65_get_codeword_length((pCodec)->pQraCode) +#define qra65_get_code_rate(pCodec) _qra65_get_code_rate((pCodec)->pQraCode) +#define qra65_get_alphabet_size(pCodec) _qra65_get_alphabet_size((pCodec)->pQraCode) +#define qra65_get_bits_per_symbol(pCodec) _qra65_get_bits_per_symbol((pCodec)->pQraCode) + +// internally used but made publicly available for the defines above +int _qra65_get_message_length(const qracode *pCode); +int _qra65_get_codeword_length(const qracode *pCode); +float _qra65_get_code_rate(const qracode *pCode); +void _qra65_mask(const qracode *pcode, float *ix, const int *mask, const int *x); +int _qra65_get_alphabet_size(const qracode *pCode); +int _qra65_get_bits_per_symbol(const qracode *pCode); + +#endif // _qra65_h \ No newline at end of file diff --git a/lib/qra/q65/qracodes.c b/lib/qra/q65/qracodes.c new file mode 100644 index 000000000..748a9c9cd --- /dev/null +++ b/lib/qra/q65/qracodes.c @@ -0,0 +1,474 @@ +// qracodes.c +// Q-ary RA codes encoding/decoding functions +// +// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy +// ------------------------------------------------------------------------------ +// This file is part of the qracodes project, a Forward Error Control +// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. +// +// qracodes is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// qracodes is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with qracodes source distribution. +// If not, see . + +#include +#include + +#include "npfwht.h" +#include "pdmath.h" + +#include "qracodes.h" + +int qra_encode(const qracode *pcode, int *y, const int *x) +{ + int k,j,kk,jj; + int t, chk = 0; + + const int K = pcode->K; + const int M = pcode->M; + const int NC= pcode->NC; + const int a = pcode->a; + const int *acc_input_idx = pcode->acc_input_idx; + const int *acc_input_wlog = pcode->acc_input_wlog; + const int *gflog = pcode->gflog; + const int *gfexp = pcode->gfexp; + + // copy the systematic symbols to destination + memcpy(y,x,K*sizeof(int)); + + y = y+K; // point to check symbols + + // compute the code check symbols as a weighted accumulation of a permutated + // sequence of the (repeated) systematic input symbols: + // chk(k+1) = x(idx(k))*alfa^(logw(k)) + chk(k) + // (all operations performed over GF(M)) + + if (a==1) { // grouping factor = 1 + for (k=0;k 1 + for (k=0;k80.f) // avoid floating point exp() overflows + v=80.f; + + src[nitems] = (float)exp(v); + } +} + + +float qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N, float EsNoMetric) +{ + // Computes the codeword symbols intrinsic probabilities + // given the square of the received input amplitudes. + + // The input vector rqs must be a linear array of size M*N, where M=2^m, + // containing the squared amplitudes (rp*rp+rq*rq) of the input samples + + // First symbol amplitudes should be stored in the first M positions, + // second symbol amplitudes stored at positions [M ... 2*M-1], and so on. + + // Output vector is the intrinsic symbol metric (the probability distribution) + // assuming that symbols are transmitted using a M-FSK modulation + // and incoherent demodulation. + + // As the input Es/No is generally unknown (as it cannot be exstimated accurately + // when the codeword length is few tens symbols) but an exact metric requires it + // we simply fix it to a predefined EsNoMetric value so that the metric is what + // expected at that specific value. + // The metric computed in this way is optimal only at this predefined Es/No value, + // nevertheless it is usually better than a generic parameter-free metric which + // makes no assumptions on the input Es/No. + + // returns the estimated noise standard deviation + + int k; + float rsum = 0.f; + float sigmaest, cmetric; + + const int M = 1<M; + const int qra_m = pcode->m; + const int qra_V = pcode->V; + const int qra_MAXVDEG = pcode->MAXVDEG; + const int *qra_vdeg = pcode->vdeg; + const int qra_C = pcode->C; + const int qra_MAXCDEG = pcode->MAXCDEG; + const int *qra_cdeg = pcode->cdeg; + const int *qra_v2cmidx = pcode->v2cmidx; + const int *qra_c2vmidx = pcode->c2vmidx; + const int *qra_pmat = pcode->gfpmat; + const int *qra_msgw = pcode->msgw; + +// float msgout[qra_M]; // buffer to store temporary results + float msgout[QRACODE_MAX_M]; // we use a fixed size in order to avoid mallocs + + float totex; // total extrinsic information + int nit; // current iteration + int nv; // current variable + int nc; // current check + int k,kk; // loop indexes + + int ndeg; // current node degree + int msgbase; // current offset in the table of msg indexes + int imsg; // current message index + int wmsg; // current message weight + + int rc = -1; // rc>=0 extrinsic converged to 1 at iteration rc (rc=0..maxiter-1) + // rc=-1 no convergence in the given number of iterations + // rc=-2 error in the code tables (code checks degrees must be >1) + // rc=-3 M is larger than QRACODE_MAX_M + + + + if (qra_M>QRACODE_MAX_M) + return -3; + + // message initialization ------------------------------------------------------- + + // init c->v variable intrinsic msgs + pd_init(C2VMSG(0),pix,qra_M*qra_V); + + // init the v->c messages directed to code factors (k=1..ndeg) with the intrinsic info + for (nv=0;nvc + for (k=1;kv step ----------------------------------------------------- + // Computes messages from code checks to code variables. + // As the first qra_V checks are associated with intrinsic information + // (the code tables have been constructed in this way) + // we need to do this step only for code checks in the range [qra_V..qra_C) + + // The convolutions of probability distributions over the alphabet of a finite field GF(qra_M) + // are performed with a fast convolution algorithm over the given field. + // + // I.e. given the code check x1+x2+x3 = 0 (with x1,x2,x3 in GF(2^m)) + // and given Prob(x2) and Prob(x3), we have that: + // Prob(x1=X1) = Prob((x2+x3)=X1) = sum((Prob(x2=X2)*Prob(x3=(X1+X2))) for all the X2s in the field + // This translates to Prob(x1) = IWHT(WHT(Prob(x2))*WHT(Prob(x3))) + // where WHT and IWHT are the direct and inverse Walsh-Hadamard transforms of the argument. + // Note that the WHT and the IWHF differs only by a multiplicative coefficent and since in this step + // we don't need that the output distribution is normalized we use the relationship + // Prob(x1) =(proportional to) WH(WH(Prob(x2))*WH(Prob(x3))) + + // In general given the check code x1+x2+x3+..+xm = 0 + // the output distribution of a variable given the distributions of the other m-1 variables + // is the inverse WHT of the product of the WHTs of the distribution of the other m-1 variables + // The complexity of this algorithm scales with M*log2(M) instead of the M^2 complexity of + // the brute force approach (M=size of the alphabet) + + for (nc=qra_V;nc1) + return -2; // bad code tables + + msgbase = nc*qra_MAXCDEG; // base to msg index row for the current node + + // transforms inputs in the Walsh-Hadamard "frequency" domain + // v->c -> fwht(v->c) + for (k=0;kv = prod(fwht(v->c)) + // TODO: we assume that checks degrees are not larger than three but + // if they are larger the products can be computed more efficiently + for (kk=0;kkc steps when multipling + // small fp numbers + msgout[0]+=1E-7f; // TODO: define the bias accordingly to the field size + + np_fwht(qra_m,msgout,msgout); + + // inverse weight and output + imsg = qra_c2vmidx[msgbase+k]; // current output msg index + wmsg = qra_msgw[imsg]; // current msg weight + + if (wmsg==0) + pd_init(C2VMSG(imsg),msgout,qra_M); + else + // output p(alfa^(-w)*x) + pd_bwdperm(C2VMSG(imsg),msgout, MSGPERM(wmsg), qra_M); + + } // for (k=0;kc step ----------------------------------------------------- + for (nv=0;nvc msg = prod(c->v) + // TODO: factor factors to reduce the number of computations for high degree nodes + for (kk=0;kkc are null + // normalize output to a probability distribution + if (pd_norm(msgout,qra_m)<=0) { + // dump msgin; + printf("warning: v->c pd with invalid norm. nit=%d nv=%d k=%d\n",nit,nv,k); + for (kk=0;kk(1.*(qra_V)-0.01)) { + // the total maximum extrinsic information of each symbol in the codeword + // is very close to one. This means that we have reached the (1,1) point in the + // code EXIT chart(s) and we have successfully decoded the input. + rc = nit; + break; // remove the break to evaluate the decoder speed performance as a function of the max iterations number) + } + + } // for (nit=0;nitM; + const int qra_m = pcode->m; + const int qra_K = pcode->K; + + int k; + + for (k=0;k. + +#ifndef _qracodes_h_ +#define _qracodes_h_ + +// type of codes +#define QRATYPE_NORMAL 0x00 // normal code +#define QRATYPE_CRC 0x01 // code with crc - last information symbol is a CRC-6 +#define QRATYPE_CRCPUNCTURED 0x02 // the CRC-6 symbol is punctured (not sent along the channel) +#define QRATYPE_CRCPUNCTURED2 0x03 // code with CRC-12. The two crc symbols are punctured + + +typedef struct { + // code parameters + const int K; // number of information symbols + const int N; // codeword length in symbols + const int m; // bits/symbol + const int M; // Symbol alphabet cardinality (2^m) + const int a; // code grouping factor + const int NC; // number of check symbols (N-K) + const int V; // number of variables in the code graph (N) + const int C; // number of factors in the code graph (N +(N-K)+1) + const int NMSG; // number of msgs in the code graph + const int MAXVDEG; // maximum variable degree + const int MAXCDEG; // maximum factor degree + const int type; // see QRATYPE_xx defines + const float R; // code rate (K/N) + const char name[64]; // code name + // tables used by the encoder + const int *acc_input_idx; + const int *acc_input_wlog; + const int *gflog; + const int *gfexp; + // tables used by the decoder ------------------------- + const int *msgw; + const int *vdeg; + const int *cdeg; + const int *v2cmidx; + const int *c2vmidx; + const int *gfpmat; +} qracode; +// Uncomment the header file of the code which needs to be tested + +//#include "qra12_63_64_irr_b.h" // irregular code (12,63) over GF(64) +//#include "qra13_64_64_irr_e.h" // irregular code with good performance and best UER protection at AP56 +//#include "qra13_64_64_reg_a.h" // regular code with good UER but perf. inferior to that of code qra12_63_64_irr_b + +#ifdef __cplusplus +extern "C" { +#endif + +int qra_encode(const qracode *pcode, int *y, const int *x); +float qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N, float EsNoMetric); +int qra_extrinsic(const qracode *pcode, float *pex, const float *pix, int maxiter,float *qra_v2cmsg,float *qra_c2vmsg); +void qra_mapdecode(const qracode *pcode, int *xdec, float *pex, const float *pix); + +#ifdef __cplusplus +} +#endif + +#endif // _qracodes_h_ diff --git a/lib/qra/q65/wer-ff-qra15_65_64_irr_e23-ap00.txt b/lib/qra/q65/wer-ff-qra15_65_64_irr_e23-ap00.txt new file mode 100644 index 000000000..b087ad9fb --- /dev/null +++ b/lib/qra/q65/wer-ff-qra15_65_64_irr_e23-ap00.txt @@ -0,0 +1,19 @@ +#Code Name: qra15_65_64_irr_e23 +#ChannelType (0=AWGN,1=Rayleigh,2=Fast-Fading) +#Eb/No (dB) +#Transmitted Codewords +#Errors +#CRC Errors +#Undetected +#Avg dec. time (ms) +#WER +#UER +2 -30.00 106 106 0 0 4.87 1.00e+000 0.00e+000 +2 0.50 1006 1006 0 0 4.91 1.00e+000 0.00e+000 +2 1.00 1007 1006 0 0 4.98 9.99e-001 0.00e+000 +2 1.50 1009 1007 0 0 4.97 9.98e-001 0.00e+000 +2 2.00 1017 1007 1 0 4.84 9.90e-001 2.40e-007 +2 2.50 1047 1006 1 0 4.79 9.61e-001 2.33e-007 +2 3.00 1148 1006 3 0 4.61 8.76e-001 6.38e-007 +2 3.50 1338 1006 6 0 4.43 7.52e-001 1.10e-006 +2 4.00 1902 1006 7 0 3.94 5.29e-001 8.99e-007 From 676d3578f167601d0e21ca3927eedb31880e36a4 Mon Sep 17 00:00:00 2001 From: Nico Palermo/IV3NWV Date: Mon, 26 Oct 2020 02:20:44 +0100 Subject: [PATCH 102/426] removed unused qra65 files --- lib/qra/q65/qra65.c | 795 -------------------------------------------- lib/qra/q65/qra65.h | 101 ------ 2 files changed, 896 deletions(-) delete mode 100644 lib/qra/q65/qra65.c delete mode 100644 lib/qra/q65/qra65.h diff --git a/lib/qra/q65/qra65.c b/lib/qra/q65/qra65.c deleted file mode 100644 index efb3e94a0..000000000 --- a/lib/qra/q65/qra65.c +++ /dev/null @@ -1,795 +0,0 @@ -// qra65.c -// QRA65 modes encoding/decoding functions -// -// (c) 2020 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy -// ------------------------------------------------------------------------------ -// This file is part of the qracodes project, a Forward Error Control -// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. -// -// qracodes is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// qracodes is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with qracodes source distribution. -// If not, see . - -#include -#include -#include - -#include "qra65.h" -#include "pdmath.h" - - -static int _qra65_crc6(int *x, int sz); -static void _qra65_crc12(int *y, int *x, int sz); - - -int qra65_init(qra65_codec_ds *pCodec, const qracode *pqracode) -{ - // Eb/No value for which we optimize the decoder metric (AWGN/Rayleigh cases) - const float EbNodBMetric = 2.8f; - const float EbNoMetric = (float)pow(10,EbNodBMetric/10); - - float R; // code effective rate (after puncturing) - int nm; // bits per symbol - - if (!pCodec) - return -1; // why do you called me? - - if (!pqracode) - return -2; // invalid qra code - - if (pqracode->M!=64) - return -3; // QRA65 supports only codes over GF(64) - - pCodec->pQraCode = pqracode; - - // allocate buffers used by encoding/decoding functions - pCodec->x = (int*)malloc(pqracode->K*sizeof(int)); - pCodec->y = (int*)malloc(pqracode->N*sizeof(int)); - pCodec->qra_v2cmsg = (float*)malloc(pqracode->NMSG*pqracode->M*sizeof(float)); - pCodec->qra_c2vmsg = (float*)malloc(pqracode->NMSG*pqracode->M*sizeof(float)); - pCodec->ix = (float*)malloc(pqracode->N*pqracode->M*sizeof(float)); - pCodec->ex = (float*)malloc(pqracode->N*pqracode->M*sizeof(float)); - - if (pCodec->x== NULL || - pCodec->y== NULL || - pCodec->qra_v2cmsg== NULL || - pCodec->qra_c2vmsg== NULL || - pCodec->ix== NULL || - pCodec->ex== NULL) { - qra65_free(pCodec); - return -4; // out of memory - } - - // compute and store the AWGN/Rayleigh Es/No ratio for which we optimize - // the decoder metric - nm = _qra65_get_bits_per_symbol(pqracode); - R = _qra65_get_code_rate(pqracode); - pCodec->decoderEsNoMetric = 1.0f*nm*R*EbNoMetric; - - return 1; -} - -void qra65_free(qra65_codec_ds *pCodec) -{ - if (!pCodec) - return; - - // free internal buffers - if (pCodec->x!=NULL) - free(pCodec->x); - - if (pCodec->y!=NULL) - free(pCodec->y); - - if (pCodec->qra_v2cmsg!=NULL) - free(pCodec->qra_v2cmsg); - - if (pCodec->qra_c2vmsg!=NULL) - free(pCodec->qra_c2vmsg); - - if (pCodec->ix!=NULL) - free(pCodec->ix); - - if (pCodec->ex!=NULL) - free(pCodec->ex); - - pCodec->pQraCode = NULL; - pCodec->x = NULL; - pCodec->y = NULL; - pCodec->qra_v2cmsg = NULL; - pCodec->qra_c2vmsg = NULL; - pCodec->qra_v2cmsg = NULL; - pCodec->ix = NULL; - pCodec->ex = NULL; - - return; -} - -int qra65_encode(const qra65_codec_ds *pCodec, int *pOutputCodeword, const int *pInputMsg) -{ - const qracode *pQraCode; - int *px; - int *py; - int nK; - int nN; - - if (!pCodec) - return -1; // which codec? - - pQraCode = pCodec->pQraCode; - px = pCodec->x; - py = pCodec->y; - nK = _qra65_get_message_length(pQraCode); - nN = _qra65_get_codeword_length(pQraCode); - - // copy the information symbols into the internal buffer - memcpy(px,pInputMsg,nK*sizeof(int)); - - // compute and append the appropriate CRC if required - switch (pQraCode->type) { - case QRATYPE_NORMAL: - break; - case QRATYPE_CRC: - case QRATYPE_CRCPUNCTURED: - px[nK] = _qra65_crc6(px,nK); - break; - case QRATYPE_CRCPUNCTURED2: - _qra65_crc12(px+nK,px,nK); - break; - default: - return -2; // code type not supported - } - - // encode with the given qra code - qra_encode(pQraCode,py,px); - - // puncture the CRC symbols as required - // and copy the result to the destination buffer - switch (pQraCode->type) { - case QRATYPE_NORMAL: - case QRATYPE_CRC: - // no puncturing - memcpy(pOutputCodeword,py,nN*sizeof(int)); - break; - case QRATYPE_CRCPUNCTURED: - // strip the single CRC symbol from the encoded codeword - memcpy(pOutputCodeword,py,nK*sizeof(int)); // copy the systematic symbols - memcpy(pOutputCodeword+nK,py+nK+1,(nN-nK)*sizeof(int)); // copy the check symbols skipping the CRC symbol - break; - case QRATYPE_CRCPUNCTURED2: - // strip the 2 CRC symbols from the encoded codeword - memcpy(pOutputCodeword,py,nK*sizeof(int)); // copy the systematic symbols - memcpy(pOutputCodeword+nK,py+nK+2,(nN-nK)*sizeof(int)); // copy the check symbols skipping the two CRC symbols - break; - default: - return -2; // code type unsupported - } - - return 1; // ok -} - -int qra65_intrinsics(qra65_codec_ds *pCodec, float *pIntrinsics, const float *pInputEnergies) -{ - // compute observations intrinsics probabilities - // for the AWGN/Rayleigh channels - - // NOTE: - // A true Rayleigh channel metric would require that the channel gains were known - // for each symbol in the codeword. Such gains cannot be estimated reliably when - // the Es/No ratio is small. Therefore we compute intrinsic probabilities assuming - // that, on average, these channel gains are unitary. - // In general it is even difficult to estimate the Es/No ratio for the AWGN channel - // Therefore we always compute the intrinsic probabilities assuming that the Es/No - // ratio is known and equal to the constant decoderEsNoMetric. This assumption will - // generate the true intrinsic probabilities only when the actual Eb/No ratio is - // equal to this constant. As in all the other cases the probabilities are evaluated - // with a wrong scaling constant we can expect that the decoder performance at different - // Es/No will be worse. Anyway, since the EsNoMetric constant has been chosen so that the - // decoder error rate is about 50%, we obtain almost optimal error rates down to - // any useful Es/No ratio. - - const qracode *pQraCode; - int nN, nBits; - float EsNoMetric; - - if (pCodec==NULL) - return -1; // which codec? - - pQraCode = pCodec->pQraCode; - nN = _qra65_get_codeword_length(pQraCode); - nBits = pQraCode->m; - - EsNoMetric = pCodec->decoderEsNoMetric; - qra_mfskbesselmetric(pIntrinsics,pInputEnergies,nBits,nN,EsNoMetric); - - return 1; // success -} - -int qra65_esnodb(const qra65_codec_ds *pCodec, float *pEsNodB, const int *ydec, const float *pInputEnergies) -{ - // compute average Es/No for the AWGN/Rayleigh channel cases - - int k,j; - float sigplusnoise=0; - float noise=0; - int nN, nM; - const float *pIn = pInputEnergies; - const int *py = ydec; - float EsNodB; - - nN = qra65_get_codeword_length(pCodec); - nM = qra65_get_alphabet_size(pCodec); - - for (k=0;k4) - return QRA65_DECODE_INVPARAMS; // invalid submode - - // As the symbol duration in QRA65 is longer than in QRA64 the fading tables continue - // to be valid if the B90 parameter is scaled by the actual symbol rate - // Compute index to most appropriate weighting function coefficients - hidx = (int)(logf(B90*TS_QRA65/TS_QRA64)/logf(1.09f) - 0.499f); - -// if (hidx<0 || hidx > 64) -// // index of weighting function out of range -// // B90 out of range -// return QRA65_DECODE_INVPARAMS; - - // Unlike in QRA64 we accept any B90, anyway limiting it to - // the extreme cases (0.9 to 210 Hz approx.) - if (hidx<0) - hidx = 0; - else - if (hidx > 64) - hidx=64; - - // select the appropriate weighting fading coefficients array - if (fadingModel==0) { // gaussian fading model - // point to gaussian energy weighting taps - hlen = glen_tab_gauss[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun) - hptr = gptr_tab_gauss[hidx]; // pointer to the first (L+1)/2 coefficients of w fun - } - else if (fadingModel==1) { - // point to lorentzian energy weighting taps - hlen = glen_tab_lorentz[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun) - hptr = gptr_tab_lorentz[hidx]; // pointer to the first (L+1)/2 coefficients of w fun - } - else - return QRA65_DECODE_INVPARAMS; // invalid fading model - - // compute (euristically) the optimal decoder metric accordingly the given spread amount - // We assume that the decoder 50% decoding threshold is: - // Es/No(dB) = Es/No(AWGN)(dB) + 8*log(B90)/log(240)(dB) - // that's to say, at the maximum Doppler spread bandwidth (240 Hz for QRA64) - // there's a ~8 dB Es/No degradation over the AWGN case - fTemp = 8.0f*logf(B90)/logf(240.0f); // assumed Es/No degradation for the given fading bandwidth - EsNoMetric = pCodec->decoderEsNoMetric*powf(10.0f,fTemp/10.0f); - - nM = qra65_get_alphabet_size(pCodec); - nN = qra65_get_codeword_length(pCodec); - nBinsPerTone = 1<ffNoiseVar = fNoiseVar; - pCodec->ffEsNoMetric = EsNoMetric; - pCodec->nBinsPerTone = nBinsPerTone; - pCodec->nBinsPerSymbol = nBinsPerSymbol; - pCodec->nWeights = hlen; - weight = pCodec->ffWeight; - - // compute the fast fading weights accordingly to the Es/No ratio - // for which we compute the exact intrinsics probabilities - for (k=0;kmaxlogp) // keep track of the max - maxlogp = fTemp; - pCurIx[k]=fTemp; - - pCurBin += nBinsPerTone; // next tone - } - - // exponentiate and accumulate the normalization constant - sumix = 0.0f; - for (k=0;knBinsPerTone; - nBinsPerSymbol = pCodec->nBinsPerSymbol; - nWeights = pCodec->nWeights; - ffNoiseVar = pCodec->ffNoiseVar; - ffEsNoMetric = pCodec->ffEsNoMetric; - nTotWeights = 2*nWeights-1; - - // compute symbols energy (noise included) summing the - // energies pertaining to the decoded symbols in the codeword - - EsPlusWNo = 0.0f; - pCurSym = pInputEnergies + nM; // point to first central bin of first symbol tone - for (n=0;npQraCode; - ix = pCodec->ix; - ex = pCodec->ex; - - nK = _qra65_get_message_length(pQraCode); - nN = _qra65_get_codeword_length(pQraCode); - nM = pQraCode->M; - nBits = pQraCode->m; - - px = pCodec->x; - py = pCodec->y; - - // Depuncture intrinsics observations as required by the code type - switch (pQraCode->type) { - case QRATYPE_CRCPUNCTURED: - memcpy(ix,pIntrinsics,nK*nM*sizeof(float)); // information symbols - pd_init(PD_ROWADDR(ix,nM,nK),pd_uniform(nBits),nM); // crc - memcpy(ix+(nK+1)*nM,pIntrinsics+nK*nM,(nN-nK)*nM*sizeof(float)); // parity checks - break; - case QRATYPE_CRCPUNCTURED2: - memcpy(ix,pIntrinsics,nK*nM*sizeof(float)); // information symbols - pd_init(PD_ROWADDR(ix,nM,nK),pd_uniform(nBits),nM); // crc - pd_init(PD_ROWADDR(ix,nM,nK+1),pd_uniform(nBits),nM); // crc - memcpy(ix+(nK+2)*nM,pIntrinsics+nK*nM,(nN-nK)*nM*sizeof(float)); // parity checks - break; - case QRATYPE_NORMAL: - case QRATYPE_CRC: - default: - // no puncturing - memcpy(ix,pIntrinsics,nN*nM*sizeof(float)); // as they are - } - - // mask the intrinsics with the available a priori knowledge - if (pAPMask!=NULL) - _qra65_mask(pQraCode,ix,pAPMask,pAPSymbols); - - - // Compute the extrinsic symbols probabilities with the message-passing algorithm - // Stop if the extrinsics information does not converges to unity - // within the given number of iterations - rc = qra_extrinsic( pQraCode, - ex, - ix, - 100, - pCodec->qra_v2cmsg, - pCodec->qra_c2vmsg); - - if (rc<0) - // failed to converge to a solution - return QRA65_DECODE_FAILED; - - // decode the information symbols (punctured information symbols included) - qra_mapdecode(pQraCode,px,ex,ix); - - // verify CRC match - - switch (pQraCode->type) { - case QRATYPE_CRC: - case QRATYPE_CRCPUNCTURED: - crc6=_qra65_crc6(px,nK); // compute crc-6 - if (crc6!=px[nK]) - return QRA65_DECODE_CRCMISMATCH; // crc doesn't match - break; - case QRATYPE_CRCPUNCTURED2: - _qra65_crc12(crc12, px,nK); // compute crc-12 - if (crc12[0]!=px[nK] || - crc12[1]!=px[nK+1]) - return QRA65_DECODE_CRCMISMATCH; // crc doesn't match - break; - case QRATYPE_NORMAL: - default: - // nothing to check - break; - } - - // copy the decoded msg to the user buffer (excluding punctured symbols) - if (pDecodedMsg) - memcpy(pDecodedMsg,px,nK*sizeof(int)); - - if (pDecodedCodeword==NULL) // user is not interested in it - return rc; // return the number of iterations required to decode - - // crc matches therefore we can reconstruct the transmitted codeword - // reencoding the information available in px... - - qra_encode(pQraCode, py, px); - - // ...and strip the punctured symbols from the codeword - switch (pQraCode->type) { - case QRATYPE_CRCPUNCTURED: - memcpy(pDecodedCodeword,py,nK*sizeof(int)); - memcpy(pDecodedCodeword+nK,py+nK+1,(nN-nK)*sizeof(int)); // puncture crc-6 symbol - break; - case QRATYPE_CRCPUNCTURED2: - memcpy(pDecodedCodeword,py,nK*sizeof(int)); - memcpy(pDecodedCodeword+nK,py+nK+2,(nN-nK)*sizeof(int)); // puncture crc-12 symbols - break; - case QRATYPE_CRC: - case QRATYPE_NORMAL: - default: - memcpy(pDecodedCodeword,py,nN*sizeof(int)); // no puncturing - } - - return rc; // return the number of iterations required to decode - -} - - - - -// helper functions ------------------------------------------------------------- - -int _qra65_get_message_length(const qracode *pCode) -{ - // return the actual information message length (in symbols) - // excluding any punctured symbol - - int nMsgLength; - - switch (pCode->type) { - case QRATYPE_NORMAL: - nMsgLength = pCode->K; - break; - case QRATYPE_CRC: - case QRATYPE_CRCPUNCTURED: - // one information symbol of the underlying qra code is reserved for CRC - nMsgLength = pCode->K-1; - break; - case QRATYPE_CRCPUNCTURED2: - // two code information symbols are reserved for CRC - nMsgLength = pCode->K-2; - break; - default: - nMsgLength = -1; - } - - return nMsgLength; -} - -int _qra65_get_codeword_length(const qracode *pCode) -{ - // return the actual codeword length (in symbols) - // excluding any punctured symbol - - int nCwLength; - - switch (pCode->type) { - case QRATYPE_NORMAL: - case QRATYPE_CRC: - // no puncturing - nCwLength = pCode->N; - break; - case QRATYPE_CRCPUNCTURED: - // the CRC symbol is punctured - nCwLength = pCode->N-1; - break; - case QRATYPE_CRCPUNCTURED2: - // the two CRC symbols are punctured - nCwLength = pCode->N-2; - break; - default: - nCwLength = -1; - } - - return nCwLength; -} - -float _qra65_get_code_rate(const qracode *pCode) -{ - return 1.0f*_qra65_get_message_length(pCode)/_qra65_get_codeword_length(pCode); -} - -int _qra65_get_alphabet_size(const qracode *pCode) -{ - return pCode->M; -} -int _qra65_get_bits_per_symbol(const qracode *pCode) -{ - return pCode->m; -} -static void _qra65_mask(const qracode *pcode, float *ix, const int *mask, const int *x) -{ - // mask intrinsic information ix with available a priori knowledge - - int k,kk, smask; - const int nM=pcode->M; - const int nm=pcode->m; - int nK; - - // Exclude from masking the symbols which have been punctured. - // nK is the length of the mask and x arrays, which do - // not include any punctured symbol - nK = _qra65_get_message_length(pcode); - - // for each symbol set to zero the probability - // of the values which are not allowed by - // the a priori information - - for (k=0;k>1) ^ CRC6_GEN_POL; - else - sr = (sr>>1); - t>>=1; - } - } - - return sr; -} - -static void _qra65_crc12(int *y, int *x, int sz) -{ - int k,j,t,sr = 0; - for (k=0;k>1) ^ CRC12_GEN_POL; - else - sr = (sr>>1); - t>>=1; - } - } - - y[0] = sr&0x3F; - y[1] = (sr>>6); -} - - diff --git a/lib/qra/q65/qra65.h b/lib/qra/q65/qra65.h deleted file mode 100644 index ffa383279..000000000 --- a/lib/qra/q65/qra65.h +++ /dev/null @@ -1,101 +0,0 @@ -// qra65.h -// QRA65 modes encoding/decoding functions -// -// (c) 2020 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy -// ------------------------------------------------------------------------------ -// This file is part of the qracodes project, a Forward Error Control -// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. -// -// qracodes is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// qracodes is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with qracodes source distribution. -// If not, see . - -#ifndef _qra65_h -#define _qra65_h - -#include "qracodes.h" - -// Error codes returned by qra65_decode(...) -#define QRA65_DECODE_INVPARAMS -1 -#define QRA65_DECODE_FAILED -2 -#define QRA65_DECODE_CRCMISMATCH -3 - -// maximum number of weights for the fast-fading metric evaluation -#define QRA65_FASTFADING_MAXWEIGTHS 65 - -typedef struct { - const qracode *pQraCode; // qra code to be used by the codec - float decoderEsNoMetric; // value for which we optimize the decoder metric - int *x; // codec input - int *y; // codec output - float *qra_v2cmsg; // decoder v->c messages - float *qra_c2vmsg; // decoder c->v messages - float *ix; // decoder intrinsic information - float *ex; // decoder extrinsic information - // variables used to compute the intrinsics in the fast-fading case - int nBinsPerTone; - int nBinsPerSymbol; - float ffNoiseVar; - float ffEsNoMetric; - int nWeights; - float ffWeight[QRA65_FASTFADING_MAXWEIGTHS]; -} qra65_codec_ds; - -int qra65_init(qra65_codec_ds *pCodec, const qracode *pQraCode); -void qra65_free(qra65_codec_ds *pCodec); - -int qra65_encode(const qra65_codec_ds *pCodec, int *pOutputCodeword, const int *pInputMsg); - -int qra65_intrinsics(qra65_codec_ds *pCodec, float *pIntrinsics, const float *pInputEnergies); - -int qra65_intrinsics_fastfading(qra65_codec_ds *pCodec, - float *pIntrinsics, // intrinsic symbol probabilities output - const float *pInputEnergies, // received energies input - const int submode, // submode idx (0=A ... 4=E) - const float B90, // spread bandwidth (90% fractional energy) - const int fadingModel); // 0=Gaussian 1=Lorentzian fade model - - -int qra65_decode(qra65_codec_ds *pCodec, - int* pDecodedCodeword, - int *pDecodedMsg, - const float *pIntrinsics, - const int *pAPMask, - const int *pAPSymbols); - -int qra65_esnodb(const qra65_codec_ds *pCodec, - float *pEsNodB, - const int *ydec, - const float *pInputEnergies); - -int qra65_esnodb_fastfading( - const qra65_codec_ds *pCodec, - float *pEsNodB, - const int *ydec, - const float *pInputEnergies); - - -#define qra65_get_message_length(pCodec) _qra65_get_message_length((pCodec)->pQraCode) -#define qra65_get_codeword_length(pCodec) _qra65_get_codeword_length((pCodec)->pQraCode) -#define qra65_get_code_rate(pCodec) _qra65_get_code_rate((pCodec)->pQraCode) -#define qra65_get_alphabet_size(pCodec) _qra65_get_alphabet_size((pCodec)->pQraCode) -#define qra65_get_bits_per_symbol(pCodec) _qra65_get_bits_per_symbol((pCodec)->pQraCode) - -// internally used but made publicly available for the defines above -int _qra65_get_message_length(const qracode *pCode); -int _qra65_get_codeword_length(const qracode *pCode); -float _qra65_get_code_rate(const qracode *pCode); -void _qra65_mask(const qracode *pcode, float *ix, const int *mask, const int *x); -int _qra65_get_alphabet_size(const qracode *pCode); -int _qra65_get_bits_per_symbol(const qracode *pCode); - -#endif // _qra65_h \ No newline at end of file From 5d352d3068a302d0df4e9187f658e99e2f0aeb46 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 25 Oct 2020 21:29:44 -0400 Subject: [PATCH 103/426] Declare _q65_mask() as static when first declared, in q65.h. Add qracodes.o to Makefile.Win. --- lib/qra/q65/Makefile.Win | 2 +- lib/qra/q65/q65.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/qra/q65/Makefile.Win b/lib/qra/q65/Makefile.Win index cedf2ef17..99b34d726 100644 --- a/lib/qra/q65/Makefile.Win +++ b/lib/qra/q65/Makefile.Win @@ -16,7 +16,7 @@ CFLAGS = -O2 -Wall -I. -D_WIN32 all: libq65.a q65.exe OBJS1 = normrnd.o npfwht.o pdmath.o qra15_65_64_irr_e23.o \ - q65.o + q65.o qracodes.o libq65.a: $(OBJS1) ar cr libq65.a $(OBJS1) diff --git a/lib/qra/q65/q65.h b/lib/qra/q65/q65.h index 04b5d8365..d36cb608f 100644 --- a/lib/qra/q65/q65.h +++ b/lib/qra/q65/q65.h @@ -96,8 +96,8 @@ int q65_esnodb_fastfading( int _q65_get_message_length(const qracode *pCode); int _q65_get_codeword_length(const qracode *pCode); float _q65_get_code_rate(const qracode *pCode); -void _q65_mask(const qracode *pcode, float *ix, const int *mask, const int *x); +static void _q65_mask(const qracode *pcode, float *ix, const int *mask, const int *x); int _q65_get_alphabet_size(const qracode *pCode); int _q65_get_bits_per_symbol(const qracode *pCode); -#endif // _qra65_h \ No newline at end of file +#endif // _qra65_h From cc5e0c7f2c7c8a49b0fd6a35fa0045523c904a28 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 26 Oct 2020 16:54:05 -0400 Subject: [PATCH 104/426] Add a Fortran program to test calls to Nico's Q65 codec. --- CMakeLists.txt | 6 +++ lib/qra/q65/Makefile.Win | 9 +++- lib/qra/q65/q65_ftn_test.f90 | 50 ++++++++++++++++++++ lib/qra/q65/q65_subs.c | 90 ++++++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 lib/qra/q65/q65_ftn_test.f90 create mode 100644 lib/qra/q65/q65_subs.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ac4ad4f8..4af7e5dcb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -587,6 +587,9 @@ set (qra_CSRCS lib/qra/qracodes/qra13_64_64_irr_e.c lib/qra/qracodes/qracodes.c lib/qra/qracodes/normrnd.c + lib/qra/q65/qra15_65_64_irr_e23.c + lib/qra/q65/q65.c + lib/qra/q65/q65_subs.c ) set (wsjt_CSRCS @@ -1123,6 +1126,9 @@ target_link_libraries (test_qra64 wsjt_fort wsjt_cxx) add_executable (test_q65 lib/test_q65.f90) target_link_libraries (test_q65 wsjt_fort wsjt_cxx) +add_executable (q65_ftn_test lib/qra/q65/q65_ftn_test.f90) +target_link_libraries (q65_ftn_test wsjt_fort wsjt_cxx) + add_executable (jt49sim lib/jt49sim.f90) target_link_libraries (jt49sim wsjt_fort wsjt_cxx) diff --git a/lib/qra/q65/Makefile.Win b/lib/qra/q65/Makefile.Win index 99b34d726..8f2214d99 100644 --- a/lib/qra/q65/Makefile.Win +++ b/lib/qra/q65/Makefile.Win @@ -1,5 +1,7 @@ CC = gcc CFLAGS = -O2 -Wall -I. -D_WIN32 +FC = gfortran +FFLAGS = -Wall -fbounds-check # Default rules %.o: %.c @@ -13,7 +15,7 @@ CFLAGS = -O2 -Wall -I. -D_WIN32 %.o: %.F90 ${FC} ${FFLAGS} -c $< -all: libq65.a q65.exe +all: libq65.a q65.exe q65_ftn_test.exe OBJS1 = normrnd.o npfwht.o pdmath.o qra15_65_64_irr_e23.o \ q65.o qracodes.o @@ -27,6 +29,11 @@ OBJS2 = q65test.o q65.exe: $(OBJS2) ${CC} -o q65.exe $(OBJS2) libq65.a -lm +OBJS3 = q65_ftn_test.o q65_subs.o + +q65_ftn_test.exe: $(OBJS3) + ${FC} -o q65_ftn_test.exe $(OBJS3) libq65.a -lm + .PHONY : clean clean: diff --git a/lib/qra/q65/q65_ftn_test.f90 b/lib/qra/q65/q65_ftn_test.f90 new file mode 100644 index 000000000..6291950a2 --- /dev/null +++ b/lib/qra/q65/q65_ftn_test.f90 @@ -0,0 +1,50 @@ +program q65_ftn_test + + use packjt77 + parameter (LL=64,NN=63) + integer x(13) !User's 78-bit message as 13 six-bit integers + integer y(63) !Q65 codeword for x + integer xdec(13) !Decoded message + integer APmask(13) + integer APsymbols(13) + real s3(0:LL-1,NN) + real s3prob(0:LL-1,NN) + character*37 msg0,msg,msgsent + character*77 c77 + logical unpk77_success + + narg=iargc() + if(narg.ne.1) then + print*,'Usage: q65_ftn_test "message"' + print*,'Example: q65_ftn_test "K1ABC W9XYZ EN37"' + go to 999 + endif + call getarg(1,msg0) + call pack77(msg0,i3,n3,c77) + call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent + read(c77,1000) x +1000 format(12b6.6,b5.5) + + call q65_enc(x,y) + + write(*,1010) x,msg0 +1010 format('User message:'/13i3,2x,a) + write(*,1020) y +1020 format(/'Generated codeword:'/(20i3)) + + s3=0. + s3prob=0. + do j=1,NN + s3(y(j),j)=1.0 + enddo + APmask=0 + APsymbols=0 + + call q65_dec(s3,APmask,APsymbols,s3prob,snr2500,xdec,irc) + + write(c77,1000) xdec + call unpack77(c77,0,msg,unpk77_success) !Unpack to get msgsent + write(*,1100) xdec,trim(msg) +1100 format(/'Decoded message:'/13i3,2x,a) + +999 end program q65_ftn_test diff --git a/lib/qra/q65/q65_subs.c b/lib/qra/q65/q65_subs.c new file mode 100644 index 000000000..97889871f --- /dev/null +++ b/lib/qra/q65/q65_subs.c @@ -0,0 +1,90 @@ +// q65_subs.c + +/* Fortran interface for Q65 codec + + To encode a Q65 message: + + integer x(13) !Message payload, 78 bits as 13 six-bit integers + integer y(63) !Codeword, 63 six-bit integers + call q65_enc(imsg,icodeword) + + To decode a Q65 message: + + parameter (LL=64,NN=63) + real s3(LL,NN) !Received energies + real s3prob(LL,NN) !Symbol-value probabilities + integer APmask(13) + integer APsymbols(13) + real snr2500 + integer xdec(13) !Decoded 78-bit message as 13 six-bit integers + integer irc !Return code from q65_decode() + + call q65_dec(s3,APmask,APsymbols,s3prob,snr2500,xdec,irc) +*/ + +#include "qra15_65_64_irr_e23.h" // QRA code used by Q65 +#include "q65.h" +#include +#include + +static q65_codec_ds codec; + +void q65_enc_(int x[], int y[]) +{ + + static int first=1; + if (first) { + // Set the QRA code, allocate memory, and initialize + int rc = q65_init(&codec,&qra15_65_64_irr_e23); + if (rc<0) { + printf("error in q65_init()\n"); + exit(0); + } + first=0; + } + // Encode message x[13], producing codeword y[63] + q65_encode(&codec,y,x); +} + +void q65_dec_(float s3[], int APmask[], int APsymbols[], float s3prob[], + float* snr2500, int xdec[], int* rc0) +{ + +/* Input: s3[LL,NN] Received energies + * APmask[13] AP information to be used in decoding + * APsymbols[13] Available AP informtion + * Output: s3prob[LL,NN] Symbol-value intrinsic probabilities + * snr2500 SNR_2500 of decoded signal, or lower limit + * xdec[13] Decoded 78-bit message as 13 six-bit integers + * rc0 Return code from q65_decode() + */ + + int rc; + int ydec[63]; + float esnodb; + + // rc = q65_intrinsics_fastfding(&codec,s3prob,s3,submode,B90,fadingModel); + rc = q65_intrinsics(&codec,s3prob,s3); + if(rc<0) { + printf("error in q65_intrinsics()\n"); + exit(0); + } + + rc = q65_decode(&codec,ydec,xdec,s3prob,APmask,APsymbols); + *rc0=rc; + if(rc<0) { + printf("Error in q65_decode(), rc = %d\n",rc); + // rc = -1: Invalid params + // rc = -2: Decode failed + // rc = -3: CRC mismatch + return; + } + + // rc = q65_esnodb_fastfading(&codec,&esnodb,ydec,s3); + rc = q65_esnodb(&codec,&esnodb,ydec,s3); + if(rc<0) { + printf("error in q65_esnodb_fastfading()\n"); + exit(0); + } + *snr2500 = esnodb - 31.0; +} From 0d8734de47af5547b01d97ced551ebb7b423c707 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 26 Oct 2020 19:25:16 -0400 Subject: [PATCH 105/426] Update some path names for QRA64/Q65 files. --- CMakeLists.txt | 8 ++++---- lib/qra/q65/q65_ftn_test.f90 | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4af7e5dcb..da53e1be8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -581,12 +581,12 @@ set_source_files_properties (${ka9q_CSRCS} PROPERTIES COMPILE_FLAGS -Wno-sign-co set (qra_CSRCS lib/qra/qra64/qra64.c lib/qra/qra64/qra64_subs.c - lib/qra/qracodes/npfwht.c - lib/qra/qracodes/pdmath.c lib/qra/qracodes/qra12_63_64_irr_b.c lib/qra/qracodes/qra13_64_64_irr_e.c - lib/qra/qracodes/qracodes.c - lib/qra/qracodes/normrnd.c + lib/qra/q65/npfwht.c + lib/qra/q65/pdmath.c + lib/qra/q65/qracodes.c + lib/qra/q65/normrnd.c lib/qra/q65/qra15_65_64_irr_e23.c lib/qra/q65/q65.c lib/qra/q65/q65_subs.c diff --git a/lib/qra/q65/q65_ftn_test.f90 b/lib/qra/q65/q65_ftn_test.f90 index 6291950a2..6ff17ddf6 100644 --- a/lib/qra/q65/q65_ftn_test.f90 +++ b/lib/qra/q65/q65_ftn_test.f90 @@ -25,7 +25,7 @@ program q65_ftn_test read(c77,1000) x 1000 format(12b6.6,b5.5) - call q65_enc(x,y) + call q65_enc(x,y) !Encode message, x(1:13) ==> y(1:63) write(*,1010) x,msg0 1010 format('User message:'/13i3,2x,a) From a473cee2ea4ecc21680b8f7e3e1d14740e766f18 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 26 Oct 2020 19:42:03 -0400 Subject: [PATCH 106/426] Move q65sim.f90 to the q65 directory. --- CMakeLists.txt | 2 +- lib/qra/{qra65 => q65}/q65sim.f90 | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/qra/{qra65 => q65}/q65sim.f90 (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index da53e1be8..41eec794b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1117,7 +1117,7 @@ target_link_libraries (sumsim wsjt_fort wsjt_cxx) add_executable (qra64sim lib/qra/qra64/qra64sim.f90) target_link_libraries (qra64sim wsjt_fort wsjt_cxx) -add_executable (q65sim lib/qra/qra65/q65sim.f90) +add_executable (q65sim lib/qra/q65/q65sim.f90) target_link_libraries (q65sim wsjt_fort wsjt_cxx) add_executable (test_qra64 lib/test_qra64.f90) diff --git a/lib/qra/qra65/q65sim.f90 b/lib/qra/q65/q65sim.f90 similarity index 100% rename from lib/qra/qra65/q65sim.f90 rename to lib/qra/q65/q65sim.f90 From 079177579fc4503e84b8e7c6f67a1aa8b00cc310 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 27 Oct 2020 12:01:41 -0400 Subject: [PATCH 107/426] Add genq65.f90 to Fortran sources. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 41eec794b..8e8e993bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -424,6 +424,7 @@ set (wsjt_FSRCS lib/gen9.f90 lib/geniscat.f90 lib/ft8/genft8.f90 + lib/qra/q65/genq65.f90 lib/genmsk_128_90.f90 lib/genmsk40.f90 lib/ft4/ft4code.f90 From ccdaf49e3a19f543f7beff0a1ee140962567d1fe Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 27 Oct 2020 12:02:49 -0400 Subject: [PATCH 108/426] Must initialize Q65 codec is decode is called before encode. --- lib/qra/q65/q65_subs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/qra/q65/q65_subs.c b/lib/qra/q65/q65_subs.c index 97889871f..c7cb0d8c4 100644 --- a/lib/qra/q65/q65_subs.c +++ b/lib/qra/q65/q65_subs.c @@ -62,7 +62,17 @@ void q65_dec_(float s3[], int APmask[], int APsymbols[], float s3prob[], int rc; int ydec[63]; float esnodb; + static int first=1; + if (first) { + // Set the QRA code, allocate memory, and initialize + int rc = q65_init(&codec,&qra15_65_64_irr_e23); + if (rc<0) { + printf("error in q65_init()\n"); + exit(0); + } + first=0; + } // rc = q65_intrinsics_fastfding(&codec,s3prob,s3,submode,B90,fadingModel); rc = q65_intrinsics(&codec,s3prob,s3); if(rc<0) { From 5a2f9e4f3fd585a0881e0da6be7d4a88591a9960 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 27 Oct 2020 12:04:15 -0400 Subject: [PATCH 109/426] Updte q65sim to use the new Q65 code. --- lib/qra/q65/q65sim.f90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/qra/q65/q65sim.f90 b/lib/qra/q65/q65sim.f90 index 8841b92cd..24dce31e6 100644 --- a/lib/qra/q65/q65sim.f90 +++ b/lib/qra/q65/q65sim.f90 @@ -14,8 +14,8 @@ program q65sim complex cspread(0:NMAX-1) !Complex amplitude for Rayleigh fading complex z real*8 f0,dt,twopi,phi,dphi,baud,fsample,freq - character msg*22,fname*17,csubmode*1,arg*12,cd*1 - character msgsent*22 + character msg*37,fname*17,csubmode*1,arg*12,cd*1 + character msgsent*37 logical lsync data lsync/.false./ @@ -74,8 +74,8 @@ program q65sim nsym=85 !Number of channel symbols mode65=2**(ichar(csubmode) - ichar('A')) - ichk=65 !Flag sent to genqra64 - call genqra64(msg,ichk,msgsent,itone,itype) + ichk=0 + call genq65(msg,ichk,msgsent,itone,i3,n3) write(*,1001) itone 1001 format('Channel symbols:'/(20i3)) @@ -107,8 +107,8 @@ program q65sim 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,f0,csubmode,snrdb,xdt,fspread,msgsent -1020 format(i4,i6,f7.1,2x,a1,2x,f5.1,f6.2,f6.1,2x,a22) + write(*,1020) ifile,ntrperiod,f0,csubmode,snrdb,xdt,fspread,trim(msgsent) +1020 format(i4,i6,f7.1,2x,a1,2x,f5.1,f6.2,f6.1,2x,a) phi=0.d0 dphi=0.d0 k=(xdt+0.5)*12000 !Start audio at t=xdt+0.5 s (TR=15 and 30 s) From 2dad27f1ad679b2b3861c9161f2c4da865082b95 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 27 Oct 2020 13:08:07 -0400 Subject: [PATCH 110/426] First decodes of Q65 from within wsjtx. --- CMakeLists.txt | 1 + lib/q65_decode.f90 | 52 ++++++----- lib/qra/q65/q65_loops.f90 | 175 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+), 21 deletions(-) create mode 100644 lib/qra/q65/q65_loops.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e8e993bf..c001db3f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -497,6 +497,7 @@ set (wsjt_FSRCS lib/ps4.f90 lib/qra64a.f90 lib/qra_loops.f90 + lib/qra/q65/q65_loops.f90 lib/refspectrum.f90 lib/savec2.f90 lib/sec0.f90 diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index d1f874649..3daf8c4c3 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -42,7 +42,7 @@ contains ! Output: sent to the callback routine for display to user use timer_module, only: timer - use packjt + use packjt77 use, intrinsic :: iso_c_binding parameter (NMAX=300*12000) !Max TRperiod is 300 s class(q65_decoder), intent(inout) :: this @@ -50,10 +50,13 @@ contains character(len=12) :: mycall, hiscall !Used for AP decoding character(len=6) :: hisgrid character*37 decoded !Decoded message + character*77 c77 integer*2 iwave(NMAX) !Raw data real, allocatable :: dd(:) !Raw data integer dat4(12) !Decoded message as 12 6-bit integers + integer dat4a(13) logical ltext + logical unpk77_success complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/,nsubmodez/-1/ @@ -86,9 +89,9 @@ contains if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning ! 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) +! call packcall(mycall(1:6),nc1,ltext) +! call packcall(hiscall(1:6),nc2,ltext) +! call packgrid(hisgrid(1:4),ng2,ltext) b90=20.0 !8 to 25 is OK; not very critical nFadingModel=1 @@ -100,18 +103,18 @@ contains minsync=-2 call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) - 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(s3dummy,nc1,nc2,ng2,naptype,1,nSubmode,b90, & - nFadingModel,dat4,snr2,irc) - enddo - nc1z=nc1 - nc2z=nc2 - ng2z=ng2 - maxaptypez=maxaptype - endif +! 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(s3dummy,nc1,nc2,ng2,naptype,1,nSubmode,b90, & +! nFadingModel,dat4,snr2,irc) +! enddo +! nc1z=nc1 +! nc2z=nc2 +! ng2z=ng2 +! maxaptypez=maxaptype +! endif naptype=maxaptype call timer('sync_q65',0) @@ -127,16 +130,23 @@ contains dd=fac*iwave nmode=65 call ana64(dd,npts,c00) - call timer('qraloops',0) - call qra_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode,nFadingModel, & + call timer('q65loops',0) + call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode,nFadingModel, & ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) - call timer('qraloops',1) + call timer('q65loops',1) snr2=snr2 + db(6912.0/nsps) endif decoded=' ' if(irc.ge.0) then - call unpackmsg(dat4,decoded) !Unpack the user message - call fmtmsg(decoded,iz) + dat4a(1:12)=dat4 + dat4a(13)=9 + write(74,3074) dat4a,1 +3074 format(13i3,i6) + write(c77,1000) dat4a +1000 format(12b6.6,b5.5) + call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent +! 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=' ' diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 new file mode 100644 index 000000000..c0dbf59c1 --- /dev/null +++ b/lib/qra/q65/q65_loops.f90 @@ -0,0 +1,175 @@ +subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & + ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) + + use packjt77 + use timer_module, only: timer + parameter (LN=2176*63) !LN=LL*NN; LL = 64*(mode64+2) + character*37 decoded + character*77 c77 + complex c00(0:npts2-1) !Analytic representation of dd(), 6000 Hz + complex ,allocatable :: c0(:) !Ditto, with freq shift + real a(3) !twkfreq params f,f1,f2 + real s3(LN) !Symbol spectra + real s3avg(LN) !Averaged symbol spectra + real s3prob(LN) !Symbol-value probabilities + real s3tmp(4032) + logical unpk77_success + integer APmask(13) + integer APsymbols(13) + integer dat4(13),dat4x(13) !Decoded message (as 13 six-bit integers) + integer nap(0:11) !AP return codes + data nap/0,2,3,2,3,4,2,3,6,4,6,6/,nsave/0/ + save nsave,s3avg + + allocate(c0(0:npts2-1)) + irc=-99 + s3lim=20. + ibwmax=11 + if(mode64.le.4) ibwmax=9 + ibwmin=ibwmax + idtmax=3 + call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) + LL=64*(mode64+2) + NN=63 + napmin=99 + ncall=0 + + do iavg=0,1 + if(iavg.eq.1) then + idfmax=1 + idtmax=1 + endif + do idf=1,idfmax + ndf=idf/2 + if(mod(idf,2).eq.0) ndf=-ndf + a=0. + a(1)=-(f0+0.4*ndf) + call twkfreq(c00,c0,npts2,6000.0,a) + do idt=1,idtmax + ndt=idt/2 + if(iavg.eq.0) then + if(mod(idt,2).eq.0) ndt=-ndt + jpk=jpk0 + 240*ndt !240/6000 = 0.04 s = tsym/32 + if(jpk.lt.0) jpk=0 + call timer('spec64 ',0) + call spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) + call timer('spec64 ',1) + call pctile(s3,LL*NN,40,base) + s3=s3/base + where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim + else + s3(1:LL*NN)=s3avg(1:LL*NN) + endif + do ibw=ibwmax,ibwmin,-2 + ndist=ndf**2 + ndt**2 + ((ibwmax-ibw)/2)**2 + if(ndist.gt.maxdist) cycle + b90=1.728**ibw + if(b90.gt.230.0) cycle + if(b90.lt.0.15*width) exit + ncall=ncall+1 + call timer('qra64_de',0) +!### +! call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & +! nFadingModel,dat4,snr2,irc) + APmask=0 + APsymbols=0 + call s3fix(s3,s3tmp) + call q65_dec(s3tmp,APmask,APsymbols,s3prob,snr2,dat4,irc) + write(74,3074) dat4,0 +3074 format(13i3,i6) +!### + call timer('qra64_de',1) + if(irc.eq.0) go to 200 +! if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) + iirc=max(0,min(irc,11)) + if(irc.gt.0 .and. nap(iirc).lt.napmin) then + dat4x=dat4 + b90x=b90 + snr2x=snr2 + napmin=nap(iirc) + irckeep=irc + xdtkeep=jpk/6000.0 - 1.0 + f0keep=-a(1) + idfkeep=idf + idtkeep=idt + ibwkeep=ibw + ndistx=ndist + go to 100 !### + endif + enddo ! ibw (b90 loop) + !### if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 + enddo ! idt (DT loop) + enddo ! idf (f0 loop) +! if(iavg.eq.0 .and. abs(jpk0-4320).le.1300) then + if(iavg.eq.0) then + a=0. + a(1)=-f0 + call twkfreq(c00,c0,npts2,6000.0,a) + jpk=3000 !### These definitions need work ### +! if(nsps.ge.3600) jpk=4080 !### + if(nsps.ge.3600) jpk=6000 !### + call spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) + call pctile(s3,LL*NN,40,base) + s3=s3/base + where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim + s3avg(1:LL*NN)=s3avg(1:LL*NN)+s3(1:LL*NN) + nsave=nsave+1 + endif + if(iavg.eq.0 .and. nsave.lt.2) exit + enddo ! iavg + +100 if(napmin.ne.99) then + dat4=dat4x + b90=b90x + snr2=snr2x + irc=irckeep + xdt=xdtkeep + f0=f0keep + idt=idtkeep + idf=idfkeep + ibw=ibwkeep + ndist=ndistx + endif + +200 if(mode.eq.65 .and. nsps.eq.7200/2) xdt=xdt+0.4 !### Empirical -- WHY ??? ### + + if(irc.ge.0) then + navg=nsave + if(iavg.eq.0) navg=0 + !### For tests only: + open(53,file='fort.53',status='unknown',position='append') + write(c77,1200) dat4 +1200 format(12b6.6,b5.5) + call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent +! call unpackmsg(dat4,decoded) !Unpack the user message + write(53,3053) idf,idt,ibw,b90,xdt,f0,snr2,ndist,irc,navg,decoded(1:22) +3053 format(3i5,f7.1,f7.2,2f7.1,3i4,2x,a22) + close(53) + !### + nsave=0 + s3avg=0. + irc=irc + 100*navg + endif + return +end subroutine q65_loops + +subroutine s3fix(s3,s3tmp) + real s3(0:191,63) + real s3tmp(0:63,63) + integer ipk1(1) + integer y(63) + + do j=1,63 + s3tmp(0:63,j)=s3(64:127,j) +! s3tmp(y(j),j)=1.0 + ipk1=maxloc(s3(0:191,j)) + m=ipk1(1)-65 + ipk1=maxloc(s3tmp(0:63,j)) + mtmp=ipk1(1)-1 + write(72,3072) j,y(j),m,m-y(j),mtmp,mtmp-y(j) +3072 format(6i7) + enddo + write(70) s3tmp + + return +end subroutine s3fix From 4d739a31ca9f4b8a5075ede6458f5a1bc754c4b4 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 27 Oct 2020 13:22:02 -0400 Subject: [PATCH 111/426] Minor cleanup of Q65 decoder sequence. Much more needed! --- lib/q65_decode.f90 | 16 ++-------------- lib/qra/q65/q65_loops.f90 | 4 +--- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 3daf8c4c3..062952c84 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -53,8 +53,7 @@ contains character*77 c77 integer*2 iwave(NMAX) !Raw data real, allocatable :: dd(:) !Raw data - integer dat4(12) !Decoded message as 12 6-bit integers - integer dat4a(13) + integer dat4(13) !Decoded message as 12 6-bit integers logical ltext logical unpk77_success complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s @@ -138,20 +137,9 @@ contains endif decoded=' ' if(irc.ge.0) then - dat4a(1:12)=dat4 - dat4a(13)=9 - write(74,3074) dat4a,1 -3074 format(13i3,i6) - write(c77,1000) dat4a + write(c77,1000) dat4 1000 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent -! 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 nsnr=nint(snr2) call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index c0dbf59c1..d49af9632 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -65,7 +65,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & if(ndist.gt.maxdist) cycle b90=1.728**ibw if(b90.gt.230.0) cycle - if(b90.lt.0.15*width) exit +! if(b90.lt.0.15*width) exit ncall=ncall+1 call timer('qra64_de',0) !### @@ -75,8 +75,6 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & APsymbols=0 call s3fix(s3,s3tmp) call q65_dec(s3tmp,APmask,APsymbols,s3prob,snr2,dat4,irc) - write(74,3074) dat4,0 -3074 format(13i3,i6) !### call timer('qra64_de',1) if(irc.eq.0) go to 200 From 5d904927a0fc262e4a83589a12c1816f31d16b1c Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 27 Oct 2020 13:56:25 -0400 Subject: [PATCH 112/426] Decode Q65 using the Lorentzian fast fading model. --- lib/q65_decode.f90 | 21 +-------------------- lib/qra/q65/q65_loops.f90 | 30 ++---------------------------- lib/qra/q65/q65_subs.c | 10 +++++++--- 3 files changed, 10 insertions(+), 51 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 062952c84..d8c51816a 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -54,7 +54,6 @@ contains integer*2 iwave(NMAX) !Raw data real, allocatable :: dd(:) !Raw data integer dat4(13) !Decoded message as 12 6-bit integers - logical ltext logical unpk77_success complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s @@ -86,11 +85,6 @@ contains df1=12000.0/nfft1 this%callback => callback if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning - -! 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) b90=20.0 !8 to 25 is OK; not very critical nFadingModel=1 @@ -101,19 +95,6 @@ contains if(ndepth.ge.2) maxaptype=5 !### minsync=-2 call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) - -! 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(s3dummy,nc1,nc2,ng2,naptype,1,nSubmode,b90, & -! nFadingModel,dat4,snr2,irc) -! enddo -! nc1z=nc1 -! nc2z=nc2 -! ng2z=ng2 -! maxaptypez=maxaptype -! endif naptype=maxaptype call timer('sync_q65',0) @@ -131,7 +112,7 @@ contains call ana64(dd,npts,c00) call timer('q65loops',0) call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode,nFadingModel, & - ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) + ndepth,jpk0,xdt,f0,width,snr2,irc,dat4) call timer('q65loops',1) snr2=snr2 + db(6912.0/nsps) endif diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index d49af9632..ea116fd9a 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,5 +1,5 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & - ndepth,nc1,nc2,ng2,naptype,jpk0,xdt,f0,width,snr2,irc,dat4) + ndepth,jpk0,xdt,f0,width,snr2,irc,dat4) use packjt77 use timer_module, only: timer @@ -68,14 +68,9 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & ! if(b90.lt.0.15*width) exit ncall=ncall+1 call timer('qra64_de',0) -!### -! call qra64_dec(s3,nc1,nc2,ng2,naptype,0,nSubmode,b90, & -! nFadingModel,dat4,snr2,irc) APmask=0 APsymbols=0 - call s3fix(s3,s3tmp) - call q65_dec(s3tmp,APmask,APsymbols,s3prob,snr2,dat4,irc) -!### + call q65_dec(s3,APmask,APsymbols,nsubmode,b90,nFadingModel,s3prob,snr2,dat4,irc) call timer('qra64_de',1) if(irc.eq.0) go to 200 ! if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) @@ -150,24 +145,3 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & endif return end subroutine q65_loops - -subroutine s3fix(s3,s3tmp) - real s3(0:191,63) - real s3tmp(0:63,63) - integer ipk1(1) - integer y(63) - - do j=1,63 - s3tmp(0:63,j)=s3(64:127,j) -! s3tmp(y(j),j)=1.0 - ipk1=maxloc(s3(0:191,j)) - m=ipk1(1)-65 - ipk1=maxloc(s3tmp(0:63,j)) - mtmp=ipk1(1)-1 - write(72,3072) j,y(j),m,m-y(j),mtmp,mtmp-y(j) -3072 format(6i7) - enddo - write(70) s3tmp - - return -end subroutine s3fix diff --git a/lib/qra/q65/q65_subs.c b/lib/qra/q65/q65_subs.c index c7cb0d8c4..dfb2a50f1 100644 --- a/lib/qra/q65/q65_subs.c +++ b/lib/qra/q65/q65_subs.c @@ -46,13 +46,17 @@ void q65_enc_(int x[], int y[]) q65_encode(&codec,y,x); } -void q65_dec_(float s3[], int APmask[], int APsymbols[], float s3prob[], +void q65_dec_(float s3[], int APmask[], int APsymbols[], int* submode, + float* B90, int* fadingModel, float s3prob[], float* snr2500, int xdec[], int* rc0) { /* Input: s3[LL,NN] Received energies * APmask[13] AP information to be used in decoding * APsymbols[13] Available AP informtion + * submode 0=A, 4=E + * B90 Spread bandwidth, 90% fractional energy + * fadingModel 0=Gaussian, 1=Lorentzian * Output: s3prob[LL,NN] Symbol-value intrinsic probabilities * snr2500 SNR_2500 of decoded signal, or lower limit * xdec[13] Decoded 78-bit message as 13 six-bit integers @@ -73,8 +77,8 @@ void q65_dec_(float s3[], int APmask[], int APsymbols[], float s3prob[], } first=0; } - // rc = q65_intrinsics_fastfding(&codec,s3prob,s3,submode,B90,fadingModel); - rc = q65_intrinsics(&codec,s3prob,s3); + rc = q65_intrinsics_fastfading(&codec,s3prob,s3,*submode,*B90,*fadingModel); + // rc = q65_intrinsics(&codec,s3prob,s3); if(rc<0) { printf("error in q65_intrinsics()\n"); exit(0); From a41325d596a339763dda6f7ea664beaa6725d12a Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 27 Oct 2020 15:45:34 -0400 Subject: [PATCH 113/426] Use fast fading model in q65_ftn_test. --- lib/qra/q65/q65_ftn_test.f90 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/qra/q65/q65_ftn_test.f90 b/lib/qra/q65/q65_ftn_test.f90 index 6ff17ddf6..607d0f5ba 100644 --- a/lib/qra/q65/q65_ftn_test.f90 +++ b/lib/qra/q65/q65_ftn_test.f90 @@ -1,7 +1,7 @@ program q65_ftn_test use packjt77 - parameter (LL=64,NN=63) + parameter (LL=192,NN=63) integer x(13) !User's 78-bit message as 13 six-bit integers integer y(63) !Q65 codeword for x integer xdec(13) !Decoded message @@ -35,13 +35,14 @@ program q65_ftn_test s3=0. s3prob=0. do j=1,NN - s3(y(j),j)=1.0 + s3(y(j)+64,j)=1.0 enddo APmask=0 APsymbols=0 - - call q65_dec(s3,APmask,APsymbols,s3prob,snr2500,xdec,irc) - + nsubmode=0 + b90=1.0 + nFadingModel=1 + call q65_dec(s3,APmask,APsymbols,nsubmode,b90,nFadingModel,s3prob,snr2500,xdec,irc) write(c77,1000) xdec call unpack77(c77,0,msg,unpk77_success) !Unpack to get msgsent write(*,1100) xdec,trim(msg) From 9643e8a75332111c2c0e7a6d84109fcbb83c7875 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 27 Oct 2020 15:51:32 -0400 Subject: [PATCH 114/426] Code cleanup. --- lib/qra/q65/q65_loops.f90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index ea116fd9a..b2498e982 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -71,9 +71,12 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & APmask=0 APsymbols=0 call q65_dec(s3,APmask,APsymbols,nsubmode,b90,nFadingModel,s3prob,snr2,dat4,irc) + ! irc > 0 ==> number of iterations required to decode + ! -1 = invalid params + ! -2 = decode failed + ! -3 = CRC mismatch call timer('qra64_de',1) - if(irc.eq.0) go to 200 -! if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) + if(irc.ge.0) go to 200 iirc=max(0,min(irc,11)) if(irc.gt.0 .and. nap(iirc).lt.napmin) then dat4x=dat4 From c6424100a20942ab4c671805a7ad0dbe4a3c8602 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 27 Oct 2020 15:52:26 -0400 Subject: [PATCH 115/426] Code cleanup. --- lib/qra/q65/q65_subs.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/qra/q65/q65_subs.c b/lib/qra/q65/q65_subs.c index dfb2a50f1..6908c7c8a 100644 --- a/lib/qra/q65/q65_subs.c +++ b/lib/qra/q65/q65_subs.c @@ -67,7 +67,7 @@ void q65_dec_(float s3[], int APmask[], int APsymbols[], int* submode, int ydec[63]; float esnodb; static int first=1; - + if (first) { // Set the QRA code, allocate memory, and initialize int rc = q65_init(&codec,&qra15_65_64_irr_e23); @@ -78,7 +78,6 @@ void q65_dec_(float s3[], int APmask[], int APsymbols[], int* submode, first=0; } rc = q65_intrinsics_fastfading(&codec,s3prob,s3,*submode,*B90,*fadingModel); - // rc = q65_intrinsics(&codec,s3prob,s3); if(rc<0) { printf("error in q65_intrinsics()\n"); exit(0); @@ -86,13 +85,11 @@ void q65_dec_(float s3[], int APmask[], int APsymbols[], int* submode, rc = q65_decode(&codec,ydec,xdec,s3prob,APmask,APsymbols); *rc0=rc; - if(rc<0) { - printf("Error in q65_decode(), rc = %d\n",rc); - // rc = -1: Invalid params - // rc = -2: Decode failed - // rc = -3: CRC mismatch - return; - } + // rc = -1: Invalid params + // rc = -2: Decode failed + // rc = -3: CRC mismatch + *snr2500 = -31.0; + if(rc<0) return; // rc = q65_esnodb_fastfading(&codec,&esnodb,ydec,s3); rc = q65_esnodb(&codec,&esnodb,ydec,s3); From bbad483aaf5eb0a921b2e84ea059cb17409142ef Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 27 Oct 2020 15:52:57 -0400 Subject: [PATCH 116/426] Code cleanup in q65sim.f90. --- lib/qra/q65/q65sim.f90 | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/qra/q65/q65sim.f90 b/lib/qra/q65/q65sim.f90 index 24dce31e6..762c9ef87 100644 --- a/lib/qra/q65/q65sim.f90 +++ b/lib/qra/q65/q65sim.f90 @@ -7,7 +7,8 @@ program q65sim parameter (NMAX=300*12000) !Total samples in .wav file type(hdr) h !Header for .wav file integer*2 iwave(NMAX) !Generated waveform - integer*4 itone(85) !Channel symbols (values 0-65) + integer itone(85) !Channel symbols (values 0-65) + integer y(63) !Codeword real*4 xnoise(NMAX) !Generated random noise real*4 dat(NMAX) !Generated real data complex cdat(NMAX) !Generated complex waveform @@ -76,23 +77,35 @@ program q65sim ichk=0 call genq65(msg,ichk,msgsent,itone,i3,n3) - write(*,1001) itone -1001 format('Channel symbols:'/(20i3)) + + 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) +1001 format('Generated message:'/13i3) + write(*,1002) y +1002 format(/'Codeword:'/(20i3)) + write(*,1003) itone +1003 format(/'Channel symbols:'/(20i3)) baud=12000.d0/nsps !Keying rate (6.67 baud fot 15-s sequences) h=default_header(12000,npts) - write(*,1000) -1000 format('File TR Freq Mode S/N DT Dop Message'/60('-')) + write(*,1004) +1004 format('File TR Freq Mode S/N DT Dop Message'/60('-')) nsync=0 do ifile=1,nfiles !Loop over requested number of files if(ntrperiod.lt.60) then - write(fname,1002) ifile !Output filename -1002 format('000000_',i6.6,'.wav') + write(fname,1005) ifile !Output filename +1005 format('000000_',i6.6,'.wav') else - write(fname,1104) ifile -1104 format('000000_',i4.4,'.wav') + write(fname,1106) ifile +1106 format('000000_',i4.4,'.wav') endif open(10,file=trim(fname),access='stream',status='unknown') From 494a8fc9a7448af4bb6a347f05d55d01e6f3c9de Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 27 Oct 2020 16:08:50 -0400 Subject: [PATCH 117/426] More cleanup: Q65 SNRs, reported irc values. --- lib/q65_decode.f90 | 3 ++- lib/qra/q65/q65_subs.c | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index d8c51816a..2c238b824 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -118,6 +118,7 @@ contains endif decoded=' ' if(irc.ge.0) then + irc=0 !### TEMPORARY ??? ### write(c77,1000) dat4 1000 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent @@ -125,7 +126,7 @@ contains call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) else - nsnr=db(snr1) - 32.0 + nsnr=db(snr1) - 35.0 !### TEMPORARY? ### call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) diff --git a/lib/qra/q65/q65_subs.c b/lib/qra/q65/q65_subs.c index 6908c7c8a..f589f7faa 100644 --- a/lib/qra/q65/q65_subs.c +++ b/lib/qra/q65/q65_subs.c @@ -91,8 +91,7 @@ void q65_dec_(float s3[], int APmask[], int APsymbols[], int* submode, *snr2500 = -31.0; if(rc<0) return; - // rc = q65_esnodb_fastfading(&codec,&esnodb,ydec,s3); - rc = q65_esnodb(&codec,&esnodb,ydec,s3); + rc = q65_esnodb_fastfading(&codec,&esnodb,ydec,s3); if(rc<0) { printf("error in q65_esnodb_fastfading()\n"); exit(0); From fca76a5730832c81f719e7ce315b6620efdfaf88 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 27 Oct 2020 16:28:25 -0400 Subject: [PATCH 118/426] Report Q65 average decodes to the callback routine so test_q65 can distinguish them. --- lib/q65_decode.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 2c238b824..ec96ad4ec 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -118,7 +118,7 @@ contains endif decoded=' ' if(irc.ge.0) then - irc=0 !### TEMPORARY ??? ### + irc=(irc/100) * 100 !### TEMPORARY ??? ### write(c77,1000) dat4 1000 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent From 347fb063820653f170ef1a78f08ae106b41504d7 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 28 Oct 2020 09:28:50 -0400 Subject: [PATCH 119/426] Separate calls to q65_intrinsics_ff() and q65_dec(). --- lib/qra/q65/q65_loops.f90 | 3 ++- lib/qra/q65/q65_subs.c | 30 ++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index b2498e982..15e640ffd 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -68,9 +68,10 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & ! if(b90.lt.0.15*width) exit ncall=ncall+1 call timer('qra64_de',0) + call q65_intrinsics_ff(s3,nsubmode,b90,nFadingModel,s3prob) APmask=0 APsymbols=0 - call q65_dec(s3,APmask,APsymbols,nsubmode,b90,nFadingModel,s3prob,snr2,dat4,irc) + call q65_dec(s3,s3prob,APmask,APsymbols,snr2,dat4,irc) ! irc > 0 ==> number of iterations required to decode ! -1 = invalid params ! -2 = decode failed diff --git a/lib/qra/q65/q65_subs.c b/lib/qra/q65/q65_subs.c index f589f7faa..ffd4c0385 100644 --- a/lib/qra/q65/q65_subs.c +++ b/lib/qra/q65/q65_subs.c @@ -46,26 +46,18 @@ void q65_enc_(int x[], int y[]) q65_encode(&codec,y,x); } -void q65_dec_(float s3[], int APmask[], int APsymbols[], int* submode, - float* B90, int* fadingModel, float s3prob[], - float* snr2500, int xdec[], int* rc0) +void q65_intrinsics_ff_(float s3[], int* submode, float* B90, + int* fadingModel, float s3prob[]) { /* Input: s3[LL,NN] Received energies - * APmask[13] AP information to be used in decoding - * APsymbols[13] Available AP informtion * submode 0=A, 4=E * B90 Spread bandwidth, 90% fractional energy * fadingModel 0=Gaussian, 1=Lorentzian * Output: s3prob[LL,NN] Symbol-value intrinsic probabilities - * snr2500 SNR_2500 of decoded signal, or lower limit - * xdec[13] Decoded 78-bit message as 13 six-bit integers - * rc0 Return code from q65_decode() */ int rc; - int ydec[63]; - float esnodb; static int first=1; if (first) { @@ -82,6 +74,24 @@ void q65_dec_(float s3[], int APmask[], int APsymbols[], int* submode, printf("error in q65_intrinsics()\n"); exit(0); } +} + +void q65_dec_(float s3[], float s3prob[], int APmask[], int APsymbols[], + float* snr2500, int xdec[], int* rc0) +{ + +/* Input: s3prob[LL,NN] Symbol-value intrinsic probabilities + * APmask[13] AP information to be used in decoding + * APsymbols[13] Available AP informtion + * Output: + * snr2500 SNR_2500 of decoded signal, or lower limit + * xdec[13] Decoded 78-bit message as 13 six-bit integers + * rc0 Return code from q65_decode() + */ + + int rc; + int ydec[63]; + float esnodb; rc = q65_decode(&codec,ydec,xdec,s3prob,APmask,APsymbols); *rc0=rc; From b6071d29da18a54ef5c2149c074a627ec5eaa0d2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 28 Oct 2020 12:04:10 -0400 Subject: [PATCH 120/426] Code cleanup. --- lib/qra/q65/q65_loops.f90 | 47 +++++++-------------------------------- 1 file changed, 8 insertions(+), 39 deletions(-) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 15e640ffd..0bb0b27d4 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -77,34 +77,17 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & ! -2 = decode failed ! -3 = CRC mismatch call timer('qra64_de',1) - if(irc.ge.0) go to 200 - iirc=max(0,min(irc,11)) - if(irc.gt.0 .and. nap(iirc).lt.napmin) then - dat4x=dat4 - b90x=b90 - snr2x=snr2 - napmin=nap(iirc) - irckeep=irc - xdtkeep=jpk/6000.0 - 1.0 - f0keep=-a(1) - idfkeep=idf - idtkeep=idt - ibwkeep=ibw - ndistx=ndist - go to 100 !### - endif + if(irc.ge.0) go to 100 enddo ! ibw (b90 loop) - !### if(iand(ndepth,3).lt.3 .and. irc.ge.0) go to 100 enddo ! idt (DT loop) enddo ! idf (f0 loop) -! if(iavg.eq.0 .and. abs(jpk0-4320).le.1300) then if(iavg.eq.0) then a=0. a(1)=-f0 call twkfreq(c00,c0,npts2,6000.0,a) - jpk=3000 !### These definitions need work ### -! if(nsps.ge.3600) jpk=4080 !### - if(nsps.ge.3600) jpk=6000 !### + jpk=3000 !### These definitions need work ### +! if(nsps.ge.3600) jpk=4080 !### + if(nsps.ge.3600) jpk=6000 !### call spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) call pctile(s3,LL*NN,40,base) s3=s3/base @@ -113,32 +96,18 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & nsave=nsave+1 endif if(iavg.eq.0 .and. nsave.lt.2) exit - enddo ! iavg - -100 if(napmin.ne.99) then - dat4=dat4x - b90=b90x - snr2=snr2x - irc=irckeep - xdt=xdtkeep - f0=f0keep - idt=idtkeep - idf=idfkeep - ibw=ibwkeep - ndist=ndistx - endif + enddo ! iavg -200 if(mode.eq.65 .and. nsps.eq.7200/2) xdt=xdt+0.4 !### Empirical -- WHY ??? ### +100 if(mode.eq.65 .and. nsps.eq.7200/2) xdt=xdt+0.4 !### Empirical -- WHY ??? ### if(irc.ge.0) then navg=nsave if(iavg.eq.0) navg=0 !### For tests only: open(53,file='fort.53',status='unknown',position='append') - write(c77,1200) dat4 -1200 format(12b6.6,b5.5) + write(c77,1100) dat4 +1100 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent -! call unpackmsg(dat4,decoded) !Unpack the user message write(53,3053) idf,idt,ibw,b90,xdt,f0,snr2,ndist,irc,navg,decoded(1:22) 3053 format(3i5,f7.1,f7.2,2f7.1,3i4,2x,a22) close(53) From 20b6d65c61f7d79486048526127df35aa0fa7ede Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 28 Oct 2020 12:04:43 -0400 Subject: [PATCH 121/426] Allow WSJT-X to generate the new Q65 code. Loopback Q65 QSOs now work. --- widgets/mainwindow.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index dbb950df7..a771d8a7b 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -132,6 +132,9 @@ extern "C" { void genqra64_(char* msg, int* ichk, char* msgsent, int itone[], int* itext, fortran_charlen_t, fortran_charlen_t); + void genq65_(char* msg, int* ichk, char* msgsent, int itone[], + int* i3, int* n3, fortran_charlen_t, fortran_charlen_t); + void genwspr_(char* msg, char* msgsent, int itone[], fortran_charlen_t, fortran_charlen_t); void geniscat_(char* msg, char* msgsent, int itone[], fortran_charlen_t, fortran_charlen_t); @@ -3986,9 +3989,9 @@ void MainWindow::guiUpdate() if(m_modeTx=="QRA64") genqra64_(message, &ichk, msgsent, const_cast (itone), &m_currentMessageType, 22, 22); if(m_modeTx=="Q65") { - int ichk65=65; - genqra64_(message, &ichk65, msgsent, const_cast (itone), - &m_currentMessageType, 22, 22); + int i3=-1; + int n3=-1; + genq65_(message,&ichk,msgsent,const_cast(itone),&i3,&n3,37,37); } if(m_modeTx=="WSPR") genwspr_(message, msgsent, const_cast (itone), 22, 22); From 7786101dcf2c86c89e051baaf84e9bbcdc41a0d8 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 28 Oct 2020 13:38:06 -0400 Subject: [PATCH 122/426] Add a missing file. --- lib/qra/q65/genq65.f90 | 48 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 lib/qra/q65/genq65.f90 diff --git a/lib/qra/q65/genq65.f90 b/lib/qra/q65/genq65.f90 new file mode 100644 index 000000000..21c96bfaf --- /dev/null +++ b/lib/qra/q65/genq65.f90 @@ -0,0 +1,48 @@ +subroutine genq65(msg0,ichk,msgsent,itone,i3,n3) + +! Encodes a Q65 message to yield itone(1:85) + + use packjt77 + character*37 msg0 !Message to be generated + character*37 msgsent !Message as it will be received + character*77 c77 + logical unpk77_success + integer itone(85) !QRA64 uses only 84 + integer dgen(13) + integer sent(63) + integer isync(22) + data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ + save + + if(msg0(1:1).eq.'@') then + read(msg0(2:5),*,end=1,err=1) nfreq + go to 2 +1 nfreq=1000 +2 itone(1)=nfreq + write(msgsent,1000) nfreq +1000 format(i5,' Hz') + goto 999 + endif + i3=-1 + n3=-1 + call pack77(msg0,i3,n3,c77) + call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent + read(c77,1001) dgen +1001 format(12b6.6,b5.5) + if(ichk.eq.1) go to 999 !Return if checking only + call q65_enc(dgen,sent) !Encode message, dgen(1:13) ==> sent(1:63) + + j=1 + k=0 + do i=1,85 + if(i.eq.isync(j)) then + j=j+1 !Index for next sync symbol + itone(i)=0 !Insert sync symbol at tone 0 + else + k=k+1 + itone(i)=sent(k) + 1 !Q65 symbol=0 is transmitted at tone 1, etc. + endif + enddo + +999 return +end subroutine genq65 From e2978abcf5d74a9332c24c79f4e41cc6586501f0 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 29 Oct 2020 10:33:54 -0400 Subject: [PATCH 123/426] Pass ncontest and lapcqonly to the Q65 decoder. --- lib/decoder.f90 | 2 +- lib/q65_decode.f90 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 911352277..4c3b58c84 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -203,7 +203,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) call timer('dec_q65 ',0) call my_q65%decode(q65_decoded,id2,params%nutc,params%ntr, & params%nsubmode,params%nfqso,params%ntol,params%ndepth, & - mycall,hiscall,hisgrid) + mycall,hiscall,hisgrid,ncontest,logical(params%lapcqonly)) call timer('dec_q65 ',1) go to 800 endif diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index ec96ad4ec..bcd280b71 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -29,7 +29,7 @@ module q65_decode contains subroutine decode(this,callback,iwave,nutc,ntrperiod,nsubmode,nfqso, & - ntol,ndepth,mycall,hiscall,hisgrid) + ntol,ndepth,mycall,hiscall,hisgrid,ncontest,lapcqonly) ! Decodes Q65 signals ! Input: iwave Raw data, i*2 @@ -54,7 +54,7 @@ contains integer*2 iwave(NMAX) !Raw data real, allocatable :: dd(:) !Raw data integer dat4(13) !Decoded message as 12 6-bit integers - logical unpk77_success + logical lapcqonly,unpk77_success complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/,nsubmodez/-1/ From 1c30b9722857157e6a9bb24f9f82f12bedc91861 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 29 Oct 2020 10:53:30 -0400 Subject: [PATCH 124/426] More adjustments for adding AP decoding to Q65. --- lib/decoder.f90 | 5 +++-- lib/q65_decode.f90 | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 4c3b58c84..49dca917b 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -202,8 +202,9 @@ subroutine multimode_decoder(ss,id2,params,nfsample) ! We're in Q65 mode call timer('dec_q65 ',0) call my_q65%decode(q65_decoded,id2,params%nutc,params%ntr, & - params%nsubmode,params%nfqso,params%ntol,params%ndepth, & - mycall,hiscall,hisgrid,ncontest,logical(params%lapcqonly)) + params%nsubmode,params%nfqso,params%ntol,params%ndepth, & + mycall,hiscall,hisgrid,params%nQSOProgress,ncontest, & + logical(params%lapcqonly)) call timer('dec_q65 ',1) go to 800 endif diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index bcd280b71..1a973dd7c 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -29,7 +29,7 @@ module q65_decode contains subroutine decode(this,callback,iwave,nutc,ntrperiod,nsubmode,nfqso, & - ntol,ndepth,mycall,hiscall,hisgrid,ncontest,lapcqonly) + ntol,ndepth,mycall,hiscall,hisgrid,nQSOprogress,ncontest,lapcqonly) ! Decodes Q65 signals ! Input: iwave Raw data, i*2 @@ -83,9 +83,8 @@ contains npts=ntrperiod*12000 baud=12000.0/nsps df1=12000.0/nfft1 - this%callback => callback + this%callback => callback if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning - b90=20.0 !8 to 25 is OK; not very critical nFadingModel=1 ! AP control could be done differently, but this works well: From f974751e42482f6a5d0deecb27bb6c31273e5636 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 30 Oct 2020 11:07:44 -0400 Subject: [PATCH 125/426] Q65 AP decoding is now (sort of?) working. Problem with i3 field, and maybe other problems... --- CMakeLists.txt | 1 + lib/q65_decode.f90 | 73 +++++++++++++---- lib/qra/q65/q65_ap.f90 | 164 ++++++++++++++++++++++++++++++++++++++ lib/qra/q65/q65_loops.f90 | 11 +-- 4 files changed, 227 insertions(+), 22 deletions(-) create mode 100644 lib/qra/q65/q65_ap.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index c001db3f4..4235643cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -497,6 +497,7 @@ set (wsjt_FSRCS lib/ps4.f90 lib/qra64a.f90 lib/qra_loops.f90 + lib/qra/q65/q65_ap.f90 lib/qra/q65/q65_loops.f90 lib/refspectrum.f90 lib/savec2.f90 diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 1a973dd7c..583d50ebd 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -51,9 +51,13 @@ contains character(len=6) :: hisgrid character*37 decoded !Decoded message character*77 c77 + character*78 c78 integer*2 iwave(NMAX) !Raw data real, allocatable :: dd(:) !Raw data integer dat4(13) !Decoded message as 12 6-bit integers + integer apsym0(58),aph10(10) + integer apmask1(78),apsymbols1(78) + integer apmask(13),apsymbols(13) logical lapcqonly,unpk77_success complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s @@ -101,40 +105,75 @@ contains call timer('sync_q65',1) irc=-1 - if(snr1.ge.2.5) then - jpk0=(xdt+1.0)*6000 !### - if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !### - if(jpk0.lt.0) jpk0=0 - fac=1.0/32767.0 - dd=fac*iwave - nmode=65 - call ana64(dd,npts,c00) + if(snr1.lt.2.5) go to 100 + jpk0=(xdt+1.0)*6000 !### + if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !### + if(jpk0.lt.0) jpk0=0 + fac=1.0/32767.0 + dd=fac*iwave + nmode=65 + call ana64(dd,npts,c00) + + call ft8apset(mycall,hiscall,ncontest,apsym0,aph10) + where(apsym0.eq.-1) apsym0=0 + + npasses=2 + if(nQSOprogress.eq.3 .or.nQSOprogress.eq.4) npasses=4 + if(nQSOprogress.eq.5) npasses=3 + if(lapcqonly) npasses=1 + do ipass=0,npasses +! print*,'A',nQSOprogress,ipass,npasses + apmask=0 + apsymbols=0 + if(ipass.ge.1) then + call q65_ap(nQSOprogress,ipass,ncontest,lapcqonly,apsym0,apmask1, & + apsymbols1) + write(c78,1050) apmask1 +1050 format(78i1) + c78(75:78)=' ' + read(c78,1060) apmask +1060 format(13b6.6) + write(c78,1050) apsymbols1 + read(c78,1060) apsymbols + +! write(72,3060) 'A',ipass,apmask,apmask +!3060 format(a1,i1,1x,13b6.6/3x,13i6) +! write(72,3060) 'B',ipass,apsymbols,apsymbols + endif + call timer('q65loops',0) call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode,nFadingModel, & - ndepth,jpk0,xdt,f0,width,snr2,irc,dat4) + ndepth,jpk0,xdt,f0,width,ipass,apmask,apsymbols,snr2,irc,dat4) call timer('q65loops',1) snr2=snr2 + db(6912.0/nsps) - endif - decoded=' ' + if(irc.ge.0) exit + enddo + +100 decoded=' ' if(irc.ge.0) then - irc=(irc/100) * 100 !### TEMPORARY ??? ### +!### +! irc=(irc/100) * 100 !### TEMPORARY ??? ### + navg=irc/100 + irc=ipass +!### write(c77,1000) dat4 1000 format(12b6.6,b5.5) + +! write(72,3080) 'C',ipass,c77,'0' +!3080 format(a1,i1,1x,a77,a1) +! write(72,3060) 'C',ipass,dat4,dat4 + call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent nsnr=nint(snr2) call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) else + ! Report sync, even if no decode. nsnr=db(snr1) - 35.0 -!### TEMPORARY? ### call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) -!### endif -! write(61,3061) nutc,irc,xdt,f0,snr1,snr2,trim(decoded) -!3061 format(i6.6,i4,4f10.2,2x,a) - return end subroutine decode diff --git a/lib/qra/q65/q65_ap.f90 b/lib/qra/q65/q65_ap.f90 new file mode 100644 index 000000000..1c9fe1517 --- /dev/null +++ b/lib/qra/q65/q65_ap.f90 @@ -0,0 +1,164 @@ +subroutine q65_ap(nQSOprogress,ipass,ncontest,lapcqonly,apsym0,apmask,apsymbols) + + integer apsym0(58),aph10(10) + integer apmask(78),apsymbols(78) + integer naptypes(0:5,4) ! (nQSOProgress, ipass) maximum of 4 passes for now + integer mcqru(29),mcqfd(29),mcqtest(29),mcqww(29) + integer mcq(29),mrrr(19),m73(19),mrr73(19) + logical lapcqonly,first + 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 mcqru/0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,0,0,1,1,0,0/ + data mcqfd/0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0/ + data mcqtest/0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,1,0,1,1,1,1,1,1,0,0,1,0/ + data mcqww/0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,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 ncontest0/99/ + data first/.true./ + save naptypes,ncontest0 + +! nQSOprogress +! 0 CALLING +! 1 REPLYING +! 2 REPORT +! 3 ROGER_REPORT +! 4 ROGERS +! 5 SIGNOFF + + if(first.or.(ncontest.ne.ncontest0)) then +! iaptype +!------------------------ +! 1 CQ ??? ??? (29+4=33 ap bits) +! 2 MyCall ??? ??? (29+4=33 ap bits) +! 3 MyCall DxCall ??? (58+4=62 ap bits) +! 4 MyCall DxCall RRR (78 ap bits) +! 5 MyCall DxCall 73 (78 ap bits) +! 6 MyCall DxCall RR73 (78 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,4,5,6/) ! Tx3 + naptypes(4,1:4)=(/3,4,5,6/) ! Tx4 + naptypes(5,1:4)=(/3,1,2,0/) ! Tx5 + first=.false. + ncontest0=ncontest + endif + + apsymbols=0 + iaptype=naptypes(nQSOProgress,ipass) + if(lapcqonly) iaptype=1 + +! ncontest=0 : NONE +! 1 : NA_VHF +! 2 : EU_VHF +! 3 : FIELD DAY +! 4 : RTTY +! 5 : WW_DIGI +! 6 : FOX +! 7 : HOUND + +! Conditions that cause us to bail out of AP decoding +! if(ncontest.le.5 .and. iaptype.ge.3 .and. (abs(f1-nfqso).gt.napwid .and. abs(f1-nftx).gt.napwid) ) goto 900 +! if(ncontest.eq.6) goto 900 !No AP for Foxes +! if(ncontest.eq.7.and.f1.gt.950.0) goto 900 !Hounds use AP only below 950 Hz + if(ncontest.ge.6) goto 900 + if(iaptype.ge.2 .and. apsym0(1).gt.1) goto 900 !No, or nonstandard, mycall + if(ncontest.eq.7 .and. iaptype.ge.2 .and. aph10(1).gt.1) goto 900 + if(iaptype.ge.3 .and. apsym0(30).gt.1) goto 900 !No, or nonstandard, dxcall + + if(iaptype.eq.1) then ! CQ or CQ RU or CQ TEST or CQ FD + apmask=0 + apmask(1:29)=1 + if(ncontest.eq.0) apsymbols(1:29)=mcq + if(ncontest.eq.1) apsymbols(1:29)=mcqtest + if(ncontest.eq.2) apsymbols(1:29)=mcqtest + if(ncontest.eq.3) apsymbols(1:29)=mcqfd + if(ncontest.eq.4) apsymbols(1:29)=mcqru + if(ncontest.eq.5) apsymbols(1:29)=mcqww + if(ncontest.eq.7) apsymbols(1:29)=mcq + apmask(75:78)=1 + apsymbols(75:78)=(/0,0,1,0/) + endif + + if(iaptype.eq.2) then ! MyCall,???,??? + apmask=0 + if(ncontest.eq.0.or.ncontest.eq.1.or.ncontest.eq.5) then + apmask(1:29)=1 + apsymbols(1:29)=apsym0(1:29) + apmask(75:78)=1 + apsymbols(75:78)=(/0,0,1,0/) + else if(ncontest.eq.2) then + apmask(1:28)=1 + apsymbols(1:28)=apsym0(1:28) + apmask(72:74)=1 + apsymbols(72)=0 + apsymbols(73)=(+1) + apsymbols(74)=0 + apmask(75:78)=1 + apsymbols(75:78)=0 + else if(ncontest.eq.3) then + apmask(1:28)=1 + apsymbols(1:28)=apsym0(1:28) + apmask(75:78)=1 + apsymbols(75:78)=0 + else if(ncontest.eq.4) then + apmask(2:29)=1 + apsymbols(2:29)=apsym0(1:28) + apmask(75:78)=1 + apsymbols(75:78)=(/0,0,1,0/) + else if(ncontest.eq.7) then ! ??? RR73; MyCall ??? + apmask(29:56)=1 + apsymbols(29:56)=apsym0(1:28) + apmask(57:66)=1 + apsymbols(57:66)=aph10(1:10) + apmask(72:78)=1 + apsymbols(72:74)=(/0,0,1/) + apsymbols(75:78)=0 + endif + endif + + if(iaptype.eq.3) then ! MyCall,DxCall,??? + apmask=0 + if(ncontest.eq.0.or.ncontest.eq.1.or.ncontest.eq.2.or.ncontest.eq.5.or.ncontest.eq.7) then + apmask(1:58)=1 + apsymbols(1:58)=apsym0 + apmask(75:78)=1 + apsymbols(75:78)=(/0,0,1,0/) + else if(ncontest.eq.3) then ! Field Day + apmask(1:56)=1 + apsymbols(1:28)=apsym0(1:28) + apsymbols(29:56)=apsym0(30:57) + apmask(72:78)=1 + apsymbols(75:78)=0 + else if(ncontest.eq.4) then + apmask(2:57)=1 + apsymbols(2:29)=apsym0(1:28) + apsymbols(30:57)=apsym0(30:57) + apmask(75:78)=1 + apsymbols(75:78)=(/0,0,1,0/) + endif + endif + + if(iaptype.eq.5.and.ncontest.eq.7) goto 900 !Hound + if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype.eq.6) then + apmask=0 + if(ncontest.le.5 .or. (ncontest.eq.7.and.iaptype.eq.6)) then + apmask(1:78)=1 ! mycall, hiscall, RRR|73|RR73 + apsymbols(1:58)=apsym0 + if(iaptype.eq.4) apsymbols(59:77)=mrrr + if(iaptype.eq.5) apsymbols(59:77)=m73 + if(iaptype.eq.6) apsymbols(59:77)=mrr73 + else if(ncontest.eq.7.and.iaptype.eq.4) then ! Hound listens for MyCall RR73;... + apmask(1:28)=1 + apsymbols(1:28)=apsym0(1:28) + apmask(57:66)=1 + apsymbols(57:66)=aph10(1:10) + apmask(72:78)=1 + apsymbols(72:78)=(/0,0,1,0,0,0,0/) + endif + endif + +900 return +end subroutine q65_ap diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 0bb0b27d4..b468b00ce 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,5 +1,5 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & - ndepth,jpk0,xdt,f0,width,snr2,irc,dat4) + ndepth,jpk0,xdt,f0,width,ipass,APmask,APsymbols,snr2,irc,dat4) use packjt77 use timer_module, only: timer @@ -69,8 +69,8 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & ncall=ncall+1 call timer('qra64_de',0) call q65_intrinsics_ff(s3,nsubmode,b90,nFadingModel,s3prob) - APmask=0 - APsymbols=0 +! APmask=0 +! APsymbols=0 call q65_dec(s3,s3prob,APmask,APsymbols,snr2,dat4,irc) ! irc > 0 ==> number of iterations required to decode ! -1 = invalid params @@ -108,8 +108,9 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & write(c77,1100) dat4 1100 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent - write(53,3053) idf,idt,ibw,b90,xdt,f0,snr2,ndist,irc,navg,decoded(1:22) -3053 format(3i5,f7.1,f7.2,2f7.1,3i4,2x,a22) + write(53,3053) idf,idt,ibw,b90,xdt,f0,snr2,ndist,irc,ipass,navg, & + trim(decoded) +3053 format(3i5,f7.1,f7.2,2f7.1,4i4,2x,a) close(53) !### nsave=0 From 306c0c863354aa210e0d6b4997c1030a2503b509 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 30 Oct 2020 12:02:15 -0400 Subject: [PATCH 126/426] Limit the Q65 DT search rnge to +/- 1 s for TR=15, 30 s. --- lib/q65_decode.f90 | 1 - lib/qra/q65/q65_loops.f90 | 2 -- lib/sync_q65.f90 | 12 +++++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 583d50ebd..029d399d9 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -140,7 +140,6 @@ contains !3060 format(a1,i1,1x,13b6.6/3x,13i6) ! write(72,3060) 'B',ipass,apsymbols,apsymbols endif - call timer('q65loops',0) call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode,nFadingModel, & ndepth,jpk0,xdt,f0,width,ipass,apmask,apsymbols,snr2,irc,dat4) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index b468b00ce..8fef412d4 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -69,8 +69,6 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & ncall=ncall+1 call timer('qra64_de',0) call q65_intrinsics_ff(s3,nsubmode,b90,nFadingModel,s3prob) -! APmask=0 -! APsymbols=0 call q65_dec(s3,s3prob,APmask,APsymbols,snr2,dat4,irc) ! irc > 0 ==> number of iterations required to decode ! -1 = invalid params diff --git a/lib/sync_q65.f90 b/lib/sync_q65.f90 index 8df6da47f..c52cca12f 100644 --- a/lib/sync_q65.f90 +++ b/lib/sync_q65.f90 @@ -74,13 +74,15 @@ subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) enddo dtstep=nsps/(NSTEP*12000.0) !Step size in seconds - j0=0.5/dtstep - if(nsps.ge.6192) j0=1.0/dtstep !Nominal index for start of signal - - ccf=0. ia=min(64,nint(ntol/df)) lag1=-1.0/dtstep - lag2=4.0/dtstep + 0.9999 + lag2=1.0/dtstep + 0.9999 + j0=0.5/dtstep + if(nsps.ge.6192) then + j0=1.0/dtstep !Nominal index for start of signal + lag2=4.0/dtstep + 0.9999 !Include EME delays + endif + ccf=0. do lag=lag1,lag2 do k=1,85 From b3f0aec4075fc0ff995b63256971b017fa255ced Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 30 Oct 2020 14:52:58 -0400 Subject: [PATCH 127/426] Q65: compute snr from esnodb, using actual symbol rate. --- lib/qra/q65/q65_loops.f90 | 7 ++++--- lib/qra/q65/q65_subs.c | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 8fef412d4..f476ce0fc 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -69,7 +69,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & ncall=ncall+1 call timer('qra64_de',0) call q65_intrinsics_ff(s3,nsubmode,b90,nFadingModel,s3prob) - call q65_dec(s3,s3prob,APmask,APsymbols,snr2,dat4,irc) + call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) ! irc > 0 ==> number of iterations required to decode ! -1 = invalid params ! -2 = decode failed @@ -84,8 +84,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & a(1)=-f0 call twkfreq(c00,c0,npts2,6000.0,a) jpk=3000 !### These definitions need work ### -! if(nsps.ge.3600) jpk=4080 !### - if(nsps.ge.3600) jpk=6000 !### + if(nsps.ge.3600) jpk=6000 !### TR >= 60 s call spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) call pctile(s3,LL*NN,40,base) s3=s3/base @@ -100,6 +99,8 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & if(irc.ge.0) then navg=nsave + baud=6000.0/nsps + snr2=esnodb - db(2500.0/baud) if(iavg.eq.0) navg=0 !### For tests only: open(53,file='fort.53',status='unknown',position='append') diff --git a/lib/qra/q65/q65_subs.c b/lib/qra/q65/q65_subs.c index ffd4c0385..b36adc540 100644 --- a/lib/qra/q65/q65_subs.c +++ b/lib/qra/q65/q65_subs.c @@ -77,14 +77,14 @@ void q65_intrinsics_ff_(float s3[], int* submode, float* B90, } void q65_dec_(float s3[], float s3prob[], int APmask[], int APsymbols[], - float* snr2500, int xdec[], int* rc0) + float* esnodb0, int xdec[], int* rc0) { /* Input: s3prob[LL,NN] Symbol-value intrinsic probabilities * APmask[13] AP information to be used in decoding * APsymbols[13] Available AP informtion * Output: - * snr2500 SNR_2500 of decoded signal, or lower limit + * esnodb0 Estimated Es/No in dB * xdec[13] Decoded 78-bit message as 13 six-bit integers * rc0 Return code from q65_decode() */ @@ -98,7 +98,7 @@ void q65_dec_(float s3[], float s3prob[], int APmask[], int APsymbols[], // rc = -1: Invalid params // rc = -2: Decode failed // rc = -3: CRC mismatch - *snr2500 = -31.0; + *esnodb0 = 0.0; //Default Es/No for a failed decode if(rc<0) return; rc = q65_esnodb_fastfading(&codec,&esnodb,ydec,s3); @@ -106,5 +106,5 @@ void q65_dec_(float s3[], float s3prob[], int APmask[], int APsymbols[], printf("error in q65_esnodb_fastfading()\n"); exit(0); } - *snr2500 = esnodb - 31.0; + *esnodb0 = esnodb; } From abbb2777934a38d49bb912ddd71e58b788bdf2b0 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 31 Oct 2020 11:15:03 -0400 Subject: [PATCH 128/426] Fix the issue with using AP for the "i3" bit and 78th bit. --- lib/q65_decode.f90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 029d399d9..664e63100 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -130,12 +130,11 @@ contains apsymbols1) write(c78,1050) apmask1 1050 format(78i1) - c78(75:78)=' ' read(c78,1060) apmask 1060 format(13b6.6) write(c78,1050) apsymbols1 read(c78,1060) apsymbols - + apsymbols(13)=apsymbols(13)/2 !Fixup for c77-->c78 ! write(72,3060) 'A',ipass,apmask,apmask !3060 format(a1,i1,1x,13b6.6/3x,13i6) ! write(72,3060) 'B',ipass,apsymbols,apsymbols From 1768971931698a5bf3a57f022cd914a3b2282e6c Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 31 Oct 2020 11:33:01 -0400 Subject: [PATCH 129/426] Add timer calls for two of Nico's routines. --- lib/qra/q65/q65_loops.f90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index f476ce0fc..41f468595 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -67,14 +67,17 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & if(b90.gt.230.0) cycle ! if(b90.lt.0.15*width) exit ncall=ncall+1 - call timer('qra64_de',0) + call timer('q65_intr',0) call q65_intrinsics_ff(s3,nsubmode,b90,nFadingModel,s3prob) + call timer('q65_intr',1) + + call timer('q65_dec ',0) call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) + call timer('q65_dec ',1) ! irc > 0 ==> number of iterations required to decode ! -1 = invalid params ! -2 = decode failed ! -3 = CRC mismatch - call timer('qra64_de',1) if(irc.ge.0) go to 100 enddo ! ibw (b90 loop) enddo ! idt (DT loop) From fa92799bdad103a524059ae5ad7d1b5a049f362d Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 2 Nov 2020 15:59:10 -0500 Subject: [PATCH 130/426] OPtimize the --- lib/q65_decode.f90 | 28 +++++----------------------- lib/qra/q65/q65_loops.f90 | 32 ++++++++++++++++---------------- lib/qra64a.f90 | 2 +- lib/sync_q65.f90 | 12 ++++++++++-- 4 files changed, 32 insertions(+), 42 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 664e63100..0e3b4f47a 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -61,8 +61,6 @@ contains logical lapcqonly,unpk77_success complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s - data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/,nsubmodez/-1/ - save nc1z,nc2z,ng2z,maxaptypez,nsubmodez mode65=2**nsubmode nfft1=ntrperiod*12000 @@ -90,23 +88,16 @@ contains this%callback => callback if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning nFadingModel=1 - -! AP control could be done differently, but this works well: - maxaptype=0 -! if(ndepth.eq.2) maxaptype=3 -! if(ndepth.eq.3) maxaptype=5 - if(ndepth.ge.2) maxaptype=5 !### - minsync=-2 call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) - naptype=maxaptype call timer('sync_q65',0) - call sync_q65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0,snr1) + call sync_q65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0, & + snr1,width) call timer('sync_q65',1) irc=-1 if(snr1.lt.2.5) go to 100 - jpk0=(xdt+1.0)*6000 !### + jpk0=(xdt+1.0)*6000 !### Is this OK? if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !### if(jpk0.lt.0) jpk0=0 fac=1.0/32767.0 @@ -122,7 +113,6 @@ contains if(nQSOprogress.eq.5) npasses=3 if(lapcqonly) npasses=1 do ipass=0,npasses -! print*,'A',nQSOprogress,ipass,npasses apmask=0 apsymbols=0 if(ipass.ge.1) then @@ -135,9 +125,6 @@ contains write(c78,1050) apsymbols1 read(c78,1060) apsymbols apsymbols(13)=apsymbols(13)/2 !Fixup for c77-->c78 -! write(72,3060) 'A',ipass,apmask,apmask -!3060 format(a1,i1,1x,13b6.6/3x,13i6) -! write(72,3060) 'B',ipass,apsymbols,apsymbols endif call timer('q65loops',0) call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode,nFadingModel, & @@ -149,24 +136,19 @@ contains 100 decoded=' ' if(irc.ge.0) then -!### -! irc=(irc/100) * 100 !### TEMPORARY ??? ### +!### navg=irc/100 irc=ipass !### write(c77,1000) dat4 1000 format(12b6.6,b5.5) -! write(72,3080) 'C',ipass,c77,'0' -!3080 format(a1,i1,1x,a77,a1) -! write(72,3060) 'C',ipass,dat4,dat4 - call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent nsnr=nint(snr2) call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) else - ! Report sync, even if no decode. +! Report sync, even if no decode. nsnr=db(snr1) - 35.0 call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & irc,qual,ntrperiod,fmid,w50) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 41f468595..d36ca395f 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -21,6 +21,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & data nap/0,2,3,2,3,4,2,3,6,4,6,6/,nsave/0/ save nsave,s3avg + ircbest=9999 allocate(c0(0:npts2-1)) irc=-99 s3lim=20. @@ -32,7 +33,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & LL=64*(mode64+2) NN=63 napmin=99 - ncall=0 + baud=6000.0/nsps do iavg=0,1 if(iavg.eq.1) then @@ -43,13 +44,13 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & ndf=idf/2 if(mod(idf,2).eq.0) ndf=-ndf a=0. - a(1)=-(f0+0.4*ndf) + a(1)=-(f0+0.5*baud*ndf) call twkfreq(c00,c0,npts2,6000.0,a) do idt=1,idtmax ndt=idt/2 if(iavg.eq.0) then if(mod(idt,2).eq.0) ndt=-ndt - jpk=jpk0 + 240*ndt !240/6000 = 0.04 s = tsym/32 + jpk=jpk0 + nsps*ndt/16 !tsym/16 if(jpk.lt.0) jpk=0 call timer('spec64 ',0) call spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) @@ -60,13 +61,14 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & else s3(1:LL*NN)=s3avg(1:LL*NN) endif - do ibw=ibwmax,ibwmin,-2 - ndist=ndf**2 + ndt**2 + ((ibwmax-ibw)/2)**2 + do ibw=ibwmin,ibwmax + nbw=ibw + ndist=ndf**2 + ndt**2 + ((nbw-2))**2 if(ndist.gt.maxdist) cycle - b90=1.728**ibw +! b90=1.728**ibw + b90=3.0**nbw if(b90.gt.230.0) cycle ! if(b90.lt.0.15*width) exit - ncall=ncall+1 call timer('q65_intr',0) call q65_intrinsics_ff(s3,nsubmode,b90,nFadingModel,s3prob) call timer('q65_intr',1) @@ -74,11 +76,11 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & call timer('q65_dec ',0) call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) call timer('q65_dec ',1) + if(irc.ge.0) go to 100 ! irc > 0 ==> number of iterations required to decode ! -1 = invalid params ! -2 = decode failed ! -3 = CRC mismatch - if(irc.ge.0) go to 100 enddo ! ibw (b90 loop) enddo ! idt (DT loop) enddo ! idf (f0 loop) @@ -86,7 +88,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & a=0. a(1)=-f0 call twkfreq(c00,c0,npts2,6000.0,a) - jpk=3000 !### These definitions need work ### + jpk=3000 !### Are these definitions OK? if(nsps.ge.3600) jpk=6000 !### TR >= 60 s call spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) call pctile(s3,LL*NN,40,base) @@ -98,26 +100,24 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & if(iavg.eq.0 .and. nsave.lt.2) exit enddo ! iavg -100 if(mode.eq.65 .and. nsps.eq.7200/2) xdt=xdt+0.4 !### Empirical -- WHY ??? ### - - if(irc.ge.0) then +100 if(irc.ge.0) then navg=nsave - baud=6000.0/nsps snr2=esnodb - db(2500.0/baud) if(iavg.eq.0) navg=0 - !### For tests only: +!### For tests only: open(53,file='fort.53',status='unknown',position='append') write(c77,1100) dat4 1100 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent - write(53,3053) idf,idt,ibw,b90,xdt,f0,snr2,ndist,irc,ipass,navg, & + write(53,3053) ndf,ndt,nbw,b90,xdt,f0,snr2,ndist,irc,ipass,navg, & trim(decoded) 3053 format(3i5,f7.1,f7.2,2f7.1,4i4,2x,a) close(53) - !### +!### nsave=0 s3avg=0. irc=irc + 100*navg endif + return end subroutine q65_loops diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index c0d501fd7..fb56390e5 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -22,7 +22,7 @@ subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & nft=99 if(nfqso.lt.nf1 .or. nfqso.gt.nf2) go to 900 - mycall=mycall_12(1:6) !### May need fixing ### + mycall=mycall_12(1:6) !### May need fixing? ### hiscall=hiscall_12(1:6) hisgrid=hisgrid_6(1:4) call packcall(mycall,nc1,ltext) diff --git a/lib/sync_q65.f90 b/lib/sync_q65.f90 index c52cca12f..2abe79184 100644 --- a/lib/sync_q65.f90 +++ b/lib/sync_q65.f90 @@ -1,4 +1,4 @@ -subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) +subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1,width) ! Detect and align with the Q65 sync vector, returning time and frequency ! offsets and SNR estimate. @@ -118,9 +118,17 @@ subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1) ! enddo ! do i=-ia,ia -! write(56,3056) i*df,ccf(i,0)/rms +! write(56,3056) i*df,ccf(i,jpk)/rms !3056 format(2f10.3) ! enddo +! flush(56) + + acf0=dot_product(ccf(-ia:ia,jpk),ccf(-ia:ia,jpk)) + do i=1,ia + acf=dot_product(ccf(-ia:ia,jpk),ccf(-ia+i:ia+i,jpk)) + if(acf.le.0.5*acf0) exit + enddo + width=i*1.414*df return end subroutine sync_q65 From 1b541082fdd1238917b91571b1841b30f13d5683 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 2 Nov 2020 16:04:46 -0500 Subject: [PATCH 131/426] Optimize the Q65 inner loops. Also more code cleanup. --- lib/qra64a.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 index fb56390e5..d4189827d 100644 --- a/lib/qra64a.f90 +++ b/lib/qra64a.f90 @@ -102,8 +102,8 @@ subroutine qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist) integer iparam(7) logical first,ex -! data iparam/3,5,3,11,0,9,30/ !Maximum effort - data iparam/2,5,3,11,3,9,10/ !Default values +! data iparam/3,5,11,11,0,11,60/ !Maximum effort + data iparam/3,5,7,7,0,4,15/ !Default values data first/.true./ save first,iparam From 2c51c97b14eedb4cc8e90b0200df9f8ed81817e4 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 3 Nov 2020 10:36:19 -0500 Subject: [PATCH 132/426] Increase the Q65 minimum snr1 from 2.5 to 2.8. --- lib/q65_decode.f90 | 4 ++-- lib/qra/q65/q65_loops.f90 | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 0e3b4f47a..ae878679e 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -96,7 +96,7 @@ contains call timer('sync_q65',1) irc=-1 - if(snr1.lt.2.5) go to 100 + if(snr1.lt.2.8) go to 100 jpk0=(xdt+1.0)*6000 !### Is this OK? if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !### if(jpk0.lt.0) jpk0=0 @@ -128,7 +128,7 @@ contains endif call timer('q65loops',0) call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode,nFadingModel, & - ndepth,jpk0,xdt,f0,width,ipass,apmask,apsymbols,snr2,irc,dat4) + ndepth,jpk0,xdt,f0,width,ipass,apmask,apsymbols,snr1,snr2,irc,dat4) call timer('q65loops',1) snr2=snr2 + db(6912.0/nsps) if(irc.ge.0) exit diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index d36ca395f..01fc56054 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,5 +1,5 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & - ndepth,jpk0,xdt,f0,width,ipass,APmask,APsymbols,snr2,irc,dat4) + ndepth,jpk0,xdt,f0,width,ipass,APmask,APsymbols,snr1,snr2,irc,dat4) use packjt77 use timer_module, only: timer @@ -105,14 +105,13 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & snr2=esnodb - db(2500.0/baud) if(iavg.eq.0) navg=0 !### For tests only: - open(53,file='fort.53',status='unknown',position='append') + open(53,file='fort.53',status='unknown') write(c77,1100) dat4 1100 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent write(53,3053) ndf,ndt,nbw,b90,xdt,f0,snr2,ndist,irc,ipass,navg, & - trim(decoded) -3053 format(3i5,f7.1,f7.2,2f7.1,4i4,2x,a) - close(53) + snr1,trim(decoded) +3053 format(3i4,f6.1,f6.2,f7.1,f6.1,4i4,f7.2,1x,a) !### nsave=0 s3avg=0. From a01ebab363467ca9a562d42cbc19166a77c933dd Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 3 Nov 2020 11:31:21 -0500 Subject: [PATCH 133/426] Improve sync_q65() for larger values of FTol. --- lib/sync_q65.f90 | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/sync_q65.f90 b/lib/sync_q65.f90 index 2abe79184..68cb5d919 100644 --- a/lib/sync_q65.f90 +++ b/lib/sync_q65.f90 @@ -15,10 +15,10 @@ subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1,width) parameter (NSTEP=8) !Step size nsps/NSTEP integer*2 iwave(0:nmax-1) !Raw data integer isync(22) !Indices of sync symbols - integer ijpk(2) !Indices i and j at peak of ccf real, allocatable :: s1(:,:) !Symbol spectra, quarter-symbol steps + real, allocatable :: ccf(:,:) !CCF(freq,lag) + real, allocatable :: ccf1(:) !CCF(freq) at best lag real sync(85) !sync vector - real ccf(-64:64,-53:214) !CCF(freq,time) complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ data sync(1)/99.0/ @@ -31,9 +31,12 @@ subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1,width) txt=85.0*nsps/12000.0 jz=(txt+1.0)*12000.0/istep !Number of quarter-symbol steps if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher + ia=ntol/df allocate(s1(iz,jz)) allocate(c0(0:nfft-1)) + allocate(ccf(-ia:ia,-53:214)) + allocate(ccf1(-ia:ia)) if(sync(1).eq.99.0) then !Generate the sync vector sync=-22.0/63.0 !Sync tone OFF @@ -74,7 +77,7 @@ subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1,width) enddo dtstep=nsps/(NSTEP*12000.0) !Step size in seconds - ia=min(64,nint(ntol/df)) + ia=ntol/df lag1=-1.0/dtstep lag2=1.0/dtstep + 0.9999 j0=0.5/dtstep @@ -94,9 +97,19 @@ subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1,width) enddo enddo - ijpk=maxloc(ccf) - ipk=ijpk(1)-65 - jpk=ijpk(2)-54 + ic=ntol/df + ccfmax=0. + ipk=0 + jpk=0 + do i=-ic,ic + do j=lag1,lag2 + if(ccf(i,j).gt.ccfmax) then + ipk=i + jpk=j + ccfmax=ccf(i,j) + endif + enddo + enddo f0=nfqso + ipk*df xdt=jpk*dtstep @@ -123,9 +136,10 @@ subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1,width) ! enddo ! flush(56) - acf0=dot_product(ccf(-ia:ia,jpk),ccf(-ia:ia,jpk)) + ccf1=ccf(-ia:ia,jpk) + acf0=dot_product(ccf1,ccf1) do i=1,ia - acf=dot_product(ccf(-ia:ia,jpk),ccf(-ia+i:ia+i,jpk)) + acf=dot_product(ccf1,cshift(ccf1,i)) if(acf.le.0.5*acf0) exit enddo width=i*1.414*df From 6d8958bce6f2f80485a64452eb4ac0b0e07a7d3b Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 3 Nov 2020 15:49:11 -0500 Subject: [PATCH 134/426] Code cleanup. --- lib/qra/q65/q65_loops.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 01fc56054..eacd99549 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -12,11 +12,10 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & real s3(LN) !Symbol spectra real s3avg(LN) !Averaged symbol spectra real s3prob(LN) !Symbol-value probabilities - real s3tmp(4032) logical unpk77_success integer APmask(13) integer APsymbols(13) - integer dat4(13),dat4x(13) !Decoded message (as 13 six-bit integers) + integer dat4(13) !Decoded message (as 13 six-bit integers) integer nap(0:11) !AP return codes data nap/0,2,3,2,3,4,2,3,6,4,6,6/,nsave/0/ save nsave,s3avg @@ -105,13 +104,14 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & snr2=esnodb - db(2500.0/baud) if(iavg.eq.0) navg=0 !### For tests only: - open(53,file='fort.53',status='unknown') + open(53,file='fort.53',status='unknown',position='append') write(c77,1100) dat4 1100 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent write(53,3053) ndf,ndt,nbw,b90,xdt,f0,snr2,ndist,irc,ipass,navg, & snr1,trim(decoded) 3053 format(3i4,f6.1,f6.2,f7.1,f6.1,4i4,f7.2,1x,a) + close(53) !### nsave=0 s3avg=0. From 860b0ce21e4529b34a4d994c417fa06de697ce97 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 3 Nov 2020 15:49:24 -0500 Subject: [PATCH 135/426] Include TRperiod in the Q65 status bar label. --- widgets/mainwindow.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index a771d8a7b..7476ab9e3 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -2360,14 +2360,13 @@ void MainWindow::createStatusBar() //createStatusBar void MainWindow::setup_status_bar (bool vhf) { auto submode = current_submode (); - if (vhf && submode != QChar::Null) - { - mode_label.setText (m_mode + " " + submode); - } - else - { - mode_label.setText (m_mode); - } + if (vhf && submode != QChar::Null) { + QString t{m_mode + " " + submode}; + if(m_mode=="Q65") t=m_mode + "-" + QString::number(m_TRperiod) + submode; + mode_label.setText (t); + } else { + mode_label.setText (m_mode); + } if ("ISCAT" == m_mode) { mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff9933}"); } else if ("JT9" == m_mode) { @@ -7620,6 +7619,7 @@ void MainWindow::on_sbTR_valueChanged(int value) if(m_transmitting) { on_stopTxButton_clicked(); } + on_sbSubmode_valueChanged(ui->sbSubmode->value()); statusUpdate (); } @@ -7644,14 +7644,13 @@ void MainWindow::on_sbSubmode_valueChanged(int n) m_nSubMode=n; m_wideGraph->setSubMode(m_nSubMode); auto submode = current_submode (); - if (submode != QChar::Null) - { - mode_label.setText (m_mode + " " + submode); - } - else - { - mode_label.setText (m_mode); - } + if (submode != QChar::Null) { + QString t{m_mode + " " + submode}; + if(m_mode=="Q65") t=m_mode + "-" + QString::number(m_TRperiod) + submode; + mode_label.setText (t); + } else { + mode_label.setText (m_mode); + } if(m_mode=="ISCAT") { if(m_nSubMode==0) ui->TxFreqSpinBox->setValue(1012); if(m_nSubMode==1) ui->TxFreqSpinBox->setValue(560); From 145ddb88210b66523ff49b207fabdb7e4788eb21 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 3 Nov 2020 17:17:21 -0500 Subject: [PATCH 136/426] Correct the false-decode test in test_q65. --- lib/test_q65.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index ead15b013..1191069a6 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -109,7 +109,7 @@ program test_q65 if(index(line,' Date: Tue, 10 Nov 2020 10:14:41 -0500 Subject: [PATCH 137/426] Reactivate Q65 message averaging, and fix the reporting of those decodes. --- lib/q65_decode.f90 | 3 +-- lib/qra/q65/q65_loops.f90 | 7 ++++--- lib/sumsim.f90 | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index ae878679e..94ca0fff4 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -138,11 +138,10 @@ contains if(irc.ge.0) then !### navg=irc/100 - irc=ipass + irc=100*navg + ipass !### write(c77,1000) dat4 1000 format(12b6.6,b5.5) - call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent nsnr=nint(snr2) call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index eacd99549..0460fb621 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -47,7 +47,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & call twkfreq(c00,c0,npts2,6000.0,a) do idt=1,idtmax ndt=idt/2 - if(iavg.eq.0) then + if(ipass.eq.0 .and. iavg.eq.0) then if(mod(idt,2).eq.0) ndt=-ndt jpk=jpk0 + nsps*ndt/16 !tsym/16 if(jpk.lt.0) jpk=0 @@ -57,7 +57,8 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & call pctile(s3,LL*NN,40,base) s3=s3/base where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim - else + endif + if(iavg.eq.1) then s3(1:LL*NN)=s3avg(1:LL*NN) endif do ibw=ibwmin,ibwmax @@ -83,7 +84,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & enddo ! ibw (b90 loop) enddo ! idt (DT loop) enddo ! idf (f0 loop) - if(iavg.eq.0) then + if(ipass.eq.0 .and. iavg.eq.0) then a=0. a(1)=-f0 call twkfreq(c00,c0,npts2,6000.0,a) diff --git a/lib/sumsim.f90 b/lib/sumsim.f90 index e05f8d4d0..dba801470 100644 --- a/lib/sumsim.f90 +++ b/lib/sumsim.f90 @@ -24,14 +24,15 @@ program sumsim nfsample=h%nsamrate read(10) iwave(1:npts) n=len(trim(fname)) - wave(1:npts)=wave(1:npts)+iwave(1:npts) + wave(1:npts)=wave(1:npts) + iwave(1:npts) rms=sqrt(dot_product(wave(1:npts),wave(1:npts))/npts) write(*,1000) ifile,npts,float(npts)/nfsample,rms,fname(n-14:n) 1000 format(i3,i8,f6.1,f10.3,2x,a15) close(10) enddo - fac=1.0/sqrt(float(nargs)) +! fac=1.0/sqrt(float(nargs)) + fac=1.0/nargs iwave(1:npts)=nint(fac*wave(1:npts)) open(12,file='000000_0000.wav',access='stream',status='unknown') From 3068f0c61fc93765a272125737d6dc3428c5190a Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 11 Nov 2020 11:14:02 -0500 Subject: [PATCH 138/426] Use genwave() to create Q65 audio signal. Send "include averaging" flag to Q65 decoder. --- CMakeLists.txt | 1 + displayWidgets.txt | 2 +- lib/genwave.f90 | 52 +++++++++++++++++++++++++++++++++++++++ lib/q65_decode.f90 | 2 +- lib/qra/q65/q65_loops.f90 | 6 +++-- widgets/mainwindow.cpp | 30 ++++++++++++++++------ 6 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 lib/genwave.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 4235643cd..d4f630f47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -423,6 +423,7 @@ set (wsjt_FSRCS lib/gen65.f90 lib/gen9.f90 lib/geniscat.f90 + lib/genwave.f90 lib/ft8/genft8.f90 lib/qra/q65/genq65.f90 lib/genmsk_128_90.f90 diff --git a/displayWidgets.txt b/displayWidgets.txt index e23cc5e5d..2f176ae00 100644 --- a/displayWidgets.txt +++ b/displayWidgets.txt @@ -11,7 +11,7 @@ JT9+JT65 111010000001111000010000000000001000 JT65 111010000000111000010000000000001000 JT65/VHF 111110010000110110101100010000000000 QRA64 111110010110110110000000001000000000 -QRA65 111111010110110100010000001100000000 +Q65 111111010110110100011000001100000000 ISCAT 100111000000000110000000000000000000 MSK144 101111110100000000010001000000000000 WSPR 000000000000000001010000000000000000 diff --git a/lib/genwave.f90 b/lib/genwave.f90 new file mode 100644 index 000000000..7db4cc7de --- /dev/null +++ b/lib/genwave.f90 @@ -0,0 +1,52 @@ +subroutine genwave(itone,nsym,nsps,nwave,fsample,hmod,f0,icmplx,cwave,wave) + + real wave(nwave) + complex cwave(nwave) + integer hmod + integer itone(nsym) + logical ex + real*8 dt,phi,dphi,twopi,freq,baud + + dt=1.d0/fsample + twopi=8.d0*atan(1.d0) + baud=fsample/nsps + +! Calculate the audio waveform + phi=0.d0 + if(icmplx.le.0) wave=0. + if(icmplx.eq.1) cwave=0. + k=0 + do j=1,nsym + freq=f0 + itone(j)*hmod*baud + dphi=twopi*freq*dt + do i=1,nsps + k=k+1 + if(icmplx.eq.1) then + cwave(k)=cmplx(cos(phi),sin(phi)) + else + wave(k)=sin(phi) + endif + phi=phi+dphi + if(phi.gt.twopi) phi=phi-twopi + enddo + enddo + +!### TEMPORARY code to allow transmitting both A and B submodes + inquire(file='Q65_Tx2',exist=ex) + if(ex) then + k=0 + do j=1,nsym + freq=f0 + itone(j)*2.d0*hmod*baud + 500.d0 + dphi=twopi*freq*dt + do i=1,nsps + k=k+1 + wave(k)=0.5*(wave(k)+sin(phi)) + phi=phi+dphi + if(phi.gt.twopi) phi=phi-twopi + enddo + enddo + endif +!### + + return +end subroutine genwave diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 94ca0fff4..6566924e2 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -38,7 +38,7 @@ contains ! nsubmode Tone-spacing indicator, 0-4 for A-E ! nfqso Target signal frequency (Hz) ! ntol Search range around nfqso (Hz) -! ndepth Optional decoding level (???) +! ndepth Optional decoding level ! Output: sent to the callback routine for display to user use timer_module, only: timer diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 0460fb621..e72b38dbb 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -33,8 +33,10 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & NN=63 napmin=99 baud=6000.0/nsps - - do iavg=0,1 + + maxavg=0 + if(iand(ndepth,16).ne.0) maxavg=1 + do iavg=0,maxavg if(iavg.eq.1) then idfmax=1 idtmax=1 diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 7476ab9e3..9c76fbe51 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -117,6 +117,9 @@ extern "C" { void gen_fst4wave_(int itone[], int* nsym, int* nsps, int* nwave, float* fsample, int* hmod, float* f0, int* icmplx, float xjunk[], float wave[]); + void genwave_(int itone[], int* nsym, int* nsps, int* nwave, float* fsample, + int* hmod, float* f0, int* icmplx, float xjunk[], float wave[]); + void gen4_(char* msg, int* ichk, char* msgsent, int itone[], int* itext, fortran_charlen_t, fortran_charlen_t); @@ -3991,6 +3994,20 @@ void MainWindow::guiUpdate() int i3=-1; int n3=-1; genq65_(message,&ichk,msgsent,const_cast(itone),&i3,&n3,37,37); + int nsps=1800; + if(m_TRperiod==30) nsps=3600; + if(m_TRperiod==60) nsps=7200; + if(m_TRperiod==120) nsps=16000; + if(m_TRperiod==300) nsps=41472; + int nsps4=4*nsps; //48000 Hz sampling + int nsym=85; + float fsample=48000.0; + int nwave=(nsym+2)*nsps4; + int icmplx=0; + int hmod=1; + float f0=ui->TxFreqSpinBox->value()-m_XIT; + genwave_(const_cast(itone),&nsym,&nsps4,&nwave, + &fsample,&hmod,&f0,&icmplx,foxcom_.wave,foxcom_.wave); } if(m_modeTx=="WSPR") genwspr_(message, msgsent, const_cast (itone), 22, 22); @@ -6416,7 +6433,7 @@ void MainWindow::on_actionQ65_triggered() m_wideGraph->setTxFreq(ui->TxFreqSpinBox->value()); switch_mode (Modes::Q65); // 012345678901234567890123456789012345 - displayWidgets(nWidgets("111111010110110100010000001100000000")); + displayWidgets(nWidgets("111111010110110100011000000100000000")); statusChanged(); } @@ -6770,8 +6787,6 @@ void MainWindow::on_actionInclude_averaging_toggled (bool checked) m_ndepth ^= (-checked ^ m_ndepth) & 0x00000010; } - - void MainWindow::on_actionInclude_correlation_toggled (bool checked) { m_ndepth ^= (-checked ^ m_ndepth) & 0x00000020; @@ -7321,8 +7336,9 @@ void MainWindow::transmit (double snr) if(m_TRperiod==60) nsps=7200; if(m_TRperiod==120) nsps=16000; if(m_TRperiod==300) nsps=41472; - int mode65=pow(2.0,double(m_nSubMode)); - toneSpacing=mode65*12000.0/nsps; +// int mode65=pow(2.0,double(m_nSubMode)); +// toneSpacing=mode65*12000.0/nsps; + toneSpacing=-4.0; Q_EMIT sendMessage (m_mode, NUM_Q65_SYMBOLS, double(nsps), ui->TxFreqSpinBox->value () - m_XIT, toneSpacing, m_soundOutput, m_config.audio_output_channel (), @@ -7563,13 +7579,13 @@ void MainWindow::on_sbFtol_valueChanged(int value) void::MainWindow::VHF_features_enabled(bool b) { - if(m_mode!="JT4" and m_mode!="JT65") b=false; + if(m_mode!="JT4" and m_mode!="JT65" and m_mode!="Q65") b=false; if(b and (ui->actionInclude_averaging->isChecked() or ui->actionInclude_correlation->isChecked())) { ui->actionDeepestDecode->setChecked (true); } ui->actionInclude_averaging->setVisible (b); - ui->actionInclude_correlation->setVisible (b); + ui->actionInclude_correlation->setVisible (b && m_mode!="Q65"); ui->actionMessage_averaging->setEnabled(b); ui->actionEnable_AP_DXcall->setVisible (m_mode=="QRA64"); ui->actionEnable_AP_JT65->setVisible (b && m_mode=="JT65"); From 5fe6a539b6eef3b40cd680b5a3317f77a520e9a1 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 11 Nov 2020 15:06:24 -0500 Subject: [PATCH 139/426] Fix an error in defining dat4(13) in Q65 mode. Not backward compatible! Also fix AP and message averaging for Q65. --- lib/q65_decode.f90 | 29 ++++++++++++++++++++--------- lib/qra/q65/genq65.f90 | 1 + lib/qra/q65/q65_ap.f90 | 3 ++- lib/qra/q65/q65_loops.f90 | 25 +++++++++++++++---------- lib/qra/q65/q65sim.f90 | 4 ++-- 5 files changed, 40 insertions(+), 22 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 6566924e2..9e4bf38d8 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -88,8 +88,7 @@ contains this%callback => callback if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning nFadingModel=1 - call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) - +! call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) call timer('sync_q65',0) call sync_q65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0, & snr1,width) @@ -112,35 +111,47 @@ contains if(nQSOprogress.eq.3 .or.nQSOprogress.eq.4) npasses=4 if(nQSOprogress.eq.5) npasses=3 if(lapcqonly) npasses=1 + iaptype=0 do ipass=0,npasses +! write(54,3000) nQSOprogress,ipass +!3000 format(i1,i2) apmask=0 apsymbols=0 if(ipass.ge.1) then - call q65_ap(nQSOprogress,ipass,ncontest,lapcqonly,apsym0,apmask1, & - apsymbols1) + 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 - apsymbols(13)=apsymbols(13)/2 !Fixup for c77-->c78 +! write(54,3001) iaptype,c78 +!3001 format('a',i2,1x,a78) endif +! write(54,3002) apmask,apsymbols +!3002 format('b ',13b6.6/4x,13b6.6) call timer('q65loops',0) call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode,nFadingModel, & - ndepth,jpk0,xdt,f0,width,ipass,apmask,apsymbols,snr1,snr2,irc,dat4) + ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols,snr1,snr2, & + irc,dat4) call timer('q65loops',1) snr2=snr2 + db(6912.0/nsps) - if(irc.ge.0) exit + if(irc.ge.0) then +! write(54,3003) dat4,dat4 +!3003 format('c ',13b6.6,13i3) + exit + endif enddo 100 decoded=' ' if(irc.ge.0) then !### navg=irc/100 - irc=100*navg + ipass +! irc=100*navg + ipass + irc=100*navg + iaptype !### - write(c77,1000) dat4 + 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 nsnr=nint(snr2) diff --git a/lib/qra/q65/genq65.f90 b/lib/qra/q65/genq65.f90 index 21c96bfaf..300cbf070 100644 --- a/lib/qra/q65/genq65.f90 +++ b/lib/qra/q65/genq65.f90 @@ -29,6 +29,7 @@ subroutine genq65(msg0,ichk,msgsent,itone,i3,n3) call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent read(c77,1001) dgen 1001 format(12b6.6,b5.5) + dgen(13)=2*dgen(13) !Convert 77-bit to 78-bit payload if(ichk.eq.1) go to 999 !Return if checking only call q65_enc(dgen,sent) !Encode message, dgen(1:13) ==> sent(1:63) diff --git a/lib/qra/q65/q65_ap.f90 b/lib/qra/q65/q65_ap.f90 index 1c9fe1517..f0cfd72a2 100644 --- a/lib/qra/q65/q65_ap.f90 +++ b/lib/qra/q65/q65_ap.f90 @@ -1,4 +1,5 @@ -subroutine q65_ap(nQSOprogress,ipass,ncontest,lapcqonly,apsym0,apmask,apsymbols) +subroutine q65_ap(nQSOprogress,ipass,ncontest,lapcqonly,iaptype, & + apsym0,apmask,apsymbols) integer apsym0(58),aph10(10) integer apmask(78),apsymbols(78) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index e72b38dbb..e10f0a624 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,5 +1,5 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & - ndepth,jpk0,xdt,f0,width,ipass,APmask,APsymbols,snr1,snr2,irc,dat4) + ndepth,jpk0,xdt,f0,width,iaptype,APmask,APsymbols,snr1,snr2,irc,dat4) use packjt77 use timer_module, only: timer @@ -24,11 +24,16 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & allocate(c0(0:npts2-1)) irc=-99 s3lim=20. - ibwmax=11 - if(mode64.le.4) ibwmax=9 - ibwmin=ibwmax - idtmax=3 - call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) +! ibwmax=11 +! if(mode64.le.4) ibwmax=9 +! ibwmin=ibwmax +! idtmax=3 +! call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) + idfmax=5 + idtmax=5 + ibwmin=1 + ibwmax=2 + maxdist=15 LL=64*(mode64+2) NN=63 napmin=99 @@ -49,7 +54,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & call twkfreq(c00,c0,npts2,6000.0,a) do idt=1,idtmax ndt=idt/2 - if(ipass.eq.0 .and. iavg.eq.0) then + if(iaptype.eq.0 .and. iavg.eq.0) then if(mod(idt,2).eq.0) ndt=-ndt jpk=jpk0 + nsps*ndt/16 !tsym/16 if(jpk.lt.0) jpk=0 @@ -86,7 +91,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & enddo ! ibw (b90 loop) enddo ! idt (DT loop) enddo ! idf (f0 loop) - if(ipass.eq.0 .and. iavg.eq.0) then + if(iaptype.eq.0 .and. iavg.eq.0) then a=0. a(1)=-f0 call twkfreq(c00,c0,npts2,6000.0,a) @@ -108,10 +113,10 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & if(iavg.eq.0) navg=0 !### For tests only: open(53,file='fort.53',status='unknown',position='append') - write(c77,1100) dat4 + write(c77,1100) dat4(1:12),dat4(13)/2 1100 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent - write(53,3053) ndf,ndt,nbw,b90,xdt,f0,snr2,ndist,irc,ipass,navg, & + write(53,3053) ndf,ndt,nbw,b90,xdt,f0,snr2,ndist,irc,iaptype,navg, & snr1,trim(decoded) 3053 format(3i4,f6.1,f6.2,f7.1,f6.1,4i4,f7.2,1x,a) close(53) diff --git a/lib/qra/q65/q65sim.f90 b/lib/qra/q65/q65sim.f90 index 762c9ef87..f99ab4f1d 100644 --- a/lib/qra/q65/q65sim.f90 +++ b/lib/qra/q65/q65sim.f90 @@ -85,8 +85,8 @@ program q65sim y(j)=itone(i)-1 endif enddo - write(*,1001) y(1:13) -1001 format('Generated message:'/13i3) + 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 From 8703b669835fcd9d7a791114a35ddb82742cf821 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 11 Nov 2020 15:20:35 -0500 Subject: [PATCH 140/426] Update test_q65.f90. --- lib/test_q65.f90 | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 1191069a6..50f378064 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -4,7 +4,7 @@ program test_q65 character*22 msg character*8 arg character*1 csubmode - integer nretcode(0:11) + integer naptype(0:6) logical decok nargs=iargc() @@ -77,15 +77,15 @@ program test_q65 cmd2(38:38)=csubmode call system('rm -f *.wav') - call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist) - write(*,1000) ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist - write(12,1000) ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist -1000 format(/'Depth:',i2,' AP:',i2,' df:',i3,' dt:',i3,' bw1:',i3,' bw2:',i3, & - ' dist:',i3) +! call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist) +! write(*,1000) ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist +! write(12,1000) ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist +!1000 format(/'Depth:',i2,' AP:',i2,' df:',i3,' dt:',i3,' bw1:',i3,' bw2:',i3, & +! ' dist:',i3) - write(*,1010) (j,j=0,11) - write(12,1010) (j,j=0,11) -1010 format('SNR d Dop Sync DecN Dec1 Bad',i6,11i4,' tdec'/85('-')) + write(*,1010) (j,j=0,6) + write(12,1010) (j,j=0,6) +1010 format('SNR nd Dop Sync DecN Dec1 Bad',i6,6i4,' tdec'/66('-')) dterr=tsym/4.0 nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) @@ -95,7 +95,7 @@ program test_q65 nsync=0 ndec1=0 nfalse=0 - nretcode=0 + naptype=0 ndecn=0 write(cmd1(63:65),'(i3)') nsnr call system(cmd1) @@ -108,7 +108,6 @@ program test_q65 read(10,'(a71)',end=10) line if(index(line,' Date: Fri, 13 Nov 2020 13:39:32 -0500 Subject: [PATCH 141/426] Restore submode on program restart. --- widgets/mainwindow.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 9c76fbe51..cf1e2a03e 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -983,7 +983,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, set_mode (m_mode); if(m_mode=="Echo") monitor(false); //Don't auto-start Monitor in Echo mode. - ui->sbSubmode->setValue (vhf ? m_nSubMode : 0); //Submodes require VHF features if(m_mode=="ISCAT" and !vhf) mode_label.setText("ISCAT A"); if(m_mode=="MSK144") { @@ -1238,6 +1237,7 @@ void MainWindow::readSettings() ui->sbF_Low->setValue(m_settings->value("FST4_FLow",600).toInt()); ui->sbF_High->setValue(m_settings->value("FST4_FHigh",1400).toInt()); m_nSubMode=m_settings->value("SubMode",0).toInt(); + ui->sbSubmode->setValue(m_nSubMode); ui->sbFtol->setValue (m_settings->value("Ftol", 50).toInt()); ui->sbFST4W_FTol->setValue(m_settings->value("FST4W_FTol",100).toInt()); m_minSync=m_settings->value("MinSync",0).toInt(); @@ -6424,7 +6424,8 @@ void MainWindow::on_actionQ65_triggered() on_sbTR_valueChanged (ui->sbTR->value()); //### ui->sbSubmode->setMaximum(4); ui->sbSubmode->setMaximum(7); - ui->sbSubmode->setValue(m_nSubMode); m_wideGraph->setMode(m_mode); + ui->sbSubmode->setValue(m_nSubMode); + m_wideGraph->setMode(m_mode); m_wideGraph->setMode(m_mode); m_wideGraph->setModeTx(m_modeTx); m_wideGraph->setPeriod(m_TRperiod,6912); From 91862e61e605d4e38d8e4c3c5704dbeaf1bd9d03 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 13 Nov 2020 13:40:16 -0500 Subject: [PATCH 142/426] Display Q65 values of DT and f0 from the actual decode, not just values from sync_q65. --- lib/q65_decode.f90 | 14 +++++--------- lib/qra/q65/q65_loops.f90 | 7 ++++++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 9e4bf38d8..d2a1e468e 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -133,15 +133,11 @@ contains !3002 format('b ',13b6.6/4x,13b6.6) call timer('q65loops',0) call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode,nFadingModel, & - ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols,snr1,snr2, & - irc,dat4) + ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols,snr1,xdt1,f1, & + snr2,irc,dat4) call timer('q65loops',1) snr2=snr2 + db(6912.0/nsps) - if(irc.ge.0) then -! write(54,3003) dat4,dat4 -!3003 format('c ',13b6.6,13i3) - exit - endif + if(irc.ge.0) exit enddo 100 decoded=' ' @@ -155,12 +151,12 @@ contains 1000 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent nsnr=nint(snr2) - call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & + call this%callback(nutc,sync,nsnr,xdt1,f1,decoded, & irc,qual,ntrperiod,fmid,w50) else ! Report sync, even if no decode. nsnr=db(snr1) - 35.0 - call this%callback(nutc,sync,nsnr,xdt,f0,decoded, & + call this%callback(nutc,sync,nsnr,xdt1,f1,decoded, & irc,qual,ntrperiod,fmid,w50) endif diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index e10f0a624..ebcbc7c94 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,5 +1,6 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & - ndepth,jpk0,xdt,f0,width,iaptype,APmask,APsymbols,snr1,snr2,irc,dat4) + ndepth,jpk0,xdt0,f0,width,iaptype,APmask,APsymbols,snr1,xdt1,f1, & + snr2,irc,dat4) use packjt77 use timer_module, only: timer @@ -38,6 +39,8 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & NN=63 napmin=99 baud=6000.0/nsps + xdt1=xdt0 + f1=f0 maxavg=0 if(iand(ndepth,16).ne.0) maxavg=1 @@ -111,6 +114,8 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & navg=nsave snr2=esnodb - db(2500.0/baud) if(iavg.eq.0) navg=0 + xdt1=xdt0 + nsps*ndt/(16.0*6000.0) + f1=f0 + 0.5*baud*ndf !### For tests only: open(53,file='fort.53',status='unknown',position='append') write(c77,1100) dat4(1:12),dat4(13)/2 From dc3d567ce8399f36e1b4adc061405515b8ee7958 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 13 Nov 2020 13:45:14 -0500 Subject: [PATCH 143/426] Correct the diagnostic output to fort.53. --- lib/qra/q65/q65_loops.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index ebcbc7c94..d870c21b7 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -121,7 +121,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & write(c77,1100) dat4(1:12),dat4(13)/2 1100 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent - write(53,3053) ndf,ndt,nbw,b90,xdt,f0,snr2,ndist,irc,iaptype,navg, & + write(53,3053) ndf,ndt,nbw,b90,xdt1,f1,snr2,ndist,irc,iaptype,navg, & snr1,trim(decoded) 3053 format(3i4,f6.1,f6.2,f7.1,f6.1,4i4,f7.2,1x,a) close(53) From 4903b38220652cc9257bb057d0814ee87deb8e8c Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 15 Nov 2020 11:58:36 -0500 Subject: [PATCH 144/426] When jt9 is run from command line, set Q65 default FTol = 10. --- lib/jt9.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/jt9.f90 b/lib/jt9.f90 index 2139c66aa..e9abd6480 100644 --- a/lib/jt9.f90 +++ b/lib/jt9.f90 @@ -51,7 +51,7 @@ program jt9 option ('fft-threads', .true., 'm', & 'Number of threads to process large FFTs, default THREADS=1', & 'THREADS'), & - option ('qra66', .false., '3', 'QRA66 mode', ''), & + option ('q65', .false., '3', 'Q65 mode', ''), & option ('jt4', .false., '4', 'JT4 mode', ''), & option ('ft4', .false., '5', 'FT4 mode', ''), & option ('jt65', .false.,'6', 'JT65 mode', ''), & @@ -263,6 +263,7 @@ program jt9 shared_data%params%nfsplit=fsplit shared_data%params%nfb=fhigh shared_data%params%ntol=20 + if(mode.eq.66) shared_data%params%ntol=10 shared_data%params%kin=64800 if(mode.eq.240) shared_data%params%kin=720000 !### 60 s periods ### shared_data%params%nzhsym=nhsym From 24ed64e5ccd09808c0990c0185bafb38b4e10958 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 15 Nov 2020 12:21:08 -0500 Subject: [PATCH 145/426] Add UTC and reformat diagnostic output to fort.53. --- lib/q65_decode.f90 | 24 ++++++++++++++++++------ lib/qra/q65/q65_loops.f90 | 11 +++++++---- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index d2a1e468e..40aef5f8b 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -63,9 +63,10 @@ contains complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s mode65=2**nsubmode + npts=ntrperiod*12000 nfft1=ntrperiod*12000 nfft2=ntrperiod*6000 - allocate(dd(NMAX)) + allocate(dd(npts)) allocate (c00(0:nfft1-1)) allocate (c0(0:nfft1-1)) @@ -82,7 +83,6 @@ contains else stop 'Invalid TR period' endif - npts=ntrperiod*12000 baud=12000.0/nsps df1=12000.0/nfft1 this%callback => callback @@ -100,7 +100,19 @@ contains if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !### if(jpk0.lt.0) jpk0=0 fac=1.0/32767.0 - dd=fac*iwave + dd=fac*iwave(1:npts) +!### +! Optionslly write noise level to LU 56 +! sq=dot_product(dd,dd)/npts +! m=nutc +! if(ntrperiod.ge.60) m=100*m +! ihr=m/10000 +! imin=mod(m/100,100) +! isec=mod(m,100) +! hours=ihr + imin/60.0 + isec/3600.0 +! write(56,3056) m,hours,db(sq)+90.3 +!3056 format(i6.6,f10.6,f10.3) +!### nmode=65 call ana64(dd,npts,c00) @@ -132,9 +144,9 @@ contains ! write(54,3002) apmask,apsymbols !3002 format('b ',13b6.6/4x,13b6.6) call timer('q65loops',0) - call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode,nFadingModel, & - ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols,snr1,xdt1,f1, & - snr2,irc,dat4) + call q65_loops(c00,nutc,npts/2,nsps/2,nmode,mode65,nsubmode, & + nFadingModel,ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols, & + snr1,xdt1,f1,snr2,irc,dat4) call timer('q65loops',1) snr2=snr2 + db(6912.0/nsps) if(irc.ge.0) exit diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index d870c21b7..06d8fcd04 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,4 +1,4 @@ -subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & +subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & ndepth,jpk0,xdt0,f0,width,iaptype,APmask,APsymbols,snr1,xdt1,f1, & snr2,irc,dat4) @@ -121,9 +121,12 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & write(c77,1100) dat4(1:12),dat4(13)/2 1100 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent - write(53,3053) ndf,ndt,nbw,b90,xdt1,f1,snr2,ndist,irc,iaptype,navg, & - snr1,trim(decoded) -3053 format(3i4,f6.1,f6.2,f7.1,f6.1,4i4,f7.2,1x,a) + if(nsps.lt.3600) write(53,3001) nutc,ndf,ndt,nbw,ndist,irc, & + iaptype,navg,snr1,xdt1,f1,snr2,trim(decoded) +3001 format(i6.6,7i4,f7.1,f7.2,2f7.1,1x,a) + if(nsps.ge.3600) write(53,3002) nutc,ndf,ndt,nbw,ndist,irc, & + iaptype,navg,snr1,xdt1,f1,snr2,trim(decoded) +3002 format(i4.4,2x,7i4,f7.1,f7.2,2f7.1,1x,a) close(53) !### nsave=0 From df2daf60bda6ee68df9114666a10ed0a235782fc Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 16 Nov 2020 10:06:10 -0500 Subject: [PATCH 146/426] Remove JT9+JT65 mode. Remove labNextCall. Tighten up central array of GUI controls. --- widgets/mainwindow.cpp | 102 +-------------- widgets/mainwindow.h | 2 - widgets/mainwindow.ui | 286 ++++++++++++++++++----------------------- 3 files changed, 127 insertions(+), 263 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index cf1e2a03e..c1904af58 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -601,7 +601,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, ui->actionFT8->setActionGroup(modeGroup); ui->actionJT9->setActionGroup(modeGroup); ui->actionJT65->setActionGroup(modeGroup); - ui->actionJT9_JT65->setActionGroup(modeGroup); ui->actionJT4->setActionGroup(modeGroup); ui->actionWSPR->setActionGroup(modeGroup); ui->actionEcho->setActionGroup(modeGroup); @@ -860,9 +859,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, m_msg[0][0]=0; ui->labDXped->setVisible(false); ui->labDXped->setStyleSheet("QLabel {background-color: red; color: white;}"); - ui->labNextCall->setText(""); - ui->labNextCall->setVisible(false); - ui->labNextCall->setToolTip(""); //### Possibly temporary ? ### char const * const power[] = {"1 mW","2 mW","5 mW","10 mW","20 mW","50 mW","100 mW","200 mW","500 mW", "1 W","2 W","5 W","10 W","20 W","50 W","100 W","200 W","500 W","1 kW"}; @@ -1225,8 +1221,6 @@ void MainWindow::readSettings() m_settings->beginGroup("Common"); m_mode=m_settings->value("Mode","JT9").toString(); m_modeTx=m_settings->value("ModeTx","JT9").toString(); - if(m_modeTx.mid(0,3)=="JT9") ui->pbTxMode->setText("Tx JT9 @"); - if(m_modeTx=="JT65") ui->pbTxMode->setText("Tx JT65 #"); ui->actionNone->setChecked(m_settings->value("SaveNone",true).toBool()); ui->actionSave_decoded->setChecked(m_settings->value("SaveDecoded",false).toBool()); ui->actionSave_all->setChecked(m_settings->value("SaveAll",false).toBool()); @@ -2121,8 +2115,6 @@ void MainWindow::keyPressEvent (QKeyEvent * e) return; case Qt::Key_Escape: m_nextCall=""; - ui->labNextCall->setStyleSheet(""); - ui->labNextCall->setText(""); on_stopTxButton_clicked(); abortQSO(); return; @@ -3469,11 +3461,6 @@ void MainWindow::readFromStdout() //readFromStdout ui->decodedTextBrowser2->displayDecodedText(decodedtext0,m_baseCall,m_mode,m_config.DXCC(), m_logBook,m_currentBand,m_config.ppfx()); } - if(m_mode!="JT4") { - bool b65=decodedtext.isJT65(); - if(b65 and m_modeTx!="JT65") on_pbTxMode_clicked(); - if(!b65 and m_modeTx=="JT65") on_pbTxMode_clicked(); - } m_QSOText = decodedtext.string ().trimmed (); } @@ -4384,8 +4371,6 @@ void MainWindow::useNextCall() { ui->dxCallEntry->setText(m_nextCall); m_nextCall=""; - ui->labNextCall->setStyleSheet(""); - ui->labNextCall->setText(""); if(m_nextGrid.contains(grid_regexp)) { ui->dxGridEntry->setText(m_nextGrid); m_ntx=2; @@ -4796,11 +4781,9 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie if (message.isJT9()) { m_modeTx="JT9"; - ui->pbTxMode->setText("Tx JT9 @"); m_wideGraph->setModeTx(m_modeTx); } else if (message.isJT65()) { m_modeTx="JT65"; - ui->pbTxMode->setText("Tx JT65 #"); m_wideGraph->setModeTx(m_modeTx); } } else if ((message.isJT9 () and m_modeTx != "JT9" and m_mode != "JT4") or @@ -5032,17 +5015,6 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie } } else { // nothing for us -// if(message_words.size () > 3 // enough fields for a normal message -// && SpecOp::RTTY == m_config.special_op_id() -// && (message_words.at(1).contains(m_baseCall) || "DE" == message_words.at(1)) -// && (!message_words.at(2).contains(qso_partner_base_call) and !bEU_VHF_w2)) { -//// Queue up the next QSO partner -// m_nextCall=message_words.at(2); -// m_nextGrid=message_words.at(3); -// m_nextRpt=message.report(); -// ui->labNextCall->setText("Next: " + m_nextCall); -// ui->labNextCall->setStyleSheet("QLabel {color: #000000; background-color: #66ff66}"); -// } return; } } @@ -5908,7 +5880,7 @@ void MainWindow::displayWidgets(qint64 n) if(i==8) ui->cbFast9->setVisible(b); if(i==9) ui->cbAutoSeq->setVisible(b); if(i==10) ui->cbTx6->setVisible(b); - if(i==11) ui->pbTxMode->setVisible(b); +// if(i==11) ui->pbTxMode->setVisible(b); if(i==12) ui->pbR2T->setVisible(b); if(i==13) ui->pbT2R->setVisible(b); if(i==14) ui->cbHoldTxFreq->setVisible(b); @@ -5929,7 +5901,7 @@ void MainWindow::displayWidgets(qint64 n) if(i==25) ui->actionEnable_AP_JT65->setVisible (b); if(i==26) ui->actionEnable_AP_DXcall->setVisible (b); if(i==27) ui->cbFirst->setVisible(b); - if(i==28) ui->labNextCall->setVisible(b); +// if(i==28) ui->labNextCall->setVisible(b); if(i==29) ui->measure_check_box->setVisible(b); if(i==30) ui->labDXped->setVisible(b); if(i==31) ui->cbRxAll->setVisible(b); @@ -6279,58 +6251,14 @@ void MainWindow::on_actionJT9_triggered() statusChanged(); } -void MainWindow::on_actionJT9_JT65_triggered() -{ - m_mode="JT9+JT65"; - WSPR_config(false); - switch_mode (Modes::JT65); - if(m_modeTx != "JT65") { - ui->pbTxMode->setText("Tx JT9 @"); - m_modeTx="JT9"; - } - m_nSubMode=0; //Dual-mode always means JT9 and JT65A - m_TRperiod=60.0; - m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe - m_detector->setTRPeriod(m_TRperiod); // TODO - not thread safe - m_nsps=6912; - m_FFTSize = m_nsps / 2; - Q_EMIT FFTSize (m_FFTSize); - m_hsymStop=174; - if(m_config.decode_at_52s()) m_hsymStop=183; - m_toneSpacing=0.0; - setup_status_bar (false); - ui->actionJT9_JT65->setChecked(true); - VHF_features_enabled(false); - m_wideGraph->setPeriod(m_TRperiod,m_nsps); - m_wideGraph->setMode(m_mode); - m_wideGraph->setModeTx(m_modeTx); - m_bFastMode=false; - m_bFast9=false; - ui->sbSubmode->setValue(0); - ui->lh_decodes_title_label->setText(tr ("Band Activity")); - ui->rh_decodes_title_label->setText(tr ("Rx Frequency")); - ui->lh_decodes_headings_label->setText("UTC dB DT Freq " + tr ("Message")); - ui->rh_decodes_headings_label->setText("UTC dB DT Freq " + tr ("Message")); - displayWidgets(nWidgets("111010000001111000010000000000001000")); - fast_config(false); - statusChanged(); -} - void MainWindow::on_actionJT65_triggered() { - if(m_mode=="JT4" or m_mode=="WSPR" or m_mode=="FST4W") { -// If coming from JT4, WSPR, or FST4W mode, pretend temporarily that we're coming -// from JT9 and click the pbTxMode button - m_modeTx="JT9"; - on_pbTxMode_clicked(); - } on_actionJT9_triggered(); m_mode="JT65"; m_modeTx="JT65"; bool bVHF=m_config.enable_VHF_features(); WSPR_config(false); switch_mode (Modes::JT65); - if(m_modeTx!="JT65") on_pbTxMode_clicked(); m_TRperiod=60.0; m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe m_detector->setTRPeriod(m_TRperiod); // TODO - not thread safe @@ -6482,7 +6410,6 @@ void MainWindow::on_actionMSK144_triggered() if("JT4"==m_mode) ui->actionJT4->setChecked(true); if("JT9"==m_mode) ui->actionJT9->setChecked(true); if("JT65"==m_mode) ui->actionJT65->setChecked(true); - if("JT9_JT65"==m_mode) ui->actionJT9_JT65->setChecked(true); if("ISCAT"==m_mode) ui->actionISCAT->setChecked(true); if("QRA64"==m_mode) ui->actionQRA64->setChecked(true); if("Q65"==m_mode) ui->actionQ65->setChecked(true); @@ -7067,21 +6994,6 @@ void MainWindow::on_readFreq_clicked() } } -void MainWindow::on_pbTxMode_clicked() -{ - if(m_mode=="JT9+JT65") { - if(m_modeTx=="JT9") { - m_modeTx="JT65"; - ui->pbTxMode->setText("Tx JT65 #"); - } else { - m_modeTx="JT9"; - ui->pbTxMode->setText("Tx JT9 @"); - } - m_wideGraph->setModeTx(m_modeTx); - statusChanged(); - } -} - void MainWindow::setXIT(int n, Frequency base) { if (m_transmitting && !m_config.tx_QSY_allowed ()) return; @@ -7560,15 +7472,6 @@ void MainWindow::transmitDisplay (bool transmitting) // the following are always disallowed in transmit ui->menuMode->setEnabled (!transmitting); - //ui->bandComboBox->setEnabled (!transmitting); - if (!transmitting) { - if (m_mode == "JT9+JT65") { - // allow mode switch in Rx when in dual mode - ui->pbTxMode->setEnabled (true); - } - } else { - ui->pbTxMode->setEnabled (false); - } } } @@ -9273,7 +9176,6 @@ void MainWindow::set_mode (QString const& mode) else if ("FT8" == mode) on_actionFT8_triggered (); else if ("JT4" == mode) on_actionJT4_triggered (); else if ("JT9" == mode) on_actionJT9_triggered (); - else if ("JT9+JT65" == mode) on_actionJT9_JT65_triggered (); else if ("JT65" == mode) on_actionJT65_triggered (); else if ("QRA64" == mode) on_actionQRA64_triggered (); else if ("Q65" == mode) on_actionQ65_triggered (); diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index 4066af831..98532ece7 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -203,7 +203,6 @@ private slots: void on_logQSOButton_clicked(); void on_actionJT9_triggered(); void on_actionJT65_triggered(); - void on_actionJT9_JT65_triggered(); void on_actionJT4_triggered(); void on_actionFT4_triggered(); void on_actionFT8_triggered(); @@ -241,7 +240,6 @@ private slots: void on_bandComboBox_editTextChanged (QString const& text); void on_bandComboBox_activated (int index); void on_readFreq_clicked(); - void on_pbTxMode_clicked(); void on_RxFreqSpinBox_valueChanged(int n); void on_outAttenuation_valueChanged (int); void rigOpen (); diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index ff7986403..73e06ef21 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 805 - 589 + 637 @@ -590,7 +590,7 @@ - + @@ -1219,25 +1219,6 @@ Yellow when too low - - - - true - - - - 0 - 0 - - - - Toggle Tx mode - - - Tx JT9 @ - - - @@ -1263,53 +1244,6 @@ Yellow when too low - - - - <html><head/><body><p>Submode determines tone spacing; A is narrowest.</p></body></html> - - - Submode determines tone spacing; A is narrowest. - - - Qt::AlignCenter - - - Submode - - - 0 - - - 7 - - - - - - - Qt::AlignCenter - - - - - - F High - - - 100 - - - 5000 - - - 100 - - - 1400 - - - @@ -1379,44 +1313,6 @@ Yellow when too low - - - - Qt::AlignCenter - - - F Low - - - 100 - - - 5000 - - - 100 - - - 600 - - - - - - - <html><head/><body><p>Double-click on another caller to queue that call for your next QSO.</p></body></html> - - - Double-click on another caller to queue that call for your next QSO. - - - Next Call - - - Qt::AlignCenter - - - @@ -1523,60 +1419,6 @@ Not available to nonstandard callsign holders. - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Tx# - - - 1 - - - 4095 - - - - - - - <html><head/><body><p>Check to keep Tx frequency fixed when double-clicking on decoded text.</p></body></html> - - - Check to keep Tx frequency fixed when double-clicking on decoded text. - - - Hold Tx Freq - - - - - - - <html><head/><body><p>Synchronizing threshold. Lower numbers accept weaker sync signals.</p></body></html> - - - Synchronizing threshold. Lower numbers accept weaker sync signals. - - - Qt::AlignCenter - - - Sync - - - -2 - - - 10 - - - 1 - - - @@ -1704,6 +1546,129 @@ When not checked you can view the calibration results. + + + + <html><head/><body><p>Check to keep Tx frequency fixed when double-clicking on decoded text.</p></body></html> + + + Check to keep Tx frequency fixed when double-clicking on decoded text. + + + Hold Tx Freq + + + + + + + Qt::AlignCenter + + + F Low + + + 100 + + + 5000 + + + 100 + + + 600 + + + + + + + Qt::AlignCenter + + + + + + F High + + + 100 + + + 5000 + + + 100 + + + 1400 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Tx# + + + 1 + + + 4095 + + + + + + + <html><head/><body><p>Submode determines tone spacing; A is narrowest.</p></body></html> + + + Submode determines tone spacing; A is narrowest. + + + Qt::AlignCenter + + + Submode + + + 0 + + + 7 + + + + + + + <html><head/><body><p>Synchronizing threshold. Lower numbers accept weaker sync signals.</p></body></html> + + + Synchronizing threshold. Lower numbers accept weaker sync signals. + + + Qt::AlignCenter + + + Sync + + + -2 + + + 10 + + + 1 + + + @@ -2892,7 +2857,6 @@ list. The list can be maintained in Settings (F2). - From bbcc9419b451949527d20802a128e5c5e1e2a926 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 16 Nov 2020 10:37:09 -0500 Subject: [PATCH 147/426] Set a mainwindow minimum height. --- widgets/mainwindow.ui | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index 73e06ef21..05b739f70 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 805 - 637 + 584 @@ -16,6 +16,12 @@ 0 + + + 0 + 584 + + WSJT-X by K1JT @@ -155,7 +161,7 @@ - + 0 10 From 7f772ecc533ab313f24ec7fdf1b03995d055ade3 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 16 Nov 2020 11:49:01 -0500 Subject: [PATCH 148/426] Don't set a global minimum height: SWL mode wants much less. --- widgets/mainwindow.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index 05b739f70..78f296c50 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -19,7 +19,7 @@ 0 - 584 + 0 From 0755e52993f482ebb57d29c7ab9d7b700663eb5d Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 16 Nov 2020 13:49:31 -0500 Subject: [PATCH 149/426] Another reformatting of output to fort.53. --- lib/qra/q65/q65_loops.f90 | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 06d8fcd04..a3d62e765 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -121,12 +121,15 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & write(c77,1100) dat4(1:12),dat4(13)/2 1100 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent - if(nsps.lt.3600) write(53,3001) nutc,ndf,ndt,nbw,ndist,irc, & - iaptype,navg,snr1,xdt1,f1,snr2,trim(decoded) -3001 format(i6.6,7i4,f7.1,f7.2,2f7.1,1x,a) - if(nsps.ge.3600) write(53,3002) nutc,ndf,ndt,nbw,ndist,irc, & - iaptype,navg,snr1,xdt1,f1,snr2,trim(decoded) -3002 format(i4.4,2x,7i4,f7.1,f7.2,2f7.1,1x,a) + m=nutc + if(nsps.ge.3600) m=100*m + ihr=m/10000 + imin=mod(m/100,100) + isec=mod(m,100) + hours=ihr + imin/60.0 + isec/3600.0 + write(53,3053) m,hours,ndf,ndt,nbw,ndist,irc,iaptype,navg,snr1, & + xdt1,f1,snr2,trim(decoded) +3053 format(i6.6,f8.4,4i3,i4,2i3,f6.1,f6.2,f7.1,f6.1,1x,a) close(53) !### nsave=0 From eeed375baf46b8f647a7328d5f95bef7aa1bcece Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 16 Nov 2020 16:28:37 -0500 Subject: [PATCH 150/426] Tell user to go back it he selects F+H in a mode other than FT8. --- widgets/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index c1904af58..bb29d3da2 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -1888,7 +1888,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog if((m_config.special_op_id()==SpecOp::FOX or m_config.special_op_id()==SpecOp::HOUND) and m_mode!="FT8") { MessageBox::information_message (this, - "Fox-and-Hound operation is available only in FT8 mode."); + "Fox-and-Hound operation is available only in FT8 mode.\nGo back and change your selection."); } } } From e71bc50bbf8a5d8a1fb1a20b4af75690540b5493 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 16 Nov 2020 16:29:52 -0500 Subject: [PATCH 151/426] Set irc to -9, DT and f0 to 0 if sync amplitude it too small. --- lib/q65_decode.f90 | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 40aef5f8b..9f49868ca 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -94,7 +94,7 @@ contains snr1,width) call timer('sync_q65',1) - irc=-1 + irc=-9 if(snr1.lt.2.8) go to 100 jpk0=(xdt+1.0)*6000 !### Is this OK? if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !### @@ -125,8 +125,6 @@ contains if(lapcqonly) npasses=1 iaptype=0 do ipass=0,npasses -! write(54,3000) nQSOprogress,ipass -!3000 format(i1,i2) apmask=0 apsymbols=0 if(ipass.ge.1) then @@ -138,11 +136,7 @@ contains 1060 format(13b6.6) write(c78,1050) apsymbols1 read(c78,1060) apsymbols -! write(54,3001) iaptype,c78 -!3001 format('a',i2,1x,a78) endif -! write(54,3002) apmask,apsymbols -!3002 format('b ',13b6.6/4x,13b6.6) call timer('q65loops',0) call q65_loops(c00,nutc,npts/2,nsps/2,nmode,mode65,nsubmode, & nFadingModel,ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols, & From abe06e5dbbeee886d220a3b747d866531596cb01 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 17 Nov 2020 10:46:20 -0500 Subject: [PATCH 152/426] Access Q65 Quick-Start Guide from Help menu. --- widgets/mainwindow.cpp | 7 ++++++- widgets/mainwindow.h | 3 ++- widgets/mainwindow.ui | 10 ++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index bb29d3da2..32048bc9e 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -2491,11 +2491,16 @@ void MainWindow::on_actionFT8_DXpedition_Mode_User_Guide_triggered() QDesktopServices::openUrl (QUrl {"http://physics.princeton.edu/pulsar/k1jt/FT8_DXpedition_Mode.pdf"}); } -void MainWindow::on_actionQuick_Start_Guide_triggered() +void MainWindow::on_actionQSG_FST4_triggered() { QDesktopServices::openUrl (QUrl {"https://physics.princeton.edu/pulsar/k1jt/FST4_Quick_Start.pdf"}); } +void MainWindow::on_actionQSG_Q65_triggered() +{ + QDesktopServices::openUrl (QUrl {"https://physics.princeton.edu/pulsar/k1jt/Q65_Quick_Start.pdf"}); +} + void MainWindow::on_actionOnline_User_Guide_triggered() //Display manual { #if defined (CMAKE_BUILD) diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index 98532ece7..4ab9a4102 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -151,7 +151,8 @@ private slots: void on_stopButton_clicked(); void on_actionRelease_Notes_triggered (); void on_actionFT8_DXpedition_Mode_User_Guide_triggered(); - void on_actionQuick_Start_Guide_triggered(); + void on_actionQSG_FST4_triggered(); + void on_actionQSG_Q65_triggered(); void on_actionOnline_User_Guide_triggered(); void on_actionLocal_User_Guide_triggered(); void on_actionWide_Waterfall_triggered(); diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index 78f296c50..51dc92757 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -2843,7 +2843,8 @@ list. The list can be maintained in Settings (F2). - + + @@ -3314,7 +3315,7 @@ list. The list can be maintained in Settings (F2). Export Cabrillo log ... - + Quick-Start Guide to FST4 and FST4W @@ -3372,6 +3373,11 @@ list. The list can be maintained in Settings (F2). Hide lower panel controls to maximize deocde windows + + + Quick-Start Guide to Q65 + + From d3ed0be31895cb4a6f9c8cdbb04fe6dffd57d30e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 17 Nov 2020 10:47:07 -0500 Subject: [PATCH 153/426] Allow 2-digit input of ndepth in test_q65. --- lib/test_q65.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 50f378064..3fb39d026 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -62,7 +62,7 @@ program test_q65 ! 1 2 3 4 5 6 7 ! 1234567890123456789012345678901234567890123456789012345678901234567890123' cmd1='q65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 F -10 > junk0' - cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A *.wav > junk' + cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A *.wav > junk' write(cmd1(10:33),'(a)') '"'//msg//'"' cmd1(35:35)=csubmode @@ -73,8 +73,8 @@ program test_q65 write(cmd1(55:59),'(i5)') nfiles write(cmd2(11:13),'(i3)') ntrperiod - write(cmd2(33:33),'(i1)') ndepth - cmd2(38:38)=csubmode + write(cmd2(33:34),'(i2)') ndepth + cmd2(39:39)=csubmode call system('rm -f *.wav') ! call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist) From 2dcd8504e8109c3dad388838d241136bfb97812b Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 17 Nov 2020 15:09:03 -0500 Subject: [PATCH 154/426] Updates to test_q65: allow command-line input of QSOprogress. --- lib/test_q65.f90 | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 3fb39d026..fc6841ae7 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -8,10 +8,11 @@ program test_q65 logical decok nargs=iargc() - if(nargs.ne.9) then - print*,'Usage: test_q65 "msg" A-D depth freq DT fDop TRp nfiles SNR' - print*,'Example: test_q65 "K1ABC W9XYZ EN37" A 3 1500 0.0 5.0 60 100 -20' - print*,' SNR = 0 to loop over all relevant SNRs' + if(nargs.ne.10) then + print*,'Usage: test_q65 "msg" A-D depth freq DT fDop TRp Q nfiles SNR' + print*,'Example: test_q65 "K1ABC W9XYZ EN37" A 3 1500 0.0 5.0 60 3 100 -20' + print*,'Use SNR = 0 to loop over all relevant SNRs' + print*,'Use MyCall=K1ABC, HisCall=W9XYZ, HisGrid="EN37" for AP decodes' go to 999 endif call getarg(1,msg) @@ -27,8 +28,10 @@ program test_q65 call getarg(7,arg) read(arg,*) ntrperiod call getarg(8,arg) - read(arg,*) nfiles + read(arg,*) nQSOprogress call getarg(9,arg) + read(arg,*) nfiles + call getarg(10,arg) read(arg,*) nsnr if(ntrperiod.eq.15) then @@ -62,7 +65,7 @@ program test_q65 ! 1 2 3 4 5 6 7 ! 1234567890123456789012345678901234567890123456789012345678901234567890123' cmd1='q65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 F -10 > junk0' - cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A *.wav > junk' + cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A -Q 3 *.wav > junk' write(cmd1(10:33),'(a)') '"'//msg//'"' cmd1(35:35)=csubmode @@ -74,6 +77,7 @@ program test_q65 write(cmd2(11:13),'(i3)') ntrperiod write(cmd2(33:34),'(i2)') ndepth + write(cmd2(44:44),'(i1)') nQSOprogress cmd2(39:39)=csubmode call system('rm -f *.wav') @@ -85,7 +89,7 @@ program test_q65 write(*,1010) (j,j=0,6) write(12,1010) (j,j=0,6) -1010 format('SNR nd Dop Sync DecN Dec1 Bad',i6,6i4,' tdec'/66('-')) +1010 format('SNR d Dop Sync DecN Dec1 Bad',i6,6i4,' tdec'/66('-')) dterr=tsym/4.0 nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) From d26acd048c438f7af4b20f38eb75b76661547875 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 17 Nov 2020 15:31:33 -0500 Subject: [PATCH 155/426] Minor tweak to test_q65. --- lib/test_q65.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index fc6841ae7..a6b0e8eaa 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -13,6 +13,8 @@ program test_q65 print*,'Example: test_q65 "K1ABC W9XYZ EN37" A 3 1500 0.0 5.0 60 3 100 -20' print*,'Use SNR = 0 to loop over all relevant SNRs' print*,'Use MyCall=K1ABC, HisCall=W9XYZ, HisGrid="EN37" for AP decodes' + print*,'Option Q sets QSOprogress (0-5) for AP decoding.' + print*,'Add 16 to requested depth to enable message averaging.' go to 999 endif call getarg(1,msg) From 44343d87f6ebc5d024e98d4a9a084ccf6a1bb5e2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 19 Nov 2020 13:45:16 -0500 Subject: [PATCH 156/426] Allow input of fractional SNR to test_q65. --- lib/test_q65.f90 | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index a6b0e8eaa..5871b0579 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -1,6 +1,6 @@ program test_q65 - character*73 cmd1,cmd2,line + character*75 cmd1,cmd2,line character*22 msg character*8 arg character*1 csubmode @@ -34,7 +34,7 @@ program test_q65 call getarg(9,arg) read(arg,*) nfiles call getarg(10,arg) - read(arg,*) nsnr + read(arg,*) snr if(ntrperiod.eq.15) then nsps=1800 @@ -56,17 +56,17 @@ program test_q65 endif ia=i50 + 8 ib=i50 - 5 - if(nsnr.ne.0) then - ia=nsnr - ib=nsnr + if(snr.ne.0.0) then + ia=99 + ib=99 endif baud=12000.0/nsps tsym=1.0/baud ! 1 2 3 4 5 6 7 -! 1234567890123456789012345678901234567890123456789012345678901234567890123' - cmd1='q65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 F -10 > junk0' +! 123456789012345678901234567890123456789012345678901234567890123456789012345' + cmd1='q65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 F -10.0 > junk0' cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A -Q 3 *.wav > junk' write(cmd1(10:33),'(a)') '"'//msg//'"' @@ -91,19 +91,21 @@ program test_q65 write(*,1010) (j,j=0,6) write(12,1010) (j,j=0,6) -1010 format('SNR d Dop Sync DecN Dec1 Bad',i6,6i4,' tdec'/66('-')) +1010 format(' SNR d Dop Sync DecN Dec1 Bad',i6,6i4,' tdec'/68('-')) dterr=tsym/4.0 nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) ndec10=nfiles do nsnr=ia,ib,-1 + snr1=nsnr + if(ia.eq.99) snr1=snr nsync=0 ndec1=0 nfalse=0 naptype=0 ndecn=0 - write(cmd1(63:65),'(i3)') nsnr + write(cmd1(63:67),'(f5.1)') snr1 call system(cmd1) call sec0(0,tdec) call system(cmd2) @@ -136,13 +138,13 @@ program test_q65 10 close(10) xdt_avg=0. xdt_rms=0. - write(*,1100) nsnr,ndepth,fDop,nsync,ndecn,ndec1,nfalse,naptype, & + write(*,1100) snr1,ndepth,fDop,nsync,ndecn,ndec1,nfalse,naptype, & tdec/nfiles - write(12,1100) nsnr,ndepth,fDop,nsync,ndecn,ndec1,nfalse,naptype, & + write(12,1100) snr1,ndepth,fDop,nsync,ndecn,ndec1,nfalse,naptype, & tdec/nfiles -1100 format(i3,i3,f5.1,3i5,i4,i6,6i4,f6.2) +1100 format(f5.1,i3,f5.1,3i5,i4,i6,6i4,f6.2) if(ndec1.lt.nfiles/2 .and. ndec10.ge.nfiles/2) then - snr_thresh=nsnr + float(nfiles/2 - ndec1)/(ndec10-ndec1) + snr_thresh=snr1 + float(nfiles/2 - ndec1)/(ndec10-ndec1) write(13,1200) ndepth,fdop,csubmode,snr_thresh 1200 format(i3,f6.1,2x,a1,f7.1) flush(13) From 0bcb2f0d11df7e3eb4cd7135823bc43a512c6577 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 19 Nov 2020 13:50:59 -0500 Subject: [PATCH 157/426] Adjust SNR limits in test_q65. --- lib/test_q65.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 5871b0579..395e5a5d2 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -54,8 +54,8 @@ program test_q65 else stop 'Invalid TR period' endif - ia=i50 + 8 - ib=i50 - 5 + ia=i50 + 5 + ib=i50 - 10 if(snr.ne.0.0) then ia=99 ib=99 From 13d0b20661895f90131e94172997078c25aa127e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 19 Nov 2020 15:25:28 -0500 Subject: [PATCH 158/426] Change the unmasked bits for iaptype 4, 5, 6. Tweak the SNR limits in test_q65. --- lib/qra/q65/q65_ap.f90 | 5 +++-- lib/test_q65.f90 | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/qra/q65/q65_ap.f90 b/lib/qra/q65/q65_ap.f90 index f0cfd72a2..c30314b1a 100644 --- a/lib/qra/q65/q65_ap.f90 +++ b/lib/qra/q65/q65_ap.f90 @@ -146,11 +146,12 @@ subroutine q65_ap(nQSOprogress,ipass,ncontest,lapcqonly,iaptype, & if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype.eq.6) then apmask=0 if(ncontest.le.5 .or. (ncontest.eq.7.and.iaptype.eq.6)) then - apmask(1:78)=1 ! mycall, hiscall, RRR|73|RR73 + apmask(1:78)=1 !MyCall, HisCall, RRR|73|RR73 + apmask(59:63)=0 !Reduce the rate of false decodes apsymbols(1:58)=apsym0 if(iaptype.eq.4) apsymbols(59:77)=mrrr if(iaptype.eq.5) apsymbols(59:77)=m73 - if(iaptype.eq.6) apsymbols(59:77)=mrr73 + if(iaptype.eq.6) apsymbols(59:77)=mrr73 else if(ncontest.eq.7.and.iaptype.eq.4) then ! Hound listens for MyCall RR73;... apmask(1:28)=1 apsymbols(1:28)=apsym0(1:28) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 395e5a5d2..66caca99a 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -54,7 +54,7 @@ program test_q65 else stop 'Invalid TR period' endif - ia=i50 + 5 + ia=i50 + 7 ib=i50 - 10 if(snr.ne.0.0) then ia=99 @@ -145,9 +145,11 @@ program test_q65 1100 format(f5.1,i3,f5.1,3i5,i4,i6,6i4,f6.2) if(ndec1.lt.nfiles/2 .and. ndec10.ge.nfiles/2) then snr_thresh=snr1 + float(nfiles/2 - ndec1)/(ndec10-ndec1) - write(13,1200) ndepth,fdop,csubmode,snr_thresh -1200 format(i3,f6.1,2x,a1,f7.1) - flush(13) + open(13,file='snr_thresh.out',status='unknown',position='append') + write(13,1200) ntrperiod,csubmode,ndepth,nQSOprogress,nfiles, & + fdop,snr_thresh,trim(msg) +1200 format(i3,a1,2i3,i5,2f7.1,2x,a) + close(13) endif flush(6) flush(12) From 3662a76de7ca70db55240562da1eda5729ce5488 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 19 Nov 2020 15:41:15 -0500 Subject: [PATCH 159/426] More Q65 test program tweaks. --- lib/qra/q65/q65_ap.f90 | 2 +- lib/test_q65.f90 | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/qra/q65/q65_ap.f90 b/lib/qra/q65/q65_ap.f90 index c30314b1a..b335153c3 100644 --- a/lib/qra/q65/q65_ap.f90 +++ b/lib/qra/q65/q65_ap.f90 @@ -147,7 +147,7 @@ subroutine q65_ap(nQSOprogress,ipass,ncontest,lapcqonly,iaptype, & apmask=0 if(ncontest.le.5 .or. (ncontest.eq.7.and.iaptype.eq.6)) then apmask(1:78)=1 !MyCall, HisCall, RRR|73|RR73 - apmask(59:63)=0 !Reduce the rate of false decodes + apmask(59:66)=0 !Reduce the rate of false decodes apsymbols(1:58)=apsym0 if(iaptype.eq.4) apsymbols(59:77)=mrrr if(iaptype.eq.5) apsymbols(59:77)=m73 diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 66caca99a..4aa452458 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -91,7 +91,7 @@ program test_q65 write(*,1010) (j,j=0,6) write(12,1010) (j,j=0,6) -1010 format(' SNR d Dop Sync DecN Dec1 Bad',i6,6i4,' tdec'/68('-')) +1010 format(' SNR Mode d Dop Sync DecN Dec1 Bad',i6,6i4,' tdec'/73('-')) dterr=tsym/4.0 nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) @@ -138,11 +138,11 @@ program test_q65 10 close(10) xdt_avg=0. xdt_rms=0. - write(*,1100) snr1,ndepth,fDop,nsync,ndecn,ndec1,nfalse,naptype, & - tdec/nfiles - write(12,1100) snr1,ndepth,fDop,nsync,ndecn,ndec1,nfalse,naptype, & - tdec/nfiles -1100 format(f5.1,i3,f5.1,3i5,i4,i6,6i4,f6.2) + write(*,1100) snr1,ntrperiod,csubmode,ndepth,fDop,nsync,ndecn, & + ndec1,nfalse,naptype,tdec/nfiles + write(12,1100) snr1,ntrperiod,csubmode,ndepth,fDop,nsync,ndecn, & + ndec1,nfalse,naptype,tdec/nfiles +1100 format(f5.1,i4,a1,i3,f5.1,3i5,i4,i6,6i4,f6.2) if(ndec1.lt.nfiles/2 .and. ndec10.ge.nfiles/2) then snr_thresh=snr1 + float(nfiles/2 - ndec1)/(ndec10-ndec1) open(13,file='snr_thresh.out',status='unknown',position='append') From 09815826e2358b4a8f5843b9717f377622b9a971 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 20 Nov 2020 15:16:23 -0500 Subject: [PATCH 160/426] For Q65 with QSOprogress=3, reduce npasses to 2 (iaptype=3, 4 only); reduce APmast from 78 to 75 bits. --- lib/q65_decode.f90 | 1 - lib/qra/q65/q65_ap.f90 | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 9f49868ca..347621026 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -120,7 +120,6 @@ contains where(apsym0.eq.-1) apsym0=0 npasses=2 - if(nQSOprogress.eq.3 .or.nQSOprogress.eq.4) npasses=4 if(nQSOprogress.eq.5) npasses=3 if(lapcqonly) npasses=1 iaptype=0 diff --git a/lib/qra/q65/q65_ap.f90 b/lib/qra/q65/q65_ap.f90 index b335153c3..6c56cd2f5 100644 --- a/lib/qra/q65/q65_ap.f90 +++ b/lib/qra/q65/q65_ap.f90 @@ -147,7 +147,7 @@ subroutine q65_ap(nQSOprogress,ipass,ncontest,lapcqonly,iaptype, & apmask=0 if(ncontest.le.5 .or. (ncontest.eq.7.and.iaptype.eq.6)) then apmask(1:78)=1 !MyCall, HisCall, RRR|73|RR73 - apmask(59:66)=0 !Reduce the rate of false decodes + apmask(72:74)=0 !Check for , RRR, RR73, 73 apsymbols(1:58)=apsym0 if(iaptype.eq.4) apsymbols(59:77)=mrrr if(iaptype.eq.5) apsymbols(59:77)=m73 From 7ff3d598c80af84011212ac9feb79e1b146f5a5e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 20 Nov 2020 15:40:25 -0500 Subject: [PATCH 161/426] Use g15 = 32403 rather than 32373 for the "RR73" message fragment in Q65. --- lib/qra/q65/genq65.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/qra/q65/genq65.f90 b/lib/qra/q65/genq65.f90 index 300cbf070..4c555748a 100644 --- a/lib/qra/q65/genq65.f90 +++ b/lib/qra/q65/genq65.f90 @@ -26,6 +26,8 @@ subroutine genq65(msg0,ichk,msgsent,itone,i3,n3) i3=-1 n3=-1 call pack77(msg0,i3,n3,c77) + read(c77(60:74),'(b15)') ng15 + if(ng15.eq.32373) c77(60:74)='111111010010011' !Message is RR73 call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent read(c77,1001) dgen 1001 format(12b6.6,b5.5) From d9cb87054057d3fcc7b6761134c5bf7b5a177aa6 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 20 Nov 2020 17:07:11 -0500 Subject: [PATCH 162/426] Adjust Q65 jitter-loop ranges according to ndepth. --- lib/qra/q65/q65_loops.f90 | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index a3d62e765..8ca0b322c 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -25,16 +25,20 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & allocate(c0(0:npts2-1)) irc=-99 s3lim=20. -! ibwmax=11 -! if(mode64.le.4) ibwmax=9 -! ibwmin=ibwmax -! idtmax=3 -! call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) - idfmax=5 - idtmax=5 + + idfmax=3 + idtmax=3 ibwmin=1 ibwmax=2 maxdist=15 + if(ndepth.ge.2) then + idfmax=5 + idtmax=5 + maxdist=25 + endif + if(ndepth.eq.3) then + ibwmax=5 + endif LL=64*(mode64+2) NN=63 napmin=99 From 6da4be174c49d4b7344d3ec6e9de5e28f42a2598 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 21 Nov 2020 09:39:37 -0500 Subject: [PATCH 163/426] We now have only 4 levels of AP decoding. Modify test_q65 accordingly. --- lib/test_q65.f90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 4aa452458..0c71d64a5 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -4,7 +4,7 @@ program test_q65 character*22 msg character*8 arg character*1 csubmode - integer naptype(0:6) + integer naptype(0:4) logical decok nargs=iargc() @@ -89,9 +89,9 @@ program test_q65 !1000 format(/'Depth:',i2,' AP:',i2,' df:',i3,' dt:',i3,' bw1:',i3,' bw2:',i3, & ! ' dist:',i3) - write(*,1010) (j,j=0,6) - write(12,1010) (j,j=0,6) -1010 format(' SNR Mode d Dop Sync DecN Dec1 Bad',i6,6i4,' tdec'/73('-')) + write(*,1010) (j,j=0,4) + write(12,1010) (j,j=0,4) +1010 format(' SNR Mode d Dop Sync DecN Dec1 Bad',5i5,' tdec'/70('-')) dterr=tsym/4.0 nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) @@ -142,7 +142,7 @@ program test_q65 ndec1,nfalse,naptype,tdec/nfiles write(12,1100) snr1,ntrperiod,csubmode,ndepth,fDop,nsync,ndecn, & ndec1,nfalse,naptype,tdec/nfiles -1100 format(f5.1,i4,a1,i3,f5.1,3i5,i4,i6,6i4,f6.2) +1100 format(f5.1,i4,1x,a1,i3,f5.1,3i5,i4,i6,4i5,f6.2) if(ndec1.lt.nfiles/2 .and. ndec10.ge.nfiles/2) then snr_thresh=snr1 + float(nfiles/2 - ndec1)/(ndec10-ndec1) open(13,file='snr_thresh.out',status='unknown',position='append') From 8e4ed9b3bea6abc12465c138c39c28e36c4268b2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 21 Nov 2020 10:12:46 -0500 Subject: [PATCH 164/426] Fix a bug in jt65_decode: crash when Rx Freq is set too close to upper freq limit on waterfall. --- lib/jt65_decode.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/jt65_decode.f90 b/lib/jt65_decode.f90 index 5242b6efe..bfb78b254 100644 --- a/lib/jt65_decode.f90 +++ b/lib/jt65_decode.f90 @@ -179,6 +179,7 @@ contains ia=max(1,nint((nfa-100)/df)) ib=min(NSZ,nint((nfb+100)/df)) nz=ib-ia+1 + if(nz.lt.50) go to 900 call lorentzian(savg(ia),nz,a) baseline=a(1) amp=a(2) From 4bd7fcb4a10eb9eaa736b86993be7f7ac89c8397 Mon Sep 17 00:00:00 2001 From: Nico Palermo/IV3NWV Date: Sat, 21 Nov 2020 17:00:15 +0100 Subject: [PATCH 165/426] added likelihood check for better false rejection - added function q65_decode_fullaplist for full-ap decoding of a list of codewords --- lib/qra/q65/q65.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++- lib/qra/q65/q65.h | 18 ++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/lib/qra/q65/q65.c b/lib/qra/q65/q65.c index 856d8d7a7..03b5219f5 100644 --- a/lib/qra/q65/q65.c +++ b/lib/qra/q65/q65.c @@ -26,6 +26,12 @@ #include "q65.h" #include "pdmath.h" +// Minimum codeword loglikelihood for decoding +#define Q65_LLH_THRESHOLD -260.0f + +// This value produce the same WER performance in decode_fullaplist +// #define Q65_LLH_THRESHOLD -262.0f + static int _q65_crc6(int *x, int sz); static void _q65_crc12(int *y, int *x, int sz); @@ -608,8 +614,13 @@ int q65_decode(q65_codec_ds *pCodec, int* pDecodedCodeword, int *pDecodedMsg, co if (pDecodedMsg) memcpy(pDecodedMsg,px,nK*sizeof(int)); - if (pDecodedCodeword==NULL) // user is not interested in it +#ifndef Q65_CHECKLLH + if (pDecodedCodeword==NULL) // user is not interested in the decoded codeword return rc; // return the number of iterations required to decode +#else + if (pDecodedCodeword==NULL) // we must have a buffer + return Q65_DECODE_INVPARAMS; // return error +#endif // crc matches therefore we can reconstruct the transmitted codeword // reencoding the information available in px... @@ -632,11 +643,83 @@ int q65_decode(q65_codec_ds *pCodec, int* pDecodedCodeword, int *pDecodedMsg, co memcpy(pDecodedCodeword,py,nN*sizeof(int)); // no puncturing } +#ifdef Q65_CHECKLLH + if (q65_check_llh(NULL,pDecodedCodeword, nN, nM, pIntrinsics)==0) // llh less than threshold + return Q65_DECODE_LLHLOW; +#endif + return rc; // return the number of iterations required to decode } +// Compute and verify the loglikelihood of the decoded codeword +int q65_check_llh(float *llh, const int* ydec, const int nN, const int nM, const float *pIntrin) +{ + int k; + float t = 0; + + for (k=0;k=Q65_LLH_THRESHOLD); +} + +// Full AP decoding from a list of codewords +int q65_decode_fullaplist(q65_codec_ds *codec, + int *ydec, + int *xdec, + const float *pIntrinsics, + const int *pCodewords, + const int nCodewords) +{ + int k; + int nK, nN, nM; + + float llh; + float maxllh = Q65_LLH_THRESHOLD-1; // set to a value less than the threshold + int maxcw = -1; // index of the most likely codeword + const int *pCw; + + if (nCodewords<1 || nCodewords>Q65_FULLAPLIST_SIZE) + return Q65_DECODE_INVPARAMS; // invalid list length + + nK = q65_get_message_length(codec); + nN = q65_get_codeword_length(codec); + nM = q65_get_alphabet_size(codec); + + // compute codewords log likelihoods and find max + pCw = pCodewords; // start from the first codeword + for (k=0;kmaxllh) { + maxllh = llh; + maxcw = k; + } + // point to next codeword + pCw+=nN; + } + + if (maxcw<0) // no llh larger than threshold found + return Q65_DECODE_FAILED; + + pCw = pCodewords+nN*maxcw; + memcpy(ydec,pCw,nN*sizeof(int)); + memcpy(xdec,pCw,nK*sizeof(int)); + + return maxcw; // index to the decoded message (>=0) + +} + + + // helper functions ------------------------------------------------------------- diff --git a/lib/qra/q65/q65.h b/lib/qra/q65/q65.h index d36cb608f..bd80a934c 100644 --- a/lib/qra/q65/q65.h +++ b/lib/qra/q65/q65.h @@ -28,10 +28,18 @@ #define Q65_DECODE_INVPARAMS -1 #define Q65_DECODE_FAILED -2 #define Q65_DECODE_CRCMISMATCH -3 +#define Q65_DECODE_LLHLOW -4 +#define Q65_DECODE_UNDETERR -5 + +// Verify loglikelihood after successful decoding +#define Q65_CHECKLLH +// Max codeword list size in q65_decode_fullaplist +#define Q65_FULLAPLIST_SIZE 64 // maximum number of weights for the fast-fading metric evaluation #define Q65_FASTFADING_MAXWEIGTHS 65 + typedef struct { const qracode *pQraCode; // qra code to be used by the codec float decoderEsNoMetric; // value for which we optimize the decoder metric @@ -72,6 +80,13 @@ int q65_decode(q65_codec_ds *pCodec, const int *pAPMask, const int *pAPSymbols); +int q65_decode_fullaplist(q65_codec_ds *codec, + int *ydec, + int *xdec, + const float *pIntrinsics, + const int *pCodewords, + const int nCodewords); + int q65_esnodb(const q65_codec_ds *pCodec, float *pEsNodB, const int *ydec, @@ -100,4 +115,7 @@ static void _q65_mask(const qracode *pcode, float *ix, const int *mask, const in int _q65_get_alphabet_size(const qracode *pCode); int _q65_get_bits_per_symbol(const qracode *pCode); +// internally used but made public for threshold optimization +int q65_check_llh(float *llh, const int* ydec, const int nN, const int nM, const float *pIntrin); + #endif // _qra65_h From b2833fd6f08cf29b167e049d8518f92754b008b3 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 21 Nov 2020 11:34:17 -0500 Subject: [PATCH 166/426] Fix the use of ndepth for setting jitter-loop limits. --- lib/qra/q65/q65_loops.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 8ca0b322c..1e1576556 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -30,13 +30,13 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & idtmax=3 ibwmin=1 ibwmax=2 - maxdist=15 - if(ndepth.ge.2) then + maxdist=5 + if(iand(ndepth,3).ge.2) then idfmax=5 idtmax=5 - maxdist=25 + maxdist=15 endif - if(ndepth.eq.3) then + if(iand(ndepth,3).eq.3) then ibwmax=5 endif LL=64*(mode64+2) From 9c51e93f067cf77db598c78df4ae89e99fca31d1 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 22 Nov 2020 13:58:29 -0500 Subject: [PATCH 167/426] Implement Nico's "deep likelihood" to replace iaptype=4 and 75-bit APmask. --- lib/q65_decode.f90 | 14 ++++++++++++-- lib/qra/q65/q65_ap.f90 | 4 ++-- lib/qra/q65/q65_loops.f90 | 18 ++++++++++++------ lib/qra/q65/q65_subs.c | 39 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 63 insertions(+), 12 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 347621026..4269a3cc8 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -58,6 +58,8 @@ contains integer apsym0(58),aph10(10) integer apmask1(78),apsymbols1(78) integer apmask(13),apsymbols(13) + integer dgen(13) + integer codewords(63,64) logical lapcqonly,unpk77_success complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s @@ -88,7 +90,6 @@ contains this%callback => callback if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning nFadingModel=1 -! call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) call timer('sync_q65',0) call sync_q65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0, & snr1,width) @@ -135,17 +136,26 @@ contains 1060 format(13b6.6) write(c78,1050) apsymbols1 read(c78,1060) apsymbols + if(iaptype.eq.4) then + do j=1,3 + ng15=32401+j + write(c78(60:74),'(b15.15)') ng15 + read(c78,1060) dgen + call q65_enc(dgen,codewords(1,j)) + enddo + endif endif call timer('q65loops',0) call q65_loops(c00,nutc,npts/2,nsps/2,nmode,mode65,nsubmode, & nFadingModel,ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols, & - snr1,xdt1,f1,snr2,irc,dat4) + codewords,snr1,xdt1,f1,snr2,irc,dat4) call timer('q65loops',1) snr2=snr2 + db(6912.0/nsps) if(irc.ge.0) exit enddo 100 decoded=' ' +! if(irc.lt.0 .and.iaptype.eq.4) print*,'AAA',irc,iaptype if(irc.ge.0) then !### navg=irc/100 diff --git a/lib/qra/q65/q65_ap.f90 b/lib/qra/q65/q65_ap.f90 index 6c56cd2f5..32e26a4ed 100644 --- a/lib/qra/q65/q65_ap.f90 +++ b/lib/qra/q65/q65_ap.f90 @@ -149,8 +149,8 @@ subroutine q65_ap(nQSOprogress,ipass,ncontest,lapcqonly,iaptype, & apmask(1:78)=1 !MyCall, HisCall, RRR|73|RR73 apmask(72:74)=0 !Check for , RRR, RR73, 73 apsymbols(1:58)=apsym0 - if(iaptype.eq.4) apsymbols(59:77)=mrrr - if(iaptype.eq.5) apsymbols(59:77)=m73 + if(iaptype.eq.4) apsymbols(59:77)=mrrr + if(iaptype.eq.5) apsymbols(59:77)=m73 if(iaptype.eq.6) apsymbols(59:77)=mrr73 else if(ncontest.eq.7.and.iaptype.eq.4) then ! Hound listens for MyCall RR73;... apmask(1:28)=1 diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 1e1576556..5f680f18a 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,6 +1,6 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & - ndepth,jpk0,xdt0,f0,width,iaptype,APmask,APsymbols,snr1,xdt1,f1, & - snr2,irc,dat4) + ndepth,jpk0,xdt0,f0,width,iaptype,APmask,APsymbols,codewords,snr1, & + xdt1,f1,snr2,irc,dat4) use packjt77 use timer_module, only: timer @@ -16,6 +16,7 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & logical unpk77_success integer APmask(13) integer APsymbols(13) + integer codewords(63,64) integer dat4(13) !Decoded message (as 13 six-bit integers) integer nap(0:11) !AP return codes data nap/0,2,3,2,3,4,2,3,6,4,6,6/,nsave/0/ @@ -86,10 +87,15 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & call timer('q65_intr',0) call q65_intrinsics_ff(s3,nsubmode,b90,nFadingModel,s3prob) call timer('q65_intr',1) - - call timer('q65_dec ',0) - call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) - call timer('q65_dec ',1) + if(iaptype.eq.4) then + call timer('q65_apli',0) + call q65_dec_fullaplist(s3,s3prob,codewords,3,esnodb,dat4,irc) + call timer('q65_apli',1) + else + call timer('q65_dec ',0) + call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) + call timer('q65_dec ',1) + endif if(irc.ge.0) go to 100 ! irc > 0 ==> number of iterations required to decode ! -1 = invalid params diff --git a/lib/qra/q65/q65_subs.c b/lib/qra/q65/q65_subs.c index b36adc540..688ef899a 100644 --- a/lib/qra/q65/q65_subs.c +++ b/lib/qra/q65/q65_subs.c @@ -80,11 +80,12 @@ void q65_dec_(float s3[], float s3prob[], int APmask[], int APsymbols[], float* esnodb0, int xdec[], int* rc0) { -/* Input: s3prob[LL,NN] Symbol-value intrinsic probabilities +/* Input: s3[LL,NN] Symbol spectra + * s3prob[LL,NN] Symbol-value intrinsic probabilities * APmask[13] AP information to be used in decoding * APsymbols[13] Available AP informtion * Output: - * esnodb0 Estimated Es/No in dB + * esnodb0 Estimated Es/No (dB) * xdec[13] Decoded 78-bit message as 13 six-bit integers * rc0 Return code from q65_decode() */ @@ -108,3 +109,37 @@ void q65_dec_(float s3[], float s3prob[], int APmask[], int APsymbols[], } *esnodb0 = esnodb; } + +void q65_dec_fullaplist_(float s3[], float s3prob[], int codewords[], + int* ncw, float* esnodb0, int xdec[], int* rc0) +{ +/* Input: s3[LL,NN] Symbol spectra + * s3prob[LL,NN] Symbol-value intrinsic probabilities + * codewords[63,ncw] Full codewords to search for + * ncw Number of codewords + * Output: + * esnodb0 Estimated Es/No (dB) + * xdec[13] Decoded 78-bit message as 13 six-bit integers + * rc0 Return code from q65_decode() + */ + + int rc; + int ydec[63]; + float esnodb; + + rc = q65_decode_fullaplist(&codec,ydec,xdec,s3prob,codewords,*ncw); + *rc0=rc; + + // rc = -1: Invalid params + // rc = -2: Decode failed + // rc = -3: CRC mismatch + *esnodb0 = 0.0; //Default Es/No for a failed decode + if(rc<0) return; + + rc = q65_esnodb_fastfading(&codec,&esnodb,ydec,s3); + if(rc<0) { + printf("error in q65_esnodb_fastfading()\n"); + exit(0); + } + *esnodb0 = esnodb; +} From 6fa9f76a7575449a1960c9b24dfee74ff95620f5 Mon Sep 17 00:00:00 2001 From: Nico Palermo/IV3NWV Date: Mon, 23 Nov 2020 06:03:59 +0100 Subject: [PATCH 168/426] Interface to q65_intrinsics_fastfading changed to support B90Ts instead of B90 and then supporting correctly modes with any T/R interval --- lib/q65_decode.f90 | 6 +++++- lib/qra/q65/q65.c | 24 ++++++++++++------------ lib/qra/q65/q65.h | 2 +- lib/qra/q65/q65_loops.f90 | 5 +++-- lib/qra/q65/q65_subs.c | 4 ++-- 5 files changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 347621026..76b8faec9 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -137,9 +137,13 @@ contains read(c78,1060) apsymbols endif call timer('q65loops',0) +! call q65_loops(c00,nutc,npts/2,nsps/2,nmode,mode65,nsubmode, & +! nFadingModel,ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols, & +! snr1,xdt1,f1,snr2,irc,dat4) +! baud rate required to compute B90TS later call q65_loops(c00,nutc,npts/2,nsps/2,nmode,mode65,nsubmode, & nFadingModel,ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols, & - snr1,xdt1,f1,snr2,irc,dat4) + snr1,xdt1,f1,snr2,irc,dat4,baud) call timer('q65loops',1) snr2=snr2 + db(6912.0/nsps) if(irc.ge.0) exit diff --git a/lib/qra/q65/q65.c b/lib/qra/q65/q65.c index 03b5219f5..c91571ba6 100644 --- a/lib/qra/q65/q65.c +++ b/lib/qra/q65/q65.c @@ -277,9 +277,9 @@ int q65_esnodb(const q65_codec_ds *pCodec, float *pEsNodB, const int *ydec, cons // Symbol time interval in seconds #define TS_QRA64 0.576 -#define TS_Q65 0.640 +// #define TS_Q65 0.640 // T/R = 60 s // The tables are computed assuming that the bin spacing is that of QRA64, that's to say -// 1/Ts = 12000/6912 Hz, but in q65 Ts is longer (0.640 s) and the table index +// 1/Ts = 12000/6912 Hz, but in Q65 Ts depends on the T/R interval and the table index // corresponding to a given B90 must be scaled appropriately. // See below. @@ -287,7 +287,7 @@ int q65_intrinsics_fastfading(q65_codec_ds *pCodec, float *pIntrinsics, // intrinsic symbol probabilities output const float *pInputEnergies, // received energies input const int submode, // submode idx (0=A ... 4=E) - const float B90, // spread bandwidth (90% fractional energy) + const float B90Ts, // spread bandwidth (90% fractional energy) const int fadingModel) // 0=Gaussian 1=Lorentzian fade model { int n, k, j; @@ -295,26 +295,26 @@ int q65_intrinsics_fastfading(q65_codec_ds *pCodec, int hidx, hlen, hhsz, hlast; const float *hptr; float fTemp, fNoiseVar, sumix, maxlogp; - float EsNoMetric; + float EsNoMetric,B90; float *weight; const float *pCurSym, *pCurBin; float *pCurIx; +// printf("pcodec=%08x submode=%d fadingmodel=%d B90Ts=%f\n",pcodec, submode,fadingModel, B90Ts); + if (pCodec==NULL) return Q65_DECODE_INVPARAMS; // invalid pCodec pointer + if (submode<0 || submode>4) return Q65_DECODE_INVPARAMS; // invalid submode - // As the symbol duration in q65 is longer than in QRA64 the fading tables continue - // to be valid if the B90 parameter is scaled by the actual symbol rate + // As the symbol duration in q65 is different than in QRA64, + // the fading tables continue to be valid if the B90Ts parameter + // is properly scaled to the QRA64 symbol interval // Compute index to most appropriate weighting function coefficients - hidx = (int)(logf(B90*TS_Q65/TS_QRA64)/logf(1.09f) - 0.499f); - -// if (hidx<0 || hidx > 64) -// // index of weighting function out of range -// // B90 out of range -// return q65_DECODE_INVPARAMS; + B90 = B90Ts/TS_QRA64; + hidx = (int)(logf(B90)/logf(1.09f) - 0.499f); // Unlike in QRA64 we accept any B90, anyway limiting it to // the extreme cases (0.9 to 210 Hz approx.) diff --git a/lib/qra/q65/q65.h b/lib/qra/q65/q65.h index bd80a934c..2e764a32b 100644 --- a/lib/qra/q65/q65.h +++ b/lib/qra/q65/q65.h @@ -69,7 +69,7 @@ int q65_intrinsics_fastfading(q65_codec_ds *pCodec, float *pIntrinsics, // intrinsic symbol probabilities output const float *pInputEnergies, // received energies input const int submode, // submode idx (0=A ... 4=E) - const float B90, // spread bandwidth (90% fractional energy) + const float B90Ts, // normalized spread bandwidth (90% fractional energy) const int fadingModel); // 0=Gaussian 1=Lorentzian fade model diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 1e1576556..1a976e26b 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,6 +1,6 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & ndepth,jpk0,xdt0,f0,width,iaptype,APmask,APsymbols,snr1,xdt1,f1, & - snr2,irc,dat4) + snr2,irc,dat4,baud) use packjt77 use timer_module, only: timer @@ -84,7 +84,8 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & if(b90.gt.230.0) cycle ! if(b90.lt.0.15*width) exit call timer('q65_intr',0) - call q65_intrinsics_ff(s3,nsubmode,b90,nFadingModel,s3prob) + b90ts = b90/baud + call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) call timer('q65_intr',1) call timer('q65_dec ',0) diff --git a/lib/qra/q65/q65_subs.c b/lib/qra/q65/q65_subs.c index b36adc540..9d765c86b 100644 --- a/lib/qra/q65/q65_subs.c +++ b/lib/qra/q65/q65_subs.c @@ -46,7 +46,7 @@ void q65_enc_(int x[], int y[]) q65_encode(&codec,y,x); } -void q65_intrinsics_ff_(float s3[], int* submode, float* B90, +void q65_intrinsics_ff_(float s3[], int* submode, float* B90Ts, int* fadingModel, float s3prob[]) { @@ -69,7 +69,7 @@ void q65_intrinsics_ff_(float s3[], int* submode, float* B90, } first=0; } - rc = q65_intrinsics_fastfading(&codec,s3prob,s3,*submode,*B90,*fadingModel); + rc = q65_intrinsics_fastfading(&codec,s3prob,s3,*submode,*B90Ts,*fadingModel); if(rc<0) { printf("error in q65_intrinsics()\n"); exit(0); From cbfb6dd25029a410bfea204cca17c152f45c0ead Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 23 Nov 2020 14:30:36 -0500 Subject: [PATCH 169/426] Implement full-message sync possibility when QSOprogress > 0. --- lib/q65_decode.f90 | 4 ++-- lib/qra/q65/q65_loops.f90 | 19 +++++++++++----- lib/sync_q65.f90 | 47 +++++++++++++++++++++++++++------------ 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 86d9264ab..9fffe9530 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -91,8 +91,8 @@ contains if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning nFadingModel=1 call timer('sync_q65',0) - call sync_q65(iwave,ntrperiod*12000,mode65,nsps,nfqso,ntol,xdt,f0, & - snr1,width) + call sync_q65(iwave,ntrperiod*12000,mode65,nQSOprogress,nsps,nfqso, & + ntol,xdt,f0,snr1,width) call timer('sync_q65',1) irc=-9 diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 72eade730..e1eac4e0e 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,10 +1,10 @@ -subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & +subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & ndepth,jpk0,xdt0,f0,width,iaptype,APmask,APsymbols,codewords,snr1, & xdt1,f1,snr2,irc,dat4) use packjt77 use timer_module, only: timer - parameter (LN=2176*63) !LN=LL*NN; LL = 64*(mode64+2) + parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 character*37 decoded character*77 c77 complex c00(0:npts2-1) !Analytic representation of dd(), 6000 Hz @@ -17,9 +17,15 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & integer APmask(13) integer APsymbols(13) integer codewords(63,64) +! integer cw4(63) integer dat4(13) !Decoded message (as 13 six-bit integers) integer nap(0:11) !AP return codes data nap/0,2,3,2,3,4,2,3,6,4,6,6/,nsave/0/ +! data cw4/0, 0, 0, 0, 8, 4,60,35,17,48,33,25,34,43,43,43,35,15,46,30, & +! 54,24,26,26,57,57,42, 3,23,11,49,49,16, 2, 6, 6,55,21,39,51, & +! 51,51,42,42,50,25,31,35,57,30, 1,54,54,10,10,22,44,58,57,40, & +! 21,21,19/ + save nsave,s3avg ircbest=9999 @@ -40,7 +46,7 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & if(iand(ndepth,3).eq.3) then ibwmax=5 endif - LL=64*(mode64+2) + LL=64*(mode_q65+2) NN=63 napmin=99 baud=6000.0/nsps @@ -67,7 +73,7 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & jpk=jpk0 + nsps*ndt/16 !tsym/16 if(jpk.lt.0) jpk=0 call timer('spec64 ',0) - call spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) + call spec64(c0,nsps,mode,mode_q65,jpk,s3,LL,NN) call timer('spec64 ',1) call pctile(s3,LL*NN,40,base) s3=s3/base @@ -85,10 +91,11 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & if(b90.gt.230.0) cycle ! if(b90.lt.0.15*width) exit call timer('q65_intr',0) - b90ts = b90/baud + b90ts = b90/baud call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) call timer('q65_intr',1) if(iaptype.eq.4) then +! codewords(1:63,4)=cw4 call timer('q65_apli',0) call q65_dec_fullaplist(s3,s3prob,codewords,3,esnodb,dat4,irc) call timer('q65_apli',1) @@ -111,7 +118,7 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode64,nsubmode,nFadingModel, & call twkfreq(c00,c0,npts2,6000.0,a) jpk=3000 !### Are these definitions OK? if(nsps.ge.3600) jpk=6000 !### TR >= 60 s - call spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) + call spec64(c0,nsps,mode,mode_q65,jpk,s3,LL,NN) call pctile(s3,LL*NN,40,base) s3=s3/base where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim diff --git a/lib/sync_q65.f90 b/lib/sync_q65.f90 index 1839aab0a..c802c27b0 100644 --- a/lib/sync_q65.f90 +++ b/lib/sync_q65.f90 @@ -1,4 +1,5 @@ -subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1,width) +subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & + xdt,f0,snr1,width) ! Detect and align with the Q65 sync vector, returning time and frequency ! offsets and SNR estimate. @@ -13,6 +14,7 @@ subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1,width) ! snr1 Relative SNR of sync signal parameter (NSTEP=8) !Step size nsps/NSTEP + parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 character*37 msg,msgsent integer*2 iwave(0:nmax-1) !Raw data integer isync(22) !Indices of sync symbols @@ -20,7 +22,9 @@ subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1,width) real, allocatable :: s1(:,:) !Symbol spectra, quarter-symbol steps real, allocatable :: ccf(:,:) !CCF(freq,lag) real, allocatable :: ccf1(:) !CCF(freq) at best lag - real sync(85) !sync vector + real sync(85) !sync vector + real s3(LN) !Symbol spectra + real s3prob(LN) !Symbol-value probabilities complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ data sync(1)/99.0/ @@ -147,14 +151,15 @@ subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1,width) width=i*1.414*df !### Experimental: - nQSOprogress=3 if(nQSOprogress.lt.1) go to 900 ! "Deep Likelihood" decode attempt - do imsg=1,3 + snr1a_best=0. + do imsg=1,4 ccf=0. msg='K1ABC W9XYZ RRR' if(imsg.eq.2) msg='K1ABC W9XYZ RR73' if(imsg.eq.3) msg='K1ABC W9XYZ 73' + if(imsg.eq.4) msg='CQ K9AN EN50' call genq65(msg,0,msgsent,itone,i3,n3) do lag=lag1,lag2 @@ -196,19 +201,33 @@ subroutine sync_q65(iwave,nmax,mode65,nsps,nfqso,ntol,xdt,f0,snr1,width) rms=sqrt(sq/nsq) smax=ccf(ipk,jpk) snr1a=smax/rms - write(57,3001) imsg,xdt,xdta,f0,f0a,snr1,snr1a -3001 format(i1,6f8.2) + if(snr1a.gt.snr1a_best) then + snr1a_best=snr1a + imsg_best=imsg + xdta_best=xdta + f0a_best=f0a + endif +! write(57,3001) imsg,xdt,xdta,f0,f0a,snr1,snr1a +!3001 format(i1,6f8.2) - do j=lag1,lag2 - write(55,3055) j,j*dtstep,ccf(ipk,j)/rms -3055 format(i5,f8.3,f10.3) - enddo +! do j=lag1,lag2 +! write(55,3055) j,j*dtstep,ccf(ipk,j)/rms +!3055 format(i5,f8.3,f10.3) +! enddo - do i=-ia,ia - write(56,3056) i*df,ccf(i,jpk)/rms -3056 format(2f10.3) - enddo +! do i=-ia,ia +! write(56,3056) i*df,ccf(i,jpk)/rms +!3056 format(2f10.3) +! enddo enddo + if(snr1a_best.gt.2.0) then + xdt=xdta_best + f0=f0a_best + snr1=1.4*snr1a_best + endif + +! write(58,3006) xdta_best,f0a_best,snr1a_best,imsg_best +!3006 format(3f8.2,i3) 900 return end subroutine sync_q65 From f0a1694816934fd1d17aac0bd40cf7d3940c787e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 28 Nov 2020 10:12:12 -0500 Subject: [PATCH 170/426] Minor code cleanup. --- lib/sync_q65.f90 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/sync_q65.f90 b/lib/sync_q65.f90 index c802c27b0..5b8847e3e 100644 --- a/lib/sync_q65.f90 +++ b/lib/sync_q65.f90 @@ -19,12 +19,10 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & integer*2 iwave(0:nmax-1) !Raw data integer isync(22) !Indices of sync symbols integer itone(85) - real, allocatable :: s1(:,:) !Symbol spectra, quarter-symbol steps + real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps real, allocatable :: ccf(:,:) !CCF(freq,lag) real, allocatable :: ccf1(:) !CCF(freq) at best lag real sync(85) !sync vector - real s3(LN) !Symbol spectra - real s3prob(LN) !Symbol-value probabilities complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ data sync(1)/99.0/ From 64516e6abbd21ccbd1698daf04693e5ec9432cf2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 29 Nov 2020 10:00:33 -0500 Subject: [PATCH 171/426] Still testing various parts of the Q65 decoding chain. --- lib/q65_decode.f90 | 16 ---------------- lib/qra/q65/q65_loops.f90 | 39 ++++++++++++++++++++++----------------- lib/sync_q65.f90 | 8 ++++---- 3 files changed, 26 insertions(+), 37 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 9fffe9530..304ca45b4 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -102,18 +102,6 @@ contains if(jpk0.lt.0) jpk0=0 fac=1.0/32767.0 dd=fac*iwave(1:npts) -!### -! Optionslly write noise level to LU 56 -! sq=dot_product(dd,dd)/npts -! m=nutc -! if(ntrperiod.ge.60) m=100*m -! ihr=m/10000 -! imin=mod(m/100,100) -! isec=mod(m,100) -! hours=ihr + imin/60.0 + isec/3600.0 -! write(56,3056) m,hours,db(sq)+90.3 -!3056 format(i6.6,f10.6,f10.3) -!### nmode=65 call ana64(dd,npts,c00) @@ -146,10 +134,6 @@ contains endif endif call timer('q65loops',0) -! call q65_loops(c00,nutc,npts/2,nsps/2,nmode,mode65,nsubmode, & -! nFadingModel,ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols, & -! snr1,xdt1,f1,snr2,irc,dat4) -! baud rate required to compute B90TS later call q65_loops(c00,nutc,npts/2,nsps/2,nmode,mode65,nsubmode, & nFadingModel,ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols, & codewords,snr1,xdt1,f1,snr2,irc,dat4) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index e1eac4e0e..bb1033771 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -4,7 +4,8 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & use packjt77 use timer_module, only: timer - parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 + parameter (NN=63) + parameter (LN=1152*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 character*37 decoded character*77 c77 complex c00(0:npts2-1) !Analytic representation of dd(), 6000 Hz @@ -12,19 +13,19 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & real a(3) !twkfreq params f,f1,f2 real s3(LN) !Symbol spectra real s3avg(LN) !Averaged symbol spectra - real s3prob(LN) !Symbol-value probabilities + real s3prob(64*NN) !Symbol-value probabilities logical unpk77_success integer APmask(13) integer APsymbols(13) integer codewords(63,64) -! integer cw4(63) + integer cw4(63) integer dat4(13) !Decoded message (as 13 six-bit integers) integer nap(0:11) !AP return codes data nap/0,2,3,2,3,4,2,3,6,4,6,6/,nsave/0/ -! data cw4/0, 0, 0, 0, 8, 4,60,35,17,48,33,25,34,43,43,43,35,15,46,30, & -! 54,24,26,26,57,57,42, 3,23,11,49,49,16, 2, 6, 6,55,21,39,51, & -! 51,51,42,42,50,25,31,35,57,30, 1,54,54,10,10,22,44,58,57,40, & -! 21,21,19/ + data cw4/0, 0, 0, 0, 8, 4,60,35,17,48,33,25,34,43,43,43,35,15,46,30, & + 54,24,26,26,57,57,42, 3,23,11,49,49,16, 2, 6, 6,55,21,39,51, & + 51,51,42,42,50,25,31,35,57,30, 1,54,54,10,10,22,44,58,57,40, & + 21,21,19/ save nsave,s3avg @@ -47,12 +48,11 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & ibwmax=5 endif LL=64*(mode_q65+2) - NN=63 napmin=99 baud=6000.0/nsps xdt1=xdt0 f1=f0 - + maxavg=0 if(iand(ndepth,16).ne.0) maxavg=1 do iavg=0,maxavg @@ -79,8 +79,10 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & s3=s3/base where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim endif - if(iavg.eq.1) then + kavg=0 + if(iavg.eq.1 .and. nsave.ge.2) then s3(1:LL*NN)=s3avg(1:LL*NN) + kavg=nsave endif do ibw=ibwmin,ibwmax nbw=ibw @@ -91,19 +93,22 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & if(b90.gt.230.0) cycle ! if(b90.lt.0.15*width) exit call timer('q65_intr',0) - b90ts = b90/baud + b90ts = b90/baud call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) call timer('q65_intr',1) if(iaptype.eq.4) then -! codewords(1:63,4)=cw4 + codewords(1:63,4)=cw4 call timer('q65_apli',0) - call q65_dec_fullaplist(s3,s3prob,codewords,3,esnodb,dat4,irc) + call q65_dec_fullaplist(s3,s3prob,codewords,4,esnodb,dat4,irc) call timer('q65_apli',1) else call timer('q65_dec ',0) call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) call timer('q65_dec ',1) endif +! write(71,3071) 100*nutc,0.0,ndf,ndt,nbw,ndist,irc,iaptype, & +! kavg,nsave +!3071 format(i6.6,f8.4,8i5) if(irc.ge.0) go to 100 ! irc > 0 ==> number of iterations required to decode ! -1 = invalid params @@ -122,16 +127,16 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & call pctile(s3,LL*NN,40,base) s3=s3/base where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim - s3avg(1:LL*NN)=s3avg(1:LL*NN)+s3(1:LL*NN) + s3avg(1:LL*NN)=s3avg(1:LL*NN) + s3(1:LL*NN) nsave=nsave+1 endif if(iavg.eq.0 .and. nsave.lt.2) exit enddo ! iavg -100 if(irc.ge.0) then +100 if(irc.ge.0) then navg=nsave snr2=esnodb - db(2500.0/baud) - if(iavg.eq.0) navg=0 + if(kavg.eq.0) navg=0 xdt1=xdt0 + nsps*ndt/(16.0*6000.0) f1=f0 + 0.5*baud*ndf !### For tests only: @@ -145,7 +150,7 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & imin=mod(m/100,100) isec=mod(m,100) hours=ihr + imin/60.0 + isec/3600.0 - write(53,3053) m,hours,ndf,ndt,nbw,ndist,irc,iaptype,navg,snr1, & + write(53,3053) m,hours,ndf,ndt,nbw,ndist,irc,iaptype,kavg,snr1, & xdt1,f1,snr2,trim(decoded) 3053 format(i6.6,f8.4,4i3,i4,2i3,f6.1,f6.2,f7.1,f6.1,1x,a) close(53) diff --git a/lib/sync_q65.f90 b/lib/sync_q65.f90 index 5b8847e3e..201d2a080 100644 --- a/lib/sync_q65.f90 +++ b/lib/sync_q65.f90 @@ -28,7 +28,7 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & data sync(1)/99.0/ save sync - nfft=2*nsps + nfft=nsps df=12000.0/nfft !Freq resolution = 0.5*baud istep=nsps/NSTEP iz=5000.0/df !Uppermost frequency bin, at 5000 Hz @@ -165,7 +165,7 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & j=j0 + NSTEP*(k-1) + 1 + lag if(j.ge.1 .and. j.le.jz) then do i=-ia,ia - ii=i0+2*itone(k)+i + ii=i0+itone(k)+i ccf(i,lag)=ccf(i,lag) + s1(ii,j) enddo endif @@ -207,7 +207,7 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & endif ! write(57,3001) imsg,xdt,xdta,f0,f0a,snr1,snr1a !3001 format(i1,6f8.2) - + ! do j=lag1,lag2 ! write(55,3055) j,j*dtstep,ccf(ipk,j)/rms !3055 format(i5,f8.3,f10.3) @@ -223,7 +223,7 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & f0=f0a_best snr1=1.4*snr1a_best endif - + ! write(58,3006) xdta_best,f0a_best,snr1a_best,imsg_best !3006 format(3f8.2,i3) From 9ff6f5b4d3c2359e7ca1bfee80facc3492599873 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 30 Nov 2020 09:52:47 -0500 Subject: [PATCH 172/426] Temporary save, much work in progress. --- CMakeLists.txt | 2 +- lib/q65_decode.f90 | 19 ++++- lib/{sync_q65.f90 => q65_sync.f90} | 129 ++++++++++++++--------------- lib/qra/q65/q65.c | 3 +- lib/qra/q65/q65.h | 2 +- lib/qra/q65/q65_loops.f90 | 34 ++++---- lib/qra/q65/q65_subs.c | 3 +- lib/qra/q65/q65sim.f90 | 30 +++---- 8 files changed, 114 insertions(+), 108 deletions(-) rename lib/{sync_q65.f90 => q65_sync.f90} (74%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d4f630f47..b910350da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -496,6 +496,7 @@ set (wsjt_FSRCS lib/polyfit.f90 lib/prog_args.f90 lib/ps4.f90 + lib/q65_sync.f90 lib/qra64a.f90 lib/qra_loops.f90 lib/qra/q65/q65_ap.f90 @@ -529,7 +530,6 @@ set (wsjt_FSRCS lib/sync4.f90 lib/sync64.f90 lib/sync65.f90 - lib/sync_q65.f90 lib/ft4/getcandidates4.f90 lib/ft4/get_ft4_bitmetrics.f90 lib/ft8/sync8.f90 diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 304ca45b4..819aba0d1 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -90,11 +90,22 @@ contains this%callback => callback if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning nFadingModel=1 + dgen=0 + call q65_enc(dgen,codewords) !Initialize Q65 +! nQSOprogress=3 !### + dat4=0 call timer('sync_q65',0) - call sync_q65(iwave,ntrperiod*12000,mode65,nQSOprogress,nsps,nfqso, & - ntol,xdt,f0,snr1,width) + call q65_sync(iwave,ntrperiod*12000,mode65,nQSOprogress,nsps,nfqso, & + ntol,xdt,f0,snr1,dat4,snr2,irc) call timer('sync_q65',1) - + write(55,3055) nutc,xdt,f0,snr1,snr2,irc +3055 format(i4.4,4f9.2,i5) + if(irc.ge.0) then + xdt1=xdt + f1=f0 + go to 100 + endif + irc=-9 if(snr1.lt.2.8) go to 100 jpk0=(xdt+1.0)*6000 !### Is this OK? @@ -135,7 +146,7 @@ contains endif call timer('q65loops',0) call q65_loops(c00,nutc,npts/2,nsps/2,nmode,mode65,nsubmode, & - nFadingModel,ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols, & + nFadingModel,ndepth,jpk0,xdt,f0,iaptype,apmask,apsymbols, & codewords,snr1,xdt1,f1,snr2,irc,dat4) call timer('q65loops',1) snr2=snr2 + db(6912.0/nsps) diff --git a/lib/sync_q65.f90 b/lib/q65_sync.f90 similarity index 74% rename from lib/sync_q65.f90 rename to lib/q65_sync.f90 index 201d2a080..d81cbd5f8 100644 --- a/lib/sync_q65.f90 +++ b/lib/q65_sync.f90 @@ -1,11 +1,11 @@ -subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & - xdt,f0,snr1,width) +subroutine q65_sync(iwave,nmax,mode_q65,nQSOprogress,nsps,nfqso,ntol, & + xdt,f0,snr1,dat4,snr2,irc) ! Detect and align with the Q65 sync vector, returning time and frequency ! offsets and SNR estimate. ! Input: iwave(0:nmax-1) Raw data -! mode65 Tone spacing 1 2 4 8 16 (A-E) +! mode_q65 Tone spacing 1 2 4 8 16 (A-E) ! nsps Samples per symbol at 12000 Sa/s ! nfqso Target frequency (Hz) ! ntol Search range around nfqso (Hz) @@ -19,17 +19,23 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & integer*2 iwave(0:nmax-1) !Raw data integer isync(22) !Indices of sync symbols integer itone(85) + integer codewords(63,64) + integer dat4(13) + integer ijpk(2) real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps + real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) real, allocatable :: ccf(:,:) !CCF(freq,lag) real, allocatable :: ccf1(:) !CCF(freq) at best lag + real s3prob(0:63,63) !Symbol-value probabilities real sync(85) !sync vector complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ data sync(1)/99.0/ save sync + LL=64*(2+mode_q65) nfft=nsps - df=12000.0/nfft !Freq resolution = 0.5*baud + df=12000.0/nfft !Freq resolution = baud istep=nsps/NSTEP iz=5000.0/df !Uppermost frequency bin, at 5000 Hz txt=85.0*nsps/12000.0 @@ -38,6 +44,7 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & ia=ntol/df allocate(s1(iz,jz)) + allocate(s3(-64:LL-65,63)) allocate(c0(0:nfft-1)) allocate(ccf(-ia:ia,-53:214)) allocate(ccf1(-ia:ia)) @@ -66,12 +73,13 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 enddo ! For large Doppler spreads, should we smooth the spectra here? - call smo121(s1(1:iz,j),iz) +! call smo121(s1(1:iz,j),iz) enddo i0=nint(nfqso/df) !Target QSO frequency call pctile(s1(i0-64:i0+192,1:jz),129*jz,40,base) - s1=s1/base - 1.0 +! s1=s1/base - 1.0 + s1=s1/base ! Apply fast AGC s1max=20.0 !Empirical choice @@ -101,19 +109,9 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & enddo enddo - ic=ntol/df - ccfmax=0. - ipk=0 - jpk=0 - do i=-ic,ic - do j=lag1,lag2 - if(ccf(i,j).gt.ccfmax) then - ipk=i - jpk=j - ccfmax=ccf(i,j) - endif - enddo - enddo + ijpk=maxloc(ccf) + ipk=ijpk(1)-ia-1 + jpk=ijpk(2)-53-1 f0=nfqso + ipk*df xdt=jpk*dtstep @@ -129,28 +127,10 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & smax=ccf(ipk,jpk) snr1=smax/rms -! do j=lag1,lag2 -! write(55,3055) j,j*dtstep,ccf(ipk,j)/rms -!3055 format(i5,f8.3,f10.3) -! enddo +!###################################################################### +! Experimental: Try early list decoding via "Deep Likelihood". -! do i=-ia,ia -! write(56,3056) i*df,ccf(i,jpk)/rms -!3056 format(2f10.3) -! enddo -! flush(56) - - ccf1=ccf(-ia:ia,jpk) - acf0=dot_product(ccf1,ccf1) - do i=1,ia - acf=dot_product(ccf1,cshift(ccf1,i)) - if(acf.le.0.5*acf0) exit - enddo - width=i*1.414*df - -!### Experimental: if(nQSOprogress.lt.1) go to 900 -! "Deep Likelihood" decode attempt snr1a_best=0. do imsg=1,4 ccf=0. @@ -159,7 +139,14 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & if(imsg.eq.3) msg='K1ABC W9XYZ 73' if(imsg.eq.4) msg='CQ K9AN EN50' call genq65(msg,0,msgsent,itone,i3,n3) + j=0 + do k=1,85 + if(sync(k)>0.) cycle + j=j+1 + codewords(j,imsg)=itone(k) - 1 + enddo +! Compute 2D ccf using all 85 symbols in the list message do lag=lag1,lag2 do k=1,85 j=j0 + NSTEP*(k-1) + 1 + lag @@ -172,22 +159,12 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & enddo enddo - ic=ntol/df - ccfmax=0. - ipk=0 - jpk=0 - do i=-ic,ic - do j=lag1,lag2 - if(ccf(i,j).gt.ccfmax) then - ipk=i - jpk=j - ccfmax=ccf(i,j) - endif - enddo - enddo + ijpk=maxloc(ccf) + ipk=ijpk(1)-ia-1 + jpk=ijpk(2)-53-1 f0a=nfqso + ipk*df xdta=jpk*dtstep - + sq=0. nsq=0 do j=lag1,lag2 @@ -205,27 +182,43 @@ subroutine sync_q65(iwave,nmax,mode65,nQSOprogress,nsps,nfqso,ntol, & xdta_best=xdta f0a_best=f0a endif -! write(57,3001) imsg,xdt,xdta,f0,f0a,snr1,snr1a -!3001 format(i1,6f8.2) + enddo ! imsg -! do j=lag1,lag2 -! write(55,3055) j,j*dtstep,ccf(ipk,j)/rms -!3055 format(i5,f8.3,f10.3) -! enddo - -! do i=-ia,ia -! write(56,3056) i*df,ccf(i,jpk)/rms -!3056 format(2f10.3) -! enddo - enddo if(snr1a_best.gt.2.0) then xdt=xdta_best f0=f0a_best snr1=1.4*snr1a_best endif -! write(58,3006) xdta_best,f0a_best,snr1a_best,imsg_best -!3006 format(3f8.2,i3) + ia=i0+ipk-63 + ib=ia+LL-1 + j=j0+jpk-5 + n=0 + do k=1,85 + j=j+8 + if(sync(k).gt.0.0) then + cycle + endif + n=n+1 + s3(-64:LL-65,n)=s1(ia:ib,j) + enddo + + nsubmode=0 + nFadingModel=1 + baud=12000.0/nsps + dat4=0 + irc=-2 + do ibw=0,10 + b90=1.72**ibw + call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) + call q65_dec_fullaplist(s3,s3prob,codewords,4,esnodb,dat4,plog,irc) + if(irc.ge.0) then + xdt=xdta_best + f0=f0a_best + snr2=esnodb - db(2500.0/baud) + exit + endif + enddo 900 return -end subroutine sync_q65 +end subroutine q65_sync diff --git a/lib/qra/q65/q65.c b/lib/qra/q65/q65.c index c91571ba6..c92335007 100644 --- a/lib/qra/q65/q65.c +++ b/lib/qra/q65/q65.c @@ -703,10 +703,11 @@ int q65_decode_fullaplist(q65_codec_ds *codec, maxllh = llh; maxcw = k; } + // printf("BBB %d %f\n",k,llh); // point to next codeword pCw+=nN; } - + q65_llh=maxllh; if (maxcw<0) // no llh larger than threshold found return Q65_DECODE_FAILED; diff --git a/lib/qra/q65/q65.h b/lib/qra/q65/q65.h index 2e764a32b..f48c40da9 100644 --- a/lib/qra/q65/q65.h +++ b/lib/qra/q65/q65.h @@ -39,7 +39,7 @@ // maximum number of weights for the fast-fading metric evaluation #define Q65_FASTFADING_MAXWEIGTHS 65 - +float q65_llh; typedef struct { const qracode *pQraCode; // qra code to be used by the codec float decoderEsNoMetric; // value for which we optimize the decoder metric diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index bb1033771..5c48f85a1 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,5 +1,5 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & - ndepth,jpk0,xdt0,f0,width,iaptype,APmask,APsymbols,codewords,snr1, & + ndepth,jpk0,xdt0,f0,iaptype,APmask,APsymbols,codewords,snr1, & xdt1,f1,snr2,irc,dat4) use packjt77 @@ -91,7 +91,6 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & ! b90=1.728**ibw b90=3.0**nbw if(b90.gt.230.0) cycle -! if(b90.lt.0.15*width) exit call timer('q65_intr',0) b90ts = b90/baud call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) @@ -99,7 +98,8 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & if(iaptype.eq.4) then codewords(1:63,4)=cw4 call timer('q65_apli',0) - call q65_dec_fullaplist(s3,s3prob,codewords,4,esnodb,dat4,irc) + call q65_dec_fullaplist(s3,s3prob,codewords,4,esnodb, & + dat4,plog,irc) call timer('q65_apli',1) else call timer('q65_dec ',0) @@ -140,20 +140,20 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & xdt1=xdt0 + nsps*ndt/(16.0*6000.0) f1=f0 + 0.5*baud*ndf !### For tests only: - open(53,file='fort.53',status='unknown',position='append') - write(c77,1100) dat4(1:12),dat4(13)/2 -1100 format(12b6.6,b5.5) - call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent - m=nutc - if(nsps.ge.3600) m=100*m - ihr=m/10000 - imin=mod(m/100,100) - isec=mod(m,100) - hours=ihr + imin/60.0 + isec/3600.0 - write(53,3053) m,hours,ndf,ndt,nbw,ndist,irc,iaptype,kavg,snr1, & - xdt1,f1,snr2,trim(decoded) -3053 format(i6.6,f8.4,4i3,i4,2i3,f6.1,f6.2,f7.1,f6.1,1x,a) - close(53) +! open(53,file='fort.53',status='unknown',position='append') +! write(c77,1100) dat4(1:12),dat4(13)/2 +!1100 format(12b6.6,b5.5) +! call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent +! m=nutc +! if(nsps.ge.3600) m=100*m +! ihr=m/10000 +! imin=mod(m/100,100) +! isec=mod(m,100) +! hours=ihr + imin/60.0 + isec/3600.0 +! write(53,3053) m,hours,ndf,ndt,nbw,ndist,irc,iaptype,kavg,snr1, & +! xdt1,f1,snr2,trim(decoded) +!3053 format(i6.6,f8.4,4i3,i4,2i3,f6.1,f6.2,f7.1,f6.1,1x,a) +! close(53) !### nsave=0 s3avg=0. diff --git a/lib/qra/q65/q65_subs.c b/lib/qra/q65/q65_subs.c index 9f29da01c..e55fe927d 100644 --- a/lib/qra/q65/q65_subs.c +++ b/lib/qra/q65/q65_subs.c @@ -111,7 +111,7 @@ void q65_dec_(float s3[], float s3prob[], int APmask[], int APsymbols[], } void q65_dec_fullaplist_(float s3[], float s3prob[], int codewords[], - int* ncw, float* esnodb0, int xdec[], int* rc0) + int* ncw, float* esnodb0, int xdec[], float* plog, int* rc0) { /* Input: s3[LL,NN] Symbol spectra * s3prob[LL,NN] Symbol-value intrinsic probabilities @@ -128,6 +128,7 @@ void q65_dec_fullaplist_(float s3[], float s3prob[], int codewords[], float esnodb; rc = q65_decode_fullaplist(&codec,ydec,xdec,s3prob,codewords,*ncw); + *plog=q65_llh; *rc0=rc; // rc = -1: Invalid params diff --git a/lib/qra/q65/q65sim.f90 b/lib/qra/q65/q65sim.f90 index f99ab4f1d..241d0adb8 100644 --- a/lib/qra/q65/q65sim.f90 +++ b/lib/qra/q65/q65sim.f90 @@ -193,21 +193,21 @@ program q65sim write(10) h,iwave(1:npts) !Save the .wav file close(10) - if(lsync) then - cd=' ' - if(ifile.eq.nfiles) cd='d' - nfqso=nint(f0) - ntol=100 - call sync_q65(iwave,npts,mode65,nsps,nfqso,ntol,xdt2,f02,snr2) - terr=1.01/(8.0*baud) - ferr=1.01*mode65*baud - if(abs(xdt2-xdt).lt.terr .and. abs(f02-f0).lt.ferr) nsync=nsync+1 - open(40,file='sync65.out',status='unknown',position='append') - write(40,1030) ifile,65,csubmode,snrdb,fspread,xdt2-xdt,f02-f0, & - snr2,nsync,cd -1030 format(i4,i3,1x,a1,2f7.1,f7.2,2f8.1,i5,1x,a1) - close(40) - endif +! if(lsync) then +! cd=' ' +! if(ifile.eq.nfiles) cd='d' +! nfqso=nint(f0) +! ntol=100 +! call q65_sync(iwave,npts,mode65,nsps,nfqso,ntol,xdt2,f02,snr2) +! terr=1.01/(8.0*baud) +! ferr=1.01*mode65*baud +! if(abs(xdt2-xdt).lt.terr .and. abs(f02-f0).lt.ferr) nsync=nsync+1 +! open(40,file='sync65.out',status='unknown',position='append') +! write(40,1030) ifile,65,csubmode,snrdb,fspread,xdt2-xdt,f02-f0, & +! snr2,nsync,cd +!1030 format(i4,i3,1x,a1,2f7.1,f7.2,2f8.1,i5,1x,a1) +! close(40) +! endif enddo if(lsync) write(*,1040) snrdb,nfiles,nsync 1040 format('SNR:',f6.1,' nfiles:',i5,' nsynced:',i5) From afc4f2fb5477ddf7c0e2d7248ecc9e955a0a0d07 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 30 Nov 2020 10:45:52 -0500 Subject: [PATCH 173/426] Reorganize things in q65_sync. --- lib/q65_sync.f90 | 109 +++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 64 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index d81cbd5f8..d47997d9a 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -97,41 +97,13 @@ subroutine q65_sync(iwave,nmax,mode_q65,nQSOprogress,nsps,nfqso,ntol, & j0=1.0/dtstep !Nominal index for start of signal lag2=4.0/dtstep + 0.9999 !Include EME delays endif - ccf=0. - - do lag=lag1,lag2 - do k=1,85 - n=NSTEP*(k-1) + 1 - j=n+lag+j0 - if(j.ge.1 .and. j.le.jz) then - ccf(-ia:ia,lag)=ccf(-ia:ia,lag) + sync(k)*s1(i0-ia:i0+ia,j) - endif - enddo - enddo - - ijpk=maxloc(ccf) - ipk=ijpk(1)-ia-1 - jpk=ijpk(2)-53-1 - f0=nfqso + ipk*df - xdt=jpk*dtstep - - sq=0. - nsq=0 - do j=lag1,lag2 - if(abs(j-jpk).gt.6) then - sq=sq + ccf(ipk,j)**2 - nsq=nsq+1 - endif - enddo - rms=sqrt(sq/nsq) - smax=ccf(ipk,jpk) - snr1=smax/rms !###################################################################### -! Experimental: Try early list decoding via "Deep Likelihood". +! Try list decoding via "Deep Likelihood". - if(nQSOprogress.lt.1) go to 900 - snr1a_best=0. + ipk=0 + jpk=0 + ccf_best=0. do imsg=1,4 ccf=0. msg='K1ABC W9XYZ RRR' @@ -158,38 +130,17 @@ subroutine q65_sync(iwave,nmax,mode_q65,nQSOprogress,nsps,nfqso,ntol, & endif enddo enddo - - ijpk=maxloc(ccf) - ipk=ijpk(1)-ia-1 - jpk=ijpk(2)-53-1 - f0a=nfqso + ipk*df - xdta=jpk*dtstep - - sq=0. - nsq=0 - do j=lag1,lag2 - if(abs(j-jpk).gt.6) then - sq=sq + ccf(ipk,j)**2 - nsq=nsq+1 - endif - enddo - rms=sqrt(sq/nsq) - smax=ccf(ipk,jpk) - snr1a=smax/rms - if(snr1a.gt.snr1a_best) then - snr1a_best=snr1a - imsg_best=imsg - xdta_best=xdta - f0a_best=f0a + ccfmax=maxval(ccf) + if(ccfmax.gt.ccf_best) then + ccf_best=ccfmax + ijpk=maxloc(ccf) + ipk=ijpk(1)-ia-1 + jpk=ijpk(2)-53-1 + f0=nfqso + ipk*df + xdt=jpk*dtstep endif enddo ! imsg - if(snr1a_best.gt.2.0) then - xdt=xdta_best - f0=f0a_best - snr1=1.4*snr1a_best - endif - ia=i0+ipk-63 ib=ia+LL-1 j=j0+jpk-5 @@ -213,12 +164,42 @@ subroutine q65_sync(iwave,nmax,mode_q65,nQSOprogress,nsps,nfqso,ntol, & call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,4,esnodb,dat4,plog,irc) if(irc.ge.0) then - xdt=xdta_best - f0=f0a_best snr2=esnodb - db(2500.0/baud) - exit + go to 900 endif enddo +!###################################################################### +! Establish xdt, f0, and snr1 using sync symbols (and perhaps some AP symbols) + ccf=0. + irc=-2 + dat4=0 + ia=ntol/df + do lag=lag1,lag2 + do k=1,85 + n=NSTEP*(k-1) + 1 + j=n+lag+j0 + if(j.ge.1 .and. j.le.jz) then + ccf(-ia:ia,lag)=ccf(-ia:ia,lag) + sync(k)*s1(i0-ia:i0+ia,j) + endif + enddo + enddo + ijpk=maxloc(ccf) + ipk=ijpk(1)-ia-1 + jpk=ijpk(2)-53-1 + f0=nfqso + ipk*df + xdt=jpk*dtstep + sq=0. + nsq=0 + do j=lag1,lag2 + if(abs(j-jpk).gt.6) then + sq=sq + ccf(ipk,j)**2 + nsq=nsq+1 + endif + enddo + rms=sqrt(sq/nsq) + smax=ccf(ipk,jpk) + snr1=smax/rms + 900 return end subroutine q65_sync From 8285fd28a8ed64d4be10bd3cea0eb88c65c714f4 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 30 Nov 2020 11:41:50 -0500 Subject: [PATCH 174/426] List decoding now supports 57 list messages. --- CMakeLists.txt | 1 + lib/q65_decode.f90 | 9 +++++---- lib/q65_sync.f90 | 34 +++++++++++++++++----------------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b910350da..2dfdbc897 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -501,6 +501,7 @@ set (wsjt_FSRCS lib/qra_loops.f90 lib/qra/q65/q65_ap.f90 lib/qra/q65/q65_loops.f90 + lib/qra/q65/q65_set_list.f90 lib/refspectrum.f90 lib/savec2.f90 lib/sec0.f90 diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 819aba0d1..dcbcabc8e 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -90,16 +90,17 @@ contains this%callback => callback if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning nFadingModel=1 + call q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) dgen=0 call q65_enc(dgen,codewords) !Initialize Q65 ! nQSOprogress=3 !### dat4=0 call timer('sync_q65',0) - call q65_sync(iwave,ntrperiod*12000,mode65,nQSOprogress,nsps,nfqso, & - ntol,xdt,f0,snr1,dat4,snr2,irc) + call q65_sync(iwave,ntrperiod*12000,mode65,codewords,ncw,nsps, & + nfqso,ntol,xdt,f0,snr1,dat4,snr2,irc) call timer('sync_q65',1) - write(55,3055) nutc,xdt,f0,snr1,snr2,irc -3055 format(i4.4,4f9.2,i5) +! write(55,3055) nutc,xdt,f0,snr1,snr2,irc +!3055 format(i4.4,4f9.2,i5) if(irc.ge.0) then xdt1=xdt f1=f0 diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index d47997d9a..3062cbb8d 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -1,4 +1,4 @@ -subroutine q65_sync(iwave,nmax,mode_q65,nQSOprogress,nsps,nfqso,ntol, & +subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & xdt,f0,snr1,dat4,snr2,irc) ! Detect and align with the Q65 sync vector, returning time and frequency @@ -15,7 +15,6 @@ subroutine q65_sync(iwave,nmax,mode_q65,nQSOprogress,nsps,nfqso,ntol, & parameter (NSTEP=8) !Step size nsps/NSTEP parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 - character*37 msg,msgsent integer*2 iwave(0:nmax-1) !Raw data integer isync(22) !Indices of sync symbols integer itone(85) @@ -98,27 +97,28 @@ subroutine q65_sync(iwave,nmax,mode_q65,nQSOprogress,nsps,nfqso,ntol, & lag2=4.0/dtstep + 0.9999 !Include EME delays endif + if(ncw.lt.1) go to 100 + !###################################################################### ! Try list decoding via "Deep Likelihood". ipk=0 jpk=0 ccf_best=0. - do imsg=1,4 - ccf=0. - msg='K1ABC W9XYZ RRR' - if(imsg.eq.2) msg='K1ABC W9XYZ RR73' - if(imsg.eq.3) msg='K1ABC W9XYZ 73' - if(imsg.eq.4) msg='CQ K9AN EN50' - call genq65(msg,0,msgsent,itone,i3,n3) - j=0 - do k=1,85 - if(sync(k)>0.) cycle - j=j+1 - codewords(j,imsg)=itone(k) - 1 + do imsg=1,ncw + i=1 + k=0 + do j=1,85 + if(j.eq.isync(i)) then + i=i+1 + itone(j)=-1 + else + k=k+1 + itone(j)=codewords(k,imsg) + endif enddo - ! Compute 2D ccf using all 85 symbols in the list message + ccf=0. do lag=lag1,lag2 do k=1,85 j=j0 + NSTEP*(k-1) + 1 + lag @@ -162,7 +162,7 @@ subroutine q65_sync(iwave,nmax,mode_q65,nQSOprogress,nsps,nfqso,ntol, & do ibw=0,10 b90=1.72**ibw call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) - call q65_dec_fullaplist(s3,s3prob,codewords,4,esnodb,dat4,plog,irc) + call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) if(irc.ge.0) then snr2=esnodb - db(2500.0/baud) go to 900 @@ -171,7 +171,7 @@ subroutine q65_sync(iwave,nmax,mode_q65,nQSOprogress,nsps,nfqso,ntol, & !###################################################################### ! Establish xdt, f0, and snr1 using sync symbols (and perhaps some AP symbols) - ccf=0. +100 ccf=0. irc=-2 dat4=0 ia=ntol/df From de6f5e497509753539696240e944282f797fc85a Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 30 Nov 2020 13:14:18 -0500 Subject: [PATCH 175/426] Q65 code cleanup. Use 3-digit format for the end-of-line flag. --- lib/decoder.f90 | 18 ++++++------------ lib/q65_decode.f90 | 39 +++++++++++++++------------------------ lib/q65_sync.f90 | 11 +++++++---- lib/qra/q65/q65_loops.f90 | 6 +++++- 4 files changed, 33 insertions(+), 41 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 49dca917b..011b98aa6 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -777,8 +777,7 @@ contains return end subroutine fst4_decoded - subroutine q65_decoded (this,nutc,sync,nsnr,dt,freq,decoded,irc, & - qual,ntrperiod,fmid,w50) + subroutine q65_decoded (this,nutc,sync,nsnr,dt,freq,decoded,idec,ntrperiod) use q65_decode implicit none @@ -790,22 +789,17 @@ contains real, intent(in) :: dt real, intent(in) :: freq character(len=37), intent(in) :: decoded - integer, intent(in) :: irc - real, intent(in) :: qual + integer, intent(in) :: idec integer, intent(in) :: ntrperiod - real, intent(in) :: fmid - real, intent(in) :: w50 - integer navg - navg=irc/100 if(ntrperiod.lt.60) then - write(*,1001) nutc,nsnr,dt,nint(freq),decoded,mod(irc,100),navg -1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i2,i4) + write(*,1001) nutc,nsnr,dt,nint(freq),decoded,idec +1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i3.3) write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded 1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') else - write(*,1003) nutc,nsnr,dt,nint(freq),decoded,mod(irc,100),navg -1003 format(i4.4,i4,f5.1,i5,' + ',1x,a37,1x,i2,i4) + write(*,1003) nutc,nsnr,dt,nint(freq),decoded,idec +1003 format(i4.4,i4,f5.1,i5,' + ',1x,a37,1x,i3.3) write(13,1004) nutc,nint(sync),nsnr,dt,freq,0,decoded 1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index dcbcabc8e..5bfcc5215 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -8,7 +8,7 @@ module q65_decode abstract interface subroutine q65_decode_callback (this,nutc,sync,nsnr,dt,freq, & - decoded,nap,qual,ntrperiod,fmid,w50) + decoded,nap,ntrperiod) import q65_decoder implicit none class(q65_decoder), intent(inout) :: this @@ -19,10 +19,7 @@ module q65_decode 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 q65_decode_callback end interface @@ -64,6 +61,9 @@ contains complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s + id1=0 + id2=0 + id3=0 mode65=2**nsubmode npts=ntrperiod*12000 nfft1=ntrperiod*12000 @@ -93,21 +93,16 @@ contains call q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) dgen=0 call q65_enc(dgen,codewords) !Initialize Q65 -! nQSOprogress=3 !### - dat4=0 call timer('sync_q65',0) - call q65_sync(iwave,ntrperiod*12000,mode65,codewords,ncw,nsps, & - nfqso,ntol,xdt,f0,snr1,dat4,snr2,irc) + call q65_sync(nutc,iwave,ntrperiod*12000,mode65,codewords,ncw,nsps, & + nfqso,ntol,xdt,f0,snr1,dat4,snr2,id1) call timer('sync_q65',1) -! write(55,3055) nutc,xdt,f0,snr1,snr2,irc -!3055 format(i4.4,4f9.2,i5) - if(irc.ge.0) then + if(id1.eq.1) then xdt1=xdt f1=f0 go to 100 endif - irc=-9 if(snr1.lt.2.8) go to 100 jpk0=(xdt+1.0)*6000 !### Is this OK? if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !### @@ -116,7 +111,6 @@ contains dd=fac*iwave(1:npts) nmode=65 call ana64(dd,npts,c00) - call ft8apset(mycall,hiscall,ncontest,apsym0,aph10) where(apsym0.eq.-1) apsym0=0 @@ -148,31 +142,28 @@ contains call timer('q65loops',0) call q65_loops(c00,nutc,npts/2,nsps/2,nmode,mode65,nsubmode, & nFadingModel,ndepth,jpk0,xdt,f0,iaptype,apmask,apsymbols, & - codewords,snr1,xdt1,f1,snr2,irc,dat4) + codewords,snr1,xdt1,f1,snr2,dat4,id2,id3) call timer('q65loops',1) snr2=snr2 + db(6912.0/nsps) - if(irc.ge.0) exit + if(id2+id3.gt.0) exit enddo 100 decoded=' ' -! if(irc.lt.0 .and.iaptype.eq.4) print*,'AAA',irc,iaptype - if(irc.ge.0) then -!### - navg=irc/100 -! irc=100*navg + ipass - irc=100*navg + iaptype -!### + idec=100*id1 + 10*id2 + id3 + write(71,3071) nutc,id1,id2,id3,irc +3071 format(5i6) + if(idec.gt.0) then 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 nsnr=nint(snr2) call this%callback(nutc,sync,nsnr,xdt1,f1,decoded, & - irc,qual,ntrperiod,fmid,w50) + idec,ntrperiod) else ! Report sync, even if no decode. nsnr=db(snr1) - 35.0 call this%callback(nutc,sync,nsnr,xdt1,f1,decoded, & - irc,qual,ntrperiod,fmid,w50) + idec,ntrperiod) endif return diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 3062cbb8d..87ac1fac5 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -1,5 +1,5 @@ -subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & - xdt,f0,snr1,dat4,snr2,irc) +subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & + xdt,f0,snr1,dat4,snr2,id1) ! Detect and align with the Q65 sync vector, returning time and frequency ! offsets and SNR estimate. @@ -32,6 +32,8 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & data sync(1)/99.0/ save sync + id1=0 + dat4=0 LL=64*(2+mode_q65) nfft=nsps df=12000.0/nfft !Freq resolution = baud @@ -157,14 +159,15 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & nsubmode=0 nFadingModel=1 baud=12000.0/nsps - dat4=0 - irc=-2 do ibw=0,10 b90=1.72**ibw call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) if(irc.ge.0) then snr2=esnodb - db(2500.0/baud) + id1=1 +! write(55,3055) nutc,xdt,f0,snr2,plog,irc +!3055 format(i4.4,4f9.2,i5) go to 900 endif enddo diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 5c48f85a1..59ba36c0e 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,6 +1,6 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & ndepth,jpk0,xdt0,f0,iaptype,APmask,APsymbols,codewords,snr1, & - xdt1,f1,snr2,irc,dat4) + xdt1,f1,snr2,dat4,id2,id3) use packjt77 use timer_module, only: timer @@ -29,6 +29,8 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & save nsave,s3avg + id2=0 + id3=0 ircbest=9999 allocate(c0(0:npts2-1)) irc=-99 @@ -101,10 +103,12 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & call q65_dec_fullaplist(s3,s3prob,codewords,4,esnodb, & dat4,plog,irc) call timer('q65_apli',1) + if(irc.ge.0) id2=4 else call timer('q65_dec ',0) call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) call timer('q65_dec ',1) + if(irc.ge.0) id2=iaptype endif ! write(71,3071) 100*nutc,0.0,ndf,ndt,nbw,ndist,irc,iaptype, & ! kavg,nsave From 5c947178ce2f2f97691ed0ec3f2de6cc23b61ab8 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 30 Nov 2020 13:26:14 -0500 Subject: [PATCH 176/426] Protect against a bounds error. --- lib/q65_sync.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 87ac1fac5..a9d58537d 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -153,7 +153,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & cycle endif n=n+1 - s3(-64:LL-65,n)=s1(ia:ib,j) + if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(ia:ib,j) enddo nsubmode=0 From adc4c3d78aa27b86e816e06da1599bec89b71417 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 30 Nov 2020 14:13:37 -0500 Subject: [PATCH 177/426] More Q65 cleanup. Single-digit at end of line. --- lib/decoder.f90 | 4 +- lib/q65_decode.f90 | 14 ++-- lib/qra/q65/q65_loops.f90 | 162 ++++++++++++-------------------------- 3 files changed, 60 insertions(+), 120 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 011b98aa6..d93f5a73a 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -794,12 +794,12 @@ contains if(ntrperiod.lt.60) then write(*,1001) nutc,nsnr,dt,nint(freq),decoded,idec -1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i3.3) +1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i1) write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded 1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') else write(*,1003) nutc,nsnr,dt,nint(freq),decoded,idec -1003 format(i4.4,i4,f5.1,i5,' + ',1x,a37,1x,i3.3) +1003 format(i4.4,i4,f5.1,i5,' + ',1x,a37,1x,i1) write(13,1004) nutc,nint(sync),nsnr,dt,freq,0,decoded 1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 5bfcc5215..0473b7119 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -63,7 +63,6 @@ contains id1=0 id2=0 - id3=0 mode65=2**nsubmode npts=ntrperiod*12000 nfft1=ntrperiod*12000 @@ -140,19 +139,17 @@ contains endif endif call timer('q65loops',0) - call q65_loops(c00,nutc,npts/2,nsps/2,nmode,mode65,nsubmode, & + call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode, & nFadingModel,ndepth,jpk0,xdt,f0,iaptype,apmask,apsymbols, & - codewords,snr1,xdt1,f1,snr2,dat4,id2,id3) + xdt1,f1,snr2,dat4,id2) call timer('q65loops',1) snr2=snr2 + db(6912.0/nsps) - if(id2+id3.gt.0) exit + if(id2.gt.0) exit enddo 100 decoded=' ' - idec=100*id1 + 10*id2 + id3 - write(71,3071) nutc,id1,id2,id3,irc -3071 format(5i6) - if(idec.gt.0) then + if(id1.gt.0 .or. id2.gt.0) then + idec=id1+id2 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 @@ -162,6 +159,7 @@ contains else ! Report sync, even if no decode. nsnr=db(snr1) - 35.0 + idec=-1 call this%callback(nutc,sync,nsnr,xdt1,f1,decoded, & idec,ntrperiod) endif diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 59ba36c0e..b440e076f 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,23 +1,17 @@ -subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & - ndepth,jpk0,xdt0,f0,iaptype,APmask,APsymbols,codewords,snr1, & - xdt1,f1,snr2,dat4,id2,id3) +subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & + ndepth,jpk0,xdt0,f0,iaptype,APmask,APsymbols,xdt1,f1,snr2,dat4,id2) use packjt77 use timer_module, only: timer parameter (NN=63) parameter (LN=1152*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 - character*37 decoded - character*77 c77 complex c00(0:npts2-1) !Analytic representation of dd(), 6000 Hz complex ,allocatable :: c0(:) !Ditto, with freq shift real a(3) !twkfreq params f,f1,f2 real s3(LN) !Symbol spectra - real s3avg(LN) !Averaged symbol spectra real s3prob(64*NN) !Symbol-value probabilities - logical unpk77_success integer APmask(13) integer APsymbols(13) - integer codewords(63,64) integer cw4(63) integer dat4(13) !Decoded message (as 13 six-bit integers) integer nap(0:11) !AP return codes @@ -27,10 +21,7 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & 51,51,42,42,50,25,31,35,57,30, 1,54,54,10,10,22,44,58,57,40, & 21,21,19/ - save nsave,s3avg - - id2=0 - id3=0 + id2=-1 ircbest=9999 allocate(c0(0:npts2-1)) irc=-99 @@ -55,113 +46,64 @@ subroutine q65_loops(c00,nutc,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & xdt1=xdt0 f1=f0 - maxavg=0 - if(iand(ndepth,16).ne.0) maxavg=1 - do iavg=0,maxavg - if(iavg.eq.1) then - idfmax=1 - idtmax=1 - endif - do idf=1,idfmax - ndf=idf/2 - if(mod(idf,2).eq.0) ndf=-ndf - a=0. - a(1)=-(f0+0.5*baud*ndf) - call twkfreq(c00,c0,npts2,6000.0,a) - do idt=1,idtmax - ndt=idt/2 - if(iaptype.eq.0 .and. iavg.eq.0) then - if(mod(idt,2).eq.0) ndt=-ndt - jpk=jpk0 + nsps*ndt/16 !tsym/16 - if(jpk.lt.0) jpk=0 - call timer('spec64 ',0) - call spec64(c0,nsps,mode,mode_q65,jpk,s3,LL,NN) - call timer('spec64 ',1) - call pctile(s3,LL*NN,40,base) - s3=s3/base - where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim - endif - kavg=0 - if(iavg.eq.1 .and. nsave.ge.2) then - s3(1:LL*NN)=s3avg(1:LL*NN) - kavg=nsave - endif - do ibw=ibwmin,ibwmax - nbw=ibw - ndist=ndf**2 + ndt**2 + ((nbw-2))**2 - if(ndist.gt.maxdist) cycle -! b90=1.728**ibw - b90=3.0**nbw - if(b90.gt.230.0) cycle - call timer('q65_intr',0) - b90ts = b90/baud - call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) - call timer('q65_intr',1) - if(iaptype.eq.4) then - codewords(1:63,4)=cw4 - call timer('q65_apli',0) - call q65_dec_fullaplist(s3,s3prob,codewords,4,esnodb, & - dat4,plog,irc) - call timer('q65_apli',1) - if(irc.ge.0) id2=4 - else - call timer('q65_dec ',0) - call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) - call timer('q65_dec ',1) - if(irc.ge.0) id2=iaptype - endif -! write(71,3071) 100*nutc,0.0,ndf,ndt,nbw,ndist,irc,iaptype, & -! kavg,nsave -!3071 format(i6.6,f8.4,8i5) - if(irc.ge.0) go to 100 + do idf=1,idfmax + ndf=idf/2 + if(mod(idf,2).eq.0) ndf=-ndf + a=0. + a(1)=-(f0+0.5*baud*ndf) + call twkfreq(c00,c0,npts2,6000.0,a) + do idt=1,idtmax + ndt=idt/2 + if(iaptype.eq.0) then + if(mod(idt,2).eq.0) ndt=-ndt + jpk=jpk0 + nsps*ndt/16 !tsym/16 + if(jpk.lt.0) jpk=0 + call timer('spec64 ',0) + call spec64(c0,nsps,mode,mode_q65,jpk,s3,LL,NN) + call timer('spec64 ',1) + call pctile(s3,LL*NN,40,base) + s3=s3/base + where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim + endif + do ibw=ibwmin,ibwmax + nbw=ibw + ndist=ndf**2 + ndt**2 + ((nbw-2))**2 + if(ndist.gt.maxdist) cycle + ! b90=1.728**ibw + b90=3.0**nbw + if(b90.gt.230.0) cycle + call timer('q65_intr',0) + b90ts = b90/baud + call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) + call timer('q65_intr',1) + call timer('q65_dec ',0) + call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) + call timer('q65_dec ',1) + if(irc.ge.0) id2=iaptype+2 + if(irc.ge.0) go to 100 ! irc > 0 ==> number of iterations required to decode ! -1 = invalid params ! -2 = decode failed ! -3 = CRC mismatch - enddo ! ibw (b90 loop) - enddo ! idt (DT loop) - enddo ! idf (f0 loop) - if(iaptype.eq.0 .and. iavg.eq.0) then - a=0. - a(1)=-f0 - call twkfreq(c00,c0,npts2,6000.0,a) - jpk=3000 !### Are these definitions OK? - if(nsps.ge.3600) jpk=6000 !### TR >= 60 s - call spec64(c0,nsps,mode,mode_q65,jpk,s3,LL,NN) - call pctile(s3,LL*NN,40,base) - s3=s3/base - where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim - s3avg(1:LL*NN)=s3avg(1:LL*NN) + s3(1:LL*NN) - nsave=nsave+1 - endif - if(iavg.eq.0 .and. nsave.lt.2) exit - enddo ! iavg + enddo ! ibw (b90 loop) + enddo ! idt (DT loop) + enddo ! idf (f0 loop) + if(iaptype.eq.0) then + a=0. + a(1)=-f0 + call twkfreq(c00,c0,npts2,6000.0,a) + jpk=3000 !### Are these definitions OK? + if(nsps.ge.3600) jpk=6000 !### TR >= 60 s + call spec64(c0,nsps,mode,mode_q65,jpk,s3,LL,NN) + call pctile(s3,LL*NN,40,base) + s3=s3/base + where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim + endif 100 if(irc.ge.0) then - navg=nsave snr2=esnodb - db(2500.0/baud) - if(kavg.eq.0) navg=0 xdt1=xdt0 + nsps*ndt/(16.0*6000.0) f1=f0 + 0.5*baud*ndf -!### For tests only: -! open(53,file='fort.53',status='unknown',position='append') -! write(c77,1100) dat4(1:12),dat4(13)/2 -!1100 format(12b6.6,b5.5) -! call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent -! m=nutc -! if(nsps.ge.3600) m=100*m -! ihr=m/10000 -! imin=mod(m/100,100) -! isec=mod(m,100) -! hours=ihr + imin/60.0 + isec/3600.0 -! write(53,3053) m,hours,ndf,ndt,nbw,ndist,irc,iaptype,kavg,snr1, & -! xdt1,f1,snr2,trim(decoded) -!3053 format(i6.6,f8.4,4i3,i4,2i3,f6.1,f6.2,f7.1,f6.1,1x,a) -! close(53) -!### - nsave=0 - s3avg=0. - irc=irc + 100*navg endif return From 7cb87e315b081d6bf15a7a47650afef0de585017 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 30 Nov 2020 14:15:59 -0500 Subject: [PATCH 178/426] Fix a format. --- lib/decoder.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index d93f5a73a..a9cd010ae 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -794,12 +794,12 @@ contains if(ntrperiod.lt.60) then write(*,1001) nutc,nsnr,dt,nint(freq),decoded,idec -1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i1) +1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i2) write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded 1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') else write(*,1003) nutc,nsnr,dt,nint(freq),decoded,idec -1003 format(i4.4,i4,f5.1,i5,' + ',1x,a37,1x,i1) +1003 format(i4.4,i4,f5.1,i5,' + ',1x,a37,1x,i2) write(13,1004) nutc,nint(sync),nsnr,dt,freq,0,decoded 1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') From 65dda491d28f8fe33d0e009237dc6ccafb952b35 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 30 Nov 2020 15:20:19 -0500 Subject: [PATCH 179/426] More Q65 code cleanup. Make submodes higher than A work with List Decoding. --- lib/q65_decode.f90 | 2 +- lib/q65_sync.f90 | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 0473b7119..ef4e67060 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -93,7 +93,7 @@ contains dgen=0 call q65_enc(dgen,codewords) !Initialize Q65 call timer('sync_q65',0) - call q65_sync(nutc,iwave,ntrperiod*12000,mode65,codewords,ncw,nsps, & + call q65_sync(iwave,ntrperiod*12000,mode65,codewords,ncw,nsps, & nfqso,ntol,xdt,f0,snr1,dat4,snr2,id1) call timer('sync_q65',1) if(id1.eq.1) then diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index a9d58537d..cbdcbe4dc 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -1,4 +1,4 @@ -subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & +subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & xdt,f0,snr1,dat4,snr2,id1) ! Detect and align with the Q65 sync vector, returning time and frequency @@ -126,7 +126,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & j=j0 + NSTEP*(k-1) + 1 + lag if(j.ge.1 .and. j.le.jz) then do i=-ia,ia - ii=i0+itone(k)+i + ii=i0+mode_q65*itone(k)+i ccf(i,lag)=ccf(i,lag) + s1(ii,j) enddo endif @@ -157,6 +157,10 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & enddo nsubmode=0 + if(mode_q65.eq.2) nsubmode=1 + if(mode_q65.eq.4) nsubmode=2 + if(mode_q65.eq.8) nsubmode=3 + if(mode_q65.eq.16) nsubmode=4 nFadingModel=1 baud=12000.0/nsps do ibw=0,10 From a03758e4904996de79740f26ee12eb7b83d12507 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 30 Nov 2020 17:44:33 -0500 Subject: [PATCH 180/426] Add a file that was missing. --- lib/qra/q65/q65_set_list.f90 | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 lib/qra/q65/q65_set_list.f90 diff --git a/lib/qra/q65/q65_set_list.f90 b/lib/qra/q65/q65_set_list.f90 new file mode 100644 index 000000000..f899cac66 --- /dev/null +++ b/lib/qra/q65/q65_set_list.f90 @@ -0,0 +1,42 @@ +subroutine q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) + + character*12 mycall,hiscall + character*6 hisgrid + character*37 msg0,msg,msgsent + integer codewords(63,64) + integer itone(85) + integer isync(22) + data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ + + msg0=trim(mycall)//' '//trim(hiscall) + j0=len(trim(msg0))+2 + isnr0=-35 + do i=1,57 + msg=msg0 + if(i.eq.2) msg(j0:j0+2)='RRR' + if(i.eq.3) msg(j0:j0+3)='RR73' + if(i.eq.4) msg(j0:j0+1)='73' + if(i.ge.5 .and. i.le.56) then + isnr=isnr0 + (i-5)/2 + if(iand(i,1).eq.1) write(msg(j0:j0+2),'(i3.2)') isnr + if(iand(i,1).eq.0) write(msg(j0:j0+3),'("R",i3.2)') isnr + endif + if(i.eq.57) msg='CQ '//trim(hiscall)//' '//hisgrid(1:4) + call genq65(msg,0,msgsent,itone,i3,n3) + i0=1 + j=0 + do k=1,85 + if(k.eq.isync(i0)) then + i0=i0+1 + cycle + endif + j=j+1 + codewords(j,i)=itone(k) - 1 + enddo + ncw=57 +! write(*,3001) i,isnr,codewords(1:13,i),trim(msg) +!3001 format(i2,2x,i3.2,2x,13i3,2x,a) + enddo + + return +end subroutine q65_set_list From 5a3dd69331b81b3186efeb119ebb7de4ae64283a Mon Sep 17 00:00:00 2001 From: Nico Palermo/IV3NWV Date: Tue, 1 Dec 2020 03:26:00 +0100 Subject: [PATCH 181/426] decode_fullaplist updated. the decoding threshold is now adjusted in function of the number of codewords passed --- lib/qra/q65/q65.c | 14 +++++++------- lib/qra/q65/q65.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/qra/q65/q65.c b/lib/qra/q65/q65.c index c91571ba6..9c8544ffb 100644 --- a/lib/qra/q65/q65.c +++ b/lib/qra/q65/q65.c @@ -681,8 +681,7 @@ int q65_decode_fullaplist(q65_codec_ds *codec, int k; int nK, nN, nM; - float llh; - float maxllh = Q65_LLH_THRESHOLD-1; // set to a value less than the threshold + float llh, maxllh, llh_threshold; int maxcw = -1; // index of the most likely codeword const int *pCw; @@ -693,6 +692,12 @@ int q65_decode_fullaplist(q65_codec_ds *codec, nN = q65_get_codeword_length(codec); nM = q65_get_alphabet_size(codec); + // we adjust the llh threshold in order to mantain the + // same false decode rate independently from the size + // of the list + llh_threshold = Q65_LLH_THRESHOLD + logf(1.0f*nCodewords/3); + maxllh = llh_threshold; // at least one llh should be larger than the threshold + // compute codewords log likelihoods and find max pCw = pCodewords; // start from the first codeword for (k=0;k>6); } - - diff --git a/lib/qra/q65/q65.h b/lib/qra/q65/q65.h index 2e764a32b..1962509a3 100644 --- a/lib/qra/q65/q65.h +++ b/lib/qra/q65/q65.h @@ -34,7 +34,7 @@ // Verify loglikelihood after successful decoding #define Q65_CHECKLLH // Max codeword list size in q65_decode_fullaplist -#define Q65_FULLAPLIST_SIZE 64 +#define Q65_FULLAPLIST_SIZE 256 // maximum number of weights for the fast-fading metric evaluation #define Q65_FASTFADING_MAXWEIGTHS 65 From 68d964b5c7435f3977c2cb01064908ecee97eeda Mon Sep 17 00:00:00 2001 From: Nico Palermo/IV3NWV Date: Tue, 1 Dec 2020 03:41:23 +0100 Subject: [PATCH 182/426] merged Joe's changes in decode_fullaplist --- lib/qra/q65/q65.c | 4 +++- lib/qra/q65/q65.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/qra/q65/q65.c b/lib/qra/q65/q65.c index 46d88301c..ddd764615 100644 --- a/lib/qra/q65/q65.c +++ b/lib/qra/q65/q65.c @@ -712,7 +712,9 @@ int q65_decode_fullaplist(q65_codec_ds *codec, // point to next codeword pCw+=nN; } - q65_llh=maxllh; + + q65_llh=maxllh; // save for Joe's use + if (maxcw<0) // no llh larger than threshold found return Q65_DECODE_FAILED; diff --git a/lib/qra/q65/q65.h b/lib/qra/q65/q65.h index 94f6d568c..9e3d79adb 100644 --- a/lib/qra/q65/q65.h +++ b/lib/qra/q65/q65.h @@ -39,7 +39,8 @@ // maximum number of weights for the fast-fading metric evaluation #define Q65_FASTFADING_MAXWEIGTHS 65 -float q65_llh; +extern float q65_llh; + typedef struct { const qracode *pQraCode; // qra code to be used by the codec float decoderEsNoMetric; // value for which we optimize the decoder metric From 70e0a84fe872ed2ceff6d2a1f9f2ebfc00e07c0b Mon Sep 17 00:00:00 2001 From: Nico Palermo/IV3NWV Date: Tue, 1 Dec 2020 04:13:30 +0100 Subject: [PATCH 183/426] corrected a compile error. q65_llh declared as an external variable in q65.h and placed in q65.c --- lib/qra/q65/q65.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/qra/q65/q65.c b/lib/qra/q65/q65.c index ddd764615..c9d776bc6 100644 --- a/lib/qra/q65/q65.c +++ b/lib/qra/q65/q65.c @@ -36,6 +36,7 @@ static int _q65_crc6(int *x, int sz); static void _q65_crc12(int *y, int *x, int sz); +float q65_llh; int q65_init(q65_codec_ds *pCodec, const qracode *pqracode) { From 7f1a537e6eaadbed440a9625aea192b9e9b26970 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 1 Dec 2020 16:06:56 +0000 Subject: [PATCH 184/426] Fix up versioning after merge from release branch --- CMakeLists.txt | 4 ++-- widgets/mainwindow.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1597eddca..9088471f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ if (POLICY CMP0075) endif () project (wsjtx - VERSION 2.3.0.0 + VERSION 2.4.0.0 LANGUAGES C CXX Fortran ) set (PROJECT_DESCRIPTION "WSJT-X: Digital Modes for Weak Signal Communications in Amateur Radio") @@ -70,7 +70,7 @@ message (STATUS "******************************************************") include (set_build_type) # RC 0 or omitted is a development build, GA is a General Availability release build -set_build_type (RC 3) +set_build_type (RC 0) set (wsjtx_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${BUILD_TYPE_REVISION}") # diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 7e7bf0ebf..4667c4168 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -1029,14 +1029,14 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, void MainWindow::not_GA_warning_message () { - MessageBox::critical_message (this, - "This is a pre-release version of WSJT-X 2.3.0 made\n" - "available for testing purposes. By design it will\n" - "be nonfunctional after 0000 UTC on Jan 19, 2021."); - auto now = QDateTime::currentDateTimeUtc (); - if (now >= QDateTime {{2021, 1, 19}, {0, 0}, Qt::UTC}) { - Q_EMIT finished (); - } + // MessageBox::critical_message (this, + // "This is a pre-release version of WSJT-X 2.3.0 made\n" + // "available for testing purposes. By design it will\n" + // "be nonfunctional after 0000 UTC on Jan 19, 2021."); + // auto now = QDateTime::currentDateTimeUtc (); + // if (now >= QDateTime {{2021, 1, 19}, {0, 0}, Qt::UTC}) { + // Q_EMIT finished (); + // } } void MainWindow::initialize_fonts () From 6b540aaad06230fb0d0a9c30d26a3d6d06aacf75 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 1 Dec 2020 11:23:35 -0500 Subject: [PATCH 185/426] Correct offset in (i,j) indices for selecting s3 from s1. --- lib/q65_sync.f90 | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index cbdcbe4dc..f1b056cb5 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -79,7 +79,6 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & i0=nint(nfqso/df) !Target QSO frequency call pctile(s1(i0-64:i0+192,1:jz),129*jz,40,base) -! s1=s1/base - 1.0 s1=s1/base ! Apply fast AGC @@ -107,6 +106,7 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ipk=0 jpk=0 ccf_best=0. + imsg_best=-1 do imsg=1,ncw i=1 k=0 @@ -140,12 +140,16 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & jpk=ijpk(2)-53-1 f0=nfqso + ipk*df xdt=jpk*dtstep + imsg_best=imsg endif enddo ! imsg - ia=i0+ipk-63 + write(71,3071) imsg_best,ipk,jpk,xdt,f0,ccf_best +3071 format(3i5,3f10.2) + + ia=i0+ipk-64 ib=ia+LL-1 - j=j0+jpk-5 + j=j0+jpk-7 n=0 do k=1,85 j=j+8 @@ -155,6 +159,13 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & n=n+1 if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(ia:ib,j) enddo + + write(73,3001) codewords(1:10,3) +3001 format(3x,10i7) + do i=-5,68 + write(73,3073) i,(s3(i,j),j=1,10) +3073 format(i3,10f7.1) + enddo nsubmode=0 if(mode_q65.eq.2) nsubmode=1 @@ -167,6 +178,8 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & b90=1.72**ibw call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) + write(72,3072) ibw,dat4,plog,irc +3072 format(i2,2x,13i3,f8.1,i5) if(irc.ge.0) then snr2=esnodb - db(2500.0/baud) id1=1 From df3169ba22d6868b21df247865a625c061d08e2c Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 1 Dec 2020 11:24:21 -0500 Subject: [PATCH 186/426] Update test_q65. --- lib/test_q65.f90 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 0c71d64a5..68e28ed3d 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -4,7 +4,7 @@ program test_q65 character*22 msg character*8 arg character*1 csubmode - integer naptype(0:4) + integer naptype(1:6) logical decok nargs=iargc() @@ -89,9 +89,9 @@ program test_q65 !1000 format(/'Depth:',i2,' AP:',i2,' df:',i3,' dt:',i3,' bw1:',i3,' bw2:',i3, & ! ' dist:',i3) - write(*,1010) (j,j=0,4) - write(12,1010) (j,j=0,4) -1010 format(' SNR Mode d Dop Sync DecN Dec1 Bad',5i5,' tdec'/70('-')) + write(*,1010) (j,j=1,6) + write(12,1010) (j,j=1,6) +1010 format(' SNR Mode d Dop Sync DecN Dec1 Bad',6i5,' tdec'/75('-')) dterr=tsym/4.0 nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) @@ -120,16 +120,16 @@ program test_q65 if((abs(xdt-dt).le.dterr .and. abs(nf-nf0).le.nferr) .or. decok) then nsync=nsync+1 endif - irc=-1 + idec=-1 iavg=0 i0=23 if(ntrperiod.le.30) i0=25 - if(line(i0:i0).ne.' ') read(line(60:),*) irc,iavg - if(irc.lt.0) cycle + if(line(i0:i0).ne.' ') read(line(60:),*) idec + if(idec.lt.0) cycle if(decok) then ndecn=ndecn + 1 if(iavg.le.1) ndec1=ndec1 + 1 - naptype(irc)=naptype(irc) + 1 + naptype(idec)=naptype(idec) + 1 else nfalse=nfalse + 1 print*,'False: ',line @@ -142,7 +142,7 @@ program test_q65 ndec1,nfalse,naptype,tdec/nfiles write(12,1100) snr1,ntrperiod,csubmode,ndepth,fDop,nsync,ndecn, & ndec1,nfalse,naptype,tdec/nfiles -1100 format(f5.1,i4,1x,a1,i3,f5.1,3i5,i4,i6,4i5,f6.2) +1100 format(f5.1,i4,1x,a1,i3,f5.1,3i5,i4,i6,5i5,f6.2) if(ndec1.lt.nfiles/2 .and. ndec10.ge.nfiles/2) then snr_thresh=snr1 + float(nfiles/2 - ndec1)/(ndec10-ndec1) open(13,file='snr_thresh.out',status='unknown',position='append') From d1ac5f71e2c76f5f4209b53d4a5e58991c63a18d Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 1 Dec 2020 11:31:52 -0500 Subject: [PATCH 187/426] Remove several diagnostic writes. --- lib/q65_sync.f90 | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index f1b056cb5..9685b9099 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -144,9 +144,6 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & endif enddo ! imsg - write(71,3071) imsg_best,ipk,jpk,xdt,f0,ccf_best -3071 format(3i5,3f10.2) - ia=i0+ipk-64 ib=ia+LL-1 j=j0+jpk-7 @@ -160,13 +157,6 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(ia:ib,j) enddo - write(73,3001) codewords(1:10,3) -3001 format(3x,10i7) - do i=-5,68 - write(73,3073) i,(s3(i,j),j=1,10) -3073 format(i3,10f7.1) - enddo - nsubmode=0 if(mode_q65.eq.2) nsubmode=1 if(mode_q65.eq.4) nsubmode=2 @@ -178,8 +168,6 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & b90=1.72**ibw call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) - write(72,3072) ibw,dat4,plog,irc -3072 format(i2,2x,13i3,f8.1,i5) if(irc.ge.0) then snr2=esnodb - db(2500.0/baud) id1=1 From 53e4052b8cb4883014d176714ba9ac7d7199f9a1 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 3 Dec 2020 14:28:30 -0500 Subject: [PATCH 188/426] Q65 additions for diagnostic purposes. --- lib/q65_decode.f90 | 2 +- lib/q65_sync.f90 | 31 ++++++++++++++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index ef4e67060..0473b7119 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -93,7 +93,7 @@ contains dgen=0 call q65_enc(dgen,codewords) !Initialize Q65 call timer('sync_q65',0) - call q65_sync(iwave,ntrperiod*12000,mode65,codewords,ncw,nsps, & + call q65_sync(nutc,iwave,ntrperiod*12000,mode65,codewords,ncw,nsps, & nfqso,ntol,xdt,f0,snr1,dat4,snr2,id1) call timer('sync_q65',1) if(id1.eq.1) then diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 9685b9099..aa238d69d 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -1,4 +1,4 @@ -subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & +subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & xdt,f0,snr1,dat4,snr2,id1) ! Detect and align with the Q65 sync vector, returning time and frequency @@ -12,7 +12,8 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ! Output: xdt Time offset from nominal (s) ! f0 Frequency of sync tone ! snr1 Relative SNR of sync signal - + + use packjt77 parameter (NSTEP=8) !Step size nsps/NSTEP parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 integer*2 iwave(0:nmax-1) !Raw data @@ -21,6 +22,8 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & integer codewords(63,64) integer dat4(13) integer ijpk(2) + logical unpk77_success + character*77 c77,decoded*37 real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) real, allocatable :: ccf(:,:) !CCF(freq,lag) @@ -78,14 +81,14 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & enddo i0=nint(nfqso/df) !Target QSO frequency - call pctile(s1(i0-64:i0+192,1:jz),129*jz,40,base) + call pctile(s1(i0-64:i0-65+LL,1:jz),LL*jz,40,base) s1=s1/base ! Apply fast AGC s1max=20.0 !Empirical choice - do j=1,jz - smax=maxval(s1(i0-64:i0+192,j)) - if(smax.gt.s1max) s1(i0-64:i0+192,j)=s1(i0-64:i0+192,j)*s1max/smax + do j=1,jz !### Maybe wrong way? ### + smax=maxval(s1(i0-64:i0-65+LL,j)) + if(smax.gt.s1max) s1(i0-64:i0-65+LL,j)=s1(i0-64:i0-65+LL,j)*s1max/smax enddo dtstep=nsps/(NSTEP*12000.0) !Step size in seconds @@ -93,7 +96,7 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & lag1=-1.0/dtstep lag2=1.0/dtstep + 0.9999 j0=0.5/dtstep - if(nsps.ge.6192) then + if(nsps.ge.7200) then j0=1.0/dtstep !Nominal index for start of signal lag2=4.0/dtstep + 0.9999 !Include EME delays endif @@ -164,15 +167,21 @@ subroutine q65_sync(iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & if(mode_q65.eq.16) nsubmode=4 nFadingModel=1 baud=12000.0/nsps - do ibw=0,10 + do ibw=2,4 b90=1.72**ibw call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) - if(irc.ge.0) then + if(irc.ge.0 .and. plog.ge.-255.0) then snr2=esnodb - db(2500.0/baud) id1=1 -! write(55,3055) nutc,xdt,f0,snr2,plog,irc -!3055 format(i4.4,4f9.2,i5) + 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 + open(55,file='fort.55',status='unknown',position='append') + write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & + irc,trim(decoded) +3055 format(i6,i3,6f8.2,i5,2x,a) + close(55) go to 900 endif enddo From bb08150e4bb61f4bfa14cbd80c56d6ab10476a9c Mon Sep 17 00:00:00 2001 From: Nico Palermo/IV3NWV Date: Fri, 4 Dec 2020 09:43:41 +0100 Subject: [PATCH 189/426] Threshold in q65_sync.f90 increased to -250 only for reports. Assumes that the list is in the order defined in q65_set_list.f90 (reports are at indexes 5..56). Beware if q65_set_list is changed!!! --- lib/q65_decode.f90 | 5 +++ lib/q65_sync.f90 | 103 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 0473b7119..e52870b61 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -102,6 +102,11 @@ contains go to 100 endif + ! Nico's trick to speed up data analysis + ! with fullaplist decoding + ! Uncomment to check only fullaplist synch and decode + ! snr1 = 1 ! skip non fullaplist decoding attempts + if(snr1.lt.2.8) go to 100 jpk0=(xdt+1.0)*6000 !### Is this OK? if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !### diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index aa238d69d..710a35c5b 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -27,6 +27,12 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) real, allocatable :: ccf(:,:) !CCF(freq,lag) + + ! real, allocatable :: ccfcw(:) ! Method 2 + ! real, allocatable :: ccfpk(:) ! Method 2 + ! integer cwpk(1) ! Method 2 + ! integer, allocatable :: cwtone(:,:) ! Method 2 + real, allocatable :: ccf1(:) !CCF(freq) at best lag real s3prob(0:63,63) !Symbol-value probabilities real sync(85) !sync vector @@ -52,6 +58,10 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & allocate(c0(0:nfft-1)) allocate(ccf(-ia:ia,-53:214)) allocate(ccf1(-ia:ia)) + + ! allocate(ccfcw(1:ncw)) ! Method 2 + ! allocate(ccfpk(1:ncw)) ! Method 2 + ! allocate(cwtone(ncw,85)) ! Method 2 if(sync(1).eq.99.0) then !Generate the sync vector sync=-22.0/63.0 !Sync tone OFF @@ -106,6 +116,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & !###################################################################### ! Try list decoding via "Deep Likelihood". +! Joe's method ipk=0 jpk=0 ccf_best=0. @@ -147,6 +158,66 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & endif enddo ! imsg +! Nico's Method 2 +! Computes the ccf in a different order so that +! we can see what the ccfs of the list looked like +! on ccfmax + +! Compute codeword tones +! cwtone = 0 +! do imsg=1,ncw +! i=1 +! k=0 +! do j=1,85 +! if(j.eq.isync(i)) then +! i=i+1 +! cwtone(imsg,j)=-1 +! else +! k=k+1 +! cwtone(imsg,j)=codewords(k,imsg) +! endif +! enddo +! enddo + +! ipk=0 +! jpk=0 +! ccf_best=0. +! imsg_best=-1 +! ccf=0. +! do lag=lag1,lag2 +! do i=-ia,ia +! ccfcw = 0. +! do k=1,85 +! j=j0 + NSTEP*(k-1) + 1 + lag +! if(j.ge.1 .and. j.le.jz) then +! do imsg=1,ncw +! ! if we would like to check only non systematic symbols +! ! uncomment the following if +! !if (k.gt.18 .or. cwtone(imsg,k).eq.-1) then +! ii=i0+mode_q65*cwtone(imsg,k)+i +! ccfcw(imsg)=ccfcw(imsg) + s1(ii,j) +! !endif +! enddo +! endif +! enddo +! ccfmax=maxval(ccfcw) +! ccf(i,lag) = ccfmax +! if(ccfmax.gt.ccf_best) then +! ccfpk = ccfcw +! cwpk = maxloc(ccfpk) +! imsg_best = cwpk(1) +! ccf_best = ccfmax +! endif +! enddo +! enddo +! ccfmax=maxval(ccf) +! ccf_best=ccfmax +! ijpk=maxloc(ccf) +! ipk=ijpk(1)-ia-1 +! jpk=ijpk(2)-53-1 +! f0=nfqso + ipk*df +! xdt=jpk*dtstep + ia=i0+ipk-64 ib=ia+LL-1 j=j0+jpk-7 @@ -167,22 +238,46 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & if(mode_q65.eq.16) nsubmode=4 nFadingModel=1 baud=12000.0/nsps + isreport = 0; do ibw=2,4 b90=1.72**ibw call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) - if(irc.ge.0 .and. plog.ge.-255.0) then +! Joe's threshold +! if(irc.ge.0 .and. plog.ge.-255.0) then +! Nico's threshold + if (irc.ge.0) then + ! if it is an RRR type or a CQ Mycall grid + ! we accept the fullaplist threshold + ! other cases in the list are reports which + ! are more frequent in the list and then + ! must be filtered with a higher threshold + if((irc.le.3.0 .or. irc.eq.56.0) .or. plog.ge.-250.0) then snr2=esnodb - db(2500.0/baud) id1=1 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 open(55,file='fort.55',status='unknown',position='append') - write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & - irc,trim(decoded) -3055 format(i6,i3,6f8.2,i5,2x,a) + +! Joe's dump +! write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & +! irc,trim(decoded) +!3055 format(i6,i3,6f8.2,i5,2x,a) + +! Nico's dump for Method 2 +! This are the ccfs of the codeword in the list at the +! ccf peak +! write(55,3083) ccfpk +!3083 format(10f6.1) +! Show also the imsg_best in order to compare it with +! irc. (Interestingly they are always equal!) +! write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & +! irc,(imsg_best-1),trim(decoded) +!3055 format(i6,i3,6f8.2,2i5,2x,a) close(55) go to 900 + endif endif enddo From 72bc2572e6b2e706afd02b1e0edba26146ed240a Mon Sep 17 00:00:00 2001 From: Nico Palermo/IV3NWV Date: Fri, 4 Dec 2020 10:07:56 +0100 Subject: [PATCH 190/426] forgot to uncomment fort.55 output. corrected --- lib/q65_sync.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 710a35c5b..39475b4c3 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -261,9 +261,9 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & open(55,file='fort.55',status='unknown',position='append') ! Joe's dump -! write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & -! irc,trim(decoded) -!3055 format(i6,i3,6f8.2,i5,2x,a) + write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & + irc,trim(decoded) +3055 format(i6,i3,6f8.2,i5,2x,a) ! Nico's dump for Method 2 ! This are the ccfs of the codeword in the list at the From 978ea0f3b57b66da22832d279020d151231fd88a Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 4 Dec 2020 12:56:23 -0500 Subject: [PATCH 191/426] Revert "forgot to uncomment fort.55 output. corrected" This reverts commit 72bc2572e6b2e706afd02b1e0edba26146ed240a. --- lib/q65_sync.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 39475b4c3..710a35c5b 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -261,9 +261,9 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & open(55,file='fort.55',status='unknown',position='append') ! Joe's dump - write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & - irc,trim(decoded) -3055 format(i6,i3,6f8.2,i5,2x,a) +! write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & +! irc,trim(decoded) +!3055 format(i6,i3,6f8.2,i5,2x,a) ! Nico's dump for Method 2 ! This are the ccfs of the codeword in the list at the From aaf832cbd2c5e91392ba11c59d07b0b19609da83 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 4 Dec 2020 12:56:45 -0500 Subject: [PATCH 192/426] Revert "Threshold in q65_sync.f90 increased to -250 only for reports. Assumes that the list is in the order defined in q65_set_list.f90 (reports are at indexes 5..56). Beware if q65_set_list is changed!!!" This reverts commit bb08150e4bb61f4bfa14cbd80c56d6ab10476a9c. --- lib/q65_decode.f90 | 5 --- lib/q65_sync.f90 | 103 ++------------------------------------------- 2 files changed, 4 insertions(+), 104 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index e52870b61..0473b7119 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -102,11 +102,6 @@ contains go to 100 endif - ! Nico's trick to speed up data analysis - ! with fullaplist decoding - ! Uncomment to check only fullaplist synch and decode - ! snr1 = 1 ! skip non fullaplist decoding attempts - if(snr1.lt.2.8) go to 100 jpk0=(xdt+1.0)*6000 !### Is this OK? if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !### diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 710a35c5b..aa238d69d 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -27,12 +27,6 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) real, allocatable :: ccf(:,:) !CCF(freq,lag) - - ! real, allocatable :: ccfcw(:) ! Method 2 - ! real, allocatable :: ccfpk(:) ! Method 2 - ! integer cwpk(1) ! Method 2 - ! integer, allocatable :: cwtone(:,:) ! Method 2 - real, allocatable :: ccf1(:) !CCF(freq) at best lag real s3prob(0:63,63) !Symbol-value probabilities real sync(85) !sync vector @@ -58,10 +52,6 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & allocate(c0(0:nfft-1)) allocate(ccf(-ia:ia,-53:214)) allocate(ccf1(-ia:ia)) - - ! allocate(ccfcw(1:ncw)) ! Method 2 - ! allocate(ccfpk(1:ncw)) ! Method 2 - ! allocate(cwtone(ncw,85)) ! Method 2 if(sync(1).eq.99.0) then !Generate the sync vector sync=-22.0/63.0 !Sync tone OFF @@ -116,7 +106,6 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & !###################################################################### ! Try list decoding via "Deep Likelihood". -! Joe's method ipk=0 jpk=0 ccf_best=0. @@ -158,66 +147,6 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & endif enddo ! imsg -! Nico's Method 2 -! Computes the ccf in a different order so that -! we can see what the ccfs of the list looked like -! on ccfmax - -! Compute codeword tones -! cwtone = 0 -! do imsg=1,ncw -! i=1 -! k=0 -! do j=1,85 -! if(j.eq.isync(i)) then -! i=i+1 -! cwtone(imsg,j)=-1 -! else -! k=k+1 -! cwtone(imsg,j)=codewords(k,imsg) -! endif -! enddo -! enddo - -! ipk=0 -! jpk=0 -! ccf_best=0. -! imsg_best=-1 -! ccf=0. -! do lag=lag1,lag2 -! do i=-ia,ia -! ccfcw = 0. -! do k=1,85 -! j=j0 + NSTEP*(k-1) + 1 + lag -! if(j.ge.1 .and. j.le.jz) then -! do imsg=1,ncw -! ! if we would like to check only non systematic symbols -! ! uncomment the following if -! !if (k.gt.18 .or. cwtone(imsg,k).eq.-1) then -! ii=i0+mode_q65*cwtone(imsg,k)+i -! ccfcw(imsg)=ccfcw(imsg) + s1(ii,j) -! !endif -! enddo -! endif -! enddo -! ccfmax=maxval(ccfcw) -! ccf(i,lag) = ccfmax -! if(ccfmax.gt.ccf_best) then -! ccfpk = ccfcw -! cwpk = maxloc(ccfpk) -! imsg_best = cwpk(1) -! ccf_best = ccfmax -! endif -! enddo -! enddo -! ccfmax=maxval(ccf) -! ccf_best=ccfmax -! ijpk=maxloc(ccf) -! ipk=ijpk(1)-ia-1 -! jpk=ijpk(2)-53-1 -! f0=nfqso + ipk*df -! xdt=jpk*dtstep - ia=i0+ipk-64 ib=ia+LL-1 j=j0+jpk-7 @@ -238,46 +167,22 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & if(mode_q65.eq.16) nsubmode=4 nFadingModel=1 baud=12000.0/nsps - isreport = 0; do ibw=2,4 b90=1.72**ibw call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) -! Joe's threshold -! if(irc.ge.0 .and. plog.ge.-255.0) then -! Nico's threshold - if (irc.ge.0) then - ! if it is an RRR type or a CQ Mycall grid - ! we accept the fullaplist threshold - ! other cases in the list are reports which - ! are more frequent in the list and then - ! must be filtered with a higher threshold - if((irc.le.3.0 .or. irc.eq.56.0) .or. plog.ge.-250.0) then + if(irc.ge.0 .and. plog.ge.-255.0) then snr2=esnodb - db(2500.0/baud) id1=1 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 open(55,file='fort.55',status='unknown',position='append') - -! Joe's dump -! write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & -! irc,trim(decoded) -!3055 format(i6,i3,6f8.2,i5,2x,a) - -! Nico's dump for Method 2 -! This are the ccfs of the codeword in the list at the -! ccf peak -! write(55,3083) ccfpk -!3083 format(10f6.1) -! Show also the imsg_best in order to compare it with -! irc. (Interestingly they are always equal!) -! write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & -! irc,(imsg_best-1),trim(decoded) -!3055 format(i6,i3,6f8.2,2i5,2x,a) + write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & + irc,trim(decoded) +3055 format(i6,i3,6f8.2,i5,2x,a) close(55) go to 900 - endif endif enddo From 7500adce8b42b3d2f5c42e4dc23437de285deae5 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 4 Dec 2020 14:53:45 -0500 Subject: [PATCH 193/426] Allow "@1500", etc., single-tone transmissions in Q65 mode. --- Modulator/Modulator.cpp | 2 +- widgets/mainwindow.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modulator/Modulator.cpp b/Modulator/Modulator.cpp index 32645bec2..0d24485cd 100644 --- a/Modulator/Modulator.cpp +++ b/Modulator/Modulator.cpp @@ -318,7 +318,7 @@ qint64 Modulator::readData (char * data, qint64 maxSize) sample=qRound(m_amp*qSin(m_phi)); //Here's where we transmit from a precomputed wave[] array: - if(!m_tuning and (m_toneSpacing < 0)) { + if(!m_tuning and (m_toneSpacing < 0) and (itone[0]<100)) { m_amp=32767.0; sample=qRound(m_amp*foxcom_.wave[m_ic]); } diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 32048bc9e..41e2098f3 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -5156,7 +5156,7 @@ void MainWindow::genCQMsg () msgtype (QString {"%1 %2"}.arg(m_CQtype).arg(m_config.my_callsign()),ui->tx6); } } - if ((m_mode=="JT4" or m_mode=="QRA64") and ui->cbShMsgs->isChecked()) { + if ((m_mode=="JT4" or m_mode=="QRA64" or m_mode=="Q65") and ui->cbShMsgs->isChecked()) { if (ui->cbTx6->isChecked ()) { msgtype ("@1250 (SEND MSGS)", ui->tx6); } else { From 3dd34e13bafa7310666bfd315acfb652dbd9c34e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 4 Dec 2020 15:29:46 -0500 Subject: [PATCH 194/426] dd "MyCall DxCall DxGrid" to list. Set PLOG_MIN=-240. No list decoding if DxCall is not defined. --- lib/q65_sync.f90 | 3 ++- lib/qra/q65/q65_set_list.f90 | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index aa238d69d..62e3a72e6 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -16,6 +16,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & use packjt77 parameter (NSTEP=8) !Step size nsps/NSTEP parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 + parameter (PLOG_MIN=-240.0) !List decoding threshold integer*2 iwave(0:nmax-1) !Raw data integer isync(22) !Indices of sync symbols integer itone(85) @@ -171,7 +172,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & b90=1.72**ibw call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) - if(irc.ge.0 .and. plog.ge.-255.0) then + if(irc.ge.0 .and. plog.ge.PLOG_MIN) then snr2=esnodb - db(2500.0/baud) id1=1 write(c77,1000) dat4(1:12),dat4(13)/2 diff --git a/lib/qra/q65/q65_set_list.f90 b/lib/qra/q65/q65_set_list.f90 index f899cac66..4e67e0ff4 100644 --- a/lib/qra/q65/q65_set_list.f90 +++ b/lib/qra/q65/q65_set_list.f90 @@ -8,10 +8,14 @@ subroutine q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) integer isync(22) data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ + ncw=0 + if(hiscall(1:1).eq. ' ') return + + ncw=58 msg0=trim(mycall)//' '//trim(hiscall) j0=len(trim(msg0))+2 isnr0=-35 - do i=1,57 + do i=1,ncw msg=msg0 if(i.eq.2) msg(j0:j0+2)='RRR' if(i.eq.3) msg(j0:j0+3)='RR73' @@ -22,6 +26,7 @@ subroutine q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) if(iand(i,1).eq.0) write(msg(j0:j0+3),'("R",i3.2)') isnr endif if(i.eq.57) msg='CQ '//trim(hiscall)//' '//hisgrid(1:4) + if(i.eq.58) msg(j0:j0+3)=hisgrid(1:4) call genq65(msg,0,msgsent,itone,i3,n3) i0=1 j=0 @@ -33,7 +38,6 @@ subroutine q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) j=j+1 codewords(j,i)=itone(k) - 1 enddo - ncw=57 ! write(*,3001) i,isnr,codewords(1:13,i),trim(msg) !3001 format(i2,2x,i3.2,2x,13i3,2x,a) enddo From 8b9b71c8946b1f8011eacf593da5f5615de95529 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 4 Dec 2020 15:37:54 -0500 Subject: [PATCH 195/426] Fix the "off by one" issue in measuring f0 for list decodes. --- lib/q65_sync.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 62e3a72e6..d761fd044 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -142,7 +142,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ijpk=maxloc(ccf) ipk=ijpk(1)-ia-1 jpk=ijpk(2)-53-1 - f0=nfqso + ipk*df + f0=nfqso + (ipk-1)*df xdt=jpk*dtstep imsg_best=imsg endif From 6416777278b878fc2b43f87b7a792d5a7ede578f Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 4 Dec 2020 15:55:03 -0500 Subject: [PATCH 196/426] Set ':' as the "mode character" for Q65. --- lib/decoder.f90 | 4 ++-- widgets/displaytext.cpp | 1 + widgets/mainwindow.cpp | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index a9cd010ae..cb695e708 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -794,12 +794,12 @@ contains if(ntrperiod.lt.60) then write(*,1001) nutc,nsnr,dt,nint(freq),decoded,idec -1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,i2) +1001 format(i6.6,i4,f5.1,i5,' : ',1x,a37,1x,i2) write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded 1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') else write(*,1003) nutc,nsnr,dt,nint(freq),decoded,idec -1003 format(i4.4,i4,f5.1,i5,' + ',1x,a37,1x,i2) +1003 format(i4.4,i4,f5.1,i5,' : ',1x,a37,1x,i2) write(13,1004) nutc,nint(sync),nsnr,dt,freq,0,decoded 1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') diff --git a/widgets/displaytext.cpp b/widgets/displaytext.cpp index d498f180e..75af0af68 100644 --- a/widgets/displaytext.cpp +++ b/widgets/displaytext.cpp @@ -484,6 +484,7 @@ void DisplayText::displayTransmittedText(QString text, QString modeTx, qint32 tx if(modeTx=="FT4") t1=" + "; if(modeTx=="FT8") t1=" ~ "; if(modeTx=="JT4") t1=" $ "; + if(modeTx=="Q65") t1=" : "; if(modeTx=="JT65") t1=" # "; if(modeTx=="MSK144") t1=" & "; if(modeTx=="FST4") t1=" ` "; diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 41e2098f3..cba367735 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -4711,7 +4711,8 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie || ("JT65" == m_mode && mode != "#") || ("JT9" == m_mode && mode != "@") || ("MSK144" == m_mode && !("&" == mode || "^" == mode)) - || ("QRA64" == m_mode && mode.left (1) != ":")) { + || ("QRA64" == m_mode && mode.left (1) != ":") + || ("Q65" == m_mode && mode.left (1) != ":")) { return; //Currently we do auto-sequencing only in FT4, FT8, MSK144, FST4, and Q65 } From 077811ae6dd7b99579da27f023ac1be01c1f60d5 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 5 Dec 2020 11:03:16 -0500 Subject: [PATCH 197/426] Update q65params.f90. --- lib/q65params.f90 | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/q65params.f90 b/lib/q65params.f90 index c519cc94e..932dcfb36 100644 --- a/lib/q65params.f90 +++ b/lib/q65params.f90 @@ -3,7 +3,7 @@ program q65params integer ntrp(5) integer nsps(5) data ntrp/15,30,60,120,300/ - data nsps/1800,3600,7200,15680,40960/ + data nsps/1800,3600,7200,16000,41472/ write(*,1000) 1000 format('T/R tsym baud BW TxT SNR'/39('-')) @@ -17,4 +17,16 @@ program q65params 1010 format(i3,2f7.3,3f7.1) enddo + do j=1,5 + write(*,1020) char(ichar('A')+j-1) +1020 format(/a1,' T/R baud BW'/20('-')) + do i=1,5 + baud=12000.0/nsps(i) + spacing=baud*2**(j-1) + bw=65.0*spacing + write(*,1030) ntrp(i),spacing,nint(bw) +1030 format(i6,f7.2,i6) + enddo + enddo + end program q65params From 9f8336de4ecb22f0198bee35e234820ea07b0fad Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 5 Dec 2020 11:09:15 -0500 Subject: [PATCH 198/426] Code cleanup. --- lib/test_q65.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 68e28ed3d..8c48d3639 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -95,7 +95,7 @@ program test_q65 dterr=tsym/4.0 nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) - ndec10=nfiles + ndec1z=nfiles do nsnr=ia,ib,-1 snr1=nsnr @@ -143,8 +143,8 @@ program test_q65 write(12,1100) snr1,ntrperiod,csubmode,ndepth,fDop,nsync,ndecn, & ndec1,nfalse,naptype,tdec/nfiles 1100 format(f5.1,i4,1x,a1,i3,f5.1,3i5,i4,i6,5i5,f6.2) - if(ndec1.lt.nfiles/2 .and. ndec10.ge.nfiles/2) then - snr_thresh=snr1 + float(nfiles/2 - ndec1)/(ndec10-ndec1) + if(ndec1.lt.nfiles/2 .and. ndec1z.ge.nfiles/2) then + snr_thresh=snr1 + float(nfiles/2 - ndec1)/(ndec1z-ndec1) open(13,file='snr_thresh.out',status='unknown',position='append') write(13,1200) ntrperiod,csubmode,ndepth,nQSOprogress,nfiles, & fdop,snr_thresh,trim(msg) @@ -154,7 +154,7 @@ program test_q65 flush(6) flush(12) if(ndec1.eq.0 .and. ndecn.eq.0) exit !Bail out if no decodes at this SNR - ndec10=ndec1 + ndec1z=ndec1 enddo ! nsnr 999 end program test_q65 From 742e2ef59f8fab21bebe98d37fac7aabb007f9f8 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 5 Dec 2020 11:09:27 -0500 Subject: [PATCH 199/426] Fix pctile for larger arrays. --- lib/pctile.f90 | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/pctile.f90 b/lib/pctile.f90 index 3bf2ffcf8..c0a6cdb72 100644 --- a/lib/pctile.f90 +++ b/lib/pctile.f90 @@ -1,16 +1,11 @@ subroutine pctile(x,npts,npct,xpct) - parameter (NMAX=256*1024) - real*4 x(npts) - real*4 tmp(NMAX) + real x(npts) + real,allocatable :: tmp(:) - if(npts.le.0) then - xpct=1.0 - go to 900 - endif - if(npts.gt.NMAX) stop + allocate(tmp(npts)) - tmp(1:npts)=x + tmp=x call shell(npts,tmp) j=nint(npts*0.01*npct) if(j.lt.1) j=1 From c50134691db53e9d164e0559c55b61ab896fa42b Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 5 Dec 2020 13:46:07 -0500 Subject: [PATCH 200/426] Adjustments to test_q65 for wider submodes. --- lib/test_q65.f90 | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 8c48d3639..77b3d106c 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -38,13 +38,13 @@ program test_q65 if(ntrperiod.eq.15) then nsps=1800 - i50=-21 + i50=-23 else if(ntrperiod.eq.30) then nsps=3600 - i50=-24 + i50=-26 else if(ntrperiod.eq.60) then nsps=7200 - i50=-28 + i50=-29 else if(ntrperiod.eq.120) then nsps=16000 i50=-31 @@ -54,6 +54,8 @@ program test_q65 else stop 'Invalid TR period' endif + + i50=i50 + 8.0*log(fDop)/log(240.0) ia=i50 + 7 ib=i50 - 10 if(snr.ne.0.0) then @@ -67,12 +69,12 @@ program test_q65 ! 1 2 3 4 5 6 7 ! 123456789012345678901234567890123456789012345678901234567890123456789012345' cmd1='q65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 F -10.0 > junk0' - cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A -Q 3 *.wav > junk' + cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A -Q 3 -f 1500 *.wav > junk' write(cmd1(10:33),'(a)') '"'//msg//'"' cmd1(35:35)=csubmode write(cmd1(37:40),'(i4)') nf0 - write(cmd1(41:45),'(f5.1)') fDop + write(cmd1(41:45),'(f5.0)') fDop write(cmd1(46:50),'(f5.2)') dt write(cmd1(51:54),'(i4)') ntrperiod write(cmd1(55:59),'(i5)') nfiles @@ -80,7 +82,9 @@ program test_q65 write(cmd2(11:13),'(i3)') ntrperiod write(cmd2(33:34),'(i2)') ndepth write(cmd2(44:44),'(i1)') nQSOprogress + write(cmd2(49:52),'(i4)') nf0 cmd2(39:39)=csubmode + call system('rm -f *.wav') ! call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist) @@ -142,7 +146,7 @@ program test_q65 ndec1,nfalse,naptype,tdec/nfiles write(12,1100) snr1,ntrperiod,csubmode,ndepth,fDop,nsync,ndecn, & ndec1,nfalse,naptype,tdec/nfiles -1100 format(f5.1,i4,1x,a1,i3,f5.1,3i5,i4,i6,5i5,f6.2) +1100 format(f5.1,i4,1x,a1,i3,f5.0,3i5,i4,i6,5i5,f6.2) if(ndec1.lt.nfiles/2 .and. ndec1z.ge.nfiles/2) then snr_thresh=snr1 + float(nfiles/2 - ndec1)/(ndec1z-ndec1) open(13,file='snr_thresh.out',status='unknown',position='append') From 53f5e26580fe58ae3063b97088a4d29753fca380 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 5 Dec 2020 13:46:44 -0500 Subject: [PATCH 201/426] First attempt ar reasonabkle values for nsmo and ibw range. --- lib/q65_sync.f90 | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index d761fd044..6a723745c 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -47,6 +47,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & jz=(txt+1.0)*12000.0/istep !Number of quarter-symbol steps if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher ia=ntol/df + nsmo=int(0.7*mode_q65*mode_q65) allocate(s1(iz,jz)) allocate(s3(-64:LL-65,63)) @@ -78,7 +79,9 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 enddo ! For large Doppler spreads, should we smooth the spectra here? -! call smo121(s1(1:iz,j),iz) + do i=1,nsmo + call smo121(s1(1:iz,j),iz) + enddo enddo i0=nint(nfqso/df) !Target QSO frequency @@ -168,7 +171,9 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & if(mode_q65.eq.16) nsubmode=4 nFadingModel=1 baud=12000.0/nsps - do ibw=2,4 + ibwa=1.8*log(baud*mode_q65) + 2 + ibwb=ibwa+4 + do ibw=ibwa,ibwb b90=1.72**ibw call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) From a47f5e5103e27f98194196fd4200d69f16f87b90 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 6 Dec 2020 08:09:28 -0500 Subject: [PATCH 202/426] Correct what appears to be an error in q65_intrinsics_fastfading: max hidx=63, not 64? --- lib/qra/q65/q65.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/qra/q65/q65.c b/lib/qra/q65/q65.c index c9d776bc6..f47f25fa3 100644 --- a/lib/qra/q65/q65.c +++ b/lib/qra/q65/q65.c @@ -301,8 +301,7 @@ int q65_intrinsics_fastfading(q65_codec_ds *pCodec, const float *pCurSym, *pCurBin; float *pCurIx; -// printf("pcodec=%08x submode=%d fadingmodel=%d B90Ts=%f\n",pcodec, submode,fadingModel, B90Ts); - + // printf("pcodec=%08x submode=%d fadingmodel=%d B90Ts=%f\n",pCodec, submode,fadingModel, B90Ts); if (pCodec==NULL) return Q65_DECODE_INVPARAMS; // invalid pCodec pointer @@ -322,8 +321,8 @@ int q65_intrinsics_fastfading(q65_codec_ds *pCodec, if (hidx<0) hidx = 0; else - if (hidx > 64) - hidx=64; + if (hidx > 63) //Changed by K1JT: previously max was 64. + hidx=63; //Changed by K1JT: previously max was 64. // select the appropriate weighting fading coefficients array if (fadingModel==0) { // gaussian fading model @@ -337,7 +336,7 @@ int q65_intrinsics_fastfading(q65_codec_ds *pCodec, hptr = gptr_tab_lorentz[hidx]; // pointer to the first (L+1)/2 coefficients of w fun } else - return Q65_DECODE_INVPARAMS; // invalid fading model + return Q65_DECODE_INVPARAMS; // invalid fading model // compute (euristically) the optimal decoder metric accordingly the given spread amount // We assume that the decoder 50% decoding threshold is: @@ -400,7 +399,8 @@ int q65_intrinsics_fastfading(q65_codec_ds *pCodec, // compute the fast fading weights accordingly to the Es/No ratio // for which we compute the exact intrinsics probabilities for (k=0;k Date: Sun, 6 Dec 2020 08:12:16 -0500 Subject: [PATCH 203/426] Allow fDop = 0.0 in test_q65. --- lib/test_q65.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 77b3d106c..d8c1f0763 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -55,7 +55,7 @@ program test_q65 stop 'Invalid TR period' endif - i50=i50 + 8.0*log(fDop)/log(240.0) + i50=i50 + 8.0*log(1.0+fDop)/log(240.0) ia=i50 + 7 ib=i50 - 10 if(snr.ne.0.0) then From 4b42937c3528c2d79ffefa3719c711968011a4ce Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 6 Dec 2020 08:12:58 -0500 Subject: [PATCH 204/426] Minor tweaks in q65_sync. --- lib/q65_sync.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 6a723745c..3e45f9b56 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -48,6 +48,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher ia=ntol/df nsmo=int(0.7*mode_q65*mode_q65) + if(nsmo.lt.1) nsmo=1 allocate(s1(iz,jz)) allocate(s3(-64:LL-65,63)) @@ -172,7 +173,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & nFadingModel=1 baud=12000.0/nsps ibwa=1.8*log(baud*mode_q65) + 2 - ibwb=ibwa+4 + ibwb=min(10,ibwa+4) do ibw=ibwa,ibwb b90=1.72**ibw call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) From f290cec93b41eca05107780aed7e5ba0279498b6 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 6 Dec 2020 17:51:06 +0000 Subject: [PATCH 205/426] Revert "Remove JT9+JT65 mode. Remove labNextCall. Tighten up central array of GUI controls." This reverts commit df2daf60bda6ee68df9114666a10ed0a235782fc. --- widgets/mainwindow.cpp | 102 ++++++++++++++- widgets/mainwindow.h | 2 + widgets/mainwindow.ui | 286 +++++++++++++++++++++++------------------ 3 files changed, 263 insertions(+), 127 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index cba367735..265d1e4f5 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -601,6 +601,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, ui->actionFT8->setActionGroup(modeGroup); ui->actionJT9->setActionGroup(modeGroup); ui->actionJT65->setActionGroup(modeGroup); + ui->actionJT9_JT65->setActionGroup(modeGroup); ui->actionJT4->setActionGroup(modeGroup); ui->actionWSPR->setActionGroup(modeGroup); ui->actionEcho->setActionGroup(modeGroup); @@ -859,6 +860,9 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, m_msg[0][0]=0; ui->labDXped->setVisible(false); ui->labDXped->setStyleSheet("QLabel {background-color: red; color: white;}"); + ui->labNextCall->setText(""); + ui->labNextCall->setVisible(false); + ui->labNextCall->setToolTip(""); //### Possibly temporary ? ### char const * const power[] = {"1 mW","2 mW","5 mW","10 mW","20 mW","50 mW","100 mW","200 mW","500 mW", "1 W","2 W","5 W","10 W","20 W","50 W","100 W","200 W","500 W","1 kW"}; @@ -1221,6 +1225,8 @@ void MainWindow::readSettings() m_settings->beginGroup("Common"); m_mode=m_settings->value("Mode","JT9").toString(); m_modeTx=m_settings->value("ModeTx","JT9").toString(); + if(m_modeTx.mid(0,3)=="JT9") ui->pbTxMode->setText("Tx JT9 @"); + if(m_modeTx=="JT65") ui->pbTxMode->setText("Tx JT65 #"); ui->actionNone->setChecked(m_settings->value("SaveNone",true).toBool()); ui->actionSave_decoded->setChecked(m_settings->value("SaveDecoded",false).toBool()); ui->actionSave_all->setChecked(m_settings->value("SaveAll",false).toBool()); @@ -2115,6 +2121,8 @@ void MainWindow::keyPressEvent (QKeyEvent * e) return; case Qt::Key_Escape: m_nextCall=""; + ui->labNextCall->setStyleSheet(""); + ui->labNextCall->setText(""); on_stopTxButton_clicked(); abortQSO(); return; @@ -3466,6 +3474,11 @@ void MainWindow::readFromStdout() //readFromStdout ui->decodedTextBrowser2->displayDecodedText(decodedtext0,m_baseCall,m_mode,m_config.DXCC(), m_logBook,m_currentBand,m_config.ppfx()); } + if(m_mode!="JT4") { + bool b65=decodedtext.isJT65(); + if(b65 and m_modeTx!="JT65") on_pbTxMode_clicked(); + if(!b65 and m_modeTx=="JT65") on_pbTxMode_clicked(); + } m_QSOText = decodedtext.string ().trimmed (); } @@ -4376,6 +4389,8 @@ void MainWindow::useNextCall() { ui->dxCallEntry->setText(m_nextCall); m_nextCall=""; + ui->labNextCall->setStyleSheet(""); + ui->labNextCall->setText(""); if(m_nextGrid.contains(grid_regexp)) { ui->dxGridEntry->setText(m_nextGrid); m_ntx=2; @@ -4787,9 +4802,11 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie if (message.isJT9()) { m_modeTx="JT9"; + ui->pbTxMode->setText("Tx JT9 @"); m_wideGraph->setModeTx(m_modeTx); } else if (message.isJT65()) { m_modeTx="JT65"; + ui->pbTxMode->setText("Tx JT65 #"); m_wideGraph->setModeTx(m_modeTx); } } else if ((message.isJT9 () and m_modeTx != "JT9" and m_mode != "JT4") or @@ -5021,6 +5038,17 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie } } else { // nothing for us +// if(message_words.size () > 3 // enough fields for a normal message +// && SpecOp::RTTY == m_config.special_op_id() +// && (message_words.at(1).contains(m_baseCall) || "DE" == message_words.at(1)) +// && (!message_words.at(2).contains(qso_partner_base_call) and !bEU_VHF_w2)) { +//// Queue up the next QSO partner +// m_nextCall=message_words.at(2); +// m_nextGrid=message_words.at(3); +// m_nextRpt=message.report(); +// ui->labNextCall->setText("Next: " + m_nextCall); +// ui->labNextCall->setStyleSheet("QLabel {color: #000000; background-color: #66ff66}"); +// } return; } } @@ -5886,7 +5914,7 @@ void MainWindow::displayWidgets(qint64 n) if(i==8) ui->cbFast9->setVisible(b); if(i==9) ui->cbAutoSeq->setVisible(b); if(i==10) ui->cbTx6->setVisible(b); -// if(i==11) ui->pbTxMode->setVisible(b); + if(i==11) ui->pbTxMode->setVisible(b); if(i==12) ui->pbR2T->setVisible(b); if(i==13) ui->pbT2R->setVisible(b); if(i==14) ui->cbHoldTxFreq->setVisible(b); @@ -5907,7 +5935,7 @@ void MainWindow::displayWidgets(qint64 n) if(i==25) ui->actionEnable_AP_JT65->setVisible (b); if(i==26) ui->actionEnable_AP_DXcall->setVisible (b); if(i==27) ui->cbFirst->setVisible(b); -// if(i==28) ui->labNextCall->setVisible(b); + if(i==28) ui->labNextCall->setVisible(b); if(i==29) ui->measure_check_box->setVisible(b); if(i==30) ui->labDXped->setVisible(b); if(i==31) ui->cbRxAll->setVisible(b); @@ -6257,14 +6285,58 @@ void MainWindow::on_actionJT9_triggered() statusChanged(); } +void MainWindow::on_actionJT9_JT65_triggered() +{ + m_mode="JT9+JT65"; + WSPR_config(false); + switch_mode (Modes::JT65); + if(m_modeTx != "JT65") { + ui->pbTxMode->setText("Tx JT9 @"); + m_modeTx="JT9"; + } + m_nSubMode=0; //Dual-mode always means JT9 and JT65A + m_TRperiod=60.0; + m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe + m_detector->setTRPeriod(m_TRperiod); // TODO - not thread safe + m_nsps=6912; + m_FFTSize = m_nsps / 2; + Q_EMIT FFTSize (m_FFTSize); + m_hsymStop=174; + if(m_config.decode_at_52s()) m_hsymStop=183; + m_toneSpacing=0.0; + setup_status_bar (false); + ui->actionJT9_JT65->setChecked(true); + VHF_features_enabled(false); + m_wideGraph->setPeriod(m_TRperiod,m_nsps); + m_wideGraph->setMode(m_mode); + m_wideGraph->setModeTx(m_modeTx); + m_bFastMode=false; + m_bFast9=false; + ui->sbSubmode->setValue(0); + ui->lh_decodes_title_label->setText(tr ("Band Activity")); + ui->rh_decodes_title_label->setText(tr ("Rx Frequency")); + ui->lh_decodes_headings_label->setText("UTC dB DT Freq " + tr ("Message")); + ui->rh_decodes_headings_label->setText("UTC dB DT Freq " + tr ("Message")); + displayWidgets(nWidgets("111010000001111000010000000000001000")); + fast_config(false); + statusChanged(); +} + void MainWindow::on_actionJT65_triggered() { + if(m_mode=="JT4" or m_mode=="WSPR" or m_mode=="FST4W") { +// If coming from JT4, WSPR, or FST4W mode, pretend temporarily that we're coming +// from JT9 and click the pbTxMode button + m_modeTx="JT9"; + on_pbTxMode_clicked(); + } on_actionJT9_triggered(); m_mode="JT65"; m_modeTx="JT65"; bool bVHF=m_config.enable_VHF_features(); WSPR_config(false); switch_mode (Modes::JT65); + if(m_modeTx!="JT65") on_pbTxMode_clicked(); m_TRperiod=60.0; m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe m_detector->setTRPeriod(m_TRperiod); // TODO - not thread safe @@ -6416,6 +6488,7 @@ void MainWindow::on_actionMSK144_triggered() if("JT4"==m_mode) ui->actionJT4->setChecked(true); if("JT9"==m_mode) ui->actionJT9->setChecked(true); if("JT65"==m_mode) ui->actionJT65->setChecked(true); + if("JT9_JT65"==m_mode) ui->actionJT9_JT65->setChecked(true); if("ISCAT"==m_mode) ui->actionISCAT->setChecked(true); if("QRA64"==m_mode) ui->actionQRA64->setChecked(true); if("Q65"==m_mode) ui->actionQ65->setChecked(true); @@ -7000,6 +7073,21 @@ void MainWindow::on_readFreq_clicked() } } +void MainWindow::on_pbTxMode_clicked() +{ + if(m_mode=="JT9+JT65") { + if(m_modeTx=="JT9") { + m_modeTx="JT65"; + ui->pbTxMode->setText("Tx JT65 #"); + } else { + m_modeTx="JT9"; + ui->pbTxMode->setText("Tx JT9 @"); + } + m_wideGraph->setModeTx(m_modeTx); + statusChanged(); + } +} + void MainWindow::setXIT(int n, Frequency base) { if (m_transmitting && !m_config.tx_QSY_allowed ()) return; @@ -7478,6 +7566,15 @@ void MainWindow::transmitDisplay (bool transmitting) // the following are always disallowed in transmit ui->menuMode->setEnabled (!transmitting); + //ui->bandComboBox->setEnabled (!transmitting); + if (!transmitting) { + if (m_mode == "JT9+JT65") { + // allow mode switch in Rx when in dual mode + ui->pbTxMode->setEnabled (true); + } + } else { + ui->pbTxMode->setEnabled (false); + } } } @@ -9182,6 +9279,7 @@ void MainWindow::set_mode (QString const& mode) else if ("FT8" == mode) on_actionFT8_triggered (); else if ("JT4" == mode) on_actionJT4_triggered (); else if ("JT9" == mode) on_actionJT9_triggered (); + else if ("JT9+JT65" == mode) on_actionJT9_JT65_triggered (); else if ("JT65" == mode) on_actionJT65_triggered (); else if ("QRA64" == mode) on_actionQRA64_triggered (); else if ("Q65" == mode) on_actionQ65_triggered (); diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index 4ab9a4102..3f3fad372 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -204,6 +204,7 @@ private slots: void on_logQSOButton_clicked(); void on_actionJT9_triggered(); void on_actionJT65_triggered(); + void on_actionJT9_JT65_triggered(); void on_actionJT4_triggered(); void on_actionFT4_triggered(); void on_actionFT8_triggered(); @@ -241,6 +242,7 @@ private slots: void on_bandComboBox_editTextChanged (QString const& text); void on_bandComboBox_activated (int index); void on_readFreq_clicked(); + void on_pbTxMode_clicked(); void on_RxFreqSpinBox_valueChanged(int n); void on_outAttenuation_valueChanged (int); void rigOpen (); diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index 51dc92757..b42410cd7 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 805 - 584 + 589 @@ -596,7 +596,7 @@ - + @@ -1225,6 +1225,25 @@ Yellow when too low + + + + true + + + + 0 + 0 + + + + Toggle Tx mode + + + Tx JT9 @ + + + @@ -1250,6 +1269,53 @@ Yellow when too low + + + + <html><head/><body><p>Submode determines tone spacing; A is narrowest.</p></body></html> + + + Submode determines tone spacing; A is narrowest. + + + Qt::AlignCenter + + + Submode + + + 0 + + + 7 + + + + + + + Qt::AlignCenter + + + + + + F High + + + 100 + + + 5000 + + + 100 + + + 1400 + + + @@ -1319,6 +1385,44 @@ Yellow when too low + + + + Qt::AlignCenter + + + F Low + + + 100 + + + 5000 + + + 100 + + + 600 + + + + + + + <html><head/><body><p>Double-click on another caller to queue that call for your next QSO.</p></body></html> + + + Double-click on another caller to queue that call for your next QSO. + + + Next Call + + + Qt::AlignCenter + + + @@ -1425,6 +1529,60 @@ Not available to nonstandard callsign holders. + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Tx# + + + 1 + + + 4095 + + + + + + + <html><head/><body><p>Check to keep Tx frequency fixed when double-clicking on decoded text.</p></body></html> + + + Check to keep Tx frequency fixed when double-clicking on decoded text. + + + Hold Tx Freq + + + + + + + <html><head/><body><p>Synchronizing threshold. Lower numbers accept weaker sync signals.</p></body></html> + + + Synchronizing threshold. Lower numbers accept weaker sync signals. + + + Qt::AlignCenter + + + Sync + + + -2 + + + 10 + + + 1 + + + @@ -1552,129 +1710,6 @@ When not checked you can view the calibration results. - - - - <html><head/><body><p>Check to keep Tx frequency fixed when double-clicking on decoded text.</p></body></html> - - - Check to keep Tx frequency fixed when double-clicking on decoded text. - - - Hold Tx Freq - - - - - - - Qt::AlignCenter - - - F Low - - - 100 - - - 5000 - - - 100 - - - 600 - - - - - - - Qt::AlignCenter - - - - - - F High - - - 100 - - - 5000 - - - 100 - - - 1400 - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Tx# - - - 1 - - - 4095 - - - - - - - <html><head/><body><p>Submode determines tone spacing; A is narrowest.</p></body></html> - - - Submode determines tone spacing; A is narrowest. - - - Qt::AlignCenter - - - Submode - - - 0 - - - 7 - - - - - - - <html><head/><body><p>Synchronizing threshold. Lower numbers accept weaker sync signals.</p></body></html> - - - Synchronizing threshold. Lower numbers accept weaker sync signals. - - - Qt::AlignCenter - - - Sync - - - -2 - - - 10 - - - 1 - - - @@ -2864,6 +2899,7 @@ list. The list can be maintained in Settings (F2). + From 3d9a099fcea2a705b701b4e6ca2fe651f1867a6d Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Mon, 7 Dec 2020 00:18:52 +0000 Subject: [PATCH 206/426] Reinstate changes that were reverted from commit df2daf6 Changes were moved aside to simplify a big merge commit with many conflicts. --- widgets/mainwindow.cpp | 102 +---------------------------------------- widgets/mainwindow.h | 2 - widgets/mainwindow.ui | 46 +------------------ 3 files changed, 3 insertions(+), 147 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index ad2713291..d6c407d7f 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -593,7 +593,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, ui->actionFT8->setActionGroup(modeGroup); ui->actionJT9->setActionGroup(modeGroup); ui->actionJT65->setActionGroup(modeGroup); - ui->actionJT9_JT65->setActionGroup(modeGroup); ui->actionJT4->setActionGroup(modeGroup); ui->actionWSPR->setActionGroup(modeGroup); ui->actionEcho->setActionGroup(modeGroup); @@ -853,9 +852,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, m_msg[0][0]=0; ui->labDXped->setVisible(false); ui->labDXped->setStyleSheet("QLabel {background-color: red; color: white;}"); - ui->labNextCall->setText(""); - ui->labNextCall->setVisible(false); - ui->labNextCall->setToolTip(""); //### Possibly temporary ? ### char const * const power[] = {"1 mW","2 mW","5 mW","10 mW","20 mW","50 mW","100 mW","200 mW","500 mW", "1 W","2 W","5 W","10 W","20 W","50 W","100 W","200 W","500 W","1 kW"}; @@ -1246,8 +1242,6 @@ void MainWindow::readSettings() m_settings->beginGroup("Common"); m_mode=m_settings->value("Mode","JT9").toString(); m_modeTx=m_settings->value("ModeTx","JT9").toString(); - if(m_modeTx.mid(0,3)=="JT9") ui->pbTxMode->setText("Tx JT9 @"); - if(m_modeTx=="JT65") ui->pbTxMode->setText("Tx JT65 #"); ui->actionNone->setChecked(m_settings->value("SaveNone",true).toBool()); ui->actionSave_decoded->setChecked(m_settings->value("SaveDecoded",false).toBool()); ui->actionSave_all->setChecked(m_settings->value("SaveAll",false).toBool()); @@ -2141,8 +2135,6 @@ void MainWindow::keyPressEvent (QKeyEvent * e) return; case Qt::Key_Escape: m_nextCall=""; - ui->labNextCall->setStyleSheet(""); - ui->labNextCall->setText(""); on_stopTxButton_clicked(); abortQSO(); return; @@ -3524,11 +3516,6 @@ void MainWindow::readFromStdout() //readFromStdout ui->decodedTextBrowser2->displayDecodedText(decodedtext0,m_baseCall,m_mode,m_config.DXCC(), m_logBook,m_currentBand,m_config.ppfx()); } - if(m_mode!="JT4") { - bool b65=decodedtext.isJT65(); - if(b65 and m_modeTx!="JT65") on_pbTxMode_clicked(); - if(!b65 and m_modeTx=="JT65") on_pbTxMode_clicked(); - } m_QSOText = decodedtext.string ().trimmed (); } @@ -4450,8 +4437,6 @@ void MainWindow::useNextCall() { ui->dxCallEntry->setText(m_nextCall); m_nextCall=""; - ui->labNextCall->setStyleSheet(""); - ui->labNextCall->setText(""); if(m_nextGrid.contains(grid_regexp)) { ui->dxGridEntry->setText(m_nextGrid); m_ntx=2; @@ -4865,11 +4850,9 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie if (message.isJT9()) { m_modeTx="JT9"; - ui->pbTxMode->setText("Tx JT9 @"); m_wideGraph->setModeTx(m_modeTx); } else if (message.isJT65()) { m_modeTx="JT65"; - ui->pbTxMode->setText("Tx JT65 #"); m_wideGraph->setModeTx(m_modeTx); } } else if ((message.isJT9 () and m_modeTx != "JT9" and m_mode != "JT4") or @@ -5112,17 +5095,6 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie } } else { // nothing for us -// if(message_words.size () > 3 // enough fields for a normal message -// && SpecOp::RTTY == m_config.special_op_id() -// && (message_words.at(1).contains(m_baseCall) || "DE" == message_words.at(1)) -// && (!message_words.at(2).contains(qso_partner_base_call) and !bEU_VHF_w2)) { -//// Queue up the next QSO partner -// m_nextCall=message_words.at(2); -// m_nextGrid=message_words.at(3); -// m_nextRpt=message.report(); -// ui->labNextCall->setText("Next: " + m_nextCall); -// ui->labNextCall->setStyleSheet("QLabel {color: #000000; background-color: #66ff66}"); -// } return; } } @@ -5989,7 +5961,7 @@ void MainWindow::displayWidgets(qint64 n) if(i==8) ui->cbFast9->setVisible(b); if(i==9) ui->cbAutoSeq->setVisible(b); if(i==10) ui->cbTx6->setVisible(b); - if(i==11) ui->pbTxMode->setVisible(b); + // if(i==11) ui->pbTxMode->setVisible(b); if(i==12) ui->pbR2T->setVisible(b); if(i==13) ui->pbT2R->setVisible(b); if(i==14) ui->cbHoldTxFreq->setVisible(b); @@ -6010,7 +5982,7 @@ void MainWindow::displayWidgets(qint64 n) if(i==25) ui->actionEnable_AP_JT65->setVisible (b); if(i==26) ui->actionEnable_AP_DXcall->setVisible (b); if(i==27) ui->cbFirst->setVisible(b); - if(i==28) ui->labNextCall->setVisible(b); + // if(i==28) ui->labNextCall->setVisible(b); if(i==29) ui->measure_check_box->setVisible(b); if(i==30) ui->labDXped->setVisible(b); if(i==31) ui->cbRxAll->setVisible(b); @@ -6364,58 +6336,14 @@ void MainWindow::on_actionJT9_triggered() statusChanged(); } -void MainWindow::on_actionJT9_JT65_triggered() -{ - m_mode="JT9+JT65"; - WSPR_config(false); - switch_mode (Modes::JT65); - if(m_modeTx != "JT65") { - ui->pbTxMode->setText("Tx JT9 @"); - m_modeTx="JT9"; - } - m_nSubMode=0; //Dual-mode always means JT9 and JT65A - m_TRperiod=60.0; - m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe - m_detector->setTRPeriod(m_TRperiod); // TODO - not thread safe - m_nsps=6912; - m_FFTSize = m_nsps / 2; - Q_EMIT FFTSize (m_FFTSize); - m_hsymStop=174; - if(m_config.decode_at_52s()) m_hsymStop=183; - m_toneSpacing=0.0; - setup_status_bar (false); - ui->actionJT9_JT65->setChecked(true); - VHF_features_enabled(false); - m_wideGraph->setPeriod(m_TRperiod,m_nsps); - m_wideGraph->setMode(m_mode); - m_wideGraph->setModeTx(m_modeTx); - m_bFastMode=false; - m_bFast9=false; - ui->sbSubmode->setValue(0); - ui->lh_decodes_title_label->setText(tr ("Band Activity")); - ui->rh_decodes_title_label->setText(tr ("Rx Frequency")); - ui->lh_decodes_headings_label->setText("UTC dB DT Freq " + tr ("Message")); - ui->rh_decodes_headings_label->setText("UTC dB DT Freq " + tr ("Message")); - displayWidgets(nWidgets("111010000001111000010000000000001000")); - fast_config(false); - statusChanged(); -} - void MainWindow::on_actionJT65_triggered() { - if(m_mode=="JT4" or m_mode=="WSPR" or m_mode=="FST4W") { -// If coming from JT4, WSPR, or FST4W mode, pretend temporarily that we're coming -// from JT9 and click the pbTxMode button - m_modeTx="JT9"; - on_pbTxMode_clicked(); - } on_actionJT9_triggered(); m_mode="JT65"; m_modeTx="JT65"; bool bVHF=m_config.enable_VHF_features(); WSPR_config(false); switch_mode (Modes::JT65); - if(m_modeTx!="JT65") on_pbTxMode_clicked(); m_TRperiod=60.0; m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe m_detector->setTRPeriod(m_TRperiod); // TODO - not thread safe @@ -6567,7 +6495,6 @@ void MainWindow::on_actionMSK144_triggered() if("JT4"==m_mode) ui->actionJT4->setChecked(true); if("JT9"==m_mode) ui->actionJT9->setChecked(true); if("JT65"==m_mode) ui->actionJT65->setChecked(true); - if("JT9_JT65"==m_mode) ui->actionJT9_JT65->setChecked(true); if("ISCAT"==m_mode) ui->actionISCAT->setChecked(true); if("QRA64"==m_mode) ui->actionQRA64->setChecked(true); if("Q65"==m_mode) ui->actionQ65->setChecked(true); @@ -7152,21 +7079,6 @@ void MainWindow::on_readFreq_clicked() } } -void MainWindow::on_pbTxMode_clicked() -{ - if(m_mode=="JT9+JT65") { - if(m_modeTx=="JT9") { - m_modeTx="JT65"; - ui->pbTxMode->setText("Tx JT65 #"); - } else { - m_modeTx="JT9"; - ui->pbTxMode->setText("Tx JT9 @"); - } - m_wideGraph->setModeTx(m_modeTx); - statusChanged(); - } -} - void MainWindow::setXIT(int n, Frequency base) { if (m_transmitting && !m_config.tx_QSY_allowed ()) return; @@ -7647,15 +7559,6 @@ void MainWindow::transmitDisplay (bool transmitting) // the following are always disallowed in transmit ui->menuMode->setEnabled (!transmitting); - //ui->bandComboBox->setEnabled (!transmitting); - if (!transmitting) { - if (m_mode == "JT9+JT65") { - // allow mode switch in Rx when in dual mode - ui->pbTxMode->setEnabled (true); - } - } else { - ui->pbTxMode->setEnabled (false); - } } } @@ -9381,7 +9284,6 @@ void MainWindow::set_mode (QString const& mode) else if ("FT8" == mode) on_actionFT8_triggered (); else if ("JT4" == mode) on_actionJT4_triggered (); else if ("JT9" == mode) on_actionJT9_triggered (); - else if ("JT9+JT65" == mode) on_actionJT9_JT65_triggered (); else if ("JT65" == mode) on_actionJT65_triggered (); else if ("QRA64" == mode) on_actionQRA64_triggered (); else if ("Q65" == mode) on_actionQ65_triggered (); diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index d798d96bd..b9c43be17 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -209,7 +209,6 @@ private slots: void on_logQSOButton_clicked(); void on_actionJT9_triggered(); void on_actionJT65_triggered(); - void on_actionJT9_JT65_triggered(); void on_actionJT4_triggered(); void on_actionFT4_triggered(); void on_actionFT8_triggered(); @@ -247,7 +246,6 @@ private slots: void on_bandComboBox_editTextChanged (QString const& text); void on_bandComboBox_activated (int index); void on_readFreq_clicked(); - void on_pbTxMode_clicked(); void on_RxFreqSpinBox_valueChanged(int n); void on_outAttenuation_valueChanged (int); void rigOpen (); diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index 639a5b567..b945e9210 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -1504,25 +1504,6 @@ When not checked you can view the calibration results. - - - - true - - - - 0 - 0 - - - - Toggle Tx mode - - - Tx JT9 @ - - - @@ -1555,22 +1536,6 @@ When not checked you can view the calibration results. 0 - - - - <html><head/><body><p>Double-click on another caller to queue that call for your next QSO.</p></body></html> - - - Double-click on another caller to queue that call for your next QSO. - - - Next Call - - - Qt::AlignCenter - - - @@ -2828,7 +2793,7 @@ Double-click to reset to the standard 73 message 0 0 - 834 + 1110 21 @@ -2918,7 +2883,6 @@ Double-click to reset to the standard 73 message - @@ -3115,14 +3079,6 @@ Double-click to reset to the standard 73 message JT65 - - - true - - - JT9+JT65 - - true From d42363587ecec1ce4707be685e04e0758cd4b9e7 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 6 Dec 2020 21:42:30 -0500 Subject: [PATCH 207/426] Swtich from a fast mode to Q65 now closes FastGraph and opens WideGraph, as it should. --- widgets/mainwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index d6c407d7f..1a1990f07 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6428,6 +6428,7 @@ void MainWindow::on_actionQ65_triggered() m_modeTx="Q65"; ui->actionQ65->setChecked(true); switch_mode(Modes::Q65); + fast_config(false); setup_status_bar(true); m_nsps=6912; //For symspec only m_FFTSize = m_nsps / 2; From 7e1b74ecc0b3f874ce26e261985f950b285a6669 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 8 Dec 2020 00:01:46 +0000 Subject: [PATCH 208/426] Moon distance in astronomical data window, tnx Charlie, G3WDG --- widgets/astro.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/widgets/astro.cpp b/widgets/astro.cpp index 87ca7224f..e161199c1 100644 --- a/widgets/astro.cpp +++ b/widgets/astro.cpp @@ -157,7 +157,8 @@ auto Astro::astroUpdate(QDateTime const& t, QString const& mygrid, QString const if(freq>=5000000ull) { //Suppress data not relevant below VHF out << "Tsky: " << ntsky << "\n" "Dpol: " << poloffset << "\n" - "MNR: " << xnr << "\n" + "MNR: " << xnr << "\n" + "Dist: " << int((techo*149896)) << "\n" //wdg "Dgrd: " << dgrd; } } From 84591fa2effb6793c7b6d8f361c2d63bf2ab45a5 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 8 Dec 2020 11:53:22 -0500 Subject: [PATCH 209/426] Protect against a bounds error with very large FTol. --- lib/q65_sync.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 3e45f9b56..842f14c94 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -135,7 +135,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & if(j.ge.1 .and. j.le.jz) then do i=-ia,ia ii=i0+mode_q65*itone(k)+i - ccf(i,lag)=ccf(i,lag) + s1(ii,j) + if(ii.ge.1 .and. ii.le.iz) ccf(i,lag)=ccf(i,lag) + s1(ii,j) enddo endif enddo From 218d0bf3590b0e198af3c41323e189836bc5c260 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 8 Dec 2020 13:49:17 -0500 Subject: [PATCH 210/426] Remove version number from the User Guide's title. (It's on the next line, anyway.) --- doc/user_guide/en/wsjtx-main.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user_guide/en/wsjtx-main.adoc b/doc/user_guide/en/wsjtx-main.adoc index 9bed15dc6..b6acd256f 100644 --- a/doc/user_guide/en/wsjtx-main.adoc +++ b/doc/user_guide/en/wsjtx-main.adoc @@ -1,5 +1,5 @@ // This is a comment line, anything with // is ignored at process time. -= _WSJT-X {VERSION_MAJOR}.{VERSION_MINOR}_ User Guide += _WSJT-X_ User Guide Joseph H Taylor, Jr, K1JT :revnumber: {VERSION} // For web-pages, adding :badges: is ok, but is a security issue for From 5b59633b2fd85fe351c00a630ee53f8ffc8f0818 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 9 Dec 2020 09:34:09 -0500 Subject: [PATCH 211/426] Re-enable transmission in Q65 submodes B and higher. --- widgets/mainwindow.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index f903bdede..8075f6b52 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -7340,9 +7340,9 @@ void MainWindow::transmit (double snr) if(m_TRperiod==60) nsps=7200; if(m_TRperiod==120) nsps=16000; if(m_TRperiod==300) nsps=41472; -// int mode65=pow(2.0,double(m_nSubMode)); -// toneSpacing=mode65*12000.0/nsps; - toneSpacing=-4.0; + int mode65=pow(2.0,double(m_nSubMode)); + toneSpacing=mode65*12000.0/nsps; +// toneSpacing=-4.0; Q_EMIT sendMessage (m_mode, NUM_Q65_SYMBOLS, double(nsps), ui->TxFreqSpinBox->value () - m_XIT, toneSpacing, m_soundOutput, m_config.audio_output_channel (), From cd95df4089a22ba66f9de25785903e98dfe1e843 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 9 Dec 2020 13:45:05 -0500 Subject: [PATCH 212/426] Protect against pathological combinations of RxFreq and FTol. --- lib/q65_sync.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 842f14c94..72f8a8f3b 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -36,6 +36,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & data sync(1)/99.0/ save sync + snr1=0. id1=0 dat4=0 LL=64*(2+mode_q65) @@ -86,6 +87,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & enddo i0=nint(nfqso/df) !Target QSO frequency + if(i0-64.lt.1 .or. i0-65+LL.gt.iz) go to 900 call pctile(s1(i0-64:i0-65+LL,1:jz),LL*jz,40,base) s1=s1/base From c4dbfbbdca04a85c41eada45c6a233efc3802c15 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 9 Dec 2020 13:55:47 -0500 Subject: [PATCH 213/426] Must call wspr_config(false) when entering Q65 mode. --- widgets/mainwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 8075f6b52..e17979bae 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6432,6 +6432,7 @@ void MainWindow::on_actionQ65_triggered() ui->actionQ65->setChecked(true); switch_mode(Modes::Q65); fast_config(false); + WSPR_config(false); setup_status_bar(true); m_nsps=6912; //For symspec only m_FFTSize = m_nsps / 2; From 2a25a188d13ddbfff064ffa6a4e285e0e8bd8ccf Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 11 Dec 2020 09:13:38 -0500 Subject: [PATCH 214/426] Add IV3NWV to mainwindow title bar. --- revision_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/revision_utils.cpp b/revision_utils.cpp index c80e07993..597403d0c 100644 --- a/revision_utils.cpp +++ b/revision_utils.cpp @@ -80,5 +80,5 @@ QString version (bool include_patch) QString program_title (QString const& revision) { QString id {QCoreApplication::applicationName () + " v" + QCoreApplication::applicationVersion ()}; - return id + " " + revision + " by K1JT, G4WJS, and K9AN"; + return id + " " + revision + " by K1JT, G4WJS, K9AN, and IV3NWV"; } From 535c02d900c2164b6f0a0deea4a7309d972cd904 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Sun, 13 Dec 2020 15:33:12 -0600 Subject: [PATCH 215/426] Implements decoding of FST4W messages as (240,50) crc-less codewords. By cascading the full 24-bit crc generator matrix with the (240,74) LDPC code generator, create a (240,50) generator that is used to decode with approximately 1 dB better sensitivity than the (240,64) with 14-bit CRC approach that is normally used. This approach treats the CRC bits as additional parity bits and provides no means for identifying incorrect codewords. All codewords on the list generated by the OSD algorithm have CRCs that match the CRC of the message payload. Codewords are validated by unpacking the message and comparing the unpacked message with the list of stored callsign/grid pairs stored in the fst4w_calls.txt file. --- CMakeLists.txt | 1 + lib/fst4/decode240_74.f90 | 3 +- lib/fst4/fastosd240_74.f90 | 291 +++++++++++++++++++++++++++++++++++++ lib/fst4/ldpcsim240_74.f90 | 2 +- lib/fst4_decode.f90 | 232 +++++++++++++++++++---------- 5 files changed, 452 insertions(+), 77 deletions(-) create mode 100644 lib/fst4/fastosd240_74.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 41e2d74a0..2ab9a20b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -570,6 +570,7 @@ set (wsjt_FSRCS lib/fst4/ldpcsim240_74.f90 lib/fst4/osd240_101.f90 lib/fst4/osd240_74.f90 + lib/fst4/fastosd240_74.f90 lib/fst4/get_crc24.f90 lib/fst4/fst4_baseline.f90 ) diff --git a/lib/fst4/decode240_74.f90 b/lib/fst4/decode240_74.f90 index be18f6e09..20a83362f 100644 --- a/lib/fst4/decode240_74.f90 +++ b/lib/fst4/decode240_74.f90 @@ -135,7 +135,8 @@ subroutine decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw,ntype,nharder do i=1,nosd zn=zsave(:,i) - call osd240_74(zn,Keff,apmask,norder,message74,cw,nharderror,dminosd) +! call osd240_74(zn,Keff,apmask,norder,message74,cw,nharderror,dminosd) + call fastosd240_74(zn,Keff,apmask,norder,message74,cw,nharderror,dminosd) if(nharderror.gt.0) then hdec=0 where(llr .ge. 0) hdec=1 diff --git a/lib/fst4/fastosd240_74.f90 b/lib/fst4/fastosd240_74.f90 new file mode 100644 index 000000000..03ae4071c --- /dev/null +++ b/lib/fst4/fastosd240_74.f90 @@ -0,0 +1,291 @@ +subroutine fastosd240_74(llr,k,apmask,ndeep,message74,cw,nhardmin,dmin) +! +! An ordered-statistics decoder for the (240,74) code. +! Message payload is 50 bits. Any or all of a 24-bit CRC can be +! used for detecting incorrect codewords. The remaining CRC bits are +! cascaded with the LDPC code for the purpose of improving the +! distance spectrum of the code. +! +! If p1 (0.le.p1.le.24) is the number of CRC24 bits that are +! to be used for bad codeword detection, then the argument k should +! be set to 77+p1. +! +! Valid values for k are in the range [50,74]. +! + character*24 c24 + integer, parameter:: N=240 + integer*1 apmask(N),apmaskr(N) + integer*1, allocatable, save :: gen(:,:) + integer*1, allocatable :: genmrb(:,:),g2(:,:) + integer*1, allocatable :: temp(:),temprow(:),m0(:),me(:),mi(:) + integer indices(N),indices2(N),nxor(N) + integer*1 cw(N),ce(N),c0(N),hdec(N) + integer*1, allocatable :: decoded(:) + integer*1 message74(74) + integer*1, allocatable :: sp(:) + integer indx(N),ksave + real llr(N),rx(N),absrx(N) + + logical first + data first/.true./,ksave/64/ + save first,ksave + + allocate( genmrb(k,N), g2(N,k) ) + allocate( temp(k), temprow(n), m0(k), me(k), mi(k) ) + allocate( decoded(k) ) + + if( first .or. k.ne.ksave) then ! fill the generator matrix +! +! Create generator matrix for partial CRC cascaded with LDPC code. +! +! Let p2=74-k and p1+p2=24. +! +! The last p2 bits of the CRC24 are cascaded with the LDPC code. +! +! The first p1=k-50 CRC24 bits will be used for error detection. +! + if( allocated(gen) ) deallocate(gen) + allocate( gen(k,N) ) + gen=0 + do i=1,k + message74=0 + message74(i)=1 + if(i.le.50) then + call get_crc24(message74,74,ncrc24) + write(c24,'(b24.24)') ncrc24 + read(c24,'(24i1)') message74(51:74) + message74(51:k)=0 + endif + call encode240_74(message74,cw) + gen(i,:)=cw + enddo + + first=.false. + ksave=k + endif + +! Use best k elements from the sorted list for the first basis. For the 2nd basis replace +! the nswap lowest quality symbols with the best nswap elements from the parity symbols. + nswap=20 + + do ibasis=1,2 + rx=llr + apmaskr=apmask + +! Hard decisions on the received word. + hdec=0 + where(rx .ge. 0) hdec=1 + +! Use magnitude of received symbols as a measure of reliability. + absrx=abs(llr) + call indexx(absrx,N,indx) + +! Re-order the columns of the generator matrix in order of decreasing reliability. + do i=1,N + genmrb(1:k,i)=gen(1:k,indx(N+1-i)) + indices(i)=indx(N+1-i) + enddo + + if(ibasis.eq.2) then + do i=k-nswap+1,k + temp(1:k)=genmrb(1:k,i) + genmrb(1:k,i)=genmrb(1:k,i+nswap) + genmrb(1:k,i+nswap)=temp(1:k) + itmp=indices(i) + indices(i)=indices(i+nswap) + indices(i+nswap)=itmp + enddo + endif + +! Do gaussian elimination to create a generator matrix with the most reliable +! received bits in positions 1:k in order of decreasing reliability (more or less). + + icol=1 + indices2=0 + nskipped=0 + do id=1,k + iflag=0 + do while(iflag.eq.0) + if(genmrb(id,icol).ne.1) then + do j=id+1,k + if(genmrb(j,icol).eq.1) then + temprow=genmrb(id,:) + genmrb(id,:)=genmrb(j,:) + genmrb(j,:)=temprow + iflag=1 + endif + enddo + if(iflag.eq.0) then ! skip this column + nskipped=nskipped+1 + indices2(k+nskipped)=icol ! put icol where skipped columns go + icol=icol+1 ! look at the next column + endif + else + iflag=1 + endif + enddo + indices2(id)=icol + do j=1,k + if(id.ne.j .and. genmrb(j,icol).eq.1) then + genmrb(j,:)=ieor(genmrb(id,:),genmrb(j,:)) + endif + enddo + icol=icol+1 + enddo + do i=k+nskipped+1,240 + indices2(i)=i + enddo + genmrb(1:k,:)=genmrb(1:k,indices2) + indices=indices(indices2) + +!************************************ + g2=transpose(genmrb) + +! The hard decisions for the k MRB bits define the order 0 message, m0. +! Encode m0 using the modified generator matrix to find the "order 0" codeword. +! Flip various combinations of bits in m0 and re-encode to generate a list of +! codewords. Return the member of the list that has the smallest Euclidean +! distance to the received word. + + hdec=hdec(indices) ! hard decisions from received symbols + m0=hdec(1:k) ! zero'th order message + absrx=abs(llr) + absrx=absrx(indices) + rx=rx(indices) + apmaskr=apmaskr(indices) + + call mrbencode74(m0,c0,g2,N,k) + nxor=ieor(c0,hdec) + nhardmin=sum(nxor) + dmin=sum(nxor*absrx) + np=32 + if(ibasis.eq.1) allocate(sp(np)) + + cw=c0 + ntotal=0 + nrejected=0 + + if(ndeep.eq.0) goto 998 ! norder=0 + if(ndeep.gt.4) ndeep=4 + if( ndeep.eq. 1) then + nord=1 + xlambda=0.0 + nsyncmax=np + elseif(ndeep.eq.2) then + nord=2 + xlambda=0.0 + nsyncmax=np + elseif(ndeep.eq.3) then + nord=3 + xlambda=4.0 + nsyncmax=11 + elseif(ndeep.eq.4) then + nord=4 + xlambda=3.5 + nsyndmax=11 + endif + + s1=sum(absrx(1:k)) + s2=sum(absrx(k+1:N)) + rho=s1/(s1+xlambda*s2) + rhodmin=rho*dmin + nerr64=-1 + do iorder=1,nord +!beta=0.0 +!if(iorder.ge.3) beta=0.4 +!spnc_order=sum(absrx(k-iorder+1:k))+beta*(N-k) +!if(dmin.lt.spnc_order) cycle + mi(1:k-iorder)=0 + mi(k-iorder+1:k)=1 + iflag=k-iorder+1 + do while(iflag .ge.0) + ntotal=ntotal+1 + me=ieor(m0,mi) + d1=sum(mi(1:k)*absrx(1:k)) + if(d1.gt.rhodmin) exit + call partial_syndrome(me,sp,np,g2,N,K) + nwhsp=sum(ieor(sp(1:np),hdec(k:k+np-1))) + if(nwhsp.le.nsyndmax) then + call mrbencode74(me,ce,g2,N,k) + nxor=ieor(ce,hdec) + dd=sum(nxor*absrx(1:N)) + if( dd .lt. dmin ) then + dmin=dd + rhodmin=rho*dmin + cw=ce + nhardmin=sum(nxor) + nwhspmin=nwhsp + nerr64=sum(nxor(1:K)) + endif + endif +! Get the next test error pattern, iflag will go negative +! when the last pattern with weight iorder has been generated. + call nextpat74(mi,k,iorder,iflag) + enddo + enddo + +998 continue +! Re-order the codeword to [message bits][parity bits] format. + cw(indices)=cw + hdec(indices)=hdec + message74=cw(1:74) + call get_crc24(message74,74,nbadcrc) + if(nbadcrc.eq.0) exit + nhardmin=-nhardmin + enddo ! basis loop + return +end subroutine fastosd240_74 + +subroutine mrbencode74(me,codeword,g2,N,K) + integer*1 me(K),codeword(N),g2(N,K) +! fast encoding for low-weight test patterns + codeword=0 + do i=1,K + if( me(i) .eq. 1 ) then + codeword=ieor(codeword,g2(1:N,i)) + endif + enddo + return +end subroutine mrbencode74 + +subroutine partial_syndrome(me,sp,np,g2,N,K) + integer*1 me(K),sp(np),g2(N,K) +! compute partial syndrome + sp=0 + do i=1,K + if( me(i) .eq. 1 ) then + sp=ieor(sp,g2(K:K+np-1,i)) + endif + enddo + return +end subroutine partial_syndrome + +subroutine nextpat74(mi,k,iorder,iflag) + integer*1 mi(k),ms(k) +! generate the next test error pattern + ind=-1 + do i=1,k-1 + if( mi(i).eq.0 .and. mi(i+1).eq.1) ind=i + enddo + if( ind .lt. 0 ) then ! no more patterns of this order + iflag=ind + return + endif + ms=0 + ms(1:ind-1)=mi(1:ind-1) + ms(ind)=1 + ms(ind+1)=0 + if( ind+1 .lt. k ) then + nz=iorder-sum(ms) + ms(k-nz+1:k)=1 + endif + mi=ms + do i=1,k ! iflag will point to the lowest-index 1 in mi + if(mi(i).eq.1) then + iflag=i + exit + endif + enddo + return +end subroutine nextpat74 + diff --git a/lib/fst4/ldpcsim240_74.f90 b/lib/fst4/ldpcsim240_74.f90 index b488aa6b6..78e8e6b5f 100644 --- a/lib/fst4/ldpcsim240_74.f90 +++ b/lib/fst4/ldpcsim240_74.f90 @@ -101,7 +101,7 @@ write(*,'(24i1)') msgbits(51:74) llr=2.0*rxdata/(ss*ss) apmask=0 dmin=0.0 - maxosd=0 + maxosd=2 call decode240_74(llr, Keff, maxosd, norder, apmask, message74, cw, ntype, nharderror, dmin) if(nharderror.ge.0) then n2err=0 diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index ba49fb54d..ccf6d9662 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -33,15 +33,17 @@ contains ndepth,ntrperiod,nexp_decode,ntol,emedelay,lagain,lapcqonly,mycall, & hiscall,iwspr) + use prog_args use timer_module, only: timer use packjt77 use, intrinsic :: iso_c_binding include 'fst4/fst4_params.f90' - parameter (MAXCAND=100) + parameter (MAXCAND=100,MAXWCALLS=100) class(fst4_decoder), intent(inout) :: this procedure(fst4_decode_callback) :: callback character*37 decodes(100) character*37 msg,msgsent + character*20 wcalls(MAXWCALLS), wpart character*77 c77 character*12 mycall,hiscall character*12 mycall0,hiscall0 @@ -66,7 +68,8 @@ contains integer mcq(29),mrrr(19),m73(19),mrr73(19) logical badsync,unpk77_success,single_decode - logical first,nohiscall,lwspr,ex + logical first,nohiscall,lwspr,ex,wcalls_exists,donocrcdecode + logical new_callsign integer*2 iwave(30*60*12000) @@ -80,6 +83,7 @@ contains 0,1,0,1,0,1,1,0,1,1,1,1,1,0,0,0,1,0,1/ data first/.true./,hmod/1/ save first,apbits,nappasses,naptypes,mycall0,hiscall0 + save wcalls,nwcalls this%callback => callback dxcall13=hiscall ! initialize for use in packjt77 @@ -88,6 +92,24 @@ contains if(iwspr.ne.0.and.iwspr.ne.1) return if(first) then +! read the fst4_calls.txt file + write(*,*) 'data_dir is:',trim(data_dir) + inquire(file=trim(data_dir)//'/fst4w_calls.txt',exist=wcalls_exists) + if( wcalls_exists ) then + open(42,file=trim(data_dir)//'/fst4w_calls.txt',status='unknown') + write(*,*) 'fst4w_calls.txt exists' + do i=1,MAXWCALLS + wcalls(i)='' + read(42,fmt='(a)',end=2867) wcalls(i) + wcalls(i)=adjustl(wcalls(i)) + if(len(trim(wcalls(i))).eq.0) exit + write(*,*) 'record ',i,':',wcalls(i),':',len(trim(wcalls(i))) + enddo +2867 nwcalls=i-1 + write(*,*) 'nwcalls ',nwcalls + close(42) + endif + mcq=2*mod(mcq+rvec(1:29),2)-1 mrrr=2*mod(mrrr+rvec(59:77),2)-1 m73=2*mod(m73+rvec(59:77),2)-1 @@ -216,12 +238,15 @@ contains if(ndepth.eq.3) then nblock=4 jittermax=2 + donocrcdecode=.true. elseif(ndepth.eq.2) then - nblock=3 - jittermax=0 + nblock=4 + jittermax=2 + donocrcdecode=.false. elseif(ndepth.eq.1) then - nblock=1 + nblock=4 jittermax=0 + donocrcdecode=.false. endif ndropmax=1 @@ -244,7 +269,7 @@ contains if(iwspr.eq.1) then !FST4W !300 Hz wide noise-fit window - nfa=max(100,nint(nfqso+1.5*baud-150)) + nfa=max(100,nint(nfqso+1.5*baud-150)) nfb=min(4800,nint(nfqso+1.5*baud+150)) fa=max(100,nint(nfqso+1.5*baud-ntol)) ! signal search window fb=min(4800,nint(nfqso+1.5*baud+ntol)) @@ -252,18 +277,19 @@ contains fa=max(100,nint(nfa+1.5*baud)) fb=min(4800,nint(nfb+1.5*baud)) ! extend noise fit 100 Hz outside of search window - nfa=max(100,nfa-100) + nfa=max(100,nfa-100) nfb=min(4800,nfb+100) else fa=max(100,nint(nfa+1.5*baud)) fb=min(4800,nint(nfb+1.5*baud)) ! extend noise fit 100 Hz outside of search window - nfa=max(100,nfa-100) + nfa=max(100,nfa-100) nfb=min(4800,nfb+100) endif - + ndecodes=0 decodes=' ' + new_callsign=.false. do inb=0,inb1,inb2 if(nb.lt.0) npct=inb call blanker(iwave,nfft1,ndropmax,npct,c_bigfft) @@ -275,16 +301,16 @@ contains nsyncoh=8 minsync=1.20 if(ntrperiod.eq.15) minsync=1.15 - + ! Get first approximation of candidate frequencies call get_candidates_fst4(c_bigfft,nfft1,nsps,hmod,fs,fa,fb,nfa,nfb, & - minsync,ncand,candidates0) + minsync,ncand,candidates0) isbest=0 fc2=0. do icand=1,ncand fc0=candidates0(icand,1) if(iwspr.eq.0 .and. nb.lt.0 .and. npct.ne.0 .and. & - abs(fc0-(nfqso+1.5*baud)).gt.ntol) cycle + abs(fc0-(nfqso+1.5*baud)).gt.ntol) cycle detmet=candidates0(icand,2) ! Downconvert and downsample a slice of the spectrum centered on the @@ -330,7 +356,7 @@ contains endif enddo ncand=ic - + ! If FST4 and Single Decode is not checked, then find candidates within ! 20 Hz of nfqso and put them at the top of the list if(iwspr.eq.0 .and. .not.single_decode) then @@ -373,7 +399,7 @@ contains bitmetrics=0 call timer('bitmetrc',0) call get_fst4_bitmetrics(cframe,nss,nblock,nhicoh,bitmetrics, & - s4,nsync_qual,badsync) + s4,nsync_qual,badsync) call timer('bitmetrc',1) if(badsync) cycle @@ -405,7 +431,7 @@ contains iaptype=0 endif - if(itry.gt.nblock) then ! do ap passes + if(itry.gt.nblock .and. iwspr.eq.0) then ! do ap passes llr=llrs(:,nblock) ! Use largest blocksize as the basis for AP passes iaptype=naptypes(nQSOProgress,itry-nblock) if(lapcqonly) iaptype=1 @@ -428,7 +454,7 @@ contains apmask(1:58)=1 llr(1:58)=apmag*apbits(1:58) endif - + if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype .eq.6) then apmask=0 apmask(1:77)=1 @@ -438,7 +464,7 @@ contains if(iaptype.eq.6) llr(59:77)=apmag*mrr73(1:19) endif endif - + dmin=0.0 nharderrors=-1 unpk77_success=.false. @@ -448,83 +474,133 @@ contains norder=3 call timer('d240_101',0) call decode240_101(llr,Keff,maxosd,norder,apmask,message101, & - cw,ntype,nharderrors,dmin) + cw,ntype,nharderrors,dmin) call timer('d240_101',1) - elseif(iwspr.eq.1) then - maxosd=2 - call timer('d240_74 ',0) - Keff=64 - norder=4 - call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & - ntype,nharderrors,dmin) - call timer('d240_74 ',1) - endif - - if(nharderrors .ge.0) then if(count(cw.eq.1).eq.0) then nharderrors=-nharderrors cycle endif - if(iwspr.eq.0) then - write(c77,'(77i1)') mod(message101(1:77)+rvec,2) + write(c77,'(77i1)') mod(message101(1:77)+rvec,2) + call unpack77(c77,1,msg,unpk77_success) + elseif(iwspr.eq.1) then + iaptype=0 + if( donocrcdecode ) then + maxosd=1 + call timer('d240_74 ',0) + Keff=50 + norder=4 + call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & + ntype,nharderrors,dmin) + call timer('d240_74 ',1) + if(count(cw.eq.1).eq.0) then + nharderrors=-nharderrors + cycle + endif + write(c77,'(50i1)') message74(1:50) + c77(51:77)='000000000000000000000110000' call unpack77(c77,1,msg,unpk77_success) - else + if(unpk77_success) then + unpk77_success=.false. + do i=1,nwcalls + if(index(msg,trim(wcalls(i))).gt.0) then + unpk77_success=.true. + iaptype=8 + endif + enddo + endif + endif + if(.not. unpk77_success) then + maxosd=2 + call timer('d240_74 ',0) + Keff=64 + norder=3 + call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & + ntype,nharderrors,dmin) + call timer('d240_74 ',1) + if(nharderrors.lt.0) cycle + if(count(cw.eq.1).eq.0) then + nharderrors=-nharderrors + cycle + endif write(c77,'(50i1)') message74(1:50) c77(51:77)='000000000000000000000110000' call unpack77(c77,1,msg,unpk77_success) endif - if(unpk77_success) then - idupe=0 - do i=1,ndecodes - if(decodes(i).eq.msg) idupe=1 + + if(unpk77_success.and.Keff.eq.64) then + + i1=index(msg,' ') + i2=i1+index(msg(i1+1:),' ') + wpart=trim(msg(1:i2)) + + ifound=0 + do i=1,nwcalls + if(index(wcalls(i),wpart).ne.0) ifound=1 enddo - if(idupe.eq.1) goto 800 - ndecodes=ndecodes+1 - decodes(ndecodes)=msg - - if(iwspr.eq.0) then - call get_fst4_tones_from_bits(message101,itone,0) - else - call get_fst4_tones_from_bits(message74,itone,1) - endif - inquire(file='plotspec',exist=ex) - fmid=-999.0 - call timer('dopsprd ',0) - if(ex) then - call dopspread(itone,iwave,nsps,nmax,ndown,hmod, & - isbest,fc_synced,fmid,w50) - endif - call timer('dopsprd ',1) - xsig=0 - do i=1,NN - xsig=xsig+s4(itone(i),i) - enddo - base=candidates(icand,5) - arg=600.0*(xsig/base)-1.0 - if(arg.gt.0.0) then - xsnr=10*log10(arg)-35.5-12.5*log10(nsps/8200.0) - if(ntrperiod.eq. 15) xsnr=xsnr+2 - if(ntrperiod.eq. 30) xsnr=xsnr+1 - if(ntrperiod.eq. 900) xsnr=xsnr+1 - if(ntrperiod.eq.1800) xsnr=xsnr+2 - else - xsnr=-99.9 + + if(ifound.eq.0) then ! This is a new callsign + new_callsign=.true. + if(nwcalls.lt.MAXWCALLS) then + nwcalls=nwcalls+1 + wcalls(nwcalls)=wpart + else + wcalls(1:nwcalls-1)=wcalls(2:nwcalls) + wcalls(nwcalls)=wpart + endif endif + endif + endif + + if(nharderrors .ge.0 .and. unpk77_success) then + idupe=0 + do i=1,ndecodes + if(decodes(i).eq.msg) idupe=1 + enddo + if(idupe.eq.1) goto 800 + ndecodes=ndecodes+1 + decodes(ndecodes)=msg + + if(iwspr.eq.0) then + call get_fst4_tones_from_bits(message101,itone,0) else - cycle + call get_fst4_tones_from_bits(message74,itone,1) + endif + inquire(file='plotspec',exist=ex) + fmid=-999.0 + call timer('dopsprd ',0) + if(ex) then + call dopspread(itone,iwave,nsps,nmax,ndown,hmod, & + isbest,fc_synced,fmid,w50) + endif + call timer('dopsprd ',1) + xsig=0 + do i=1,NN + xsig=xsig+s4(itone(i),i) + enddo + base=candidates(icand,5) + arg=600.0*(xsig/base)-1.0 + if(arg.gt.0.0) then + xsnr=10*log10(arg)-35.5-12.5*log10(nsps/8200.0) + if(ntrperiod.eq. 15) xsnr=xsnr+2 + if(ntrperiod.eq. 30) xsnr=xsnr+1 + if(ntrperiod.eq. 900) xsnr=xsnr+1 + if(ntrperiod.eq.1800) xsnr=xsnr+2 + else + xsnr=-99.9 endif nsnr=nint(xsnr) - qual=0. + qual=0.0 + if(iaptype.eq.8) qual=1. fsig=fc_synced - 1.5*baud if(ex) then write(21,3021) nutc,icand,itry,nsyncoh,iaptype, & - ijitter,ntype,nsync_qual,nharderrors,dmin, & - sync,xsnr,xdt,fsig,w50,trim(msg) + ijitter,ntype,nsync_qual,nharderrors,dmin, & + sync,xsnr,xdt,fsig,w50,trim(msg) 3021 format(i6.6,6i3,2i4,f6.1,f7.2,f6.1,f6.2,f7.1,f7.3,1x,a) flush(21) endif call this%callback(nutc,smax1,nsnr,xdt,fsig,msg, & - iaptype,qual,ntrperiod,lwspr,fmid,w50) + iaptype,qual,ntrperiod,lwspr,fmid,w50) if(iwspr.eq.0 .and. nb.lt.0) go to 900 goto 800 endif @@ -532,9 +608,15 @@ contains enddo ! istart jitter 800 enddo !candidate list enddo ! noise blanker loop - + if(new_callsign) then ! re-write the fst4w_calls.txt file + open(42,file=trim(data_dir)//'/fst4w_calls.txt',status='unknown') + do i=1,nwcalls + write(42,'(a20)') trim(wcalls(i)) + enddo + close(42) + endif 900 return - end subroutine decode + end subroutine decode subroutine sync_fst4(cd0,i0,f0,hmod,ncoh,np,nss,ntr,fs,sync) @@ -902,6 +984,6 @@ contains enddo return - end subroutine dopspread + end subroutine dopspread end module fst4_decode From 771e71bc8434c6cc9c8eea34df942f45ec9d3134 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Sun, 13 Dec 2020 15:47:38 -0600 Subject: [PATCH 216/426] Remove some debug prints. --- lib/fst4_decode.f90 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index ccf6d9662..c1ef0ed26 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -93,20 +93,16 @@ contains if(first) then ! read the fst4_calls.txt file - write(*,*) 'data_dir is:',trim(data_dir) inquire(file=trim(data_dir)//'/fst4w_calls.txt',exist=wcalls_exists) if( wcalls_exists ) then open(42,file=trim(data_dir)//'/fst4w_calls.txt',status='unknown') - write(*,*) 'fst4w_calls.txt exists' do i=1,MAXWCALLS wcalls(i)='' read(42,fmt='(a)',end=2867) wcalls(i) wcalls(i)=adjustl(wcalls(i)) if(len(trim(wcalls(i))).eq.0) exit - write(*,*) 'record ',i,':',wcalls(i),':',len(trim(wcalls(i))) enddo 2867 nwcalls=i-1 - write(*,*) 'nwcalls ',nwcalls close(42) endif From 939e35bd2658158a1bdfa351975ddec9a0bd4166 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Mon, 14 Dec 2020 10:19:48 -0600 Subject: [PATCH 217/426] More work on K=50 decoding. --- lib/fst4_decode.f90 | 117 +++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index c1ef0ed26..34f32bf9c 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -68,8 +68,9 @@ contains integer mcq(29),mrrr(19),m73(19),mrr73(19) logical badsync,unpk77_success,single_decode - logical first,nohiscall,lwspr,ex,wcalls_exists,donocrcdecode - logical new_callsign + logical first,nohiscall,lwspr + logical new_callsign,plotspec_exists,wcalls_exists,do_nocrc_decode + logical decdata_exists integer*2 iwave(30*60*12000) @@ -234,15 +235,15 @@ contains if(ndepth.eq.3) then nblock=4 jittermax=2 - donocrcdecode=.true. + do_nocrc_decode=.true. elseif(ndepth.eq.2) then nblock=4 jittermax=2 - donocrcdecode=.false. + do_nocrc_decode=.false. elseif(ndepth.eq.1) then nblock=4 jittermax=0 - donocrcdecode=.false. + do_nocrc_decode=.false. endif ndropmax=1 @@ -479,52 +480,24 @@ contains write(c77,'(77i1)') mod(message101(1:77)+rvec,2) call unpack77(c77,1,msg,unpk77_success) elseif(iwspr.eq.1) then - iaptype=0 - if( donocrcdecode ) then - maxosd=1 - call timer('d240_74 ',0) - Keff=50 - norder=4 - call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & - ntype,nharderrors,dmin) - call timer('d240_74 ',1) - if(count(cw.eq.1).eq.0) then - nharderrors=-nharderrors - cycle - endif - write(c77,'(50i1)') message74(1:50) - c77(51:77)='000000000000000000000110000' - call unpack77(c77,1,msg,unpk77_success) - if(unpk77_success) then - unpk77_success=.false. - do i=1,nwcalls - if(index(msg,trim(wcalls(i))).gt.0) then - unpk77_success=.true. - iaptype=8 - endif - enddo - endif +! First try decoding with Keff=64 + maxosd=2 + call timer('d240_74 ',0) + Keff=64 + norder=3 + call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & + ntype,nharderrors,dmin) + call timer('d240_74 ',1) + if(nharderrors.lt.0) goto 3465 + if(count(cw.eq.1).eq.0) then + nharderrors=-nharderrors + cycle endif - if(.not. unpk77_success) then - maxosd=2 - call timer('d240_74 ',0) - Keff=64 - norder=3 - call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & - ntype,nharderrors,dmin) - call timer('d240_74 ',1) - if(nharderrors.lt.0) cycle - if(count(cw.eq.1).eq.0) then - nharderrors=-nharderrors - cycle - endif - write(c77,'(50i1)') message74(1:50) - c77(51:77)='000000000000000000000110000' - call unpack77(c77,1,msg,unpk77_success) - endif - - if(unpk77_success.and.Keff.eq.64) then - + write(c77,'(50i1)') message74(1:50) + c77(51:77)='000000000000000000000110000' + call unpack77(c77,1,msg,unpk77_success) + if(unpk77_success) then +! If decode was obtained with Keff=64, save call/grid in fst4w_calls.txt if not there already. i1=index(msg,' ') i2=i1+index(msg(i1+1:),' ') wpart=trim(msg(1:i2)) @@ -545,6 +518,36 @@ contains endif endif endif +3465 continue + +! If no decode then try Keff=50 + iaptype=0 + if( .not. unpk77_success .and. do_nocrc_decode ) then + maxosd=1 + call timer('d240_74 ',0) + Keff=50 + norder=4 + call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & + ntype,nharderrors,dmin) + call timer('d240_74 ',1) + if(count(cw.eq.1).eq.0) then + nharderrors=-nharderrors + cycle + endif + write(c77,'(50i1)') message74(1:50) + c77(51:77)='000000000000000000000110000' + call unpack77(c77,1,msg,unpk77_success) +! No CRC in this mode, so only accept the decode if call/grid have been seen before + if(unpk77_success) then + unpk77_success=.false. + do i=1,nwcalls + if(index(msg,trim(wcalls(i))).gt.0) then + unpk77_success=.true. + endif + enddo + endif + endif + endif if(nharderrors .ge.0 .and. unpk77_success) then @@ -561,10 +564,10 @@ contains else call get_fst4_tones_from_bits(message74,itone,1) endif - inquire(file='plotspec',exist=ex) + inquire(file='plotspec',exist=plotspec_exists) fmid=-999.0 call timer('dopsprd ',0) - if(ex) then + if(plotspec_exists) then call dopspread(itone,iwave,nsps,nmax,ndown,hmod, & isbest,fc_synced,fmid,w50) endif @@ -586,13 +589,13 @@ contains endif nsnr=nint(xsnr) qual=0.0 - if(iaptype.eq.8) qual=1. fsig=fc_synced - 1.5*baud - if(ex) then + inquire(file='decdata',exist=decdata_exists) + if(decdata_exists) then write(21,3021) nutc,icand,itry,nsyncoh,iaptype, & - ijitter,ntype,nsync_qual,nharderrors,dmin, & + ijitter,ntype,Keff,nsync_qual,nharderrors,dmin, & sync,xsnr,xdt,fsig,w50,trim(msg) -3021 format(i6.6,6i3,2i4,f6.1,f7.2,f6.1,f6.2,f7.1,f7.3,1x,a) +3021 format(i6.6,6i3,3i4,f6.1,f9.2,f6.1,f6.2,f7.1,f7.3,1x,a) flush(21) endif call this%callback(nutc,smax1,nsnr,xdt,fsig,msg, & @@ -604,6 +607,7 @@ contains enddo ! istart jitter 800 enddo !candidate list enddo ! noise blanker loop + if(new_callsign) then ! re-write the fst4w_calls.txt file open(42,file=trim(data_dir)//'/fst4w_calls.txt',status='unknown') do i=1,nwcalls @@ -611,6 +615,7 @@ contains enddo close(42) endif + 900 return end subroutine decode From 2960adc5572b2c32fdb3a57694bf06ab5ef9ac39 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Mon, 14 Dec 2020 16:25:37 -0600 Subject: [PATCH 218/426] FST4W: Use K=66 for first OSD decode attempt and for updating fst4w_calls.txt. Use K=50 for 2nd attempt. --- lib/fst4_decode.f90 | 49 ++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index 34f32bf9c..d4cb47a8b 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -69,7 +69,7 @@ contains logical badsync,unpk77_success,single_decode logical first,nohiscall,lwspr - logical new_callsign,plotspec_exists,wcalls_exists,do_nocrc_decode + logical new_callsign,plotspec_exists,wcalls_exists,do_k50_decode logical decdata_exists integer*2 iwave(30*60*12000) @@ -232,18 +232,19 @@ contains allocate( cframe(0:160*nss-1) ) jittermax=2 + do_k50_decode=.false. if(ndepth.eq.3) then nblock=4 jittermax=2 - do_nocrc_decode=.true. + do_k50_decode=.true. elseif(ndepth.eq.2) then nblock=4 jittermax=2 - do_nocrc_decode=.false. + do_k50_decode=.false. elseif(ndepth.eq.1) then nblock=4 jittermax=0 - do_nocrc_decode=.false. + do_k50_decode=.false. endif ndropmax=1 @@ -480,10 +481,10 @@ contains write(c77,'(77i1)') mod(message101(1:77)+rvec,2) call unpack77(c77,1,msg,unpk77_success) elseif(iwspr.eq.1) then -! First try decoding with Keff=64 +! Try decoding with Keff=66 maxosd=2 call timer('d240_74 ',0) - Keff=64 + Keff=66 norder=3 call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & ntype,nharderrors,dmin) @@ -496,25 +497,27 @@ contains write(c77,'(50i1)') message74(1:50) c77(51:77)='000000000000000000000110000' call unpack77(c77,1,msg,unpk77_success) - if(unpk77_success) then -! If decode was obtained with Keff=64, save call/grid in fst4w_calls.txt if not there already. + if(unpk77_success .and. do_k50_decode) then +! If decode was obtained with Keff=66, save call/grid in fst4w_calls.txt if not there already. i1=index(msg,' ') i2=i1+index(msg(i1+1:),' ') wpart=trim(msg(1:i2)) +! Only save callsigns/grids from type 1 messages + if(index(wpart,'/').eq.0 .and. index(wpart,'<').eq.0) then + ifound=0 + do i=1,nwcalls + if(index(wcalls(i),wpart).ne.0) ifound=1 + enddo - ifound=0 - do i=1,nwcalls - if(index(wcalls(i),wpart).ne.0) ifound=1 - enddo - - if(ifound.eq.0) then ! This is a new callsign - new_callsign=.true. - if(nwcalls.lt.MAXWCALLS) then - nwcalls=nwcalls+1 - wcalls(nwcalls)=wpart - else - wcalls(1:nwcalls-1)=wcalls(2:nwcalls) - wcalls(nwcalls)=wpart + if(ifound.eq.0) then ! This is a new callsign + new_callsign=.true. + if(nwcalls.lt.MAXWCALLS) then + nwcalls=nwcalls+1 + wcalls(nwcalls)=wpart + else + wcalls(1:nwcalls-1)=wcalls(2:nwcalls) + wcalls(nwcalls)=wpart + endif endif endif endif @@ -522,7 +525,7 @@ contains ! If no decode then try Keff=50 iaptype=0 - if( .not. unpk77_success .and. do_nocrc_decode ) then + if( .not. unpk77_success .and. do_k50_decode ) then maxosd=1 call timer('d240_74 ',0) Keff=50 @@ -608,7 +611,7 @@ contains 800 enddo !candidate list enddo ! noise blanker loop - if(new_callsign) then ! re-write the fst4w_calls.txt file + if(new_callsign .and. do_k50_decode) then ! re-write the fst4w_calls.txt file open(42,file=trim(data_dir)//'/fst4w_calls.txt',status='unknown') do i=1,nwcalls write(42,'(a20)') trim(wcalls(i)) From 99377e4c24b6b571262fd521a2275e32d8389deb Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 15 Dec 2020 09:31:29 -0500 Subject: [PATCH 219/426] Measure avg and rms of estimated SNR values, for comparison with generated SNRs. --- lib/test_q65.f90 | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index d8c1f0763..7cc832054 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -116,11 +116,19 @@ program test_q65 call sec0(1,tdec) open(10,file='junk',status='unknown') n=0 + snrsum=0. + snrsq=0. + nsum=0 do iline=1,9999 read(10,'(a71)',end=10) line if(index(line,' Date: Tue, 15 Dec 2020 09:32:24 -0500 Subject: [PATCH 220/426] Several minor tweaks to Q65 decoding. --- lib/q65_decode.f90 | 2 +- lib/qra/q65/q65_loops.f90 | 5 +++-- lib/spec64.f90 | 19 ++----------------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 0473b7119..6bfd28c27 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -143,7 +143,7 @@ contains nFadingModel,ndepth,jpk0,xdt,f0,iaptype,apmask,apsymbols, & xdt1,f1,snr2,dat4,id2) call timer('q65loops',1) - snr2=snr2 + db(6912.0/nsps) +! snr2=snr2 + db(6912.0/nsps) if(id2.gt.0) exit enddo diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index b440e076f..0f83d7275 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -29,7 +29,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & idfmax=3 idtmax=3 - ibwmin=1 + ibwmin=0 ibwmax=2 maxdist=5 if(iand(ndepth,3).ge.2) then @@ -70,7 +70,8 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & ndist=ndf**2 + ndt**2 + ((nbw-2))**2 if(ndist.gt.maxdist) cycle ! b90=1.728**ibw - b90=3.0**nbw + b90=3.0**nbw !### Mult by ~baud/3.33 ??? ### + if(nbw.eq.0) b90=baud if(b90.gt.230.0) cycle call timer('q65_intr',0) b90ts = b90/baud diff --git a/lib/spec64.f90 b/lib/spec64.f90 index fe518d747..cf43994dc 100644 --- a/lib/spec64.f90 +++ b/lib/spec64.f90 @@ -11,7 +11,6 @@ subroutine spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ nfft=nsps - fac=1.0/nfft if(mode.eq.64) then do j=1,NN @@ -19,7 +18,7 @@ subroutine spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) if(j.ge.33) jj=j+14 !Skip middle Costas array ja=jpk + (jj-1)*nfft jb=ja+nfft-1 - cs(0:nfft-1)=fac*c0(ja:jb) + cs(0:nfft-1)=c0(ja:jb) call four2a(cs,nfft,1,-1,1) do ii=1,LL i=ii-65 @@ -38,7 +37,7 @@ subroutine spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) j=j+1 ja=(k-1)*nsps + jpk jb=ja+nsps-1 - cs(0:nfft-1)=fac*c0(ja:jb) + cs(0:nfft-1)=c0(ja:jb) call four2a(cs,nsps,1,-1,1) !c2c FFT to frequency do ii=1,LL i=ii-65+mode64 !mode64 = 1 2 4 8 16 for Q65 A B C D E @@ -64,19 +63,5 @@ subroutine spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) s3(i,1:NN)=s3(i,1:NN)/(xbase(i)+0.001) !Apply frequency equalization enddo -! print*,'a',LL,NN,jpk,mode,mode64 -! df=6000.0/nfft -! do i=1,LL -! write(71,3071) i,i-65,i*df,(s3(i,j),j=1,4) -!3071 format(2i8,f10.3,4e12.3) -! enddo - -! do j=1,NN -! ipk1=maxloc(s3(1:LL,j)) -! m=ipk1(1)-65 -! write(72,3072) j,m,m/2,m/4,m/8,m/16,m/32,m/64 -!3072 format(8i7) -! enddo - return end subroutine spec64 From 187868513401ce49ef74cb41b1b431313abe2437 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Tue, 15 Dec 2020 09:46:16 -0600 Subject: [PATCH 221/426] If file decdata is present in the data directory, then write detailed decoder data to file fst4_decodes.dat in the same directory. --- lib/fst4_decode.f90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index d4cb47a8b..d95f6d04d 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -593,13 +593,14 @@ contains nsnr=nint(xsnr) qual=0.0 fsig=fc_synced - 1.5*baud - inquire(file='decdata',exist=decdata_exists) + inquire(file=trim(data_dir)//'/decdata',exist=decdata_exists) if(decdata_exists) then + open(21,file=trim(data_dir)//'/fst4_decodes.dat',status='unknown') write(21,3021) nutc,icand,itry,nsyncoh,iaptype, & ijitter,ntype,Keff,nsync_qual,nharderrors,dmin, & sync,xsnr,xdt,fsig,w50,trim(msg) 3021 format(i6.6,6i3,3i4,f6.1,f9.2,f6.1,f6.2,f7.1,f7.3,1x,a) - flush(21) + close(21) endif call this%callback(nutc,smax1,nsnr,xdt,fsig,msg, & iaptype,qual,ntrperiod,lwspr,fmid,w50) From 1e5578b7046376dd15df471d57a73b2a9235d17a Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Tue, 15 Dec 2020 09:52:50 -0600 Subject: [PATCH 222/426] Do not save c2 files in FST4W mode. --- widgets/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 8075f6b52..d8e80d6bd 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -1609,7 +1609,7 @@ void MainWindow::dataSink(qint64 frames) m_saveWAVWatcher.setFuture (QtConcurrent::run (std::bind (&MainWindow::save_wave_file, this, m_fnameWE, &dec_data.d2[0], samples, m_config.my_callsign(), m_config.my_grid(), m_mode, m_nSubMode, m_freqNominal, m_hisCall, m_hisGrid))); - if (m_mode=="WSPR" or m_mode=="FST4W") { + if (m_mode=="WSPR") { QString c2name_string {m_fnameWE + ".c2"}; int len1=c2name_string.length(); char c2name[80]; From 841bbac56edf3cf8aa2a636683348712ce32bf3f Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 15 Dec 2020 11:03:06 -0500 Subject: [PATCH 223/426] Increase the Q65 list decodes to include reports -50 to +49 dB. --- lib/q65_decode.f90 | 2 +- lib/q65_sync.f90 | 2 +- lib/qra/q65/q65_set_list.f90 | 27 ++++++++++++++++----------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 6bfd28c27..507ce29e3 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -56,7 +56,7 @@ contains integer apmask1(78),apsymbols1(78) integer apmask(13),apsymbols(13) integer dgen(13) - integer codewords(63,64) + integer codewords(63,206) logical lapcqonly,unpk77_success complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 72f8a8f3b..1a387b2a3 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -20,7 +20,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & integer*2 iwave(0:nmax-1) !Raw data integer isync(22) !Indices of sync symbols integer itone(85) - integer codewords(63,64) + integer codewords(63,206) integer dat4(13) integer ijpk(2) logical unpk77_success diff --git a/lib/qra/q65/q65_set_list.f90 b/lib/qra/q65/q65_set_list.f90 index 4e67e0ff4..44b2e12cb 100644 --- a/lib/qra/q65/q65_set_list.f90 +++ b/lib/qra/q65/q65_set_list.f90 @@ -1,9 +1,10 @@ subroutine q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) + parameter (MAX_NCW=206) character*12 mycall,hiscall character*6 hisgrid character*37 msg0,msg,msgsent - integer codewords(63,64) + integer codewords(63,MAX_NCW) integer itone(85) integer isync(22) data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ @@ -11,22 +12,26 @@ subroutine q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) ncw=0 if(hiscall(1:1).eq. ' ') return - ncw=58 + ncw=MAX_NCW msg0=trim(mycall)//' '//trim(hiscall) j0=len(trim(msg0))+2 - isnr0=-35 do i=1,ncw msg=msg0 if(i.eq.2) msg(j0:j0+2)='RRR' if(i.eq.3) msg(j0:j0+3)='RR73' if(i.eq.4) msg(j0:j0+1)='73' - if(i.ge.5 .and. i.le.56) then - isnr=isnr0 + (i-5)/2 - if(iand(i,1).eq.1) write(msg(j0:j0+2),'(i3.2)') isnr - if(iand(i,1).eq.0) write(msg(j0:j0+3),'("R",i3.2)') isnr + if(i.eq.5) msg='CQ '//trim(hiscall)//' '//hisgrid(1:4) + if(i.eq.6) msg(j0:j0+3)=hisgrid(1:4) + if(i.ge.7 .and. i.le.206) then + isnr = -50 + (i-7)/2 + if(iand(i,1).eq.1) then + write(msg(j0:j0+2),'(i3.2)') isnr + if(msg(j0:j0).eq.' ') msg(j0:j0)='+' + else + write(msg(j0:j0+3),'("R",i3.2)') isnr + if(msg(j0+1:j0+1).eq.' ') msg(j0+1:j0+1)='+' + endif endif - if(i.eq.57) msg='CQ '//trim(hiscall)//' '//hisgrid(1:4) - if(i.eq.58) msg(j0:j0+3)=hisgrid(1:4) call genq65(msg,0,msgsent,itone,i3,n3) i0=1 j=0 @@ -38,8 +43,8 @@ subroutine q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) j=j+1 codewords(j,i)=itone(k) - 1 enddo -! write(*,3001) i,isnr,codewords(1:13,i),trim(msg) -!3001 format(i2,2x,i3.2,2x,13i3,2x,a) +! write(71,3001) i,isnr,codewords(1:13,i),trim(msg) +!3001 format(i3,2x,i3.2,2x,13i3,2x,a) enddo return From c87926e657d9e2f0becda815f89a5102acf91333 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Tue, 15 Dec 2020 10:32:57 -0600 Subject: [PATCH 224/426] Append decoder data to file fst4_decodes.dat instead of overwriting. --- lib/fst4_decode.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index d95f6d04d..3407e41f2 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -595,7 +595,7 @@ contains fsig=fc_synced - 1.5*baud inquire(file=trim(data_dir)//'/decdata',exist=decdata_exists) if(decdata_exists) then - open(21,file=trim(data_dir)//'/fst4_decodes.dat',status='unknown') + open(21,file=trim(data_dir)//'/fst4_decodes.dat',status='unknown',position='append') write(21,3021) nutc,icand,itry,nsyncoh,iaptype, & ijitter,ntype,Keff,nsync_qual,nharderrors,dmin, & sync,xsnr,xdt,fsig,w50,trim(msg) From 0ee61205aa81050e0b33032b54549fbe4a154bd3 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 15 Dec 2020 11:37:48 -0500 Subject: [PATCH 225/426] Disable diagnostic writing to 'fort.55' in CWD. --- lib/q65_sync.f90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 1a387b2a3..59f70e68a 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -186,11 +186,11 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & 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 - open(55,file='fort.55',status='unknown',position='append') - write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & - irc,trim(decoded) -3055 format(i6,i3,6f8.2,i5,2x,a) - close(55) +! open(55,file='fort.55',status='unknown',position='append') +! write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & +! irc,trim(decoded) +!3055 format(i6,i3,6f8.2,i5,2x,a) +! close(55) go to 900 endif enddo From a47ec75f562ef068ecf9e7a6824675e45cb09944 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 15 Dec 2020 13:17:31 -0500 Subject: [PATCH 226/426] Reformat the test_q65 headings and output. --- lib/test_q65.f90 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 7cc832054..73602c3a0 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -95,7 +95,8 @@ program test_q65 write(*,1010) (j,j=1,6) write(12,1010) (j,j=1,6) -1010 format(' SNR Mode d Dop Sync DecN Dec1 Bad',6i5,' tdec'/75('-')) +1010 format(' SNR Mode d Dop Sync Avg Dec Bad',6i4,' tdec avg rms'/ & + 75('-')) dterr=tsym/4.0 nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) @@ -154,11 +155,11 @@ program test_q65 snr_avg=snrsum/nsum snr_rms=sqrt(snrsq/nsum - snr_avg**2) endif - write(*,1100) snr1,ntrperiod,csubmode,ndepth,fDop,nsync,ndecn, & + write(*,1100) snr1,ntrperiod,csubmode,ndepth,nint(fDop),nsync,ndecn, & ndec1,nfalse,naptype,tdec/nfiles,snr_avg,snr_rms - write(12,1100) snr1,ntrperiod,csubmode,ndepth,fDop,nsync,ndecn, & + write(12,1100) snr1,ntrperiod,csubmode,ndepth,nint(fDop),nsync,ndecn, & ndec1,nfalse,naptype,tdec/nfiles,snr_avg,snr_rms -1100 format(f5.1,i4,1x,a1,i3,f5.0,3i5,i4,i6,5i5,f6.2,f6.1,f5.1) +1100 format(f5.1,i4,a1,i3,5i4,i5,5i4,f6.2,f6.1,f5.1) if(ndec1.lt.nfiles/2 .and. ndec1z.ge.nfiles/2) then snr_thresh=snr1 + float(nfiles/2 - ndec1)/(ndec1z-ndec1) open(13,file='snr_thresh.out',status='unknown',position='append') From 7051dc7ee6dbc6c6330f5275c4e225cd3dec71a2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 15 Dec 2020 14:48:27 -0500 Subject: [PATCH 227/426] Remove more vestiges of the obsolete "JT9+JT65" mode. --- widgets/mainwindow.cpp | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index e17979bae..cde8eff8a 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -1398,7 +1398,7 @@ void MainWindow::fixStop() } else if (m_mode=="JT9"){ m_hsymStop=173; if(m_config.decode_at_52s()) m_hsymStop=179; - } else if (m_mode=="JT65" or m_mode=="JT9+JT65"){ + } else if (m_mode=="JT65"){ m_hsymStop=174; if(m_config.decode_at_52s()) m_hsymStop=179; } else if (m_mode=="QRA64"){ @@ -2393,8 +2393,6 @@ void MainWindow::setup_status_bar (bool vhf) mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #cc99ff}"); } else if ("Echo" == m_mode) { mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #66ffff}"); - } else if ("JT9+JT65" == m_mode) { - mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ffff66}"); } else if ("JT65" == m_mode) { mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #66ff66}"); } else if ("QRA64" == m_mode) { @@ -3125,7 +3123,7 @@ void MainWindow::decode() //decode() if(m_mode=="FT8" and SpecOp::HOUND == m_config.special_op_id() and !ui->cbRxAll->isChecked()) dec_data.params.nfb=1000; if(m_mode=="FT8" and SpecOp::FOX == m_config.special_op_id() ) dec_data.params.nfqso=200; dec_data.params.ntol=ui->sbFtol->value (); - if(m_mode=="JT9+JT65" or !m_config.enable_VHF_features()) { + if(!m_config.enable_VHF_features()) { dec_data.params.ntol=20; dec_data.params.naggressive=0; } @@ -3152,7 +3150,6 @@ void MainWindow::decode() //decode() if(m_mode=="QRA64") dec_data.params.ntxmode=164; if(m_mode=="Q65") dec_data.params.nmode=66; if(m_mode=="Q65") dec_data.params.ntxmode=66; - if(m_mode=="JT9+JT65") dec_data.params.nmode=9+65; // = 74 if(m_mode=="JT4") { dec_data.params.nmode=4; dec_data.params.ntxmode=4; @@ -3686,10 +3683,6 @@ void MainWindow::pskPost (DecodedText const& decodedtext) if (m_diskData || !m_config.spot_to_psk_reporter() || decodedtext.isLowConfidence ()) return; QString msgmode=m_mode; - if(m_mode=="JT9+JT65") { - msgmode="JT9"; - if (decodedtext.isJT65()) msgmode="JT65"; - } QString deCall; QString grid; decodedtext.deCallAndGrid(/*out*/deCall,grid); @@ -4773,8 +4766,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie auto const& parts = message.string ().split (' ', SkipEmptyParts); if (parts.size () < 5) return; auto const& mode = parts.at (4).left (1); - if (("JT9+JT65" == m_mode && !("@" == mode || "#" == mode)) - || ("JT65" == m_mode && mode != "#") + if (("JT65" == m_mode && mode != "#") || ("JT9" == m_mode && mode != "@") || ("MSK144" == m_mode && !("&" == mode || "^" == mode)) || ("QRA64" == m_mode && mode.left (1) != ":") @@ -4848,19 +4840,9 @@ 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()) - { - m_modeTx="JT9"; - m_wideGraph->setModeTx(m_modeTx); - } else if (message.isJT65()) { - m_modeTx="JT65"; - m_wideGraph->setModeTx(m_modeTx); - } - } else if ((message.isJT9 () and m_modeTx != "JT9" and m_mode != "JT4") or + if ((message.isJT9 () and m_modeTx != "JT9" and m_mode != "JT4") or (message.isJT65 () and m_modeTx != "JT65" and m_mode != "JT4")) { - // if we are not allowing mode change then don't process decode + // We are not allowing mode change, so don't process decode return; } From 17195680ee673f3c7cbc5868f9562226d66fa1fb Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Tue, 15 Dec 2020 14:31:46 -0600 Subject: [PATCH 228/426] Don't waste time on excess BP iterations when doing K=50. Decode a little deeper. --- lib/fst4/decode240_74.f90 | 5 +++++ lib/fst4/fastosd240_74.f90 | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/fst4/decode240_74.f90 b/lib/fst4/decode240_74.f90 index 20a83362f..f5aac2e10 100644 --- a/lib/fst4/decode240_74.f90 +++ b/lib/fst4/decode240_74.f90 @@ -25,6 +25,8 @@ subroutine decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw,ntype,nharder include "ldpc_240_74_parity.f90" maxiterations=30 + if(Keff.eq.50) maxiterations=1 + nosd=0 if(maxosd.gt.3) maxosd=3 if(maxosd.eq.0) then ! osd with channel llrs @@ -36,6 +38,8 @@ subroutine decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw,ntype,nharder nosd=0 endif + if(maxosd.eq.0) goto 73 + toc=0 tov=0 tanhtoc=0 @@ -133,6 +137,7 @@ subroutine decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw,ntype,nharder enddo ! bp iterations +73 continue do i=1,nosd zn=zsave(:,i) ! call osd240_74(zn,Keff,apmask,norder,message74,cw,nharderror,dminosd) diff --git a/lib/fst4/fastosd240_74.f90 b/lib/fst4/fastosd240_74.f90 index 03ae4071c..f4bb61d60 100644 --- a/lib/fst4/fastosd240_74.f90 +++ b/lib/fst4/fastosd240_74.f90 @@ -181,8 +181,8 @@ subroutine fastosd240_74(llr,k,apmask,ndeep,message74,cw,nhardmin,dmin) nsyncmax=11 elseif(ndeep.eq.4) then nord=4 - xlambda=3.5 - nsyndmax=11 + xlambda=3.4 + nsyndmax=12 endif s1=sum(absrx(1:k)) From 6b8abe32b1fa6f8acd1242685343847af5d86dae Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 16 Dec 2020 15:51:46 -0500 Subject: [PATCH 229/426] One more protection against too-large FTol. --- lib/q65_sync.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 59f70e68a..90c326230 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -201,6 +201,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & irc=-2 dat4=0 ia=ntol/df + if(i0-ia.lt.1 .or. i0+ia.gt.iz) go to 900 do lag=lag1,lag2 do k=1,85 n=NSTEP*(k-1) + 1 From 8507a42655b76d8ca4d3a70cc7e640c24e0a0e38 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 17 Dec 2020 17:02:28 -0500 Subject: [PATCH 230/426] Quasi-graphical display of the sync ccf(f0,DT). --- lib/decoder.f90 | 4 ++-- lib/q65_sync.f90 | 41 +++++++++++++++++++++++++++++++++++++++-- widgets/mainwindow.cpp | 21 +++++++++++++++++++-- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index cb695e708..6499ac673 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -203,8 +203,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample) call timer('dec_q65 ',0) call my_q65%decode(q65_decoded,id2,params%nutc,params%ntr, & params%nsubmode,params%nfqso,params%ntol,params%ndepth, & - mycall,hiscall,hisgrid,params%nQSOProgress,ncontest, & - logical(params%lapcqonly)) + params%emedelay,mycall,hiscall,hisgrid,params%nQSOProgress, & + ncontest,logical(params%lapcqonly)) call timer('dec_q65 ',1) go to 800 endif diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 90c326230..2d042802d 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -1,5 +1,5 @@ -subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & - xdt,f0,snr1,dat4,snr2,id1) +subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & + emedelay,xdt,f0,snr1,dat4,snr2,id1) ! Detect and align with the Q65 sync vector, returning time and frequency ! offsets and SNR estimate. @@ -227,6 +227,43 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & rms=sqrt(sq/nsq) smax=ccf(ipk,jpk) snr1=smax/rms + call zplot_q65(ccf(-ia:ia,lag1:lag2),ia,lag1,lag2,rms,dtstep,ntol,emedelay) 900 return end subroutine q65_sync + +subroutine zplot_q65(ccf,ia,lag1,lag2,rms,dtstep,ntol,emedelay) + + real ccf(-ia:ia,lag1:lag2) + character*1 line(70),mark(0:6) + character*35 blanks + data mark/' ',' ','.','-','+','X','#'/ + data blanks/' '/ + +! open(35,file='ccf.dat',status='unknown',access='stream') +! write(35) ia,lag1,lag2,rms,dtstep +! write(35) ccf +! close(35) + open(34,file='ccf.txt',status='unknown') + write(34,1000) -ntol,blanks(1:ia-2),0,blanks(1:ia-2),ntol +1000 format(5x,i3,a,i1,a,i3) + k=0 + do j=lag2,lag1,-1 + t=j*dtstep + if(emedelay.eq.0.0 .and. abs(t).gt.1.0) cycle + do i=-ia,ia + k=i+ia+2 + n=ccf(i,j)/rms + if(n.lt.0) n=0 + if(n.gt.6) n=6 + line(k)=mark(n) + enddo + line(1)='|' + line(k+1)='|' + write(34,1010) t,line(1:k+1) +1010 format(f5.2,1x,72a1) + enddo + close(34) + + return +end subroutine zplot_q65 diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index cde8eff8a..1f9ed0198 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3306,6 +3306,22 @@ void MainWindow::decodeDone () { if(m_mode!="FT8" or dec_data.params.nzhsym==50) m_nDecodes=0; if(m_mode=="QRA64") m_wideGraph->drawRed(0,0); + + if(m_mode=="Q65" and m_msgAvgWidget!=NULL) { + if(m_msgAvgWidget->isVisible()) { +// QFile f(m_config.temp_dir ().absoluteFilePath ("ccf.txt")); + + QFont font("Courier New",7); + m_msgAvgWidget->changeFont(font); + QFile f("ccf.txt"); + if(f.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream s(&f); + QString t=s.readAll(); + m_msgAvgWidget->displayAvg(t); + } + } + } + if ("FST4W" == m_mode) { if (m_uploadWSPRSpots @@ -3591,7 +3607,8 @@ void MainWindow::readFromStdout() //readFromStdout } else { if (stdMsg && okToPost) pskPost(decodedtext); } - if((m_mode=="JT4" or m_mode=="JT65" or m_mode=="QRA64") and m_msgAvgWidget!=NULL) { + if((m_mode=="JT4" or m_mode=="JT65" or m_mode=="QRA64" or m_mode=="Q65") and + m_msgAvgWidget!=NULL) { if(m_msgAvgWidget->isVisible()) { QFile f(m_config.temp_dir ().absoluteFilePath ("avemsg.txt")); if(f.open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -4336,7 +4353,7 @@ void MainWindow::guiUpdate() //Once per second (onesec) if(nsec != m_sec0) { -// qDebug() << "AAA" << nsec; +// qDebug() << "AAA" << nsec; if(m_mode=="FST4") chk_FST4_freq_range(); m_currentBand=m_config.bands()->find(m_freqNominal); if( SpecOp::HOUND == m_config.special_op_id() ) { From d165532dc562db095aa81c3e377aa6fd8cf73336 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 17 Dec 2020 19:06:07 -0500 Subject: [PATCH 231/426] Oops! Committed the wrong version of q65_decode.f90. --- lib/q65_decode.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 507ce29e3..538ecf698 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -26,7 +26,7 @@ module q65_decode contains subroutine decode(this,callback,iwave,nutc,ntrperiod,nsubmode,nfqso, & - ntol,ndepth,mycall,hiscall,hisgrid,nQSOprogress,ncontest,lapcqonly) + ntol,ndepth,emedelay,mycall,hiscall,hisgrid,nQSOprogress,ncontest,lapcqonly) ! Decodes Q65 signals ! Input: iwave Raw data, i*2 @@ -94,7 +94,7 @@ contains call q65_enc(dgen,codewords) !Initialize Q65 call timer('sync_q65',0) call q65_sync(nutc,iwave,ntrperiod*12000,mode65,codewords,ncw,nsps, & - nfqso,ntol,xdt,f0,snr1,dat4,snr2,id1) + nfqso,ntol,emedelay,xdt,f0,snr1,dat4,snr2,id1) call timer('sync_q65',1) if(id1.eq.1) then xdt1=xdt From 5a9a2c8279d544f20afdf93f28ad050af7d6f57a Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 18 Dec 2020 10:18:29 -0500 Subject: [PATCH 232/426] Move Q65 default 6m frequency to 50.275. --- models/FrequencyList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/FrequencyList.cpp b/models/FrequencyList.cpp index b12754139..ff3602f3c 100644 --- a/models/FrequencyList.cpp +++ b/models/FrequencyList.cpp @@ -264,7 +264,7 @@ namespace {50200000, Modes::Echo, IARURegions::ALL}, {50270000, Modes::QRA64, IARURegions::ALL}, - {50270000, Modes::Q65, IARURegions::ALL}, + {50275000, Modes::Q65, IARURegions::ALL}, {50276000, Modes::JT65, IARURegions::R2}, {50276000, Modes::JT65, IARURegions::R3}, {50380000, Modes::MSK144, IARURegions::R1}, From b3769bb6c87e53623ba024170dc0988ccdd6b136 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 18 Dec 2020 13:31:14 -0500 Subject: [PATCH 233/426] For Q65, plot a red "Sync curve" in the Wide Graph. --- lib/decoder.f90 | 1 + lib/q65_sync.f90 | 55 +++++++++++++++++++++++++++++------------- widgets/mainwindow.cpp | 5 ++-- widgets/plotter.cpp | 47 ++++++++++++++++-------------------- widgets/plotter.h | 3 +++ widgets/widegraph.cpp | 11 +++++++-- widgets/widegraph.ui | 5 ++++ 7 files changed, 80 insertions(+), 47 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 6499ac673..3c5aad72b 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -200,6 +200,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) if(params%nmode.eq.66) then !NB: JT65 = 65, Q65 = 66. ! We're in Q65 mode + open(17,file=trim(temp_dir)//'/red.dat',status='unknown') call timer('dec_q65 ',0) call my_q65%decode(q65_decoded,id2,params%nutc,params%ntr, & params%nsubmode,params%nfqso,params%ntol,params%ndepth, & diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 2d042802d..8e5481d73 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -151,11 +151,12 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & f0=nfqso + (ipk-1)*df xdt=jpk*dtstep imsg_best=imsg + ccf1=ccf(-ia:ia,jpk) endif enddo ! imsg - ia=i0+ipk-64 - ib=ia+LL-1 + i1=i0+ipk-64 + i2=i1+LL-1 j=j0+jpk-7 n=0 do k=1,85 @@ -164,7 +165,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & cycle endif n=n+1 - if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(ia:ib,j) + if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) enddo nsubmode=0 @@ -191,7 +192,12 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ! irc,trim(decoded) !3055 format(i6,i3,6f8.2,i5,2x,a) ! close(55) - go to 900 + base=(sum(ccf1(-ia:-ia+9)) + sum(ccf1(ia-9:ia)))/20.0 + ccf1=ccf1-base + smax=maxval(ccf1) + if(smax.gt.10.0) ccf1=10.0*ccf1/smax + go to 200 +! go to 900 endif enddo @@ -214,31 +220,44 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ijpk=maxloc(ccf) ipk=ijpk(1)-ia-1 jpk=ijpk(2)-53-1 + f0=nfqso + ipk*df xdt=jpk*dtstep sq=0. nsq=0 - do j=lag1,lag2 - if(abs(j-jpk).gt.6) then - sq=sq + ccf(ipk,j)**2 - nsq=nsq+1 - endif + do i=-ia,ia + do j=lag1,lag2 + if(abs(j-jpk).gt.8 .and. abs(i-ipk).gt.ia/2) then + sq=sq + ccf(i,j)**2 + nsq=nsq+1 + endif + enddo enddo rms=sqrt(sq/nsq) smax=ccf(ipk,jpk) snr1=smax/rms - call zplot_q65(ccf(-ia:ia,lag1:lag2),ia,lag1,lag2,rms,dtstep,ntol,emedelay) + ccf1=ccf(-ia:ia,jpk)/rms + if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 + +200 do i=-ia,ia + freq=nfqso + i*df + write(17,1100) freq,ccf1(i) +1100 format(2f10.3) + enddo + close(17) + if(ia.le.60) call zplot_q65(ccf(-ia:ia,lag1:lag2),ia,lag1,lag2, & + rms,dtstep,ntol,smax,emedelay) 900 return end subroutine q65_sync -subroutine zplot_q65(ccf,ia,lag1,lag2,rms,dtstep,ntol,emedelay) +subroutine zplot_q65(ccf,ia,lag1,lag2,rms,dtstep,ntol,smax,emedelay) real ccf(-ia:ia,lag1:lag2) - character*1 line(70),mark(0:6) - character*35 blanks + character*1 line(130),mark(0:6) + character*60 blanks data mark/' ',' ','.','-','+','X','#'/ - data blanks/' '/ + data blanks/' '/ ! open(35,file='ccf.dat',status='unknown',access='stream') ! write(35) ia,lag1,lag2,rms,dtstep @@ -246,14 +265,16 @@ subroutine zplot_q65(ccf,ia,lag1,lag2,rms,dtstep,ntol,emedelay) ! close(35) open(34,file='ccf.txt',status='unknown') write(34,1000) -ntol,blanks(1:ia-2),0,blanks(1:ia-2),ntol -1000 format(5x,i3,a,i1,a,i3) +1000 format(4x,i4,a,i1,a,i3) + fac=1.0 + if(smax/rms.gt.7.0) fac=7.0*rms/smax k=0 do j=lag2,lag1,-1 t=j*dtstep if(emedelay.eq.0.0 .and. abs(t).gt.1.0) cycle do i=-ia,ia k=i+ia+2 - n=ccf(i,j)/rms + n=fac*ccf(i,j)/rms if(n.lt.0) n=0 if(n.gt.6) n=6 line(k)=mark(n) @@ -261,7 +282,7 @@ subroutine zplot_q65(ccf,ia,lag1,lag2,rms,dtstep,ntol,emedelay) line(1)='|' line(k+1)='|' write(34,1010) t,line(1:k+1) -1010 format(f5.2,1x,72a1) +1010 format(f5.2,1x,132a1) enddo close(34) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 1f9ed0198..b10a25a39 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3305,7 +3305,7 @@ void MainWindow::to_jt9(qint32 n, qint32 istart, qint32 idone) void MainWindow::decodeDone () { if(m_mode!="FT8" or dec_data.params.nzhsym==50) m_nDecodes=0; - if(m_mode=="QRA64") m_wideGraph->drawRed(0,0); + if(m_mode=="QRA64" or m_mode=="Q65") m_wideGraph->drawRed(0,0); if(m_mode=="Q65" and m_msgAvgWidget!=NULL) { if(m_msgAvgWidget->isVisible()) { @@ -6442,7 +6442,8 @@ void MainWindow::on_actionQ65_triggered() //### ui->sbSubmode->setMaximum(4); ui->sbSubmode->setMaximum(7); ui->sbSubmode->setValue(m_nSubMode); - m_wideGraph->setMode(m_mode); + QString fname {QDir::toNativeSeparators(m_config.temp_dir().absoluteFilePath ("red.dat"))}; + m_wideGraph->setRedFile(fname); m_wideGraph->setMode(m_mode); m_wideGraph->setModeTx(m_modeTx); m_wideGraph->setPeriod(m_TRperiod,6912); diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 2f774f5b6..a225581fd 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -224,7 +224,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) if(i==iz-1) { painter2D.drawPolyline(LineBuf,j); - if(m_mode=="QRA64") { + if(m_mode=="QRA64" or m_mode=="Q65") { painter2D.setPen(Qt::red); painter2D.drawPolyline(LineBuf2,ktop); } @@ -269,33 +269,28 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) painter2D.drawText(x1-4,y,"73"); } - if(bRed) { - std::ifstream f; - f.open(m_redFile.toLatin1()); - if(f) { - int x,y; - float freq,sync; - float slimit=6.0; - QPen pen0(Qt::red,1); - painter1.setPen(pen0); - for(int i=0; i<99999; i++) { - f >> freq >> sync; - if(f.eof()) break; - x=XfromFreq(freq); - y=(sync-slimit)*3.0; - if(y>0) { - if(y>15.0) y=15.0; - if(x>=0 and x<=m_w) { - painter1.setPen(pen0); - painter1.drawLine(x,0,x,y); - } - } - } - f.close(); + if(bRed and m_bQ65_Sync) { + int k=0; + std::ifstream f; + f.open(m_redFile.toLatin1()); + if(f) { + int x,y; + float freq,sync; + for(int i=0; i<99999; i++) { + f >> freq >> sync; + if(f.eof()) break; + x=XfromFreq(freq); + y=m_h2*(0.9 - 0.09*gain2d*sync) - m_plot2dZero; + LineBuf2[k].setX(x); + LineBuf2[k].setY(y); + k++; } -// m_bDecodeFinished=false; + f.close(); + QPen pen0(Qt::red,2); + painter2D.setPen(pen0); + painter2D.drawPolyline(LineBuf2,k); } - + } update(); //trigger a new paintEvent m_bScaleOK=true; } diff --git a/widgets/plotter.h b/widgets/plotter.h index ac13b5408..1aeacf811 100644 --- a/widgets/plotter.h +++ b/widgets/plotter.h @@ -80,6 +80,8 @@ public: void setRxBand(QString band); void setReference(bool b) {m_bReference = b;} bool Reference() const {return m_bReference;} + void setQ65_Sync(bool b) {m_bQ65_Sync = b;} + bool Q65_Sync() const {return m_bQ65_Sync;} void drawRed(int ia, int ib, float swide[]); void setVHF(bool bVHF); void setRedFile(QString fRed); @@ -113,6 +115,7 @@ private: bool m_bLinearAvg; bool m_bReference; bool m_bReference0; + bool m_bQ65_Sync; bool m_bVHF; bool m_bSingleDecode; diff --git a/widgets/widegraph.cpp b/widgets/widegraph.cpp index a369f6296..75064f776 100644 --- a/widgets/widegraph.cpp +++ b/widgets/widegraph.cpp @@ -75,10 +75,12 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) : ui->widePlot->setCumulative(m_settings->value("Cumulative",true).toBool()); ui->widePlot->setLinearAvg(m_settings->value("LinearAvg",false).toBool()); ui->widePlot->setReference(m_settings->value("Reference",false).toBool()); + ui->widePlot->setQ65_Sync(m_settings->value("Q65_Sync",false).toBool()); if(ui->widePlot->current()) ui->spec2dComboBox->setCurrentIndex(0); if(ui->widePlot->cumulative()) ui->spec2dComboBox->setCurrentIndex(1); if(ui->widePlot->linearAvg()) ui->spec2dComboBox->setCurrentIndex(2); if(ui->widePlot->Reference()) ui->spec2dComboBox->setCurrentIndex(3); + if(ui->widePlot->Q65_Sync()) ui->spec2dComboBox->setCurrentIndex(4); int nbpp=m_settings->value("BinsPerPixel",2).toInt(); ui->widePlot->setBinsPerPixel(nbpp); ui->sbPercent2dPlot->setValue(m_Percent2DScreen); @@ -135,6 +137,7 @@ void WideGraph::saveSettings() //saveS m_settings->setValue ("Cumulative", ui->widePlot->cumulative()); m_settings->setValue ("LinearAvg", ui->widePlot->linearAvg()); m_settings->setValue ("Reference", ui->widePlot->Reference()); + m_settings->setValue ("Q65_Sync", ui->widePlot->Q65_Sync()); m_settings->setValue ("BinsPerPixel", ui->widePlot->binsPerPixel ()); m_settings->setValue ("StartFreq", ui->widePlot->startFreq ()); m_settings->setValue ("WaterfallPalette", m_waterfallPalette); @@ -321,6 +324,7 @@ void WideGraph::on_spec2dComboBox_currentIndexChanged(int index) ui->widePlot->setCumulative(false); ui->widePlot->setLinearAvg(false); ui->widePlot->setReference(false); + ui->widePlot->setQ65_Sync(false); ui->smoSpinBox->setEnabled(false); switch (index) { @@ -337,6 +341,9 @@ void WideGraph::on_spec2dComboBox_currentIndexChanged(int index) case 3: // Reference ui->widePlot->setReference(true); break; + case 4: + ui->widePlot->setQ65_Sync(true); + break; } replot(); } @@ -480,7 +487,7 @@ void WideGraph::on_gain2dSlider_valueChanged(int value) //Gain2 ui->widePlot->setPlot2dGain(value); if(ui->widePlot->scaleOK ()) { ui->widePlot->draw(swide,false,false); - if(m_mode=="QRA64") ui->widePlot->draw(swide,false,true); + if(m_mode=="QRA64" or m_mode=="Q65") ui->widePlot->draw(swide,false,true); } } @@ -489,7 +496,7 @@ void WideGraph::on_zero2dSlider_valueChanged(int value) //Zero2 ui->widePlot->setPlot2dZero(value); if(ui->widePlot->scaleOK ()) { ui->widePlot->draw(swide,false,false); - if(m_mode=="QRA64") ui->widePlot->draw(swide,false,true); + if(m_mode=="QRA64" or m_mode=="Q65") ui->widePlot->draw(swide,false,true); } } diff --git a/widgets/widegraph.ui b/widgets/widegraph.ui index b0165e040..d78757444 100644 --- a/widgets/widegraph.ui +++ b/widgets/widegraph.ui @@ -335,6 +335,11 @@ Reference + + + Q65_Sync + + From 04b11fda142f3e7193c7fc2149da5bfba72025c5 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 18 Dec 2020 14:01:10 -0500 Subject: [PATCH 234/426] Don't plot the green line if using Q65_Sync. --- widgets/plotter.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index a225581fd..948b6dfb0 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -129,7 +129,6 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) { if (!m_TRperiod) return; // not ready to plot yet int j,j0; - static int ktop=0; float y,y2,ymin; double fac = sqrt(m_binsPerPixel*m_waterfallAvg/15.0); double gain = fac*pow(10.0,0.015*m_plotGain); @@ -222,12 +221,8 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) } - if(i==iz-1) { + if(i==iz-1 and !m_bQ65_Sync) { painter2D.drawPolyline(LineBuf,j); - if(m_mode=="QRA64" or m_mode=="Q65") { - painter2D.setPen(Qt::red); - painter2D.drawPolyline(LineBuf2,ktop); - } } LineBuf[j].setX(i); LineBuf[j].setY(int(0.9*m_h2-y2*m_h2/70.0)); From e7c8eba2a586042555afb77dcf44b9685c1d157e Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Fri, 18 Dec 2020 19:59:25 +0000 Subject: [PATCH 235/426] Initialize member variable in correct place --- widgets/mainwindow.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index b10a25a39..f3f21dca8 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -348,6 +348,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, m_first_error {true}, tx_status_label {tr ("Receiving")}, wsprNet {new WSPRNet {&m_network_manager, this}}, + m_baseCall {Radio::base_callsign (m_config.my_callsign ())}, m_appDir {QApplication::applicationDirPath ()}, m_cqStr {""}, m_palette {"Linrad"}, @@ -444,8 +445,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, ui->decodedTextBrowser->set_configuration (&m_config, true); ui->decodedTextBrowser2->set_configuration (&m_config); - m_baseCall = Radio::base_callsign (m_config.my_callsign ()); - m_optimizingProgress.setWindowModality (Qt::WindowModal); m_optimizingProgress.setAutoReset (false); m_optimizingProgress.setMinimumDuration (15000); // only show after 15s delay From 5315bbd88a00f71c49cdc3151b00a525060441d3 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 18 Dec 2020 15:14:17 -0500 Subject: [PATCH 236/426] Don't erase the red sync curve too soon. --- widgets/plotter.cpp | 5 ++++- widgets/plotter.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 948b6dfb0..737658de3 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -140,7 +140,10 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) //move current data down one line (must do this before attaching a QPainter object) if(bScroll and !m_bReplot) m_WaterfallPixmap.scroll(0,1,0,0,m_w,m_h1); QPainter painter1(&m_WaterfallPixmap); - m_2DPixmap = m_OverlayPixmap.copy(0,0,m_w,m_h2); + if(m_bFirst or bRed or !m_bQ65_Sync) { + m_2DPixmap = m_OverlayPixmap.copy(0,0,m_w,m_h2); + m_bFirst=false; + } QPainter painter2D(&m_2DPixmap); if(!painter2D.isActive()) return; QFont Font("Arial"); diff --git a/widgets/plotter.h b/widgets/plotter.h index 1aeacf811..ad35f4ff8 100644 --- a/widgets/plotter.h +++ b/widgets/plotter.h @@ -118,6 +118,7 @@ private: bool m_bQ65_Sync; bool m_bVHF; bool m_bSingleDecode; + bool m_bFirst=true; float m_fSpan; From 67b82c7666357fe8f73fa8812e27a2ed67700584 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 18 Dec 2020 16:00:33 -0500 Subject: [PATCH 237/426] Remove the klunky 2D sync plot. The red sync curve is better. --- lib/q65_sync.f90 | 42 +----------------------------------------- widgets/mainwindow.cpp | 16 ---------------- 2 files changed, 1 insertion(+), 57 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 8e5481d73..ff2cebcb5 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -245,46 +245,6 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & 1100 format(2f10.3) enddo close(17) - - if(ia.le.60) call zplot_q65(ccf(-ia:ia,lag1:lag2),ia,lag1,lag2, & - rms,dtstep,ntol,smax,emedelay) + 900 return end subroutine q65_sync - -subroutine zplot_q65(ccf,ia,lag1,lag2,rms,dtstep,ntol,smax,emedelay) - - real ccf(-ia:ia,lag1:lag2) - character*1 line(130),mark(0:6) - character*60 blanks - data mark/' ',' ','.','-','+','X','#'/ - data blanks/' '/ - -! open(35,file='ccf.dat',status='unknown',access='stream') -! write(35) ia,lag1,lag2,rms,dtstep -! write(35) ccf -! close(35) - open(34,file='ccf.txt',status='unknown') - write(34,1000) -ntol,blanks(1:ia-2),0,blanks(1:ia-2),ntol -1000 format(4x,i4,a,i1,a,i3) - fac=1.0 - if(smax/rms.gt.7.0) fac=7.0*rms/smax - k=0 - do j=lag2,lag1,-1 - t=j*dtstep - if(emedelay.eq.0.0 .and. abs(t).gt.1.0) cycle - do i=-ia,ia - k=i+ia+2 - n=fac*ccf(i,j)/rms - if(n.lt.0) n=0 - if(n.gt.6) n=6 - line(k)=mark(n) - enddo - line(1)='|' - line(k+1)='|' - write(34,1010) t,line(1:k+1) -1010 format(f5.2,1x,132a1) - enddo - close(34) - - return -end subroutine zplot_q65 diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index f3f21dca8..3fa84bda2 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3305,22 +3305,6 @@ void MainWindow::decodeDone () { if(m_mode!="FT8" or dec_data.params.nzhsym==50) m_nDecodes=0; if(m_mode=="QRA64" or m_mode=="Q65") m_wideGraph->drawRed(0,0); - - if(m_mode=="Q65" and m_msgAvgWidget!=NULL) { - if(m_msgAvgWidget->isVisible()) { -// QFile f(m_config.temp_dir ().absoluteFilePath ("ccf.txt")); - - QFont font("Courier New",7); - m_msgAvgWidget->changeFont(font); - QFile f("ccf.txt"); - if(f.open(QIODevice::ReadOnly | QIODevice::Text)) { - QTextStream s(&f); - QString t=s.readAll(); - m_msgAvgWidget->displayAvg(t); - } - } - } - if ("FST4W" == m_mode) { if (m_uploadWSPRSpots From 450249dd4cdbc7b6dfe93fbeabc95b946e8f9549 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 18 Dec 2020 16:19:01 -0500 Subject: [PATCH 238/426] Add a DT label to the Q65 red sync curve. --- lib/q65_sync.f90 | 4 ++-- widgets/plotter.cpp | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index ff2cebcb5..95fa5c9ff 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -241,8 +241,8 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & 200 do i=-ia,ia freq=nfqso + i*df - write(17,1100) freq,ccf1(i) -1100 format(2f10.3) + write(17,1100) freq,ccf1(i),xdt +1100 format(3f10.3) enddo close(17) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 737658de3..5dfacb3d2 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -273,20 +273,27 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) f.open(m_redFile.toLatin1()); if(f) { int x,y; - float freq,sync; + float freq,sync,xdt; for(int i=0; i<99999; i++) { - f >> freq >> sync; + f >> freq >> sync >> xdt; if(f.eof()) break; x=XfromFreq(freq); y=m_h2*(0.9 - 0.09*gain2d*sync) - m_plot2dZero; LineBuf2[k].setX(x); LineBuf2[k].setY(y); + k++; } f.close(); QPen pen0(Qt::red,2); painter2D.setPen(pen0); painter2D.drawPolyline(LineBuf2,k); + QString t; + t = t.asprintf("DT = %6.2f",xdt); + painter2D.setPen(Qt::white); + Font.setWeight(QFont::Bold); + painter2D.setFont(Font); + painter2D.drawText(m_w-100,m_h2/2,t); } } update(); //trigger a new paintEvent From 89ab3b0f01be18039d999543aa8ea6d24fad141c Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 18 Dec 2020 16:30:11 -0500 Subject: [PATCH 239/426] Fix a bounds error with very small FTol. --- lib/q65_sync.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 95fa5c9ff..1f593fd9c 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -192,7 +192,8 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ! irc,trim(decoded) !3055 format(i6,i3,6f8.2,i5,2x,a) ! close(55) - base=(sum(ccf1(-ia:-ia+9)) + sum(ccf1(ia-9:ia)))/20.0 + ic=ia/4; + base=(sum(ccf1(-ia:-ia+ic)) + sum(ccf1(ia-ic:ia)))/(2.0+2.0*ic); ccf1=ccf1-base smax=maxval(ccf1) if(smax.gt.10.0) ccf1=10.0*ccf1/smax From 7005ac3c5001e881171e8db87cf21671269b92dd Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 19 Dec 2020 09:10:34 -0500 Subject: [PATCH 240/426] Control "Call 1st" visibility with AutoSeq status in Q65 mode. --- widgets/mainwindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 3fa84bda2..f347318e0 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -8495,7 +8495,8 @@ 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" or m_mode=="FT4" or m_mode=="FST4") and b); + ui->cbFirst->setVisible((m_mode=="FT8" or m_mode=="FT4" or m_mode=="FST4" + or m_mode=="Q65") and b); } void MainWindow::on_measure_check_box_stateChanged (int state) From 7096a548ec91c09e0ba19095b117ca568bcc2102 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 19 Dec 2020 10:42:27 -0500 Subject: [PATCH 241/426] Correct a problem with estimating rms of ccf for Q65-300x submodes. --- lib/q65_sync.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 1f593fd9c..f65f79838 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -226,9 +226,10 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & xdt=jpk*dtstep sq=0. nsq=0 + jd=(lag2-lag1)/4 do i=-ia,ia do j=lag1,lag2 - if(abs(j-jpk).gt.8 .and. abs(i-ipk).gt.ia/2) then + if(abs(j-jpk).gt.jd .and. abs(i-ipk).gt.ia/2) then sq=sq + ccf(i,j)**2 nsq=nsq+1 endif From f23129c33cd60b34a57eafbaa866bb7e090cfba6 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 19 Dec 2020 11:25:18 -0500 Subject: [PATCH 242/426] Fix a problem with Q65 FTol range extending down below 0 Hz. --- lib/q65_sync.f90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index f65f79838..c6db664a3 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -208,13 +208,15 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & irc=-2 dat4=0 ia=ntol/df - if(i0-ia.lt.1 .or. i0+ia.gt.iz) go to 900 do lag=lag1,lag2 do k=1,85 n=NSTEP*(k-1) + 1 j=n+lag+j0 if(j.ge.1 .and. j.le.jz) then - ccf(-ia:ia,lag)=ccf(-ia:ia,lag) + sync(k)*s1(i0-ia:i0+ia,j) + do i=-ia,ia + if(i0+i.lt.1 .or. i0+i.gt.iz) cycle + ccf(i,lag)=ccf(i,lag) + sync(k)*s1(i0+i,j) + enddo endif enddo enddo From c70fc7b5837ae054004b55a3eac65b68eb074594 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sat, 19 Dec 2020 16:32:37 +0000 Subject: [PATCH 243/426] Fix a close down crash due to destruction ordering Static QString variables are unwise as QApplication translators may get destroyed before them. --- widgets/widegraph.cpp | 37 ++++++++++++++++--------------------- widgets/widegraph.h | 2 ++ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/widgets/widegraph.cpp b/widgets/widegraph.cpp index 75064f776..0c6af8348 100644 --- a/widgets/widegraph.cpp +++ b/widgets/widegraph.cpp @@ -13,12 +13,6 @@ #include "SettingsGroup.hpp" #include "moc_widegraph.cpp" -namespace -{ - auto user_defined = QObject::tr ("User Defined"); - float swide[MAX_SCREENSIZE]; -} - WideGraph::WideGraph(QSettings * settings, QWidget *parent) : QDialog(parent), ui(new Ui::WideGraph), @@ -26,7 +20,8 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) : m_palettes_path {":/Palettes"}, m_tr0 {0.0}, m_n {0}, - m_bHaveTransmitted {false} + m_bHaveTransmitted {false}, + m_user_defined {tr ("User Defined")} { ui->setupUi(this); @@ -105,8 +100,8 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) : if(t==m_waterfallPalette) ui->paletteComboBox->setCurrentIndex(index); index++; } - ui->paletteComboBox->addItem (user_defined); - if (user_defined == m_waterfallPalette) ui->paletteComboBox->setCurrentIndex(index); + ui->paletteComboBox->addItem (m_user_defined); + if (m_user_defined == m_waterfallPalette) ui->paletteComboBox->setCurrentIndex(index); readPalette (); } @@ -150,7 +145,7 @@ void WideGraph::saveSettings() //saveS void WideGraph::drawRed(int ia, int ib) { - ui->widePlot->drawRed(ia,ib,swide); + ui->widePlot->drawRed(ia,ib,m_swide); } void WideGraph::dataSink2(float s[], float df3, int ihsym, int ndiskdata) //dataSink2 @@ -184,8 +179,8 @@ void WideGraph::dataSink2(float s[], float df3, int ihsym, int ndiskdata) //dat ss += sp; smax=qMax(smax,sp); } -// swide[j]=nbpp*smax; - swide[j]=nbpp*ss; +// m_swide[j]=nbpp*smax; + m_swide[j]=nbpp*ss; } // Time according to this computer @@ -195,7 +190,7 @@ void WideGraph::dataSink2(float s[], float df3, int ihsym, int ndiskdata) //dat float flagValue=1.0e30; if(m_bHaveTransmitted) flagValue=2.0e30; for(int i=0; iwidePlot->draw(swide,true,false); + ui->widePlot->draw(m_swide,true,false); } } @@ -241,7 +236,7 @@ void WideGraph::keyPressEvent(QKeyEvent *e) //F1 void WideGraph::setRxFreq(int n) //setRxFreq { ui->widePlot->setRxFreq(n); - ui->widePlot->draw(swide,false,false); + ui->widePlot->draw(m_swide,false,false); } int WideGraph::rxFreq() //rxFreq @@ -391,7 +386,7 @@ void WideGraph::readPalette () //readPalette { try { - if (user_defined == m_waterfallPalette) + if (m_user_defined == m_waterfallPalette) { ui->widePlot->setColours (WFPalette {m_userPalette}.interpolate ()); } @@ -444,7 +439,7 @@ void WideGraph::on_adjust_palette_push_button_clicked (bool) //Adjust Palette { if (m_userPalette.design ()) { - m_waterfallPalette = user_defined; + m_waterfallPalette = m_user_defined; ui->paletteComboBox->setCurrentText (m_waterfallPalette); readPalette (); } @@ -486,8 +481,8 @@ void WideGraph::on_gain2dSlider_valueChanged(int value) //Gain2 { ui->widePlot->setPlot2dGain(value); if(ui->widePlot->scaleOK ()) { - ui->widePlot->draw(swide,false,false); - if(m_mode=="QRA64" or m_mode=="Q65") ui->widePlot->draw(swide,false,true); + ui->widePlot->draw(m_swide,false,false); + if(m_mode=="QRA64" or m_mode=="Q65") ui->widePlot->draw(m_swide,false,true); } } @@ -495,8 +490,8 @@ void WideGraph::on_zero2dSlider_valueChanged(int value) //Zero2 { ui->widePlot->setPlot2dZero(value); if(ui->widePlot->scaleOK ()) { - ui->widePlot->draw(swide,false,false); - if(m_mode=="QRA64" or m_mode=="Q65") ui->widePlot->draw(swide,false,true); + ui->widePlot->draw(m_swide,false,false); + if(m_mode=="QRA64" or m_mode=="Q65") ui->widePlot->draw(m_swide,false,true); } } diff --git a/widgets/widegraph.h b/widgets/widegraph.h index f5f70c281..421fc8213 100644 --- a/widgets/widegraph.h +++ b/widgets/widegraph.h @@ -117,6 +117,8 @@ private: QString m_mode; QString m_modeTx; QString m_waterfallPalette; + float m_swide[MAX_SCREENSIZE]; + QString m_user_defined; }; #endif // WIDEGRAPH_H From 31189265b9497e3274f619707eca9e99d16e8652 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 19 Dec 2020 11:33:23 -0500 Subject: [PATCH 244/426] Erase the yellow "T M R 73" single-tone markers after a mode change. --- widgets/plotter.cpp | 3 ++- widgets/plotter.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 5dfacb3d2..4b5d2a7d8 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -140,10 +140,11 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) //move current data down one line (must do this before attaching a QPainter object) if(bScroll and !m_bReplot) m_WaterfallPixmap.scroll(0,1,0,0,m_w,m_h1); QPainter painter1(&m_WaterfallPixmap); - if(m_bFirst or bRed or !m_bQ65_Sync) { + if(m_bFirst or bRed or !m_bQ65_Sync or m_mode!=m_mode0) { m_2DPixmap = m_OverlayPixmap.copy(0,0,m_w,m_h2); m_bFirst=false; } + m_mode0=m_mode; QPainter painter2D(&m_2DPixmap); if(!painter2D.isActive()) return; QFont Font("Arial"); diff --git a/widgets/plotter.h b/widgets/plotter.h index ad35f4ff8..c4ecded15 100644 --- a/widgets/plotter.h +++ b/widgets/plotter.h @@ -145,6 +145,7 @@ private: QString m_Str; QString m_HDivText[483]; QString m_mode; + QString m_mode0; QString m_modeTx; QString m_rxBand; QString m_redFile; From c7ca708569d2a42ed2a081ce37e931c91520c973 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 19 Dec 2020 11:42:42 -0500 Subject: [PATCH 245/426] Fix the problem with not honoring "Save Decoded" in Q65 mode. --- lib/decoder.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 3c5aad72b..a2bf79b20 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -354,7 +354,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample) ! JT65 is not yet producing info for nsynced, ndecoded. 800 ndecoded = my_jt4%decoded + my_jt65%decoded + my_jt9%decoded + & - my_ft8%decoded + my_ft4%decoded + my_fst4%decoded + my_ft8%decoded + my_ft4%decoded + my_fst4%decoded + & + my_q65%decoded if(params%nmode.eq.8 .and. params%nzhsym.eq.41) ndec41=ndecoded if(params%nmode.eq.8 .and. params%nzhsym.eq.47) ndec47=ndecoded if(params%nmode.eq.8 .and. params%nzhsym.eq.50) then From 3ccb7f2b85b5584af9531297c2cd04285259f8e4 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 19 Dec 2020 13:19:04 -0500 Subject: [PATCH 246/426] Suggested 2m default Q65 frequency: 144.116. --- models/FrequencyList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/FrequencyList.cpp b/models/FrequencyList.cpp index ff3602f3c..2de042efa 100644 --- a/models/FrequencyList.cpp +++ b/models/FrequencyList.cpp @@ -284,6 +284,7 @@ namespace {70091000, Modes::WSPR, IARURegions::R1}, {70230000, Modes::MSK144, IARURegions::R1}, + {144116000, Modes::Q65, IARURegions::ALL}, {144120000, Modes::JT65, IARURegions::ALL}, {144120000, Modes::Echo, IARURegions::ALL}, {144170000, Modes::FT4, IARURegions::ALL}, @@ -291,7 +292,6 @@ namespace {144360000, Modes::MSK144, IARURegions::R1}, {144150000, Modes::MSK144, IARURegions::R2}, {144489000, Modes::WSPR, IARURegions::ALL}, - {144120000, Modes::QRA64, IARURegions::ALL}, {222065000, Modes::Echo, IARURegions::R2}, {222065000, Modes::JT65, IARURegions::R2}, From 1f33c857ac63aaa71a5a41a61980078e32f6f624 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 19 Dec 2020 13:25:51 -0500 Subject: [PATCH 247/426] Measure fSpread from ccf1, then use resulting width to set effective ibwmin. --- lib/q65_decode.f90 | 6 +++--- lib/q65_sync.f90 | 10 ++++++++-- lib/qra/q65/q65_loops.f90 | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 538ecf698..70f0d7009 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -94,7 +94,7 @@ contains call q65_enc(dgen,codewords) !Initialize Q65 call timer('sync_q65',0) call q65_sync(nutc,iwave,ntrperiod*12000,mode65,codewords,ncw,nsps, & - nfqso,ntol,emedelay,xdt,f0,snr1,dat4,snr2,id1) + nfqso,ntol,emedelay,xdt,f0,snr1,width,dat4,snr2,id1) call timer('sync_q65',1) if(id1.eq.1) then xdt1=xdt @@ -139,8 +139,8 @@ contains endif endif call timer('q65loops',0) - call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode, & - nFadingModel,ndepth,jpk0,xdt,f0,iaptype,apmask,apsymbols, & + call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode, & + nFadingModel,ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols, & xdt1,f1,snr2,dat4,id2) call timer('q65loops',1) ! snr2=snr2 + db(6912.0/nsps) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index c6db664a3..644eb1644 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -1,5 +1,5 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & - emedelay,xdt,f0,snr1,dat4,snr2,id1) + emedelay,xdt,f0,snr1,width,dat4,snr2,id1) ! Detect and align with the Q65 sync vector, returning time and frequency ! offsets and SNR estimate. @@ -243,12 +243,18 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ccf1=ccf(-ia:ia,jpk)/rms if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 -200 do i=-ia,ia +200 smax=maxval(ccf1) + i1=-9999 + i2=-9999 + do i=-ia,ia + if(i1.eq.-9999 .and. ccf1(i).ge.0.5*smax) i1=i + if(i2.eq.-9999 .and. ccf1(-i).ge.0.5*smax) i2=-i freq=nfqso + i*df write(17,1100) freq,ccf1(i),xdt 1100 format(3f10.3) enddo close(17) + width=df*(i2-i1) 900 return end subroutine q65_sync diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 0f83d7275..2545573f9 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,5 +1,5 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & - ndepth,jpk0,xdt0,f0,iaptype,APmask,APsymbols,xdt1,f1,snr2,dat4,id2) + ndepth,jpk0,xdt0,f0,width,iaptype,APmask,APsymbols,xdt1,f1,snr2,dat4,id2) use packjt77 use timer_module, only: timer @@ -69,9 +69,9 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & nbw=ibw ndist=ndf**2 + ndt**2 + ((nbw-2))**2 if(ndist.gt.maxdist) cycle - ! b90=1.728**ibw b90=3.0**nbw !### Mult by ~baud/3.33 ??? ### if(nbw.eq.0) b90=baud + if(b90.lt.0.5*width) cycle if(b90.gt.230.0) cycle call timer('q65_intr',0) b90ts = b90/baud From ddf727f351ad3803ca2c6c05dfff0c95ca253a33 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 20 Dec 2020 21:42:42 -0500 Subject: [PATCH 248/426] Adjust a 3 dB offset in Q65 SNRs from list decodes. --- lib/q65_sync.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 644eb1644..fdd40f88a 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -182,7 +182,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) if(irc.ge.0 .and. plog.ge.PLOG_MIN) then - snr2=esnodb - db(2500.0/baud) + snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment id1=1 write(c77,1000) dat4(1:12),dat4(13)/2 1000 format(12b6.6,b5.5) From 545079e9510736a6126d0d45d0cda86a638b1f2b Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 20 Dec 2020 21:43:30 -0500 Subject: [PATCH 249/426] Reorder the ibw loop in q65_loops.f90. --- lib/qra/q65/q65_loops.f90 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 2545573f9..74f2a06f4 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -29,8 +29,8 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & idfmax=3 idtmax=3 - ibwmin=0 - ibwmax=2 + ibwmin=1 + ibwmax=3 maxdist=5 if(iand(ndepth,3).ge.2) then idfmax=5 @@ -66,13 +66,13 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim endif do ibw=ibwmin,ibwmax - nbw=ibw - ndist=ndf**2 + ndt**2 + ((nbw-2))**2 + nbw=ibw/2 + if(mod(ibw,2).eq.0) nbw=-nbw + ndist=ndf**2 + ndt**2 + nbw**2 if(ndist.gt.maxdist) cycle - b90=3.0**nbw !### Mult by ~baud/3.33 ??? ### - if(nbw.eq.0) b90=baud - if(b90.lt.0.5*width) cycle - if(b90.gt.230.0) cycle + xx=1.885*log(3.0*width)+nbw + b90=1.7**xx + if(b90.gt.345.0) cycle call timer('q65_intr',0) b90ts = b90/baud call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) From 44516dbbc1324b1c827032b74634c80d0c33a6ee Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 21 Dec 2020 13:33:54 -0500 Subject: [PATCH 250/426] Make the Q65 red sync curve at least 21 tone-spacings wide, even if FTol is smaller. --- lib/q65_sync.f90 | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index fdd40f88a..bcf776318 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -48,14 +48,15 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & jz=(txt+1.0)*12000.0/istep !Number of quarter-symbol steps if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher ia=ntol/df + ia2=max(ia,10*mode_q65) nsmo=int(0.7*mode_q65*mode_q65) if(nsmo.lt.1) nsmo=1 allocate(s1(iz,jz)) allocate(s3(-64:LL-65,63)) allocate(c0(0:nfft-1)) - allocate(ccf(-ia:ia,-53:214)) - allocate(ccf1(-ia:ia)) + allocate(ccf(-ia2:ia2,-53:214)) + allocate(ccf1(-ia2:ia2)) if(sync(1).eq.99.0) then !Generate the sync vector sync=-22.0/63.0 !Sync tone OFF @@ -66,10 +67,10 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & fac=1/32767.0 do j=1,jz !Compute symbol spectra at step size - ia=(j-1)*istep - ib=ia+nsps-1 + i1=(j-1)*istep + i2=i1+nsps-1 k=-1 - do i=ia,ib,2 !Load iwave data into complex array c0, for r2c FFT + do i=i1,i2,2 !Load iwave data into complex array c0, for r2c FFT xx=iwave(i) yy=iwave(i+1) k=k+1 @@ -99,7 +100,6 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & enddo dtstep=nsps/(NSTEP*12000.0) !Step size in seconds - ia=ntol/df lag1=-1.0/dtstep lag2=1.0/dtstep + 0.9999 j0=0.5/dtstep @@ -135,23 +135,23 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & do k=1,85 j=j0 + NSTEP*(k-1) + 1 + lag if(j.ge.1 .and. j.le.jz) then - do i=-ia,ia + do i=-ia2,ia2 ii=i0+mode_q65*itone(k)+i if(ii.ge.1 .and. ii.le.iz) ccf(i,lag)=ccf(i,lag) + s1(ii,j) enddo endif enddo enddo - ccfmax=maxval(ccf) + ccfmax=maxval(ccf(-ia:ia,:)) if(ccfmax.gt.ccf_best) then ccf_best=ccfmax - ijpk=maxloc(ccf) + ijpk=maxloc(ccf(-ia:ia,:)) ipk=ijpk(1)-ia-1 jpk=ijpk(2)-53-1 f0=nfqso + (ipk-1)*df xdt=jpk*dtstep imsg_best=imsg - ccf1=ccf(-ia:ia,jpk) + ccf1=ccf(:,jpk) endif enddo ! imsg @@ -192,8 +192,8 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ! irc,trim(decoded) !3055 format(i6,i3,6f8.2,i5,2x,a) ! close(55) - ic=ia/4; - base=(sum(ccf1(-ia:-ia+ic)) + sum(ccf1(ia-ic:ia)))/(2.0+2.0*ic); + ic=ia2/4; + base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); ccf1=ccf1-base smax=maxval(ccf1) if(smax.gt.10.0) ccf1=10.0*ccf1/smax @@ -207,20 +207,19 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & 100 ccf=0. irc=-2 dat4=0 - ia=ntol/df do lag=lag1,lag2 do k=1,85 n=NSTEP*(k-1) + 1 j=n+lag+j0 if(j.ge.1 .and. j.le.jz) then - do i=-ia,ia + do i=-ia2,ia2 if(i0+i.lt.1 .or. i0+i.gt.iz) cycle ccf(i,lag)=ccf(i,lag) + sync(k)*s1(i0+i,j) enddo endif enddo enddo - ijpk=maxloc(ccf) + ijpk=maxloc(ccf(-ia:ia,:)) ipk=ijpk(1)-ia-1 jpk=ijpk(2)-53-1 @@ -229,7 +228,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & sq=0. nsq=0 jd=(lag2-lag1)/4 - do i=-ia,ia + do i=-ia2,ia2 do j=lag1,lag2 if(abs(j-jpk).gt.jd .and. abs(i-ipk).gt.ia/2) then sq=sq + ccf(i,j)**2 @@ -240,7 +239,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & rms=sqrt(sq/nsq) smax=ccf(ipk,jpk) snr1=smax/rms - ccf1=ccf(-ia:ia,jpk)/rms + ccf1=ccf(:,jpk)/rms if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 200 smax=maxval(ccf1) @@ -249,6 +248,8 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & do i=-ia,ia if(i1.eq.-9999 .and. ccf1(i).ge.0.5*smax) i1=i if(i2.eq.-9999 .and. ccf1(-i).ge.0.5*smax) i2=-i + enddo + do i=-ia2,ia2 freq=nfqso + i*df write(17,1100) freq,ccf1(i),xdt 1100 format(3f10.3) From 36a089de2189e7ca339d2c7e595f3f5244f4864b Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 21 Dec 2020 13:42:03 -0500 Subject: [PATCH 251/426] Remove the sync-test option from q65sim.f90. --- lib/qra/q65/q65sim.f90 | 35 +++-------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/lib/qra/q65/q65sim.f90 b/lib/qra/q65/q65sim.f90 index 241d0adb8..c3881d673 100644 --- a/lib/qra/q65/q65sim.f90 +++ b/lib/qra/q65/q65sim.f90 @@ -17,14 +17,11 @@ program q65sim real*8 f0,dt,twopi,phi,dphi,baud,fsample,freq character msg*37,fname*17,csubmode*1,arg*12,cd*1 character msgsent*37 - logical lsync - data lsync/.false./ nargs=iargc() - if(nargs.ne.9) then - print *, 'Usage: q65sim "msg" A-E freq fDop DT TRp Nfiles Sync SNR' - print *, 'Example: q65sim "K1ABC W9XYZ EN37" A 1500 0.0 0.0 60 1 T -26' - print*,'Sync = T to include sync test.' + if(nargs.ne.8) then + print *, 'Usage: q65sim "msg" A-E freq fDop DT TRp Nfiles SNR' + print *, 'Example: q65sim "K1ABC W9XYZ EN37" A 1500 0.0 0.0 60 1 -26' go to 999 endif call getarg(1,msg) @@ -41,15 +38,8 @@ program q65sim call getarg(7,arg) read(arg,*) nfiles call getarg(8,arg) - if(arg(1:1).eq.'T' .or. arg(1:1).eq.'1') lsync=.true. - call getarg(9,arg) read(arg,*) snrdb - if(nfiles.lt.0) then - nfiles=-nfiles - lsync=.true. - endif - if(ntrperiod.eq.15) then nsps=1800 else if(ntrperiod.eq.30) then @@ -98,7 +88,6 @@ program q65sim write(*,1004) 1004 format('File TR Freq Mode S/N DT Dop Message'/60('-')) - nsync=0 do ifile=1,nfiles !Loop over requested number of files if(ntrperiod.lt.60) then write(fname,1005) ifile !Output filename @@ -192,24 +181,6 @@ program q65sim 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) - -! if(lsync) then -! cd=' ' -! if(ifile.eq.nfiles) cd='d' -! nfqso=nint(f0) -! ntol=100 -! call q65_sync(iwave,npts,mode65,nsps,nfqso,ntol,xdt2,f02,snr2) -! terr=1.01/(8.0*baud) -! ferr=1.01*mode65*baud -! if(abs(xdt2-xdt).lt.terr .and. abs(f02-f0).lt.ferr) nsync=nsync+1 -! open(40,file='sync65.out',status='unknown',position='append') -! write(40,1030) ifile,65,csubmode,snrdb,fspread,xdt2-xdt,f02-f0, & -! snr2,nsync,cd -!1030 format(i4,i3,1x,a1,2f7.1,f7.2,2f8.1,i5,1x,a1) -! close(40) -! endif enddo - if(lsync) write(*,1040) snrdb,nfiles,nsync -1040 format('SNR:',f6.1,' nfiles:',i5,' nsynced:',i5) 999 end program q65sim From 044aa80dc437f43063974e3b37861a081d26bb93 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 21 Dec 2020 15:07:53 -0500 Subject: [PATCH 252/426] Remove all vestiges of the QRA64 mode. It has been replaced by Q65. --- CMakeLists.txt | 22 ++-------- lib/decoder.f90 | 23 +--------- lib/jt65_decode.f90 | 18 -------- models/FrequencyList.cpp | 23 +++++----- models/Modes.cpp | 1 - widgets/mainwindow.cpp | 93 ++++++---------------------------------- widgets/mainwindow.h | 2 - widgets/mainwindow.ui | 11 ++++- widgets/plotter.cpp | 22 ++++------ widgets/plotter.h | 1 + widgets/widegraph.cpp | 4 +- 11 files changed, 50 insertions(+), 170 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 41e2d74a0..90f6ddc6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -345,7 +345,6 @@ set (wsjt_FSRCS lib/avecho.f90 lib/averms.f90 lib/azdist.f90 - lib/badmsg.f90 lib/ft8/baseline.f90 lib/ft4/ft4_baseline.f90 lib/blanker.f90 @@ -433,7 +432,6 @@ set (wsjt_FSRCS 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 @@ -498,8 +496,6 @@ set (wsjt_FSRCS lib/prog_args.f90 lib/ps4.f90 lib/q65_sync.f90 - lib/qra64a.f90 - lib/qra_loops.f90 lib/qra/q65/q65_ap.f90 lib/qra/q65/q65_loops.f90 lib/qra/q65/q65_set_list.f90 @@ -585,8 +581,6 @@ set (ka9q_CSRCS set_source_files_properties (${ka9q_CSRCS} PROPERTIES COMPILE_FLAGS -Wno-sign-compare) set (qra_CSRCS - lib/qra/qra64/qra64.c - lib/qra/qra64/qra64_subs.c lib/qra/qracodes/qra12_63_64_irr_b.c lib/qra/qracodes/qra13_64_64_irr_e.c lib/qra/q65/npfwht.c @@ -1114,15 +1108,9 @@ target_link_libraries (jt65sim wsjt_fort wsjt_cxx) add_executable (sumsim lib/sumsim.f90) target_link_libraries (sumsim wsjt_fort wsjt_cxx) -add_executable (qra64sim lib/qra/qra64/qra64sim.f90) -target_link_libraries (qra64sim wsjt_fort wsjt_cxx) - add_executable (q65sim lib/qra/q65/q65sim.f90) target_link_libraries (q65sim wsjt_fort wsjt_cxx) -add_executable (test_qra64 lib/test_qra64.f90) -target_link_libraries (test_qra64 wsjt_fort wsjt_cxx) - add_executable (test_q65 lib/test_q65.f90) target_link_libraries (test_q65 wsjt_fort wsjt_cxx) @@ -1132,8 +1120,8 @@ target_link_libraries (q65_ftn_test wsjt_fort wsjt_cxx) add_executable (jt49sim lib/jt49sim.f90) target_link_libraries (jt49sim wsjt_fort wsjt_cxx) -add_executable (allsim lib/allsim.f90) -target_link_libraries (allsim wsjt_fort wsjt_cxx) +#add_executable (allsim lib/allsim.f90) +#target_link_libraries (allsim wsjt_fort wsjt_cxx) add_executable (rtty_spec lib/rtty_spec.f90) target_link_libraries (rtty_spec wsjt_fort wsjt_cxx) @@ -1141,9 +1129,6 @@ target_link_libraries (rtty_spec wsjt_fort wsjt_cxx) add_executable (jt65code lib/jt65code.f90) target_link_libraries (jt65code wsjt_fort wsjt_cxx) -add_executable (qra64code lib/qra64code.f90) -target_link_libraries (qra64code wsjt_fort wsjt_cxx) - add_executable (jt9code lib/jt9code.f90) target_link_libraries (jt9code wsjt_fort wsjt_cxx) @@ -1559,8 +1544,7 @@ install (TARGETS jt9 wsprd fmtave fcal fmeasure ) if(WSJT_BUILD_UTILS) -install (TARGETS ft8code jt65code qra64code qra64sim jt9code jt4code - msk144code fst4sim q65sim +install (TARGETS ft8code jt65code jt9code jt4code msk144code fst4sim q65sim RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime ) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index a2bf79b20..67ed1ead0 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -298,9 +298,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample) !$omp parallel sections num_threads(2) copyin(/timer_private/) shared(ndecoded) if(.true.) !iif() needed on Mac !$omp section - if(params%nmode.eq.65 .or. params%nmode.eq.164 .or. & - (params%nmode.eq.(65+9) .and. params%ntxmode.eq.65)) then -! We're in JT65 or QRA64 mode, or should do JT65 first + if(params%nmode.eq.65) then +! We're in JT65 mode if(newdat65) dd(1:npts65)=id2(1:npts65) nf1=params%nfa @@ -474,24 +473,6 @@ contains cflags=' ' is_deep=ft.eq.2 - if(ft.ge.80) then !QRA64 mode - nft=ft-100 - csync=': ' - if(sync-3.4.ge.float(minsync) .or. nft.ge.0) csync=':*' - if(nft.lt.0) then - write(*,1009) params%nutc,snr,dt,freq,csync,decoded - else - write(*,1009) params%nutc,snr,dt,freq,csync,decoded,nft -1009 format(i4.4,i4,f5.1,i5,1x,a2,1x,a22,i2) - endif - write(13,1011) params%nutc,nint(sync),snr,dt,float(freq),drift, & - decoded,nft -1011 format(i4.4,i4,i5,f6.2,f8.0,i4,3x,a22,' QRA64',i3) - go to 100 - endif - -! write(*,3001) ft,nsum,qual,sync,bVHF -!3001 format('a',3i3,f5.1,L3) if(ft.eq.0 .and. minsync.ge.0 .and. int(sync).lt.minsync) then write(*,1010) params%nutc,snr,dt,freq else diff --git a/lib/jt65_decode.f90 b/lib/jt65_decode.f90 index bfb78b254..90459add1 100644 --- a/lib/jt65_decode.f90 +++ b/lib/jt65_decode.f90 @@ -99,24 +99,6 @@ contains dd=dd0 ndecoded=0 ndecoded0=0 - - if(nsubmode.ge.100) then -! This is QRA64 mode - mode64=2**(nsubmode-100) - call qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & - emedelay,mycall,hiscall,hisgrid,sync,nsnr,dtx,nfreq,decoded,nft) - if (associated(this%callback)) then - ndrift=0 - nflip=1 - width=1.0 - nsmo=0 - nqual=0 - call this%callback(sync,nsnr,dtx,nfreq,ndrift, & - nflip,width,decoded,nft,nqual,nsmo,1,minsync) - end if - go to 900 - endif - single_decode=iand(nexp_decode,32).ne.0 .or. nagain bVHF=iand(nexp_decode,64).ne.0 diff --git a/models/FrequencyList.cpp b/models/FrequencyList.cpp index 2de042efa..f35bb832f 100644 --- a/models/FrequencyList.cpp +++ b/models/FrequencyList.cpp @@ -263,7 +263,6 @@ namespace {28180000, Modes::FT4, IARURegions::ALL}, {50200000, Modes::Echo, IARURegions::ALL}, - {50270000, Modes::QRA64, IARURegions::ALL}, {50275000, Modes::Q65, IARURegions::ALL}, {50276000, Modes::JT65, IARURegions::R2}, {50276000, Modes::JT65, IARURegions::R3}, @@ -295,54 +294,54 @@ namespace {222065000, Modes::Echo, IARURegions::R2}, {222065000, Modes::JT65, IARURegions::R2}, - {222065000, Modes::QRA64, IARURegions::R2}, + {222065000, Modes::Q65, IARURegions::R2}, {432065000, Modes::Echo, IARURegions::ALL}, {432065000, Modes::JT65, IARURegions::ALL}, {432300000, Modes::WSPR, IARURegions::ALL}, {432360000, Modes::MSK144, IARURegions::ALL}, - {432065000, Modes::QRA64, IARURegions::ALL}, + {432065000, Modes::Q65, IARURegions::ALL}, {902065000, Modes::JT65, IARURegions::R2}, - {902065000, Modes::QRA64, IARURegions::R2}, + {902065000, Modes::Q65, IARURegions::R2}, {1296065000, Modes::Echo, IARURegions::ALL}, {1296065000, Modes::JT65, IARURegions::ALL}, {1296500000, Modes::WSPR, IARURegions::ALL}, - {1296065000, Modes::QRA64, IARURegions::ALL}, + {1296065000, Modes::Q65, IARURegions::ALL}, {2301000000, Modes::Echo, IARURegions::ALL}, {2301065000, Modes::JT4, IARURegions::ALL}, {2301065000, Modes::JT65, IARURegions::ALL}, - {2301065000, Modes::QRA64, IARURegions::ALL}, + {2301065000, Modes::Q65, IARURegions::ALL}, {2304065000, Modes::Echo, IARURegions::ALL}, {2304065000, Modes::JT4, IARURegions::ALL}, {2304065000, Modes::JT65, IARURegions::ALL}, - {2304065000, Modes::QRA64, IARURegions::ALL}, + {2304065000, Modes::Q65, IARURegions::ALL}, {2320065000, Modes::Echo, IARURegions::ALL}, {2320065000, Modes::JT4, IARURegions::ALL}, {2320065000, Modes::JT65, IARURegions::ALL}, - {2320065000, Modes::QRA64, IARURegions::ALL}, + {2320065000, Modes::Q65, IARURegions::ALL}, {3400065000, Modes::Echo, IARURegions::ALL}, {3400065000, Modes::JT4, IARURegions::ALL}, {3400065000, Modes::JT65, IARURegions::ALL}, - {3400065000, Modes::QRA64, IARURegions::ALL}, + {3400065000, Modes::Q65, IARURegions::ALL}, {5760065000, Modes::Echo, IARURegions::ALL}, {5760065000, Modes::JT4, IARURegions::ALL}, {5760065000, Modes::JT65, IARURegions::ALL}, - {5760200000, Modes::QRA64, IARURegions::ALL}, + {5760200000, Modes::Q65, IARURegions::ALL}, {10368100000, Modes::Echo, IARURegions::ALL}, {10368200000, Modes::JT4, IARURegions::ALL}, - {10368200000, Modes::QRA64, IARURegions::ALL}, + {10368200000, Modes::Q65, IARURegions::ALL}, {24048100000, Modes::Echo, IARURegions::ALL}, {24048200000, Modes::JT4, IARURegions::ALL}, - {24048200000, Modes::QRA64, IARURegions::ALL}, + {24048200000, Modes::Q65, IARURegions::ALL}, }; } diff --git a/models/Modes.cpp b/models/Modes.cpp index 0d3c544d4..b341ebef4 100644 --- a/models/Modes.cpp +++ b/models/Modes.cpp @@ -21,7 +21,6 @@ namespace "Echo", "ISCAT", "MSK144", - "QRA64", "FreqCal", "FT8", "FT4", diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index f347318e0..bc87c6132 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -134,9 +134,6 @@ extern "C" { void gen65_(char* msg, int* ichk, char* msgsent, int itone[], int* itext, fortran_charlen_t, fortran_charlen_t); - void genqra64_(char* msg, int* ichk, char* msgsent, int itone[], - int* itext, fortran_charlen_t, fortran_charlen_t); - void genq65_(char* msg, int* ichk, char* msgsent, int itone[], int* i3, int* n3, fortran_charlen_t, fortran_charlen_t); @@ -600,7 +597,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, ui->actionEcho->setActionGroup(modeGroup); ui->actionISCAT->setActionGroup(modeGroup); ui->actionMSK144->setActionGroup(modeGroup); - ui->actionQRA64->setActionGroup(modeGroup); ui->actionQ65->setActionGroup(modeGroup); ui->actionFreqCal->setActionGroup(modeGroup); @@ -1400,9 +1396,6 @@ void MainWindow::fixStop() } else if (m_mode=="JT65"){ m_hsymStop=174; if(m_config.decode_at_52s()) m_hsymStop=179; - } else if (m_mode=="QRA64"){ - m_hsymStop=179; - if(m_config.decode_at_52s()) m_hsymStop=186; } else if (m_mode=="Q65"){ m_hsymStop=48; if(m_TRperiod==30) m_hsymStop=96; @@ -1880,7 +1873,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog setup_status_bar (vhf); bool b = vhf && (m_mode=="JT4" or m_mode=="JT65" or m_mode=="ISCAT" or - m_mode=="JT9" or m_mode=="MSK144" or m_mode=="QRA64"); + m_mode=="JT9" or m_mode=="MSK144" or m_mode=="Q65"); if(b) VHF_features_enabled(b); set_mode (m_mode); if(b) VHF_features_enabled(b); @@ -2394,8 +2387,6 @@ void MainWindow::setup_status_bar (bool vhf) mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #66ffff}"); } else if ("JT65" == m_mode) { mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #66ff66}"); - } else if ("QRA64" == m_mode) { - mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #99ff33}"); } else if ("Q65" == m_mode) { mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #99ff33}"); } else if ("MSK144" == m_mode) { @@ -3145,8 +3136,6 @@ void MainWindow::decode() //decode() if(m_mode=="JT65") dec_data.params.nmode=65; if(m_mode=="JT65") dec_data.params.ljt65apon = ui->actionEnable_AP_JT65->isVisible () && ui->actionEnable_AP_JT65->isChecked (); - if(m_mode=="QRA64") dec_data.params.nmode=164; - if(m_mode=="QRA64") dec_data.params.ntxmode=164; if(m_mode=="Q65") dec_data.params.nmode=66; if(m_mode=="Q65") dec_data.params.ntxmode=66; if(m_mode=="JT4") { @@ -3165,7 +3154,6 @@ void MainWindow::decode() //decode() if(m_mode=="FST4W") dec_data.params.nmode=241; dec_data.params.ntrperiod=m_TRperiod; dec_data.params.nsubmode=m_nSubMode; - if(m_mode=="QRA64") dec_data.params.nsubmode=100 + m_nSubMode; dec_data.params.minw=0; dec_data.params.nclearave=m_nclearave; if(m_nclearave!=0) { @@ -3304,7 +3292,7 @@ void MainWindow::to_jt9(qint32 n, qint32 istart, qint32 idone) void MainWindow::decodeDone () { if(m_mode!="FT8" or dec_data.params.nzhsym==50) m_nDecodes=0; - if(m_mode=="QRA64" or m_mode=="Q65") m_wideGraph->drawRed(0,0); + if(m_mode=="Q65") m_wideGraph->drawRed(0,0); if ("FST4W" == m_mode) { if (m_uploadWSPRSpots @@ -3384,7 +3372,8 @@ void MainWindow::readFromStdout() //readFromStdout } else { m_nDecodes+=1; ndecodes_label.setText(QString::number(m_nDecodes)); - if(m_mode=="JT4" or m_mode=="JT65" or m_mode=="QRA64") { + if(m_mode=="JT4" or m_mode=="JT65") { + //### Do something about Q65 here ? ### int nf=line_read.indexOf("f"); if(nf>0) { navg=line_read.mid(nf+1,1).toInt(); @@ -3563,7 +3552,7 @@ void MainWindow::readFromStdout() //readFromStdout //### I think this is where we are preventing Hounds from spotting Fox ### if(m_mode!="FT8" or (SpecOp::HOUND != m_config.special_op_id())) { - if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="QRA64" or m_mode=="Q65" + if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="Q65" or m_mode=="JT4" or m_mode=="JT65" or m_mode=="JT9" or m_mode=="FST4") { auto_sequence (decodedtext, 25, 50); } @@ -3590,7 +3579,7 @@ void MainWindow::readFromStdout() //readFromStdout } else { if (stdMsg && okToPost) pskPost(decodedtext); } - if((m_mode=="JT4" or m_mode=="JT65" or m_mode=="QRA64" or m_mode=="Q65") and + if((m_mode=="JT4" or m_mode=="JT65" or m_mode=="Q65") and m_msgAvgWidget!=NULL) { if(m_msgAvgWidget->isVisible()) { QFile f(m_config.temp_dir ().absoluteFilePath ("avemsg.txt")); @@ -3769,8 +3758,7 @@ void MainWindow::guiUpdate() if(m_modeTx=="JT4") txDuration=1.0 + 207.0*2520/11025.0; // JT4 if(m_modeTx=="JT9") txDuration=1.0 + 85.0*m_nsps/12000.0; // JT9 if(m_modeTx=="JT65") txDuration=1.0 + 126*4096/11025.0; // JT65 - if(m_modeTx=="QRA64") txDuration=1.0 + 84*6912/12000.0; // QRA64 - if(m_modeTx=="Q65") { // Q65 + if(m_modeTx=="Q65") { // Q65 if(m_TRperiod==15) txDuration=0.5 + 85*1800/12000.0; if(m_TRperiod==30) txDuration=0.5 + 85*3600/12000.0; if(m_TRperiod==60) txDuration=1.0 + 85*7200/12000.0; @@ -4026,8 +4014,6 @@ void MainWindow::guiUpdate() &m_currentMessageType, 22, 22); if(m_modeTx=="JT65") gen65_(message, &ichk, msgsent, const_cast (itone), &m_currentMessageType, 22, 22); - if(m_modeTx=="QRA64") genqra64_(message, &ichk, msgsent, const_cast (itone), - &m_currentMessageType, 22, 22); if(m_modeTx=="Q65") { int i3=-1; int n3=-1; @@ -4769,7 +4755,6 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie if (("JT65" == m_mode && mode != "#") || ("JT9" == m_mode && mode != "@") || ("MSK144" == m_mode && !("&" == mode || "^" == mode)) - || ("QRA64" == m_mode && mode.left (1) != ":") || ("Q65" == m_mode && mode.left (1) != ":")) { return; //Currently we do auto-sequencing only in FT4, FT8, MSK144, FST4, and Q65 } @@ -4867,8 +4852,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 != "Q65" && m_mode!="FT8" && - m_mode!="FT4" && m_mode!="FST4") { + m_mode != "Q65" && m_mode!="FT8" && m_mode!="FT4" && m_mode!="FST4") { return; } } @@ -5216,7 +5200,7 @@ void MainWindow::genCQMsg () msgtype (QString {"%1 %2"}.arg(m_CQtype).arg(m_config.my_callsign()),ui->tx6); } } - if ((m_mode=="JT4" or m_mode=="QRA64" or m_mode=="Q65") and ui->cbShMsgs->isChecked()) { + if ((m_mode=="JT4" or m_mode=="Q65") and ui->cbShMsgs->isChecked()) { if (ui->cbTx6->isChecked ()) { msgtype ("@1250 (SEND MSGS)", ui->tx6); } else { @@ -5400,7 +5384,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional) 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"); } - if ((m_mode=="JT4" || m_mode=="QRA64") && m_bShMsgs) t="@1500 (RRR)"; + if ((m_mode=="JT4" || m_mode=="Q65") && m_bShMsgs) t="@1500 (RRR)"; msgtype(t, ui->tx4); t=t0 + "73"; @@ -5408,7 +5392,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional) if(!bHisCall and bMyCall) t=hisCall + " <" + my_callsign + "> 73"; if(bHisCall and !bMyCall) t="<" + hisCall + "> " + my_callsign + " 73"; } - if (m_mode=="JT4" || m_mode=="QRA64") { + if (m_mode=="JT4" || m_mode=="Q65") { if (m_bShMsgs) t="@1750 (73)"; msgtype(t, ui->tx5->lineEdit()); } else if ("MSK144" == m_mode && m_bShMsgs) { @@ -5439,7 +5423,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional) } else { msgtype(t + "R" + rpt, ui->tx3); } - if ((m_mode != "JT4" && m_mode != "QRA64") || !m_bShMsgs) { + if ((m_mode != "JT4" && m_mode != "Q65") || !m_bShMsgs) { msgtype(t + "73", ui->tx5->lineEdit ()); } } @@ -5454,7 +5438,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional) msgtype(t00 + my_grid, ui->tx1); msgtype(t + "R" + rpt, ui->tx3); } - if (!eme_short_codes && ((m_mode != "JT4" && m_mode != "QRA64") || !m_bShMsgs)) { + if (!eme_short_codes && ((m_mode != "JT4" && m_mode != "Q65") || !m_bShMsgs)) { msgtype(t + "73", ui->tx5->lineEdit ()); } break; @@ -6372,40 +6356,6 @@ void MainWindow::on_actionJT65_triggered() statusChanged(); } -void MainWindow::on_actionQRA64_triggered() -{ - int n=m_nSubMode; - on_actionJT65_triggered(); - m_nSubMode=n; - m_mode="QRA64"; - m_modeTx="QRA64"; - ui->actionQRA64->setChecked(true); - switch_mode (Modes::QRA64); - setup_status_bar (true); - m_hsymStop=180; - if(m_config.decode_at_52s()) m_hsymStop=188; - m_wideGraph->setMode(m_mode); - m_wideGraph->setModeTx(m_modeTx); - ui->sbSubmode->setMaximum(4); - ui->sbSubmode->setValue(m_nSubMode); - ui->actionInclude_averaging->setVisible (false); - ui->actionInclude_correlation->setVisible (false); -// ui->RxFreqSpinBox->setValue(1000); -// ui->TxFreqSpinBox->setValue(1000); - QString fname {QDir::toNativeSeparators(m_config.temp_dir ().absoluteFilePath ("red.dat"))}; - m_wideGraph->setRedFile(fname); - m_wideGraph->setMode(m_mode); - m_wideGraph->setModeTx(m_modeTx); - m_wideGraph->setPeriod(m_TRperiod,6912); - m_wideGraph->setTxFreq(ui->TxFreqSpinBox->value()); - m_wideGraph->setRxFreq(ui->RxFreqSpinBox->value()); - m_wideGraph->setTol(ui->sbFtol->value()); - switch_mode (Modes::QRA64); -// 012345678901234567890123456789012345 - displayWidgets(nWidgets("111110010010110110010000001000000000")); - statusChanged(); -} - void MainWindow::on_actionQ65_triggered() { // on_actionFST4_triggered(); @@ -6484,7 +6434,6 @@ void MainWindow::on_actionMSK144_triggered() if("JT9"==m_mode) ui->actionJT9->setChecked(true); if("JT65"==m_mode) ui->actionJT65->setChecked(true); if("ISCAT"==m_mode) ui->actionISCAT->setChecked(true); - if("QRA64"==m_mode) ui->actionQRA64->setChecked(true); if("Q65"==m_mode) ui->actionQ65->setChecked(true); if("WSPR"==m_mode) ui->actionWSPR->setChecked(true); if("Echo"==m_mode) ui->actionEcho->setChecked(true); @@ -7306,18 +7255,6 @@ void MainWindow::transmit (double snr) true, false, snr, m_TRperiod); } - if (m_modeTx == "QRA64") { - if(m_nSubMode==0) toneSpacing=12000.0/6912.0; - if(m_nSubMode==1) toneSpacing=2*12000.0/6912.0; - if(m_nSubMode==2) toneSpacing=4*12000.0/6912.0; - if(m_nSubMode==3) toneSpacing=8*12000.0/6912.0; - if(m_nSubMode==4) toneSpacing=16*12000.0/6912.0; - Q_EMIT sendMessage (m_mode, NUM_QRA64_SYMBOLS, - 6912.0, ui->TxFreqSpinBox->value () - m_XIT, - toneSpacing, m_soundOutput, m_config.audio_output_channel (), - true, false, snr, m_TRperiod); - } - if (m_modeTx == "Q65") { int nsps=1800; if(m_TRperiod==30) nsps=3600; @@ -7566,7 +7503,6 @@ void::MainWindow::VHF_features_enabled(bool b) ui->actionInclude_averaging->setVisible (b); ui->actionInclude_correlation->setVisible (b && m_mode!="Q65"); ui->actionMessage_averaging->setEnabled(b); - ui->actionEnable_AP_DXcall->setVisible (m_mode=="QRA64"); ui->actionEnable_AP_JT65->setVisible (b && m_mode=="JT65"); if(!b && m_msgAvgWidget and (SpecOp::FOX != m_config.special_op_id()) and !m_config.autoLog()) { @@ -7622,7 +7558,7 @@ void MainWindow::on_sbTR_FST4W_valueChanged(int value) QChar MainWindow::current_submode () const { QChar submode {0}; - if (m_mode.contains (QRegularExpression {R"(^(JT65|JT9|JT4|ISCAT|QRA64|Q65)$)"}) + if (m_mode.contains (QRegularExpression {R"(^(JT65|JT9|JT4|ISCAT|Q65)$)"}) && (m_config.enable_VHF_features () || "JT4" == m_mode || "ISCAT" == m_mode)) { submode = m_nSubMode + 65; @@ -9274,7 +9210,6 @@ void MainWindow::set_mode (QString const& mode) else if ("JT4" == mode) on_actionJT4_triggered (); else if ("JT9" == mode) on_actionJT9_triggered (); else if ("JT65" == mode) on_actionJT65_triggered (); - else if ("QRA64" == mode) on_actionQRA64_triggered (); else if ("Q65" == mode) on_actionQ65_triggered (); else if ("FreqCal" == mode) on_actionFreqCal_triggered (); else if ("ISCAT" == mode) on_actionISCAT_triggered (); diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index 9f85d9e7d..51898842c 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -49,7 +49,6 @@ #define NUM_WSPR_SYMBOLS 162 //(50+31)*2, embedded sync #define NUM_ISCAT_SYMBOLS 1291 //30*11025/256 #define NUM_MSK144_SYMBOLS 144 //s8 + d48 + s8 + d80 -#define NUM_QRA64_SYMBOLS 84 //63 data + 21 sync #define NUM_Q65_SYMBOLS 85 //63 data + 22 sync #define NUM_FT8_SYMBOLS 79 #define NUM_FT4_SYMBOLS 105 @@ -304,7 +303,6 @@ private slots: void on_sbCQTxFreq_valueChanged(int n); void on_cbCQTx_toggled(bool b); void on_actionMSK144_triggered(); - void on_actionQRA64_triggered(); void on_actionQ65_triggered(); void on_actionFreqCal_triggered(); void splash_done (); diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index b945e9210..d030cd0f0 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -2,6 +2,14 @@ MainWindow + + + 0 + 0 + 1124 + 582 + + WSJT-X by K1JT @@ -2793,7 +2801,7 @@ Double-click to reset to the standard 73 message 0 0 - 1110 + 1124 21 @@ -2884,7 +2892,6 @@ Double-click to reset to the standard 73 message - diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 4b5d2a7d8..30981ab3b 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -110,6 +110,7 @@ void CPlotter::resizeEvent(QResizeEvent* ) //resizeEvent() m_ScalePixmap = QPixmap(m_w,30); m_ScalePixmap.fill(Qt::white); m_Percent2DScreen0 = m_Percent2DScreen; + m_bResized = true; } DrawOverlay(); } @@ -140,9 +141,10 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) //move current data down one line (must do this before attaching a QPainter object) if(bScroll and !m_bReplot) m_WaterfallPixmap.scroll(0,1,0,0,m_w,m_h1); QPainter painter1(&m_WaterfallPixmap); - if(m_bFirst or bRed or !m_bQ65_Sync or m_mode!=m_mode0) { + if(m_bFirst or bRed or !m_bQ65_Sync or m_mode!=m_mode0 or m_bResized) { m_2DPixmap = m_OverlayPixmap.copy(0,0,m_w,m_h2); m_bFirst=false; + m_bResized=false; } m_mode0=m_mode; QPainter painter2D(&m_2DPixmap); @@ -252,8 +254,8 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) painter1.drawText (5, painter1.fontMetrics ().ascent (), t); } - if(m_mode=="JT4" or m_mode=="QRA64") { - QPen pen3(Qt::yellow); //Mark freqs of JT4 single-tone msgs + if(m_mode=="JT4") { + QPen pen3(Qt::yellow); //Mark freqs of JT4/Q65 single-tone msgs painter2D.setPen(pen3); Font.setWeight(QFont::Bold); painter2D.setFont(Font); @@ -464,14 +466,6 @@ void CPlotter::DrawOverlay() //DrawOverlay() if(m_nSubMode==7) bw=128*bw; //H } - if(m_mode=="QRA64") { //QRA64 - bw=63.0*12000.0/m_nsps; - if(m_nSubMode==1) bw=2*bw; //B - if(m_nSubMode==2) bw=4*bw; //C - if(m_nSubMode==3) bw=8*bw; //D - if(m_nSubMode==4) bw=16*bw; //E - } - if(m_mode=="Q65") { //Q65 int h=int(pow(2.0,m_nSubMode)); int nsps=1800; @@ -513,7 +507,7 @@ void CPlotter::DrawOverlay() //DrawOverlay() int yTxTop=12; int yRxBottom=yTxTop + 2*yh + 4; if(m_mode=="JT9" or m_mode=="JT65" or m_mode=="JT9+JT65" - or m_mode=="QRA64" or m_mode=="Q65" or m_mode=="FT8" or m_mode=="FT4" + or m_mode=="Q65" or m_mode=="FT8" or m_mode=="FT4" or m_mode.startsWith("FST4")) { if(m_mode=="FST4" and !m_bSingleDecode) { @@ -525,7 +519,7 @@ void CPlotter::DrawOverlay() //DrawOverlay() painter0.drawLine(x2,25,x2-5,20); } - if(m_mode=="QRA64" or m_mode=="Q65" or (m_mode=="JT65" and m_bVHF)) { + if(m_mode=="Q65" or (m_mode=="JT65" and m_bVHF)) { painter0.setPen(penGreen); x1=XfromFreq(m_rxFreq-m_tol); x2=XfromFreq(m_rxFreq+m_tol); @@ -563,7 +557,7 @@ 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=="Q65" or m_mode=="FT8" + m_mode.mid(0,4)=="WSPR" or m_mode=="Q65" or m_mode=="FT8" or m_mode=="FT4" or m_mode.startsWith("FST4")) { painter0.setPen(penRed); x1=XfromFreq(m_txFreq); diff --git a/widgets/plotter.h b/widgets/plotter.h index c4ecded15..3f8f00574 100644 --- a/widgets/plotter.h +++ b/widgets/plotter.h @@ -119,6 +119,7 @@ private: bool m_bVHF; bool m_bSingleDecode; bool m_bFirst=true; + bool m_bResized; float m_fSpan; diff --git a/widgets/widegraph.cpp b/widgets/widegraph.cpp index 0c6af8348..2bcb5936a 100644 --- a/widgets/widegraph.cpp +++ b/widgets/widegraph.cpp @@ -482,7 +482,7 @@ void WideGraph::on_gain2dSlider_valueChanged(int value) //Gain2 ui->widePlot->setPlot2dGain(value); if(ui->widePlot->scaleOK ()) { ui->widePlot->draw(m_swide,false,false); - if(m_mode=="QRA64" or m_mode=="Q65") ui->widePlot->draw(m_swide,false,true); + if(m_mode=="Q65") ui->widePlot->draw(m_swide,false,true); } } @@ -491,7 +491,7 @@ void WideGraph::on_zero2dSlider_valueChanged(int value) //Zero2 ui->widePlot->setPlot2dZero(value); if(ui->widePlot->scaleOK ()) { ui->widePlot->draw(m_swide,false,false); - if(m_mode=="QRA64" or m_mode=="Q65") ui->widePlot->draw(m_swide,false,true); + if(m_mode=="Q65") ui->widePlot->draw(m_swide,false,true); } } From 5007a4eb518718142576cf907a65391c7b4a7371 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 21 Dec 2020 15:14:34 -0500 Subject: [PATCH 253/426] Remove one more line containing QRA64. --- models/Modes.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/models/Modes.hpp b/models/Modes.hpp index 132ca8657..4a0ce25ab 100644 --- a/models/Modes.hpp +++ b/models/Modes.hpp @@ -46,7 +46,6 @@ public: Echo, ISCAT, MSK144, - QRA64, FreqCal, FT8, FT4, From c3b3dc1c6181dd451651dd2b6425584497350d74 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 21 Dec 2020 15:38:58 -0500 Subject: [PATCH 254/426] Display T M R 73 markers for Q65 (C and higher submodes). --- widgets/plotter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 30981ab3b..a68ca80b3 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -254,7 +254,8 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) painter1.drawText (5, painter1.fontMetrics ().ascent (), t); } - if(m_mode=="JT4") { + if(m_mode=="JT4" or (m_mode=="Q65" and m_nSubMode>=2)) { + DrawOverlay(); QPen pen3(Qt::yellow); //Mark freqs of JT4/Q65 single-tone msgs painter2D.setPen(pen3); Font.setWeight(QFont::Bold); From 83c917ac571b11b20f3a67246b11fee5f4c56a14 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 21 Dec 2020 15:59:33 -0500 Subject: [PATCH 255/426] Don't allow previous values of DT, f0 to be displayed with a no-decode. --- lib/q65_decode.f90 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 70f0d7009..7df4185f0 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -102,7 +102,11 @@ contains go to 100 endif - if(snr1.lt.2.8) go to 100 + if(snr1.lt.2.8) then + xdt1=0. + f1=0. + go to 100 + endif jpk0=(xdt+1.0)*6000 !### Is this OK? if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !### if(jpk0.lt.0) jpk0=0 From 938d96fecc4bed0928947602d5da3b0c92fc12ec Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 22 Dec 2020 03:48:35 +0000 Subject: [PATCH 256/426] Save sub-mode details to .WAV file metadata for Q65 mode --- widgets/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index f347318e0..75d24590a 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -1671,7 +1671,7 @@ QString MainWindow::save_wave_file (QString const& name, short const * data, int auto comment = QString {"Mode=%1%2; Freq=%3%4"} .arg (mode) .arg (QString {(mode.contains ('J') && !mode.contains ('+')) - || mode.startsWith ("FST4") || mode.startsWith ("QRA") + || mode.startsWith ("FST4") || mode.startsWith ('Q') ? QString {"; Sub Mode="} + QString::number (int (samples / 12000)) + QChar {'A' + sub_mode} : QString {}}) .arg (Radio::frequency_MHz_string (frequency)) From 575a5c5a7a07ab9aa1332705ed12b2cd69971581 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 22 Dec 2020 09:42:16 -0500 Subject: [PATCH 257/426] Allow 2x1 calls in the "strict_standard_callsign_re" regular expression. --- Radio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Radio.cpp b/Radio.cpp index 79b332fbd..ebb6c6fba 100644 --- a/Radio.cpp +++ b/Radio.cpp @@ -22,7 +22,7 @@ namespace Radio QRegularExpression valid_callsign_regexp {R"(\d[[:alpha:]]|[[:alpha:]]\d)"}; // standard callsign - QRegularExpression strict_standard_callsign_re {R"(^([A-Z][0-9]?|[0-9A-Z][A-Z])[0-9][A-Z]{0,3}$)"}; + QRegularExpression strict_standard_callsign_re {R"(^([A-Z]{1,2}[0-9]?|[0-9A-Z][A-Z])[0-9][A-Z]{0,3}$)"}; // suffixes that are often used and should not be interpreted as a // DXCC Entity prefix used as a suffix From cac6577127153e3513b051f698d8431cd69693a4 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 22 Dec 2020 09:43:25 -0500 Subject: [PATCH 258/426] Extend the DT range for 30 s Q65 submodes to include EME delays. --- lib/q65_sync.f90 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index bcf776318..96bb77e1f 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -102,11 +102,9 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & dtstep=nsps/(NSTEP*12000.0) !Step size in seconds lag1=-1.0/dtstep lag2=1.0/dtstep + 0.9999 + if(nsps.ge.3600 .and. emedelay.gt.0) lag2=4.0/dtstep + 0.9999 !Include EME j0=0.5/dtstep - if(nsps.ge.7200) then - j0=1.0/dtstep !Nominal index for start of signal - lag2=4.0/dtstep + 0.9999 !Include EME delays - endif + if(nsps.ge.7200) j0=1.0/dtstep !Nominal start-signal index if(ncw.lt.1) go to 100 From 1eb0d0471dec329c04455fc2424048ec36224fff Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 22 Dec 2020 09:56:15 -0500 Subject: [PATCH 259/426] Tweak the "m_hsymStop" stop times for optimum EME performance. --- widgets/mainwindow.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index bc87c6132..857123b12 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -1397,11 +1397,14 @@ void MainWindow::fixStop() m_hsymStop=174; if(m_config.decode_at_52s()) m_hsymStop=179; } else if (m_mode=="Q65"){ - m_hsymStop=48; - if(m_TRperiod==30) m_hsymStop=96; - if(m_TRperiod==60) m_hsymStop=196; - if(m_TRperiod==120) m_hsymStop=401; - if(m_TRperiod==300) m_hsymStop=1027; + m_hsymStop=48; // 13.8 s + if(m_TRperiod==30) { + m_hsymStop=96; // 27.6 s + if(m_config.decode_at_52s()) m_hsymStop=100; // 28.8 s + } + if(m_TRperiod==60) m_hsymStop=196; // 56.4 s + if(m_TRperiod==120) m_hsymStop=408; // 117.5 s + if(m_TRperiod==300) m_hsymStop=1030; // 296.6 s } else if (m_mode=="FreqCal"){ m_hsymStop=((int(m_TRperiod/0.288))/8)*8; } else if (m_mode=="FT8") { From e7d48e0f5bfe9934e167ba21d158f11ef2513354 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 22 Dec 2020 09:57:48 -0500 Subject: [PATCH 260/426] Revert "Allow 2x1 calls in the "strict_standard_callsign_re" regular expression." This reverts commit 575a5c5a7a07ab9aa1332705ed12b2cd69971581. --- Radio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Radio.cpp b/Radio.cpp index ebb6c6fba..79b332fbd 100644 --- a/Radio.cpp +++ b/Radio.cpp @@ -22,7 +22,7 @@ namespace Radio QRegularExpression valid_callsign_regexp {R"(\d[[:alpha:]]|[[:alpha:]]\d)"}; // standard callsign - QRegularExpression strict_standard_callsign_re {R"(^([A-Z]{1,2}[0-9]?|[0-9A-Z][A-Z])[0-9][A-Z]{0,3}$)"}; + QRegularExpression strict_standard_callsign_re {R"(^([A-Z][0-9]?|[0-9A-Z][A-Z])[0-9][A-Z]{0,3}$)"}; // suffixes that are often used and should not be interpreted as a // DXCC Entity prefix used as a suffix From 408ac7bd0de6c8c44214829f29b10baa380eca6d Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 22 Dec 2020 13:21:31 -0500 Subject: [PATCH 261/426] Correct the reported f0 for Q65 signals. It was off by one tone spacing. --- lib/q65_sync.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 96bb77e1f..834059fa1 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -146,7 +146,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ijpk=maxloc(ccf(-ia:ia,:)) ipk=ijpk(1)-ia-1 jpk=ijpk(2)-53-1 - f0=nfqso + (ipk-1)*df + f0=nfqso + (ipk-mode_q65)*df xdt=jpk*dtstep imsg_best=imsg ccf1=ccf(:,jpk) From 71ed4776f9baf91e411454190d4350f888295a55 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Tue, 22 Dec 2020 12:33:24 -0600 Subject: [PATCH 262/426] For fst4sim, use Lorentzian fading spectrum when fspread is negative. --- lib/fst4/fst4sim.f90 | 3 ++- lib/fst4/lorentzian_fading.f90 | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 lib/fst4/lorentzian_fading.f90 diff --git a/lib/fst4/fst4sim.f90 b/lib/fst4/fst4sim.f90 index 5b8f33bc3..e42bbbb02 100644 --- a/lib/fst4/fst4sim.f90 +++ b/lib/fst4/fst4sim.f90 @@ -118,7 +118,8 @@ program fst4sim do ifile=1,nfiles c=c0 - if(fspread.ne.0.0 .or. delay.ne.0.0) call watterson(c,nwave,NZ,fs,delay,fspread) + if(fspread.gt.0.0 .or. delay.ne.0.0) call watterson(c,nwave,NZ,fs,delay,fspread) + if(fspread.lt.0.0) call lorentzian_fading(c,nwave,fs,-fspread) c=sig*c wave=real(c) if(snrdb.lt.90) then diff --git a/lib/fst4/lorentzian_fading.f90 b/lib/fst4/lorentzian_fading.f90 new file mode 100644 index 000000000..c1bd5c325 --- /dev/null +++ b/lib/fst4/lorentzian_fading.f90 @@ -0,0 +1,43 @@ +subroutine lorentzian_fading(c,npts,fs,fspread) +! +! npts is the total length of the simulated data vector +! + complex c(0:npts-1) + complex cspread(0:npts-1) + complex z + + twopi=8.0*atan(1.0) + df=fs/npts + nh=npts/2 + cspread(0)=1.0 + cspread(nh)=0. + b=6.0 + do i=1,nh + f=i*df + x=b*f/fspread + z=0. + a=0. + if(x.lt.3.0) then + a=sqrt(1.111/(1.0+x*x)-0.1) + phi1=twopi*rran() + z=a*cmplx(cos(phi1),sin(phi1)) + endif + cspread(i)=z + z=0. + if(x.lt.3.0) then + phi2=twopi*rran() + z=a*cmplx(cos(phi2),sin(phi2)) + endif + cspread(npts-i)=z + enddo + + call four2a(cspread,npts,1,1,1) + + s=sum(abs(cspread)**2) + avep=s/npts + fac=sqrt(1.0/avep) + cspread=fac*cspread + c=cspread*c + + return +end subroutine lorentzian_fading From 318a0abda7c7693d3a757d803b767de79cda2e53 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Tue, 22 Dec 2020 12:34:12 -0600 Subject: [PATCH 263/426] Forgot to commit CMakeLists.txt change. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ab9a20b0..71e1adfd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -462,6 +462,7 @@ set (wsjt_FSRCS lib/jtmsg.f90 lib/libration.f90 lib/lorentzian.f90 + lib/fst4/lorentzian_fading.f90 lib/lpf1.f90 lib/mixlpf.f90 lib/makepings.f90 From 008cb37c16c97a5d13c83650e31dfc5ad8838d4a Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 22 Dec 2020 13:41:40 -0500 Subject: [PATCH 264/426] Update test_q65 for having removed the T/F sync flag from q65sim command line. --- lib/test_q65.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 73602c3a0..f614d2703 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -68,7 +68,7 @@ program test_q65 ! 1 2 3 4 5 6 7 ! 123456789012345678901234567890123456789012345678901234567890123456789012345' - cmd1='q65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 F -10.0 > junk0' + cmd1='q65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 -10.0 > junk0' cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A -Q 3 -f 1500 *.wav > junk' write(cmd1(10:33),'(a)') '"'//msg//'"' From c6e42549c483706e6e11a30d387b298fa9e91fac Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Tue, 22 Dec 2020 13:12:58 -0600 Subject: [PATCH 265/426] Remove hmod from command line parameters for fst4sim. --- lib/fst4/fst4sim.f90 | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/fst4/fst4sim.f90 b/lib/fst4/fst4sim.f90 index e42bbbb02..83ac5baed 100644 --- a/lib/fst4/fst4sim.f90 +++ b/lib/fst4/fst4sim.f90 @@ -19,10 +19,10 @@ program fst4sim ! Get command-line argument(s) nargs=iargc() - if(nargs.ne.10) then - print*,'Need 10 arguments, got ',nargs - print*,'Usage: fst4sim "message" TRsec f0 DT h fdop del nfiles snr W' - print*,'Examples: fst4sim "K1JT K9AN EN50" 60 1500 0.0 1 0.1 1.0 10 -15 F' + if(nargs.ne.9) then + print*,'Need 9 arguments, got ',nargs + print*,'Usage: fst4sim "message" TRsec f0 DT fdop del nfiles snr W' + print*,'Examples: fst4sim "K1JT K9AN EN50" 60 1500 0.0 0.1 1.0 10 -15 F' print*,'W (T or F) argument is hint to encoder to use WSPR message when there is abiguity' go to 999 endif @@ -34,16 +34,14 @@ program fst4sim call getarg(4,arg) read(arg,*) xdt !Time offset from nominal (s) call getarg(5,arg) - read(arg,*) hmod !Modulation index, h - call getarg(6,arg) read(arg,*) fspread !Watterson frequency spread (Hz) - call getarg(7,arg) + call getarg(6,arg) read(arg,*) delay !Watterson delay (ms) - call getarg(8,arg) + call getarg(7,arg) read(arg,*) nfiles !Number of files - call getarg(9,arg) + call getarg(8,arg) read(arg,*) snrdb !SNR_2500 - call getarg(10,arg) + call getarg(9,arg) read(arg,*) wspr_hint !0:break ties as 77-bit 1:break ties as 50-bit nfiles=abs(nfiles) @@ -89,8 +87,8 @@ program fst4sim call genfst4(msg37,0,msgsent37,msgbits,itone,iwspr) write(*,*) write(*,'(a9,a37,a3,L2,a7,i2)') 'Message: ',msgsent37,'W:',wspr_hint,' iwspr:',iwspr - write(*,1000) f00,xdt,hmod,txt,snrdb -1000 format('f0:',f9.3,' DT:',f6.2,' hmod:',i6,' TxT:',f6.1,' SNR:',f6.1) + write(*,1000) f00,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' @@ -106,7 +104,8 @@ program fst4sim ! call sgran() - fsample=12000.0 + fsample=12000.0 + hmod=1 icmplx=1 f0=f00+1.5*hmod*baud call gen_fst4wave(itone,NN,nsps,nwave,fsample,hmod,f0,icmplx,c0,wave) From c5a0de621016ccf1d04770aefab3b35f1a7174d2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 23 Dec 2020 09:47:11 -0500 Subject: [PATCH 266/426] Partial implementation of Q65 message averaging. --- lib/q65_decode.f90 | 2 +- lib/q65_sync.f90 | 36 ++++++++++++++++++++++++++++++++---- lib/test_q65.f90 | 4 ++++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 7df4185f0..17557cd39 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -96,7 +96,7 @@ contains call q65_sync(nutc,iwave,ntrperiod*12000,mode65,codewords,ncw,nsps, & nfqso,ntol,emedelay,xdt,f0,snr1,width,dat4,snr2,id1) call timer('sync_q65',1) - if(id1.eq.1) then + if(id1.eq.1 .or. id1.ge.12) then xdt1=xdt f1=f0 go to 100 diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 834059fa1..741436b55 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -24,17 +24,19 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & integer dat4(13) integer ijpk(2) logical unpk77_success + logical lavg character*77 c77,decoded*37 real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) + real, allocatable,save :: s3avg(:,:) !Averaged data-symbol energies real, allocatable :: ccf(:,:) !CCF(freq,lag) real, allocatable :: ccf1(:) !CCF(freq) at best lag real s3prob(0:63,63) !Symbol-value probabilities real sync(85) !sync vector complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ - data sync(1)/99.0/ - save sync + data sync(1)/99.0/,LL0/-1/ + save sync,navg,LL0 snr1=0. id1=0 @@ -54,6 +56,12 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & allocate(s1(iz,jz)) allocate(s3(-64:LL-65,63)) + if(LL.ne.LL0) then + if(allocated(s3avg)) deallocate(s3avg) + allocate(s3avg(-64:LL-65,63)) + navg=0 + LL0=LL + endif allocate(c0(0:nfft-1)) allocate(ccf(-ia2:ia2,-53:214)) allocate(ccf1(-ia2:ia2)) @@ -63,6 +71,8 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & do k=1,22 sync(isync(k))=1.0 !Sync tone ON enddo + s3avg=0. + navg=0 endif fac=1/32767.0 @@ -175,7 +185,9 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & baud=12000.0/nsps ibwa=1.8*log(baud*mode_q65) + 2 ibwb=min(10,ibwa+4) - do ibw=ibwa,ibwb + lavg=.false. + +10 do ibw=ibwa,ibwb b90=1.72**ibw call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) @@ -196,9 +208,9 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & smax=maxval(ccf1) if(smax.gt.10.0) ccf1=10.0*ccf1/smax go to 200 -! go to 900 endif enddo + if(lavg) go to 900 !###################################################################### ! Establish xdt, f0, and snr1 using sync symbols (and perhaps some AP symbols) @@ -241,6 +253,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 200 smax=maxval(ccf1) + if(lavg) id1=10+navg !This is an average decode i1=-9999 i2=-9999 do i=-ia,ia @@ -254,6 +267,21 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & enddo close(17) width=df*(i2-i1) + if(id1.ge.1) then + navg=0 + s3avg=0. + if(lavg) go to 900 + elseif(snr1.ge.0.0) then + s3avg=s3avg+s3 + navg=navg+1 + write(71,3071) nutc,navg,xdt,f0,snr1 +3071 format(2i5,3f10.2) + if(navg.ge.2) then + s3=s3avg/navg + lavg=.true. + go to 10 + endif + endif 900 return end subroutine q65_sync diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index f614d2703..49a17c678 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -139,6 +139,10 @@ program test_q65 if(ntrperiod.le.30) i0=25 if(line(i0:i0).ne.' ') read(line(60:),*) idec if(idec.lt.0) cycle + if(idec.ge.12) then + iavg=idec-10 + idec=1 + endif if(decok) then ndecn=ndecn + 1 if(iavg.le.1) ndec1=ndec1 + 1 From 844930fe754b135dd18b0162c63c8461cde38a09 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 23 Dec 2020 11:03:09 -0500 Subject: [PATCH 267/426] Correct the frequency alignment of red sync curve. --- lib/q65_sync.f90 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 741436b55..e0459edc5 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -144,7 +144,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & j=j0 + NSTEP*(k-1) + 1 + lag if(j.ge.1 .and. j.le.jz) then do i=-ia2,ia2 - ii=i0+mode_q65*itone(k)+i + ii=i0+mode_q65*(itone(k)+1)+i if(ii.ge.1 .and. ii.le.iz) ccf(i,lag)=ccf(i,lag) + s1(ii,j) enddo endif @@ -156,7 +156,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ijpk=maxloc(ccf(-ia:ia,:)) ipk=ijpk(1)-ia-1 jpk=ijpk(2)-53-1 - f0=nfqso + (ipk-mode_q65)*df + f0=nfqso + ipk*df xdt=jpk*dtstep imsg_best=imsg ccf1=ccf(:,jpk) @@ -232,7 +232,6 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ijpk=maxloc(ccf(-ia:ia,:)) ipk=ijpk(1)-ia-1 jpk=ijpk(2)-53-1 - f0=nfqso + ipk*df xdt=jpk*dtstep sq=0. From b26d29dd1e2579da1c98cbb4184614f6a041da7f Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Wed, 23 Dec 2020 11:45:13 -0600 Subject: [PATCH 268/426] Avoid a possible of bounds error. Compute some more decode diagnostic data. --- lib/fst4_decode.f90 | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index 3407e41f2..a1b39f744 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -59,7 +59,7 @@ contains integer itone(NN) integer hmod integer ipct(0:7) - integer*1 apmask(240),cw(240) + integer*1 apmask(240),cw(240),hdec(240) integer*1 message101(101),message74(74),message77(77) integer*1 rvec(77) integer apbits(240) @@ -392,8 +392,9 @@ contains if(ijitter.eq.1) ioffset=1 if(ijitter.eq.2) ioffset=-1 is0=isbest+ioffset - if(is0.lt.0) cycle - cframe=c2(is0:is0+160*nss-1) + iend=is0+160*nss-1 + if( is0.lt.0 .or. iend.gt.(nfft2-1) ) cycle + cframe=c2(is0:iend) bitmetrics=0 call timer('bitmetrc',0) call get_fst4_bitmetrics(cframe,nss,nblock,nhicoh,bitmetrics, & @@ -408,10 +409,10 @@ contains llrs(181:240,il)=bitmetrics(245:304, il) enddo - apmag=maxval(abs(llrs(:,1)))*1.1 + apmag=maxval(abs(llrs(:,4)))*1.1 ntmax=nblock+nappasses(nQSOProgress) if(lapcqonly) ntmax=nblock+1 - if(ndepth.eq.1) ntmax=nblock + if(ndepth.eq.1) ntmax=nblock ! no ap for ndepth=1 apmask=0 if(iwspr.eq.1) then ! 50-bit msgs, no ap decoding @@ -595,11 +596,15 @@ contains fsig=fc_synced - 1.5*baud inquire(file=trim(data_dir)//'/decdata',exist=decdata_exists) if(decdata_exists) then + hdec=0 + where(llrs(:,1).ge.0.0) hdec=1 + nhp=count(hdec.ne.cw) ! # hard errors wrt N=1 soft symbols + hd=sum(ieor(hdec,cw)*abs(llrs(:,1))) ! weighted distance wrt N=1 symbols open(21,file=trim(data_dir)//'/fst4_decodes.dat',status='unknown',position='append') write(21,3021) nutc,icand,itry,nsyncoh,iaptype, & - ijitter,ntype,Keff,nsync_qual,nharderrors,dmin, & + ijitter,ntype,Keff,nsync_qual,nharderrors,dmin,nhp,hd, & sync,xsnr,xdt,fsig,w50,trim(msg) -3021 format(i6.6,6i3,3i4,f6.1,f9.2,f6.1,f6.2,f7.1,f7.3,1x,a) +3021 format(i6.6,6i3,3i4,f6.1,i4,f6.1,f9.2,f6.1,f6.2,f7.1,f7.3,1x,a) close(21) endif call this%callback(nutc,smax1,nsnr,xdt,fsig,msg, & From 171e466389b84fd3e91dfe0915d2055b3fc7e080 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 24 Dec 2020 09:51:02 -0500 Subject: [PATCH 269/426] More work on Q65 message averaging. --- displayWidgets.txt | 2 +- lib/decoder.f90 | 7 +++---- lib/q65_decode.f90 | 9 +++++---- lib/q65_sync.f90 | 13 ++++++++----- widgets/mainwindow.cpp | 2 +- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/displayWidgets.txt b/displayWidgets.txt index 2f176ae00..607f2daca 100644 --- a/displayWidgets.txt +++ b/displayWidgets.txt @@ -11,7 +11,7 @@ JT9+JT65 111010000001111000010000000000001000 JT65 111010000000111000010000000000001000 JT65/VHF 111110010000110110101100010000000000 QRA64 111110010110110110000000001000000000 -Q65 111111010110110100011000001100000000 +Q65 111111010110110100111000001100000000 ISCAT 100111000000000110000000000000000000 MSK144 101111110100000000010001000000000000 WSPR 000000000000000001010000000000000000 diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 67ed1ead0..5db45f5be 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -204,8 +204,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample) call timer('dec_q65 ',0) call my_q65%decode(q65_decoded,id2,params%nutc,params%ntr, & params%nsubmode,params%nfqso,params%ntol,params%ndepth, & - params%emedelay,mycall,hiscall,hisgrid,params%nQSOProgress, & - ncontest,logical(params%lapcqonly)) + logical(params%nclearave),params%emedelay,mycall,hiscall, & + hisgrid,params%nQSOProgress,ncontest,logical(params%lapcqonly)) call timer('dec_q65 ',1) go to 800 endif @@ -523,8 +523,7 @@ contains write(13,1012) params%nutc,nint(sync),snr,dt,float(freq),drift, & decoded,ft,nsum,nsmo 1012 format(i4.4,i4,i5,f6.2,f8.0,i4,3x,a22,' JT65',3i3) - -100 call flush(6) + call flush(6) !$omp end critical(decode_results) select type(this) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 17557cd39..9fefb8088 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -25,8 +25,9 @@ module q65_decode contains - subroutine decode(this,callback,iwave,nutc,ntrperiod,nsubmode,nfqso, & - ntol,ndepth,emedelay,mycall,hiscall,hisgrid,nQSOprogress,ncontest,lapcqonly) + subroutine decode(this,callback,iwave,nutc,ntrperiod,nsubmode,nfqso, & + ntol,ndepth,lclearave,emedelay,mycall,hiscall,hisgrid,nQSOprogress, & + ncontest,lapcqonly) ! Decodes Q65 signals ! Input: iwave Raw data, i*2 @@ -57,7 +58,7 @@ contains integer apmask(13),apsymbols(13) integer dgen(13) integer codewords(63,206) - logical lapcqonly,unpk77_success + logical lclearave,lapcqonly,unpk77_success complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s @@ -94,7 +95,7 @@ contains call q65_enc(dgen,codewords) !Initialize Q65 call timer('sync_q65',0) call q65_sync(nutc,iwave,ntrperiod*12000,mode65,codewords,ncw,nsps, & - nfqso,ntol,emedelay,xdt,f0,snr1,width,dat4,snr2,id1) + nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,id1) call timer('sync_q65',1) if(id1.eq.1 .or. id1.ge.12) then xdt1=xdt diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index e0459edc5..89588852c 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -1,5 +1,5 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & - emedelay,xdt,f0,snr1,width,dat4,snr2,id1) + ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,id1) ! Detect and align with the Q65 sync vector, returning time and frequency ! offsets and SNR estimate. @@ -24,7 +24,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & integer dat4(13) integer ijpk(2) logical unpk77_success - logical lavg + logical lavg,lclearave character*77 c77,decoded*37 real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) @@ -66,13 +66,16 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & allocate(ccf(-ia2:ia2,-53:214)) allocate(ccf1(-ia2:ia2)) + if(lclearave) then + s3avg=0. + navg=0 + endif + if(sync(1).eq.99.0) then !Generate the sync vector sync=-22.0/63.0 !Sync tone OFF do k=1,22 sync(isync(k))=1.0 !Sync tone ON enddo - s3avg=0. - navg=0 endif fac=1/32767.0 @@ -270,7 +273,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & navg=0 s3avg=0. if(lavg) go to 900 - elseif(snr1.ge.0.0) then + elseif(iand(ndepth,16).eq.16) then s3avg=s3avg+s3 navg=navg+1 write(71,3071) nutc,navg,xdt,f0,snr1 diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 857123b12..dd94bf1e4 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6388,7 +6388,7 @@ void MainWindow::on_actionQ65_triggered() m_wideGraph->setTxFreq(ui->TxFreqSpinBox->value()); switch_mode (Modes::Q65); // 012345678901234567890123456789012345 - displayWidgets(nWidgets("111111010110110100011000000100000000")); + displayWidgets(nWidgets("111111010110110100111000000100000000")); statusChanged(); } From d5957f51f9b1c0da8db2cab27ebede67850515a1 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 24 Dec 2020 13:07:29 -0500 Subject: [PATCH 270/426] Insert some (commented out) diagnostics. --- lib/q65_sync.f90 | 23 ++++++++++++++++++++--- lib/qra/q65/q65_loops.f90 | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 89588852c..b8112f250 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -197,14 +197,16 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & if(irc.ge.0 .and. plog.ge.PLOG_MIN) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment id1=1 - 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 + +! 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 ! open(55,file='fort.55',status='unknown',position='append') ! write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & ! irc,trim(decoded) !3055 format(i6,i3,6f8.2,i5,2x,a) ! close(55) + ic=ia2/4; base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); ccf1=ccf1-base @@ -254,6 +256,21 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ccf1=ccf(:,jpk)/rms if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 +! Compute s3() here, then call q65_avg(). + i1=i0+ipk-64 + i2=i1+LL-1 + j=j0+jpk-7 + n=0 + do k=1,85 + j=j+8 + if(sync(k).gt.0.0) then + cycle + endif + n=n+1 + if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) + enddo + write(40) nutc,mode_q65,LL,xdt,f0,snr1,s3 + 200 smax=maxval(ccf1) if(lavg) id1=10+navg !This is an average decode i1=-9999 diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 74f2a06f4..bdcfeccc6 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -7,6 +7,8 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & parameter (LN=1152*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 complex c00(0:npts2-1) !Analytic representation of dd(), 6000 Hz complex ,allocatable :: c0(:) !Ditto, with freq shift +! character c77*77,decoded*37 +! logical unpk77_success real a(3) !twkfreq params f,f1,f2 real s3(LN) !Symbol spectra real s3prob(64*NN) !Symbol-value probabilities @@ -81,6 +83,22 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) call timer('q65_dec ',1) if(irc.ge.0) id2=iaptype+2 + +!### Temporary ### +! if(irc.ge.0) then +! 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 +! snr2=esnodb - db(2500.0/baud) +! xdt1=xdt0 + nsps*ndt/(16.0*6000.0) +! f1=f0 + 0.5*baud*ndf +! open(56,file='fort.56',status='unknown',position='append') +! write(56,3055) idf,idt,ibw,id2,irc,xdt1,f1,snr2,trim(decoded) +!3055 format(5i3,3f8.2,2x,a) +! close(56) +! endif +!### + if(irc.ge.0) go to 100 ! irc > 0 ==> number of iterations required to decode ! -1 = invalid params From 27bf7bb9640283064dd0dcab055248e5152457a3 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Fri, 25 Dec 2020 12:22:25 -0600 Subject: [PATCH 271/426] FT8: Fix jt9 crash when nagain is invoked. --- lib/ft8/get_spectrum_baseline.f90 | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/ft8/get_spectrum_baseline.f90 b/lib/ft8/get_spectrum_baseline.f90 index 9cf4e637c..f815eba14 100644 --- a/lib/ft8/get_spectrum_baseline.f90 +++ b/lib/ft8/get_spectrum_baseline.f90 @@ -35,8 +35,19 @@ subroutine get_spectrum_baseline(dd,nfa,nfb,sbase) savg=savg + s(1:NH1,j) !Average spectrum enddo - if(nfa.lt.100) nfa=100 - if(nfb.gt.4910) nfb=4910 + nwin=nfb-nfa + if(nfa.lt.100) then + nfa=100 + if(nwin.lt.100) then ! nagain + nfb=nfa+nwin + endif + endif + if(nfb.gt.4910) then + nfb=4910 + if(nwin.lt.100) then + nfa=nfb-nwin + endif + endif call baseline(savg,nfa,nfb,sbase) return From 1ba788ecc31df727a284c0a4304d26b4aeec3762 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Fri, 25 Dec 2020 12:31:02 -0600 Subject: [PATCH 272/426] FT8: Commit the rest of the fix for the nagain crash. --- lib/ft8_decode.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ft8_decode.f90 b/lib/ft8_decode.f90 index 26b12d6c9..a7a467a31 100644 --- a/lib/ft8_decode.f90 +++ b/lib/ft8_decode.f90 @@ -132,8 +132,8 @@ contains ifa=nfa ifb=nfb if(nagain) then - ifa=nfqso-10 - ifb=nfqso+10 + ifa=nfqso-20 + ifb=nfqso+20 endif ! For now: From 3d042ee56fd786159979fce68cfd7abe6f67631b Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 26 Dec 2020 10:04:17 -0500 Subject: [PATCH 273/426] Work in progress, on Q65 message averaging. --- CMakeLists.txt | 2 ++ lib/decoder.f90 | 3 ++- lib/q65_decode.f90 | 2 +- lib/q65_sync.f90 | 34 +++++++++++++++++++--------------- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 90f6ddc6f..053fee6d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -325,6 +325,7 @@ set (wsjt_FSRCS lib/options.f90 lib/packjt.f90 lib/77bit/packjt77.f90 + lib/q65.f90 lib/q65_decode.f90 lib/readwav.f90 lib/timer_C_wrapper.f90 @@ -495,6 +496,7 @@ set (wsjt_FSRCS lib/polyfit.f90 lib/prog_args.f90 lib/ps4.f90 + lib/q65_avg.f90 lib/q65_sync.f90 lib/qra/q65/q65_ap.f90 lib/qra/q65/q65_loops.f90 diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 5db45f5be..77a03d190 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -201,6 +201,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) if(params%nmode.eq.66) then !NB: JT65 = 65, Q65 = 66. ! We're in Q65 mode open(17,file=trim(temp_dir)//'/red.dat',status='unknown') + open(14,file=trim(temp_dir)//'/avemsg.txt',status='unknown') call timer('dec_q65 ',0) call my_q65%decode(q65_decoded,id2,params%nutc,params%ntr, & params%nsubmode,params%nfqso,params%ntol,params%ndepth, & @@ -368,7 +369,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) endif close(13) if(ncontest.eq.6) close(19) - if(params%nmode.eq.4 .or. params%nmode.eq.65) close(14) + if(params%nmode.eq.4 .or. params%nmode.eq.65 .or. params%nmode.eq.66) close(14) return diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 9fefb8088..e2e402059 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -94,7 +94,7 @@ contains dgen=0 call q65_enc(dgen,codewords) !Initialize Q65 call timer('sync_q65',0) - call q65_sync(nutc,iwave,ntrperiod*12000,mode65,codewords,ncw,nsps, & + call q65_sync(nutc,iwave,ntrperiod,mode65,codewords,ncw,nsps, & nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,id1) call timer('sync_q65',1) if(id1.eq.1 .or. id1.ge.12) then diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index b8112f250..5492ee58e 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -1,4 +1,4 @@ -subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & +subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,id1) ! Detect and align with the Q65 sync vector, returning time and frequency @@ -17,7 +17,7 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & parameter (NSTEP=8) !Step size nsps/NSTEP parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 parameter (PLOG_MIN=-240.0) !List decoding threshold - integer*2 iwave(0:nmax-1) !Raw data + integer*2 iwave(0:12000*ntrperiod-1) !Raw data integer isync(22) !Indices of sync symbols integer itone(85) integer codewords(63,206) @@ -142,13 +142,14 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & enddo ! Compute 2D ccf using all 85 symbols in the list message ccf=0. + iia=200.0/df do lag=lag1,lag2 do k=1,85 j=j0 + NSTEP*(k-1) + 1 + lag if(j.ge.1 .and. j.le.jz) then do i=-ia2,ia2 ii=i0+mode_q65*(itone(k)+1)+i - if(ii.ge.1 .and. ii.le.iz) ccf(i,lag)=ccf(i,lag) + s1(ii,j) + if(ii.ge.iia .and. ii.le.iz) ccf(i,lag)=ccf(i,lag) + s1(ii,j) enddo endif enddo @@ -259,20 +260,23 @@ subroutine q65_sync(nutc,iwave,nmax,mode_q65,codewords,ncw,nsps,nfqso,ntol, & ! Compute s3() here, then call q65_avg(). i1=i0+ipk-64 i2=i1+LL-1 - j=j0+jpk-7 - n=0 - do k=1,85 - j=j+8 - if(sync(k).gt.0.0) then - cycle - endif - n=n+1 - if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) - enddo - write(40) nutc,mode_q65,LL,xdt,f0,snr1,s3 + if(i1.ge.1 .and. i2.le.iz) then + j=j0+jpk-7 + n=0 + do k=1,85 + j=j+8 + if(sync(k).gt.0.0) then + cycle + endif + n=n+1 + if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) + enddo + call q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave,xdt, & + f0,snr1,s3) + endif 200 smax=maxval(ccf1) - if(lavg) id1=10+navg !This is an average decode + if(lavg) id1=10+navg !If this is an average decode i1=-9999 i2=-9999 do i=-ia,ia From c8ace441f96662d5f6e68e4bafb0bd1e1c3bb216 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 26 Dec 2020 10:08:53 -0500 Subject: [PATCH 274/426] New files for Q65 message averaging. --- lib/q65.f90 | 14 +++++++ lib/q65_avg.f90 | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 lib/q65.f90 create mode 100644 lib/q65_avg.f90 diff --git a/lib/q65.f90 b/lib/q65.f90 new file mode 100644 index 000000000..fd8d8ac20 --- /dev/null +++ b/lib/q65.f90 @@ -0,0 +1,14 @@ +module q65 + + parameter (MAXAVE=64) + integer nsave,nlist,LL0 + integer iutc(MAXAVE) + integer iseq(MAXAVE) + integer listutc(10) + real fsave(MAXAVE) + real xdtsave(MAXAVE) + real snr1save(MAXAVE) + real,allocatable :: s3save(:,:,:) + real,allocatable :: s3avg(:,:) + +end module q65 diff --git a/lib/q65_avg.f90 b/lib/q65_avg.f90 new file mode 100644 index 000000000..b3344b31c --- /dev/null +++ b/lib/q65_avg.f90 @@ -0,0 +1,109 @@ +subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) + +! Accumulate and decode averaged Q65 data + + use q65 +! class(q65_decoder), intent(inout) :: this + character*37 avemsg + character*1 csync,cused(MAXAVE) + character*6 cutc + real s3(-64:LL-65,63) + integer iused(MAXAVE) + logical first,lclearave + data first/.true./ + save + + if(first .or. LL.ne.LL0 .or.lclearave) then + iutc=-1 + iseq=-1 + fsave=0.0 + dtdiff=0.2 + nsave=0 + LL0=LL + first=.false. + if(allocated(s3save)) deallocate(s3save) + if(allocated(s3avg)) deallocate(s3avg) + allocate(s3save(-64:LL-65,63,MAXAVE)) + allocate(s3avg(-64:LL-65,63)) + endif + + do i=1,MAXAVE !Don't save info more than once for same UTC and freq + if(nutc.eq.iutc(i) .and. abs(nfreq-fsave(i)).le.ntol) go to 10 + enddo + +! Save data for message averaging + nsave=nsave+1 + n=nutc + if(ntrperiod.ge.60) n=100*n + write(cutc,'(i6.6)') n + read(cutc,'(3i2)') ih,im,is + nsec=3600*ih + 60*im + is + iseq(nsave)=mod(nsec/ntrperiod,2) + iutc(nsave)=nutc + snr1save(nsave)=snr1 + xdtsave(nsave)=xdt + fsave(nsave)=f0 + s3save(:,:,nsave)=s3(:,:) +! write(*,3001) nutc,iseq(nsave),nsave,ntrperiod,LL,nfqso,ntol,xdt,f0,snr1 +!3001 format(i6,i2,5i5,3f7.1) + +10 snr1sum=0. + xdtsum=0. + fsum=0. + nsum=0 + + do i=1,MAXAVE + cused(i)='.' + if(iutc(i).lt.0) cycle + if(mod(iutc(i),2).ne.mod(nutc,2)) cycle !Use only same sequence + if(abs(dtxx-xdtsave(i)).gt.dtdiff) cycle !DT must match + if(abs(nfreq-fsave(i)).gt.ntol) cycle !Freq must match +! sym(1:207,1:7)=sym(1:207,1:7) + ppsave(1:207,1:7,i) + snr1sum=snr1sum + snr1save(i) + xdtsum=xdtsum + xdtsave(i) + fsum=fsum + fsave(i) + cused(i)='$' + nsum=nsum+1 + iused(nsum)=i + enddo + if(nsum.lt.MAXAVE) iused(nsum+1)=0 + + snr1ave=0. + xdtave=0. + fave=0. + if(nsum.gt.0) then + snr1ave=snr1sum/nsum + xdtave=xdtsum/nsum + fave=fsum/nsum + endif + + do i=1,nsave + csync=' ' +! if(nflipsave(i).gt.0.0) csync='*' + if(ntrperiod.le.30) write(14,1000) cused(i),iutc(i),snr1save(i), & + xdtsave(i),fsave(i),csync +1000 format(a1,i7.6,f6.1,f6.2,f7.1,1x,a1) + if(ntrperiod.ge.60) write(14,1001) cused(i),iutc(i),snr1save(i), & + xdtsave(i),fsave(i),csync +1001 format(a1,i5.4,f6.1,f6.2,f7.1,1x,a1) + enddo + if(nsum.lt.2) go to 900 + + sqt=0. + sqf=0. + do j=1,MAXAVE + i=iused(j) + if(i.eq.0) exit + csync='*' + sqt=sqt + (xdtsave(i)-dtave)**2 + sqf=sqf + (fsave(i)-fave)**2 + enddo + rmst=0. + rmsf=0. + if(nsum.ge.2) then + rmst=sqrt(sqt/(nsum-1)) + rmsf=sqrt(sqf/(nsum-1)) + endif + +900 return +end subroutine q65_avg From d8be8ff0e596e9a7c5442a637aa197c1b5c5caf2 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Sat, 26 Dec 2020 09:46:19 -0600 Subject: [PATCH 275/426] FT8: Make nagain work. --- lib/ft8_decode.f90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/ft8_decode.f90 b/lib/ft8_decode.f90 index a7a467a31..73d0f8c2a 100644 --- a/lib/ft8_decode.f90 +++ b/lib/ft8_decode.f90 @@ -118,7 +118,7 @@ contains dd1=dd go to 900 endif - if(nzhsym.eq.50 .and. ndec_early.ge.1) then + if(nzhsym.eq.50 .and. ndec_early.ge.1 .and. .not.nagain) then n=47*3456 dd(1:n)=dd1(1:n) dd(n+1:)=iwave(n+1:) @@ -131,7 +131,8 @@ contains endif ifa=nfa ifb=nfb - if(nagain) then + if(nzhsym.eq.50 .and. nagain) then + dd=iwave ifa=nfqso-20 ifb=nfqso+20 endif From 5c805dfb391369b1ad104cf763f404d1d98b1dc2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 27 Dec 2020 10:35:04 -0500 Subject: [PATCH 276/426] another realignment of red sync curve and convention for f0. --- lib/q65_sync.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 5492ee58e..d78688bb2 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -148,7 +148,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, j=j0 + NSTEP*(k-1) + 1 + lag if(j.ge.1 .and. j.le.jz) then do i=-ia2,ia2 - ii=i0+mode_q65*(itone(k)+1)+i + ii=i0+mode_q65*itone(k)+i if(ii.ge.iia .and. ii.le.iz) ccf(i,lag)=ccf(i,lag) + s1(ii,j) enddo endif @@ -159,8 +159,8 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, ccf_best=ccfmax ijpk=maxloc(ccf(-ia:ia,:)) ipk=ijpk(1)-ia-1 - jpk=ijpk(2)-53-1 - f0=nfqso + ipk*df + jpk=ijpk(2)-53-1 + f0=nfqso + (ipk-mode_q65)*df xdt=jpk*dtstep imsg_best=imsg ccf1=ccf(:,jpk) @@ -284,7 +284,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, if(i2.eq.-9999 .and. ccf1(-i).ge.0.5*smax) i2=-i enddo do i=-ia2,ia2 - freq=nfqso + i*df + freq=nfqso + (i-mode_q65)*df write(17,1100) freq,ccf1(i),xdt 1100 format(3f10.3) enddo From d246a23948b038ceacb9476f826efdbd148b4f2e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 27 Dec 2020 11:35:55 -0500 Subject: [PATCH 277/426] Work in progress on message averaging: save current status. --- lib/q65.f90 | 2 +- lib/q65_avg.f90 | 114 ++++++++++++++++++++++++++++++++++------------- lib/q65_sync.f90 | 47 +++++++++++-------- 3 files changed, 113 insertions(+), 50 deletions(-) diff --git a/lib/q65.f90 b/lib/q65.f90 index fd8d8ac20..846a5cf87 100644 --- a/lib/q65.f90 +++ b/lib/q65.f90 @@ -5,7 +5,7 @@ module q65 integer iutc(MAXAVE) integer iseq(MAXAVE) integer listutc(10) - real fsave(MAXAVE) + real f0save(MAXAVE) real xdtsave(MAXAVE) real snr1save(MAXAVE) real,allocatable :: s3save(:,:,:) diff --git a/lib/q65_avg.f90 b/lib/q65_avg.f90 index b3344b31c..87d893e6d 100644 --- a/lib/q65_avg.f90 +++ b/lib/q65_avg.f90 @@ -1,22 +1,29 @@ -subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) +subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & + baud,nsubmode,ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3) -! Accumulate and decode averaged Q65 data +! Accumulate Q65 spectra s3(LL,63) and associated parameters for +! message averaging. use q65 -! class(q65_decoder), intent(inout) :: this + use packjt77 + parameter (PLOG_MIN=-240.0) !List decoding threshold character*37 avemsg character*1 csync,cused(MAXAVE) character*6 cutc - real s3(-64:LL-65,63) + character*77 c77 + real s3(-64:LL-65,63) !Symbol spectra + real s3prob(0:63,63) !Symbol-value probabilities integer iused(MAXAVE) - logical first,lclearave + integer dat4(13) + integer codewords(63,206) + logical first,lclearave,unpk77_success data first/.true./ save if(first .or. LL.ne.LL0 .or.lclearave) then iutc=-1 iseq=-1 - fsave=0.0 + f0save=0.0 dtdiff=0.2 nsave=0 LL0=LL @@ -25,10 +32,24 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s if(allocated(s3avg)) deallocate(s3avg) allocate(s3save(-64:LL-65,63,MAXAVE)) allocate(s3avg(-64:LL-65,63)) + s3save=0. + s3avg=0. + endif + + if(ntrperiod.eq.15) then + dtdiff=0.038 + else if(ntrperiod.eq.30) then + dtdiff=0.08 + else if(ntrperiod.eq.60) then + dtdiff=0.16 + else if(ntrperiod.eq.120) then + dtdiff=0.4 + else if(ntrperiod.eq.300) then + dtdiff=0.9 endif do i=1,MAXAVE !Don't save info more than once for same UTC and freq - if(nutc.eq.iutc(i) .and. abs(nfreq-fsave(i)).le.ntol) go to 10 + if(nutc.eq.iutc(i) .and. abs(nfreq-f0save(i)).le.ntol) go to 10 enddo ! Save data for message averaging @@ -38,36 +59,45 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s write(cutc,'(i6.6)') n read(cutc,'(3i2)') ih,im,is nsec=3600*ih + 60*im + is - iseq(nsave)=mod(nsec/ntrperiod,2) - iutc(nsave)=nutc - snr1save(nsave)=snr1 - xdtsave(nsave)=xdt - fsave(nsave)=f0 - s3save(:,:,nsave)=s3(:,:) -! write(*,3001) nutc,iseq(nsave),nsave,ntrperiod,LL,nfqso,ntol,xdt,f0,snr1 -!3001 format(i6,i2,5i5,3f7.1) + iseq(nsave)=mod(nsec/ntrperiod,2) !T/R sequence: 0 (even) or 1 (odd) + iutc(nsave)=nutc !UTC, hhmm or hhmmss + snr1save(nsave)=snr1 !SNR from sync + xdtsave(nsave)=xdt !DT + f0save(nsave)=f0 !f0 + s3save(:,:,nsave)=s3(:,:) !Symbol spectra -10 snr1sum=0. +10 continue + +!10 if(nsave.lt.2) go to 900 + + snr1sum=0. xdtsum=0. fsum=0. nsum=0 + s3avg=0. +! Find previously saved spectra that should be averaged with this one do i=1,MAXAVE - cused(i)='.' + cused(i)='.' !Flag for "not used" if(iutc(i).lt.0) cycle - if(mod(iutc(i),2).ne.mod(nutc,2)) cycle !Use only same sequence - if(abs(dtxx-xdtsave(i)).gt.dtdiff) cycle !DT must match - if(abs(nfreq-fsave(i)).gt.ntol) cycle !Freq must match -! sym(1:207,1:7)=sym(1:207,1:7) + ppsave(1:207,1:7,i) + if(iseq(i).ne.iseq(nsave)) cycle !Sequence must match +! write(*,3000) i,iseq(i),nutc,iutc(i),xdt-xdtsave(i),f0-f0save(i) +!3000 format(2i2,2i5,2f7.2) + if(abs(xdt-xdtsave(i)).gt.dtdiff) cycle !DT must be close + if(abs(f0-f0save(i)).gt.float(ntol)) cycle !Freq must match +! write(*,3001) 'a',i,nsave,iseq(i),snr1,xdt,f0 +!3001 format(a1,3i4,3f8.2) + cused(i)='$' !Flag for "use this one" + s3avg=s3avg + s3save(:,:,i) !Add this spectrum snr1sum=snr1sum + snr1save(i) xdtsum=xdtsum + xdtsave(i) - fsum=fsum + fsave(i) - cused(i)='$' + fsum=fsum + f0save(i) nsum=nsum+1 iused(nsum)=i enddo if(nsum.lt.MAXAVE) iused(nsum+1)=0 +! Find averages of snr1, xdt, and f0 used in this decoding attempt. snr1ave=0. xdtave=0. fave=0. @@ -77,18 +107,18 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s fave=fsum/nsum endif +! Write parameters for display to User in the Message Averaging window. do i=1,nsave - csync=' ' -! if(nflipsave(i).gt.0.0) csync='*' if(ntrperiod.le.30) write(14,1000) cused(i),iutc(i),snr1save(i), & - xdtsave(i),fsave(i),csync -1000 format(a1,i7.6,f6.1,f6.2,f7.1,1x,a1) + xdtsave(i),f0save(i) +1000 format(a1,i7.6,f6.1,f6.2,f7.1) if(ntrperiod.ge.60) write(14,1001) cused(i),iutc(i),snr1save(i), & - xdtsave(i),fsave(i),csync -1001 format(a1,i5.4,f6.1,f6.2,f7.1,1x,a1) + xdtsave(i),f0save(i) +1001 format(a1,i5.4,f6.1,f6.2,f7.1) enddo - if(nsum.lt.2) go to 900 +! if(nsum.lt.2) go to 900 !Must have at least 2 +! Find rms scatter of DT and f0 values sqt=0. sqf=0. do j=1,MAXAVE @@ -96,7 +126,7 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s if(i.eq.0) exit csync='*' sqt=sqt + (xdtsave(i)-dtave)**2 - sqf=sqf + (fsave(i)-fave)**2 + sqf=sqf + (f0save(i)-fave)**2 enddo rmst=0. rmsf=0. @@ -105,5 +135,27 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s rmsf=sqrt(sqf/(nsum-1)) endif + s3avg=s3avg/nsum + nFadingModel=1 + do ibw=ibwa,ibwb + b90=1.72**ibw + call q65_intrinsics_ff(s3avg,nsubmode,b90/baud,nFadingModel,s3prob) + call q65_dec_fullaplist(s3avg,s3prob,codewords,ncw,esnodb,dat4,plog,irc) + if(irc.ge.0 .and. plog.ge.PLOG_MIN) then + snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment + id1=1 + write(c77,3050) dat4(1:12),dat4(13)/2 +3050 format(12b6.6,b5.5) + call unpack77(c77,0,avemsg,unpk77_success) !Unpack to get msgsent + open(55,file='fort.55',status='unknown',position='append') + write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & + irc,trim(avemsg) +3055 format(i6,i3,6f8.2,i5,2x,a) + close(55) + print*,'F ',avemsg + exit + endif + enddo + 900 return end subroutine q65_avg diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index d78688bb2..6a22ce533 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -195,6 +195,14 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, b90=1.72**ibw call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) +!### + write(*,3001) 'A',ibw,irc,xdt,f0,plog,sum(s3) +3001 format(a1,2i3,f7.2,3f8.1) + if(irc.gt.0) then + s3avg=s3 + go to 100 + endif +!### if(irc.ge.0 .and. plog.ge.PLOG_MIN) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment id1=1 @@ -260,7 +268,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, ! Compute s3() here, then call q65_avg(). i1=i0+ipk-64 i2=i1+LL-1 - if(i1.ge.1 .and. i2.le.iz) then + if(snr1.ge.2.8 .and. i1.ge.1 .and. i2.le.iz) then j=j0+jpk-7 n=0 do k=1,85 @@ -271,8 +279,11 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, n=n+1 if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) enddo - call q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave,xdt, & - f0,snr1,s3) +! s3=s3avg + write(*,3002) 'B',xdt,f0,sum(s3) +3002 format(a1,f7.2,2f8.1) + call q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & + baud,nsubmode,ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3) endif 200 smax=maxval(ccf1) @@ -290,21 +301,21 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, enddo close(17) width=df*(i2-i1) - if(id1.ge.1) then - navg=0 - s3avg=0. - if(lavg) go to 900 - elseif(iand(ndepth,16).eq.16) then - s3avg=s3avg+s3 - navg=navg+1 - write(71,3071) nutc,navg,xdt,f0,snr1 -3071 format(2i5,3f10.2) - if(navg.ge.2) then - s3=s3avg/navg - lavg=.true. - go to 10 - endif - endif +! if(id1.ge.1) then +! navg=0 +! s3avg=0. +! if(lavg) go to 900 +! elseif(iand(ndepth,16).eq.16) then +! s3avg=s3avg+s3 +! navg=navg+1 +! write(71,3071) nutc,navg,xdt,f0,snr1 +!3071 format(2i5,3f10.2) +! if(navg.ge.2) then +! s3=s3avg/navg +! lavg=.true. +! go to 10 +! endif +! endif 900 return end subroutine q65_sync From 48297e068be04d2bb368ca4110e33abd1d555928 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 27 Dec 2020 14:14:10 -0500 Subject: [PATCH 278/426] More work in ptogress ... --- lib/q65_avg.f90 | 13 ++++++++++++- lib/q65_sync.f90 | 43 +++++-------------------------------------- 2 files changed, 17 insertions(+), 39 deletions(-) diff --git a/lib/q65_avg.f90 b/lib/q65_avg.f90 index 87d893e6d..97d473dfe 100644 --- a/lib/q65_avg.f90 +++ b/lib/q65_avg.f90 @@ -143,7 +143,7 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & call q65_dec_fullaplist(s3avg,s3prob,codewords,ncw,esnodb,dat4,plog,irc) if(irc.ge.0 .and. plog.ge.PLOG_MIN) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment - id1=1 + id1=1 !### write(c77,3050) dat4(1:12),dat4(13)/2 3050 format(12b6.6,b5.5) call unpack77(c77,0,avemsg,unpk77_success) !Unpack to get msgsent @@ -157,5 +157,16 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & endif enddo + do ibw=ibwa,ibwb + b90=1.72**ibw + call q65_intrinsics_ff(s3avg,nsubmode,b90/baud,nFadingModel,s3prob) + call q65_dec(s3avg,s3prob,APmask,APsymbols,esnodb,dat4,irc) + if(irc.ge.0) then + id2=iaptype+2 + print*,'G',id2 + exit + endif + enddo ! ibw (b90 loop) + 900 return end subroutine q65_avg diff --git a/lib/q65_sync.f90 b/lib/q65_sync.f90 index 6a22ce533..4f1f73b1f 100644 --- a/lib/q65_sync.f90 +++ b/lib/q65_sync.f90 @@ -24,19 +24,18 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, integer dat4(13) integer ijpk(2) logical unpk77_success - logical lavg,lclearave + logical lclearave character*77 c77,decoded*37 real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) - real, allocatable,save :: s3avg(:,:) !Averaged data-symbol energies real, allocatable :: ccf(:,:) !CCF(freq,lag) real, allocatable :: ccf1(:) !CCF(freq) at best lag real s3prob(0:63,63) !Symbol-value probabilities real sync(85) !sync vector complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ - data sync(1)/99.0/,LL0/-1/ - save sync,navg,LL0 + data sync(1)/99.0/ + save sync snr1=0. id1=0 @@ -56,20 +55,9 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, allocate(s1(iz,jz)) allocate(s3(-64:LL-65,63)) - if(LL.ne.LL0) then - if(allocated(s3avg)) deallocate(s3avg) - allocate(s3avg(-64:LL-65,63)) - navg=0 - LL0=LL - endif allocate(c0(0:nfft-1)) allocate(ccf(-ia2:ia2,-53:214)) allocate(ccf1(-ia2:ia2)) - - if(lclearave) then - s3avg=0. - navg=0 - endif if(sync(1).eq.99.0) then !Generate the sync vector sync=-22.0/63.0 !Sync tone OFF @@ -189,7 +177,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, baud=12000.0/nsps ibwa=1.8*log(baud*mode_q65) + 2 ibwb=min(10,ibwa+4) - lavg=.false. 10 do ibw=ibwa,ibwb b90=1.72**ibw @@ -198,10 +185,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, !### write(*,3001) 'A',ibw,irc,xdt,f0,plog,sum(s3) 3001 format(a1,2i3,f7.2,3f8.1) - if(irc.gt.0) then - s3avg=s3 - go to 100 - endif + if(irc.gt.0) go to 100 !### if(irc.ge.0 .and. plog.ge.PLOG_MIN) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment @@ -224,7 +208,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, go to 200 endif enddo - if(lavg) go to 900 !###################################################################### ! Establish xdt, f0, and snr1 using sync symbols (and perhaps some AP symbols) @@ -279,7 +262,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, n=n+1 if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) enddo -! s3=s3avg write(*,3002) 'B',xdt,f0,sum(s3) 3002 format(a1,f7.2,2f8.1) call q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & @@ -287,7 +269,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, endif 200 smax=maxval(ccf1) - if(lavg) id1=10+navg !If this is an average decode +! if(lavg) id1=10+navg !If this is an average decode i1=-9999 i2=-9999 do i=-ia,ia @@ -301,21 +283,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, enddo close(17) width=df*(i2-i1) -! if(id1.ge.1) then -! navg=0 -! s3avg=0. -! if(lavg) go to 900 -! elseif(iand(ndepth,16).eq.16) then -! s3avg=s3avg+s3 -! navg=navg+1 -! write(71,3071) nutc,navg,xdt,f0,snr1 -!3071 format(2i5,3f10.2) -! if(navg.ge.2) then -! s3=s3avg/navg -! lavg=.true. -! go to 10 -! endif -! endif 900 return end subroutine q65_sync From 30ab29d9e3abff9849683e34ec275c9c032f37f3 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 27 Dec 2020 15:27:26 -0500 Subject: [PATCH 279/426] Work in progress, with lots of diagnostics. Do not use on the air! --- lib/q65.f90 | 1 + lib/q65_avg.f90 | 6 +++++- lib/q65_decode.f90 | 3 ++- lib/qra/q65/q65_loops.f90 | 28 ++++++++++++++++------------ 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/q65.f90 b/lib/q65.f90 index 846a5cf87..50e7fbb16 100644 --- a/lib/q65.f90 +++ b/lib/q65.f90 @@ -5,6 +5,7 @@ module q65 integer iutc(MAXAVE) integer iseq(MAXAVE) integer listutc(10) + integer apmask(13),apsymbols(13) real f0save(MAXAVE) real xdtsave(MAXAVE) real snr1save(MAXAVE) diff --git a/lib/q65_avg.f90 b/lib/q65_avg.f90 index 97d473dfe..8a116b3bd 100644 --- a/lib/q65_avg.f90 +++ b/lib/q65_avg.f90 @@ -157,13 +157,17 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & endif enddo + APmask=0 + APsymbols=0 + read(41) LNZ,s3avg + do ibw=ibwa,ibwb b90=1.72**ibw call q65_intrinsics_ff(s3avg,nsubmode,b90/baud,nFadingModel,s3prob) call q65_dec(s3avg,s3prob,APmask,APsymbols,esnodb,dat4,irc) + print*,'G',ibw,irc,sum(s3avg) if(irc.ge.0) then id2=iaptype+2 - print*,'G',id2 exit endif enddo ! ibw (b90 loop) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index e2e402059..303fa9df3 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -42,6 +42,8 @@ contains use timer_module, only: timer use packjt77 use, intrinsic :: iso_c_binding + use q65 + parameter (NMAX=300*12000) !Max TRperiod is 300 s class(q65_decoder), intent(inout) :: this procedure(q65_decode_callback) :: callback @@ -55,7 +57,6 @@ contains integer dat4(13) !Decoded message as 12 6-bit integers integer apsym0(58),aph10(10) integer apmask1(78),apsymbols1(78) - integer apmask(13),apsymbols(13) integer dgen(13) integer codewords(63,206) logical lclearave,lapcqonly,unpk77_success diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index bdcfeccc6..95cf573f5 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -17,7 +17,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & integer cw4(63) integer dat4(13) !Decoded message (as 13 six-bit integers) integer nap(0:11) !AP return codes - data nap/0,2,3,2,3,4,2,3,6,4,6,6/,nsave/0/ + data nap/0,2,3,2,3,4,2,3,6,4,6,6/ data cw4/0, 0, 0, 0, 8, 4,60,35,17,48,33,25,34,43,43,43,35,15,46,30, & 54,24,26,26,57,57,42, 3,23,11,49,49,16, 2, 6, 6,55,21,39,51, & 51,51,42,42,50,25,31,35,57,30, 1,54,54,10,10,22,44,58,57,40, & @@ -82,6 +82,9 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & call timer('q65_dec ',0) call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) call timer('q65_dec ',1) + print*,'H',ibw,irc,iaptype,sum(s3(1:LL*NN)) +! rewind 41 +! write(41) LL*NN,s3(1:LL*NN) if(irc.ge.0) id2=iaptype+2 !### Temporary ### @@ -107,17 +110,18 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & enddo ! ibw (b90 loop) enddo ! idt (DT loop) enddo ! idf (f0 loop) - if(iaptype.eq.0) then - a=0. - a(1)=-f0 - call twkfreq(c00,c0,npts2,6000.0,a) - jpk=3000 !### Are these definitions OK? - if(nsps.ge.3600) jpk=6000 !### TR >= 60 s - call spec64(c0,nsps,mode,mode_q65,jpk,s3,LL,NN) - call pctile(s3,LL*NN,40,base) - s3=s3/base - where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim - endif + +! if(iaptype.eq.0) then +! a=0. +! a(1)=-f0 +! call twkfreq(c00,c0,npts2,6000.0,a) +! jpk=3000 !### Are these definitions OK? +! if(nsps.ge.3600) jpk=6000 !### TR >= 60 s +! call spec64(c0,nsps,mode,mode_q65,jpk,s3,LL,NN) +! call pctile(s3,LL*NN,40,base) +! s3=s3/base +! where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim +! endif 100 if(irc.ge.0) then snr2=esnodb - db(2500.0/baud) From 4e706092234e8c7e15a6cf6dce443d43c456f4fd Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Mon, 28 Dec 2020 09:27:48 -0600 Subject: [PATCH 280/426] Minor format change for nutc in fst4_decodes.dat. --- lib/fst4_decode.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index a1b39f744..0e306abe2 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -604,7 +604,7 @@ contains write(21,3021) nutc,icand,itry,nsyncoh,iaptype, & ijitter,ntype,Keff,nsync_qual,nharderrors,dmin,nhp,hd, & sync,xsnr,xdt,fsig,w50,trim(msg) -3021 format(i6.6,6i3,3i4,f6.1,i4,f6.1,f9.2,f6.1,f6.2,f7.1,f7.3,1x,a) +3021 format(i6,6i3,3i4,f6.1,i4,f6.1,f9.2,f6.1,f6.2,f7.1,f7.3,1x,a) close(21) endif call this%callback(nutc,smax1,nsnr,xdt,fsig,msg, & From a3638c456adb11fce5c567673a49e1660d8717d9 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Mon, 28 Dec 2020 09:39:08 -0600 Subject: [PATCH 281/426] Minor formatting change in fst4_decodes.dat. --- lib/fst4_decode.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index 0e306abe2..d3ecd371b 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -604,7 +604,7 @@ contains write(21,3021) nutc,icand,itry,nsyncoh,iaptype, & ijitter,ntype,Keff,nsync_qual,nharderrors,dmin,nhp,hd, & sync,xsnr,xdt,fsig,w50,trim(msg) -3021 format(i6,6i3,3i4,f6.1,i4,f6.1,f9.2,f6.1,f6.2,f7.1,f7.3,1x,a) +3021 format(i6.6,i4,5i3,3i4,f6.1,i4,f6.1,f9.2,f6.1,f6.2,f7.1,f7.3,1x,a) close(21) endif call this%callback(nutc,smax1,nsnr,xdt,fsig,msg, & From 62074ab58fc9c14e20c647336276aa73ce071e44 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 28 Dec 2020 15:27:10 -0500 Subject: [PATCH 282/426] Much reworking of Q65 decoder. Have temporarily enabled all types of decoding passes. --- CMakeLists.txt | 6 +- lib/q65_decode.f90 | 45 ++++++++----- lib/{ => qra/q65}/q65.f90 | 1 + lib/{ => qra/q65}/q65_avg.f90 | 42 ++---------- lib/qra/q65/q65_loops.f90 | 52 +++------------ lib/{ => qra/q65}/q65_sync.f90 | 118 +++++++++++++++++++++------------ 6 files changed, 124 insertions(+), 140 deletions(-) rename lib/{ => qra/q65}/q65.f90 (83%) rename lib/{ => qra/q65}/q65_avg.f90 (77%) rename lib/{ => qra/q65}/q65_sync.f90 (75%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 053fee6d2..96cdc3d05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -325,7 +325,7 @@ set (wsjt_FSRCS lib/options.f90 lib/packjt.f90 lib/77bit/packjt77.f90 - lib/q65.f90 + lib/qra/q65/q65.f90 lib/q65_decode.f90 lib/readwav.f90 lib/timer_C_wrapper.f90 @@ -496,8 +496,8 @@ set (wsjt_FSRCS lib/polyfit.f90 lib/prog_args.f90 lib/ps4.f90 - lib/q65_avg.f90 - lib/q65_sync.f90 + lib/qra/q65/q65_avg.f90 + lib/qra/q65/q65_sync.f90 lib/qra/q65/q65_ap.f90 lib/qra/q65/q65_loops.f90 lib/qra/q65/q65_set_list.f90 diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 303fa9df3..2a10b4a67 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -42,7 +42,7 @@ contains use timer_module, only: timer use packjt77 use, intrinsic :: iso_c_binding - use q65 + use q65 !Shared variables parameter (NMAX=300*12000) !Max TRperiod is 300 s class(q65_decoder), intent(inout) :: this @@ -89,44 +89,51 @@ contains baud=12000.0/nsps df1=12000.0/nfft1 this%callback => callback - if(nutc.eq.-999) print*,lapdx,nfa,nfb,nfqso !Silence warning nFadingModel=1 - call q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) +! Set up the codewords for full-AP list decoding + call q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) dgen=0 - call q65_enc(dgen,codewords) !Initialize Q65 + call q65_enc(dgen,codewords) !Initialize the Q65 codec call timer('sync_q65',0) - call q65_sync(nutc,iwave,ntrperiod,mode65,codewords,ncw,nsps, & - nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,id1) + call q65_sync(nutc,iwave,ntrperiod,mode65,codewords,ncw,nsps, & + nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4, & + snr2,id1) call timer('sync_q65',1) + if(id1.eq.1 .or. id1.ge.12) then - xdt1=xdt + xdt1=xdt !We have a list-decode result f1=f0 - go to 100 +! go to 100 !### TEMPORARILY REMOVED ### endif if(snr1.lt.2.8) then - xdt1=0. + xdt1=0. !No reliable sync, abandon decoding attempt f1=0. go to 100 endif - jpk0=(xdt+1.0)*6000 !### Is this OK? - if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !### + + 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 fac=1.0/32767.0 dd=fac*iwave(1:npts) - nmode=65 - call ana64(dd,npts,c00) + call ana64(dd,npts,c00) !Convert to complex c00() at 6000 Sa/s + +! Generate ap symbols as in FT8 call ft8apset(mycall,hiscall,ncontest,apsym0,aph10) where(apsym0.eq.-1) apsym0=0 +! Main decoding loop starts here npasses=2 if(nQSOprogress.eq.5) npasses=3 if(lapcqonly) npasses=1 iaptype=0 do ipass=0,npasses - apmask=0 + 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 @@ -144,17 +151,21 @@ contains enddo endif endif + call timer('q65loops',0) - call q65_loops(c00,npts/2,nsps/2,nmode,mode65,nsubmode, & + call q65_loops(c00,npts/2,nsps/2,mode65,nsubmode, & nFadingModel,ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols, & xdt1,f1,snr2,dat4,id2) call timer('q65loops',1) -! snr2=snr2 + db(6912.0/nsps) - if(id2.gt.0) exit + if(id2.gt.0) exit !Exit main loop after a successful decode enddo +! No single-transmission decode. +! if(iand(ndepth,16).eq.16) call q65_avg2 + 100 decoded=' ' if(id1.gt.0 .or. id2.gt.0) then +! Unpack decoded message for display to user idec=id1+id2 write(c77,1000) dat4(1:12),dat4(13)/2 1000 format(12b6.6,b5.5) diff --git a/lib/q65.f90 b/lib/qra/q65/q65.f90 similarity index 83% rename from lib/q65.f90 rename to lib/qra/q65/q65.f90 index 50e7fbb16..47849b5af 100644 --- a/lib/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -1,6 +1,7 @@ module q65 parameter (MAXAVE=64) + parameter (PLOG_MIN=-240.0) !List decoding threshold integer nsave,nlist,LL0 integer iutc(MAXAVE) integer iseq(MAXAVE) diff --git a/lib/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 similarity index 77% rename from lib/q65_avg.f90 rename to lib/qra/q65/q65_avg.f90 index 8a116b3bd..fdd086313 100644 --- a/lib/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -6,7 +6,6 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & use q65 use packjt77 - parameter (PLOG_MIN=-240.0) !List decoding threshold character*37 avemsg character*1 csync,cused(MAXAVE) character*6 cutc @@ -69,7 +68,6 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & 10 continue !10 if(nsave.lt.2) go to 900 - snr1sum=0. xdtsum=0. fsum=0. @@ -107,7 +105,7 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & fave=fsum/nsum endif -! Write parameters for display to User in the Message Averaging window. +! Write parameters for display to User in the Message Averaging (F7) window. do i=1,nsave if(ntrperiod.le.30) write(14,1000) cused(i),iutc(i),snr1save(i), & xdtsave(i),f0save(i) @@ -118,56 +116,30 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & enddo ! if(nsum.lt.2) go to 900 !Must have at least 2 -! Find rms scatter of DT and f0 values - sqt=0. - sqf=0. - do j=1,MAXAVE - i=iused(j) - if(i.eq.0) exit - csync='*' - sqt=sqt + (xdtsave(i)-dtave)**2 - sqf=sqf + (f0save(i)-fave)**2 - enddo - rmst=0. - rmsf=0. - if(nsum.ge.2) then - rmst=sqrt(sqt/(nsum-1)) - rmsf=sqrt(sqf/(nsum-1)) - endif - s3avg=s3avg/nsum nFadingModel=1 do ibw=ibwa,ibwb b90=1.72**ibw - call q65_intrinsics_ff(s3avg,nsubmode,b90/baud,nFadingModel,s3prob) - call q65_dec_fullaplist(s3avg,s3prob,codewords,ncw,esnodb,dat4,plog,irc) + b90ts=b90/baud + call q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,avemsg) if(irc.ge.0 .and. plog.ge.PLOG_MIN) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment id1=1 !### - write(c77,3050) dat4(1:12),dat4(13)/2 -3050 format(12b6.6,b5.5) - call unpack77(c77,0,avemsg,unpk77_success) !Unpack to get msgsent - open(55,file='fort.55',status='unknown',position='append') - write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & - irc,trim(avemsg) -3055 format(i6,i3,6f8.2,i5,2x,a) - close(55) - print*,'F ',avemsg + print*,'B dec1 ',ibw,irc,avemsg exit endif enddo APmask=0 APsymbols=0 - read(41) LNZ,s3avg do ibw=ibwa,ibwb b90=1.72**ibw - call q65_intrinsics_ff(s3avg,nsubmode,b90/baud,nFadingModel,s3prob) - call q65_dec(s3avg,s3prob,APmask,APsymbols,esnodb,dat4,irc) - print*,'G',ibw,irc,sum(s3avg) + b90ts=b90/baud + call q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,avemsg) if(irc.ge.0) then id2=iaptype+2 + print*,'C dec2 ',ibw,irc,avemsg exit endif enddo ! ibw (b90 loop) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 95cf573f5..b89d385ef 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,4 +1,4 @@ -subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & +subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,nFadingModel, & ndepth,jpk0,xdt0,f0,width,iaptype,APmask,APsymbols,xdt1,f1,snr2,dat4,id2) use packjt77 @@ -7,8 +7,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & parameter (LN=1152*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 complex c00(0:npts2-1) !Analytic representation of dd(), 6000 Hz complex ,allocatable :: c0(:) !Ditto, with freq shift -! character c77*77,decoded*37 -! logical unpk77_success + character decoded*37 real a(3) !twkfreq params f,f1,f2 real s3(LN) !Symbol spectra real s3prob(64*NN) !Symbol-value probabilities @@ -61,7 +60,7 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & jpk=jpk0 + nsps*ndt/16 !tsym/16 if(jpk.lt.0) jpk=0 call timer('spec64 ',0) - call spec64(c0,nsps,mode,mode_q65,jpk,s3,LL,NN) + call spec64(c0,nsps,65,mode_q65,jpk,s3,LL,NN) call timer('spec64 ',1) call pctile(s3,LL*NN,40,base) s3=s3/base @@ -75,54 +74,21 @@ subroutine q65_loops(c00,npts2,nsps,mode,mode_q65,nsubmode,nFadingModel, & xx=1.885*log(3.0*width)+nbw b90=1.7**xx if(b90.gt.345.0) cycle - call timer('q65_intr',0) b90ts = b90/baud - call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) - call timer('q65_intr',1) - call timer('q65_dec ',0) - call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) - call timer('q65_dec ',1) - print*,'H',ibw,irc,iaptype,sum(s3(1:LL*NN)) -! rewind 41 -! write(41) LL*NN,s3(1:LL*NN) - if(irc.ge.0) id2=iaptype+2 - -!### Temporary ### -! if(irc.ge.0) then -! 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 -! snr2=esnodb - db(2500.0/baud) -! xdt1=xdt0 + nsps*ndt/(16.0*6000.0) -! f1=f0 + 0.5*baud*ndf -! open(56,file='fort.56',status='unknown',position='append') -! write(56,3055) idf,idt,ibw,id2,irc,xdt1,f1,snr2,trim(decoded) -!3055 format(5i3,3f8.2,2x,a) -! close(56) -! endif -!### - - if(irc.ge.0) go to 100 + call q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) ! irc > 0 ==> number of iterations required to decode ! -1 = invalid params ! -2 = decode failed ! -3 = CRC mismatch + if(irc.ge.0) then + id2=iaptype+2 + print*,'D dec2 ',ibw,irc,decoded + go to 100 + endif enddo ! ibw (b90 loop) enddo ! idt (DT loop) enddo ! idf (f0 loop) -! if(iaptype.eq.0) then -! a=0. -! a(1)=-f0 -! call twkfreq(c00,c0,npts2,6000.0,a) -! jpk=3000 !### Are these definitions OK? -! if(nsps.ge.3600) jpk=6000 !### TR >= 60 s -! call spec64(c0,nsps,mode,mode_q65,jpk,s3,LL,NN) -! call pctile(s3,LL*NN,40,base) -! s3=s3/base -! where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim -! endif - 100 if(irc.ge.0) then snr2=esnodb - db(2500.0/baud) xdt1=xdt0 + nsps*ndt/(16.0*6000.0) diff --git a/lib/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 similarity index 75% rename from lib/q65_sync.f90 rename to lib/qra/q65/q65_sync.f90 index 4f1f73b1f..5459815b9 100644 --- a/lib/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -1,5 +1,5 @@ -subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, & - ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,id1) +subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & + nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,id1) ! Detect and align with the Q65 sync vector, returning time and frequency ! offsets and SNR estimate. @@ -23,9 +23,8 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, integer codewords(63,206) integer dat4(13) integer ijpk(2) - logical unpk77_success logical lclearave - character*77 c77,decoded*37 + character*37 decoded real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) real, allocatable :: ccf(:,:) !CCF(freq,lag) @@ -178,34 +177,22 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, ibwa=1.8*log(baud*mode_q65) + 2 ibwb=min(10,ibwa+4) -10 do ibw=ibwa,ibwb + do ibw=ibwa,ibwb b90=1.72**ibw - call q65_intrinsics_ff(s3,nsubmode,b90/baud,nFadingModel,s3prob) - call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) -!### - write(*,3001) 'A',ibw,irc,xdt,f0,plog,sum(s3) -3001 format(a1,2i3,f7.2,3f8.1) - if(irc.gt.0) go to 100 -!### - if(irc.ge.0 .and. plog.ge.PLOG_MIN) then + b90ts=b90/baud + call q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) +! irc=-99 !### TEMPORARY ### + if(irc.ge.0) then + print*,'A dec1 ',ibw,irc,decoded snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment id1=1 - -! 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 -! open(55,file='fort.55',status='unknown',position='append') -! write(55,3055) nutc,ibw,xdt,f0,85.0*base,ccfmax,snr2,plog, & -! irc,trim(decoded) -!3055 format(i6,i3,6f8.2,i5,2x,a) -! close(55) - ic=ia2/4; base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); ccf1=ccf1-base smax=maxval(ccf1) if(smax.gt.10.0) ccf1=10.0*ccf1/smax - go to 200 + go to 100 !### TEMPORARY ### +! go to 200 endif enddo @@ -248,24 +235,26 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, ccf1=ccf(:,jpk)/rms if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 -! Compute s3() here, then call q65_avg(). - i1=i0+ipk-64 - i2=i1+LL-1 - if(snr1.ge.2.8 .and. i1.ge.1 .and. i2.le.iz) then - j=j0+jpk-7 - n=0 - do k=1,85 - j=j+8 - if(sync(k).gt.0.0) then - cycle - endif - n=n+1 - if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) - enddo - write(*,3002) 'B',xdt,f0,sum(s3) -3002 format(a1,f7.2,2f8.1) - call q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & - baud,nsubmode,ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3) + if(iand(ndepth,16).eq.16) then +! Fill s3() from s1() here, then call q65_avg(). + i1=i0+ipk-64 + i2=i1+LL-1 + if(snr1.ge.2.8 .and. i1.ge.1 .and. i2.le.iz) then + j=j0+jpk-7 + n=0 + do k=1,85 + j=j+8 + if(sync(k).gt.0.0) then + cycle + endif + n=n+1 + if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) + enddo +! write(*,3002) 'B',xdt,f0,sum(s3) +!3002 format(a1,f7.2,2f8.1) + call q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & + baud,nsubmode,ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3) + endif endif 200 smax=maxval(ccf1) @@ -286,3 +275,48 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps,nfqso,ntol, 900 return end subroutine q65_sync + +subroutine q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) + + use q65 + use packjt77 + real s3prob(0:63,63) !Symbol-value probabilities + integer codewords(63,206) + integer dat4(13) + character c77*77,decoded*37 + logical unpk77_success + + nFadingModel=1 + decoded=' ' + call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) + call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) + if(irc.ge.0 .and. plog.gt.PLOG_MIN) then + 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 + endif + + return +end subroutine q65_dec1 + +subroutine q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) + + use q65 + use packjt77 + real s3prob(0:63,63) !Symbol-value probabilities + integer dat4(13) + character c77*77,decoded*37 + logical unpk77_success + + nFadingModel=1 + decoded=' ' + call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) + call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) + if(irc.ge.0) then + 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 + endif + + return +end subroutine q65_dec2 From 520a0c7cc0c46d52329613a7ada8563080ed7e0a Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 28 Dec 2020 18:53:13 -0500 Subject: [PATCH 283/426] Work in progress for Q65 decoder. --- lib/q65_decode.f90 | 8 ++++---- lib/qra/q65/q65.f90 | 1 + lib/qra/q65/q65_avg.f90 | 34 ++++++++++++++++------------------ lib/qra/q65/q65_loops.f90 | 9 +++------ lib/qra/q65/q65_sync.f90 | 11 +++++------ 5 files changed, 29 insertions(+), 34 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 2a10b4a67..7ed685da5 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -153,16 +153,16 @@ contains endif call timer('q65loops',0) - call q65_loops(c00,npts/2,nsps/2,mode65,nsubmode, & - nFadingModel,ndepth,jpk0,xdt,f0,width,iaptype,apmask,apsymbols, & - xdt1,f1,snr2,dat4,id2) + call q65_loops(c00,npts/2,nsps/2,mode65,nsubmode,ndepth,jpk0, & + xdt,f0,width,iaptype,xdt1,f1,snr2,dat4,id2) call timer('q65loops',1) if(id2.gt.0) exit !Exit main loop after a successful decode enddo ! No single-transmission decode. ! if(iand(ndepth,16).eq.16) call q65_avg2 - +! print*,'AAA: try for avg',navg + 100 decoded=' ' if(id1.gt.0 .or. id2.gt.0) then ! Unpack decoded message for display to user diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 47849b5af..fcc023d11 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -7,6 +7,7 @@ module q65 integer iseq(MAXAVE) integer listutc(10) integer apmask(13),apsymbols(13) + integer navg real f0save(MAXAVE) real xdtsave(MAXAVE) real snr1save(MAXAVE) diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index fdd086313..6a06a36f7 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -1,4 +1,4 @@ -subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & +subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave, & baud,nsubmode,ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3) ! Accumulate Q65 spectra s3(LL,63) and associated parameters for @@ -7,15 +7,13 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & use q65 use packjt77 character*37 avemsg - character*1 csync,cused(MAXAVE) + character*1 cused(MAXAVE) character*6 cutc - character*77 c77 real s3(-64:LL-65,63) !Symbol spectra - real s3prob(0:63,63) !Symbol-value probabilities integer iused(MAXAVE) integer dat4(13) integer codewords(63,206) - logical first,lclearave,unpk77_success + logical first,lclearave data first/.true./ save @@ -71,7 +69,7 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & snr1sum=0. xdtsum=0. fsum=0. - nsum=0 + navg=0 s3avg=0. ! Find previously saved spectra that should be averaged with this one @@ -90,19 +88,19 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & snr1sum=snr1sum + snr1save(i) xdtsum=xdtsum + xdtsave(i) fsum=fsum + f0save(i) - nsum=nsum+1 - iused(nsum)=i + navg=navg+1 + iused(navg)=i enddo - if(nsum.lt.MAXAVE) iused(nsum+1)=0 + if(navg.lt.MAXAVE) iused(navg+1)=0 ! Find averages of snr1, xdt, and f0 used in this decoding attempt. snr1ave=0. xdtave=0. fave=0. - if(nsum.gt.0) then - snr1ave=snr1sum/nsum - xdtave=xdtsum/nsum - fave=fsum/nsum + if(navg.gt.0) then + snr1ave=snr1sum/navg + xdtave=xdtsum/navg + fave=fsum/navg endif ! Write parameters for display to User in the Message Averaging (F7) window. @@ -114,9 +112,9 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & xdtsave(i),f0save(i) 1001 format(a1,i5.4,f6.1,f6.2,f7.1) enddo -! if(nsum.lt.2) go to 900 !Must have at least 2 +! if(navg.lt.2) go to 900 !Must have at least 2 - s3avg=s3avg/nsum + s3avg=s3avg/navg nFadingModel=1 do ibw=ibwa,ibwb b90=1.72**ibw @@ -125,7 +123,7 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & if(irc.ge.0 .and. plog.ge.PLOG_MIN) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment id1=1 !### - print*,'B dec1 ',ibw,irc,avemsg +! print*,'B dec1 ',ibw,irc,avemsg exit endif enddo @@ -139,10 +137,10 @@ subroutine q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & call q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,avemsg) if(irc.ge.0) then id2=iaptype+2 - print*,'C dec2 ',ibw,irc,avemsg +! print*,'C dec2 ',ibw,irc,avemsg exit endif enddo ! ibw (b90 loop) -900 return + return end subroutine q65_avg diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index b89d385ef..2fec7891f 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,5 +1,5 @@ -subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,nFadingModel, & - ndepth,jpk0,xdt0,f0,width,iaptype,APmask,APsymbols,xdt1,f1,snr2,dat4,id2) +subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & + xdt0,f0,width,iaptype,xdt1,f1,snr2,dat4,id2) use packjt77 use timer_module, only: timer @@ -10,9 +10,6 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,nFadingModel, & character decoded*37 real a(3) !twkfreq params f,f1,f2 real s3(LN) !Symbol spectra - real s3prob(64*NN) !Symbol-value probabilities - integer APmask(13) - integer APsymbols(13) integer cw4(63) integer dat4(13) !Decoded message (as 13 six-bit integers) integer nap(0:11) !AP return codes @@ -82,7 +79,7 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,nFadingModel, & ! -3 = CRC mismatch if(irc.ge.0) then id2=iaptype+2 - print*,'D dec2 ',ibw,irc,decoded +! print*,'D dec2 ',ibw,irc,id2,decoded go to 100 endif enddo ! ibw (b90 loop) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 5459815b9..afe36bf50 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -29,7 +29,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) real, allocatable :: ccf(:,:) !CCF(freq,lag) real, allocatable :: ccf1(:) !CCF(freq) at best lag - real s3prob(0:63,63) !Symbol-value probabilities real sync(85) !sync vector complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ @@ -183,7 +182,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & call q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) ! irc=-99 !### TEMPORARY ### if(irc.ge.0) then - print*,'A dec1 ',ibw,irc,decoded +! print*,'A dec1 ',ibw,irc,decoded snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment id1=1 ic=ia2/4; @@ -191,8 +190,8 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & ccf1=ccf1-base smax=maxval(ccf1) if(smax.gt.10.0) ccf1=10.0*ccf1/smax - go to 100 !### TEMPORARY ### -! go to 200 +! go to 100 !### TEMPORARY ### + go to 200 endif enddo @@ -252,7 +251,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & enddo ! write(*,3002) 'B',xdt,f0,sum(s3) !3002 format(a1,f7.2,2f8.1) - call q65_avg(nutc,ntrperiod,mode_q65,LL,nfqso,ntol,lclearave, & + call q65_avg(nutc,ntrperiod,LL,ntol,lclearave, & baud,nsubmode,ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3) endif endif @@ -266,7 +265,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & if(i2.eq.-9999 .and. ccf1(-i).ge.0.5*smax) i2=-i enddo do i=-ia2,ia2 - freq=nfqso + (i-mode_q65)*df + freq=nfqso + i*df write(17,1100) freq,ccf1(i),xdt 1100 format(3f10.3) enddo From b6d2002985a5266b0ea5cab244169efc8520c5dd Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 29 Dec 2020 16:41:48 -0500 Subject: [PATCH 284/426] Q65 decoder mostly working now with all (A B C D) decoding types. --- lib/q65_decode.f90 | 18 +++++++-------- lib/qra/q65/q65_avg.f90 | 48 +++++++++++++++++++++------------------ lib/qra/q65/q65_loops.f90 | 3 ++- lib/qra/q65/q65_sync.f90 | 10 ++++---- 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 7ed685da5..e7f8c23a3 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -97,13 +97,12 @@ contains call timer('sync_q65',0) call q65_sync(nutc,iwave,ntrperiod,mode65,codewords,ncw,nsps, & nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4, & - snr2,id1) + snr2,idec) call timer('sync_q65',1) - - if(id1.eq.1 .or. id1.ge.12) then + if(idec.ge.0) then xdt1=xdt !We have a list-decode result f1=f0 -! go to 100 !### TEMPORARILY REMOVED ### + go to 100 endif if(snr1.lt.2.8) then @@ -154,19 +153,20 @@ contains call timer('q65loops',0) call q65_loops(c00,npts/2,nsps/2,mode65,nsubmode,ndepth,jpk0, & - xdt,f0,width,iaptype,xdt1,f1,snr2,dat4,id2) + xdt,f0,width,iaptype,xdt1,f1,snr2,dat4,idec) call timer('q65loops',1) if(id2.gt.0) exit !Exit main loop after a successful decode enddo ! No single-transmission decode. -! if(iand(ndepth,16).eq.16) call q65_avg2 -! print*,'AAA: try for avg',navg + if(iand(ndepth,16).eq.16) then + call q65_avg2(nutc,ntrperiod,LL,ntol,lclearave, & + baud,nsubmode,ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3,dat4,idec) + endif 100 decoded=' ' - if(id1.gt.0 .or. id2.gt.0) then + if(idec.gt.0) then ! Unpack decoded message for display to user - idec=id1+id2 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 diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index 6a06a36f7..7ff7724be 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -1,5 +1,5 @@ -subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave, & - baud,nsubmode,ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3) +subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,baud,nsubmode, & + ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3) ! Accumulate Q65 spectra s3(LL,63) and associated parameters for ! message averaging. @@ -63,9 +63,14 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave, & f0save(nsave)=f0 !f0 s3save(:,:,nsave)=s3(:,:) !Symbol spectra -10 continue +10 return -!10 if(nsave.lt.2) go to 900 + entry q65_avg2(nutc,ntrperiod,LL,ntol,lclearave,baud,nsubmode, & + ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3,dat4,idec) + + mode_q65=2**nsubmode + ibwa=1.8*log(baud*mode_q65) + 2 + ibwb=min(10,ibwa+4) snr1sum=0. xdtsum=0. fsum=0. @@ -77,12 +82,8 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave, & cused(i)='.' !Flag for "not used" if(iutc(i).lt.0) cycle if(iseq(i).ne.iseq(nsave)) cycle !Sequence must match -! write(*,3000) i,iseq(i),nutc,iutc(i),xdt-xdtsave(i),f0-f0save(i) -!3000 format(2i2,2i5,2f7.2) if(abs(xdt-xdtsave(i)).gt.dtdiff) cycle !DT must be close if(abs(f0-f0save(i)).gt.float(ntol)) cycle !Freq must match -! write(*,3001) 'a',i,nsave,iseq(i),snr1,xdt,f0 -!3001 format(a1,3i4,3f8.2) cused(i)='$' !Flag for "use this one" s3avg=s3avg + s3save(:,:,i) !Add this spectrum snr1sum=snr1sum + snr1save(i) @@ -92,15 +93,16 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave, & iused(navg)=i enddo if(navg.lt.MAXAVE) iused(navg+1)=0 - + if(navg.lt.2) go to 900 + ! Find averages of snr1, xdt, and f0 used in this decoding attempt. snr1ave=0. xdtave=0. - fave=0. + f0ave=0. if(navg.gt.0) then snr1ave=snr1sum/navg xdtave=xdtsum/navg - fave=fsum/navg + f0ave=fsum/navg endif ! Write parameters for display to User in the Message Averaging (F7) window. @@ -119,28 +121,30 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave, & do ibw=ibwa,ibwb b90=1.72**ibw b90ts=b90/baud - call q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,avemsg) + call q65_dec1(s3avg,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,avemsg) +! irc=-99 !### TEMPORARY ### if(irc.ge.0 .and. plog.ge.PLOG_MIN) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment - id1=1 !### -! print*,'B dec1 ',ibw,irc,avemsg - exit + idec=10+navg !### +! print*,'C dec1 ',ibw,irc,idec,avemsg + go to 900 endif enddo - APmask=0 - APsymbols=0 +! Should loop here over full range of available AP +! APmask=0 +! APsymbols=0 do ibw=ibwa,ibwb b90=1.72**ibw b90ts=b90/baud - call q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,avemsg) + call q65_dec2(s3avg,nsubmode,b90ts,esnodb,irc,dat4,avemsg) if(irc.ge.0) then - id2=iaptype+2 -! print*,'C dec2 ',ibw,irc,avemsg - exit + idec=10*(iaptype+2) + navg +! print*,'D dec2 ',ibw,irc,avemsg + go to 900 endif enddo ! ibw (b90 loop) - return +900 return end subroutine q65_avg diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 2fec7891f..d734af305 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -77,9 +77,10 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & ! -1 = invalid params ! -2 = decode failed ! -3 = CRC mismatch +! irc=-99 !### TEMPORARY ### if(irc.ge.0) then id2=iaptype+2 -! print*,'D dec2 ',ibw,irc,id2,decoded +! print*,'B dec2 ',ibw,irc,id2,decoded go to 100 endif enddo ! ibw (b90 loop) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index afe36bf50..a27962c3a 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -1,5 +1,5 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & - nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,id1) + nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,idec) ! Detect and align with the Q65 sync vector, returning time and frequency ! offsets and SNR estimate. @@ -35,8 +35,8 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & data sync(1)/99.0/ save sync + idec=-1 snr1=0. - id1=0 dat4=0 LL=64*(2+mode_q65) nfft=nsps @@ -182,15 +182,14 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & call q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) ! irc=-99 !### TEMPORARY ### if(irc.ge.0) then -! print*,'A dec1 ',ibw,irc,decoded +! print*,'A dec1 ',ibw,irc,esnodb,baud,trim(decoded) snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment - id1=1 + idec=1 ic=ia2/4; base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); ccf1=ccf1-base smax=maxval(ccf1) if(smax.gt.10.0) ccf1=10.0*ccf1/smax -! go to 100 !### TEMPORARY ### go to 200 endif enddo @@ -257,7 +256,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & endif 200 smax=maxval(ccf1) -! if(lavg) id1=10+navg !If this is an average decode i1=-9999 i2=-9999 do i=-ia,ia From 057967a7610eb9f271e367b80c756bd16b2b0439 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 30 Dec 2020 08:44:56 -0500 Subject: [PATCH 285/426] For Q65 nsg averaging, wrap nsave when it reaches MAXAVE. --- lib/qra/q65/q65_avg.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index 7ff7724be..9d605fb72 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -51,6 +51,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,baud,nsubmode, & ! Save data for message averaging nsave=nsave+1 + nsave=mod(nsave-1,MAXAVE)+1 n=nutc if(ntrperiod.ge.60) n=100*n write(cutc,'(i6.6)') n From d81c01d23deb93440d65986c0d16a9770032236c Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 30 Dec 2020 09:07:41 -0500 Subject: [PATCH 286/426] Remove unneeded arguments from calls to q65_avg and q65_avg2. --- lib/q65_decode.f90 | 4 ++-- lib/qra/q65/q65_avg.f90 | 7 +++---- lib/qra/q65/q65_sync.f90 | 3 +-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index e7f8c23a3..de7a7dede 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -160,8 +160,8 @@ contains ! No single-transmission decode. if(iand(ndepth,16).eq.16) then - call q65_avg2(nutc,ntrperiod,LL,ntol,lclearave, & - baud,nsubmode,ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3,dat4,idec) + call q65_avg2(ntrperiod,ntol,baud,nsubmode,ibwa,ibwb,codewords,ncw, & + xdt,f0,snr1,dat4,idec) endif 100 decoded=' ' diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index 9d605fb72..1cb190136 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -1,5 +1,4 @@ -subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,baud,nsubmode, & - ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3) +subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) ! Accumulate Q65 spectra s3(LL,63) and associated parameters for ! message averaging. @@ -66,8 +65,8 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,baud,nsubmode, & 10 return - entry q65_avg2(nutc,ntrperiod,LL,ntol,lclearave,baud,nsubmode, & - ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3,dat4,idec) + entry q65_avg2(ntrperiod,ntol,baud,nsubmode,ibwa,ibwb,codewords,ncw, & + xdt,f0,snr1,dat4,idec) mode_q65=2**nsubmode ibwa=1.8*log(baud*mode_q65) + 2 diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index a27962c3a..600a5c86a 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -250,8 +250,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & enddo ! write(*,3002) 'B',xdt,f0,sum(s3) !3002 format(a1,f7.2,2f8.1) - call q65_avg(nutc,ntrperiod,LL,ntol,lclearave, & - baud,nsubmode,ibwa,ibwb,codewords,ncw,xdt,f0,snr1,s3) + call q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) endif endif From 412422db177f0fd23c6480b32829aec9b2b0612f Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 30 Dec 2020 10:42:27 -0500 Subject: [PATCH 287/426] Add 'q' to the Q65 end-of-line flags; label right text window for "Average decodes". --- lib/decoder.f90 | 16 +++++++++++----- lib/q65_decode.f90 | 13 +++++++++++++ widgets/mainwindow.cpp | 27 +++++++++++++++++---------- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 77a03d190..26e5dcfa8 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -464,7 +464,7 @@ contains integer, intent(in) :: nsum integer, intent(in) :: minsync - integer i,nap,nft + integer i,nap logical is_deep,is_average character decoded*22,csync*2,cflags*3 @@ -774,15 +774,21 @@ contains character(len=37), intent(in) :: decoded integer, intent(in) :: idec integer, intent(in) :: ntrperiod + character*3 cflags + + write(cflags,1000) idec +1000 format('q',i2) + if(cflags(2:2).eq.' ') cflags(2:3)=cflags(3:3)//' ' + if(cflags(2:2).eq.'-') cflags=' ' if(ntrperiod.lt.60) then - write(*,1001) nutc,nsnr,dt,nint(freq),decoded,idec -1001 format(i6.6,i4,f5.1,i5,' : ',1x,a37,1x,i2) + write(*,1001) nutc,nsnr,dt,nint(freq),decoded,cflags +1001 format(i6.6,i4,f5.1,i5,' : ',1x,a37,1x,a3) write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded 1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') else - write(*,1003) nutc,nsnr,dt,nint(freq),decoded,idec -1003 format(i4.4,i4,f5.1,i5,' : ',1x,a37,1x,i2) + write(*,1003) nutc,nsnr,dt,nint(freq),decoded,cflags +1003 format(i4.4,i4,f5.1,i5,' : ',1x,a37,1x,a3) write(13,1004) nutc,nint(sync),nsnr,dt,freq,0,decoded 1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index de7a7dede..19ac5ac8f 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -166,6 +166,19 @@ contains 100 decoded=' ' if(idec.gt.0) then + +! ------------------------------------------------------ +! idec Meaning +! ------------------------------------------------------ +! -1: No decode +! 1: Decode with AP for "MyCall DxCall ?" +! 2: Decode without AP information +! 3: Decode with AP for "CQ ? ?" +! 4: Decode with AP for "MyCall ? ?" +! ------------------------------------------------------ +! Second digit (if any) is number of sequences averaged. +! ------------------------------------------------------ + ! Unpack decoded message for display to user write(c77,1000) dat4(1:12),dat4(13)/2 1000 format(12b6.6,b5.5) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index dd94bf1e4..2c06d6dd6 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3359,8 +3359,7 @@ void MainWindow::readFromStdout() //readFromStdout continue; } } - if (m_mode!="FT8" and m_mode!="FT4" - && !m_mode.startsWith ("FST4")) { + if (m_mode!="FT8" and m_mode!="FT4" and !m_mode.startsWith ("FST4") and m_mode!="Q65") { //Pad 22-char msg to at least 37 chars line_read = line_read.left(44) + " " + line_read.mid(44); } @@ -3375,7 +3374,7 @@ void MainWindow::readFromStdout() //readFromStdout } else { m_nDecodes+=1; ndecodes_label.setText(QString::number(m_nDecodes)); - if(m_mode=="JT4" or m_mode=="JT65") { + if(m_mode=="JT4" or m_mode=="JT65" or m_mode=="Q65") { //### Do something about Q65 here ? ### int nf=line_read.indexOf("f"); if(nf>0) { @@ -3388,13 +3387,17 @@ void MainWindow::readFromStdout() //readFromStdout navg=line_read.mid(nd+2,1).toInt(); if(line_read.mid(nd+2,1)=="*") navg=10; } - if(m_mode=="JT65" or m_mode=="JT4") { - int na=-1; - if(nf<0 and nd<0) na=line_read.indexOf("a"); - if(na>0) { - navg=line_read.mid(na+2,1).toInt(); - if(line_read.mid(na+2,1)=="*") navg=10; - } + int na=-1; + if(nf<0 and nd<0) na=line_read.indexOf("a"); + if(na>0) { + navg=line_read.mid(na+2,1).toInt(); + if(line_read.mid(na+2,1)=="*") navg=10; + } + int nq=-1; + if(nf<0 and nd<0 and na<0) nq=line_read.indexOf("q"); + if(nq>0) { + navg=line_read.mid(nq+2,1).toInt(); + if(line_read.mid(nq+2,1)=="*") navg=10; } if(navg>=2) bAvgMsg=true; } @@ -6389,6 +6392,10 @@ void MainWindow::on_actionQ65_triggered() switch_mode (Modes::Q65); // 012345678901234567890123456789012345 displayWidgets(nWidgets("111111010110110100111000000100000000")); + ui->lh_decodes_title_label->setText(tr ("Single-Period Decodes")); + ui->rh_decodes_title_label->setText(tr ("Average Decodes")); + ui->lh_decodes_headings_label->setText("UTC dB DT Freq " + tr ("Message")); + ui->rh_decodes_headings_label->setText("UTC dB DT Freq " + tr ("Message")); statusChanged(); } From 91687c573bf07e4506dc4bf41187bf66d6a20270 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 30 Dec 2020 11:02:49 -0500 Subject: [PATCH 288/426] Fix the reporting of SNR for Q65 average decodes. --- lib/q65_decode.f90 | 2 +- lib/qra/q65/q65_avg.f90 | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 19ac5ac8f..4ec007bcb 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -161,7 +161,7 @@ contains ! No single-transmission decode. if(iand(ndepth,16).eq.16) then call q65_avg2(ntrperiod,ntol,baud,nsubmode,ibwa,ibwb,codewords,ncw, & - xdt,f0,snr1,dat4,idec) + xdt,f0,snr1,snr2,dat4,idec) endif 100 decoded=' ' diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index 1cb190136..fcf2b85bd 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -66,7 +66,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) 10 return entry q65_avg2(ntrperiod,ntol,baud,nsubmode,ibwa,ibwb,codewords,ncw, & - xdt,f0,snr1,dat4,idec) + xdt,f0,snr1,snr2,dat4,idec) mode_q65=2**nsubmode ibwa=1.8*log(baud*mode_q65) + 2 @@ -114,7 +114,6 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) xdtsave(i),f0save(i) 1001 format(a1,i5.4,f6.1,f6.2,f7.1) enddo -! if(navg.lt.2) go to 900 !Must have at least 2 s3avg=s3avg/navg nFadingModel=1 @@ -125,7 +124,8 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) ! irc=-99 !### TEMPORARY ### if(irc.ge.0 .and. plog.ge.PLOG_MIN) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment - idec=10+navg !### + snr2=snr2 - db(float(navg)) !Is this right? + idec=10+navg ! print*,'C dec1 ',ibw,irc,idec,avemsg go to 900 endif @@ -140,8 +140,10 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) b90ts=b90/baud call q65_dec2(s3avg,nsubmode,b90ts,esnodb,irc,dat4,avemsg) if(irc.ge.0) then + snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment + snr2=snr2 - db(float(navg)) !Is this right? idec=10*(iaptype+2) + navg -! print*,'D dec2 ',ibw,irc,avemsg +! print*,'D dec2 ',ibw,dec,snr2,avemsg go to 900 endif enddo ! ibw (b90 loop) From 77a0e01b05c5414cd6b3b114a81cce34e471d7e3 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 30 Dec 2020 12:10:03 -0500 Subject: [PATCH 289/426] Allow for all AP types when averaging Q65, and for navg > 9. --- lib/decoder.f90 | 19 ++++++++---- lib/q65_decode.f90 | 4 +-- lib/qra/q65/q65_avg.f90 | 64 ++++++++++++++++++++++++++++++----------- 3 files changed, 63 insertions(+), 24 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 26e5dcfa8..15ce66989 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -774,12 +774,21 @@ contains character(len=37), intent(in) :: decoded integer, intent(in) :: idec integer, intent(in) :: ntrperiod + integer iaptype,navg character*3 cflags - - write(cflags,1000) idec -1000 format('q',i2) - if(cflags(2:2).eq.' ') cflags(2:3)=cflags(3:3)//' ' - if(cflags(2:2).eq.'-') cflags=' ' + + cflags=' ' + if(idec.gt.0) then + iaptype=idec + navg=0 + if(idec.ge.100) then + iaptype=idec/100 + navg=mod(idec,100) + endif + cflags='q ' + write(cflags(2:2),'(i1)') iaptype + if(navg.ge.2) write(cflags(3:3),'(i1)') navg + endif if(ntrperiod.lt.60) then write(*,1001) nutc,nsnr,dt,nint(freq),decoded,cflags diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 4ec007bcb..584f5aaa6 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -160,8 +160,8 @@ contains ! No single-transmission decode. if(iand(ndepth,16).eq.16) then - call q65_avg2(ntrperiod,ntol,baud,nsubmode,ibwa,ibwb,codewords,ncw, & - xdt,f0,snr1,snr2,dat4,idec) + call q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & + ibwa,ibwb,codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) endif 100 decoded=' ' diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index fcf2b85bd..6c94c868e 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -5,6 +5,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) use q65 use packjt77 + character*78 c78 character*37 avemsg character*1 cused(MAXAVE) character*6 cutc @@ -12,7 +13,8 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) integer iused(MAXAVE) integer dat4(13) integer codewords(63,206) - logical first,lclearave + integer apmask1(78),apsymbols1(78) + logical first,lclearave,lapcqonly data first/.true./ save @@ -65,8 +67,8 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) 10 return - entry q65_avg2(ntrperiod,ntol,baud,nsubmode,ibwa,ibwb,codewords,ncw, & - xdt,f0,snr1,snr2,dat4,idec) + entry q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & + ibwa,ibwb,codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) mode_q65=2**nsubmode ibwa=1.8*log(baud*mode_q65) + 2 @@ -123,30 +125,58 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) call q65_dec1(s3avg,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,avemsg) ! irc=-99 !### TEMPORARY ### if(irc.ge.0 .and. plog.ge.PLOG_MIN) then - snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment + snr2=esnodb - 0.5*db(2500.0/baud) + 3.0 !Empirical adjustment snr2=snr2 - db(float(navg)) !Is this right? - idec=10+navg + idec=100+navg ! print*,'C dec1 ',ibw,irc,idec,avemsg go to 900 endif enddo -! Should loop here over full range of available AP +! Loop over full range of available AP ! APmask=0 ! APsymbols=0 + npasses=2 + if(nQSOprogress.eq.5) npasses=3 + if(lapcqonly) npasses=1 + iaptype=0 + do ipass=0,npasses + apmask=0 !Try first with no AP information + apsymbols=0 - do ibw=ibwa,ibwb - b90=1.72**ibw - b90ts=b90/baud - call q65_dec2(s3avg,nsubmode,b90ts,esnodb,irc,dat4,avemsg) - if(irc.ge.0) then - snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment - snr2=snr2 - db(float(navg)) !Is this right? - idec=10*(iaptype+2) + navg -! print*,'D dec2 ',ibw,dec,snr2,avemsg - go to 900 + 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 + if(iaptype.eq.4) then + do j=1,3 + ng15=32401+j + write(c78(60:74),'(b15.15)') ng15 + read(c78,1060) dgen + call q65_enc(dgen,codewords(1,j)) + enddo + endif endif - enddo ! ibw (b90 loop) + + do ibw=ibwa,ibwb + b90=1.72**ibw + b90ts=b90/baud + call q65_dec2(s3avg,nsubmode,b90ts,esnodb,irc,dat4,avemsg) + if(irc.ge.0) then + snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment + snr2=snr2 - 0.5*db(float(navg)) !Is this right? + idec=100*(iaptype+2) + navg +! print*,'D dec2 ',ibw,dec,snr2,avemsg + go to 900 + endif + enddo ! ibw + enddo ! ipass 900 return end subroutine q65_avg From 197fd6a5ba2eba508ddb3df873404a918690cd04 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 30 Dec 2020 13:06:33 -0500 Subject: [PATCH 290/426] Mofe the Q65 end-of-line flags so they're not so far to the right. --- widgets/displaytext.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/widgets/displaytext.cpp b/widgets/displaytext.cpp index e697958fc..91882ec2f 100644 --- a/widgets/displaytext.cpp +++ b/widgets/displaytext.cpp @@ -438,6 +438,7 @@ void DisplayText::displayDecodedText(DecodedText const& decodedText, QString con extra += QString {"%1"}.arg (fSpread, 5, 'f', fSpread < 0.95 ? 3 : 2) + QChar {' '}; } auto ap_pos = message.lastIndexOf (QRegularExpression {R"((?:\?\s)?a[0-9]$)"}); + if(ap_pos<0) ap_pos = message.lastIndexOf("q"); //Check for "q" decodes (used in Q65 only) if (ap_pos >= 0) { extra += message.mid (ap_pos) + QChar {' '}; From 74f899677c5e97e0a990ee623c8f620898e20efb Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Wed, 30 Dec 2020 19:40:44 +0000 Subject: [PATCH 291/426] Fix up regexp detecting trailing Q65 decode quality information --- widgets/displaytext.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/widgets/displaytext.cpp b/widgets/displaytext.cpp index 91882ec2f..2d916efd2 100644 --- a/widgets/displaytext.cpp +++ b/widgets/displaytext.cpp @@ -437,8 +437,7 @@ void DisplayText::displayDecodedText(DecodedText const& decodedText, QString con { extra += QString {"%1"}.arg (fSpread, 5, 'f', fSpread < 0.95 ? 3 : 2) + QChar {' '}; } - auto ap_pos = message.lastIndexOf (QRegularExpression {R"((?:\?\s)?a[0-9]$)"}); - if(ap_pos<0) ap_pos = message.lastIndexOf("q"); //Check for "q" decodes (used in Q65 only) + auto ap_pos = message.lastIndexOf (QRegularExpression {R"((?:\?\s)?(?:a[0-9]|q[0-9][0-9*]?)$)"}); if (ap_pos >= 0) { extra += message.mid (ap_pos) + QChar {' '}; From 5d7b5f83e270f8b8318ad47789359a5c2322ef13 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 30 Dec 2020 15:05:02 -0500 Subject: [PATCH 292/426] Fix several more flaws in the way Q65 decodes are presented. --- lib/q65_decode.f90 | 4 ++-- lib/qra/q65/q65_avg.f90 | 4 ++-- lib/qra/q65/q65_loops.f90 | 34 ++++++++++++++++------------------ lib/qra/q65/q65_sync.f90 | 2 +- widgets/mainwindow.cpp | 2 ++ 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 584f5aaa6..d7c75c6ea 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -153,9 +153,9 @@ contains call timer('q65loops',0) call q65_loops(c00,npts/2,nsps/2,mode65,nsubmode,ndepth,jpk0, & - xdt,f0,width,iaptype,xdt1,f1,snr2,dat4,idec) + xdt,f0,iaptype,xdt1,f1,snr2,dat4,idec) call timer('q65loops',1) - if(id2.gt.0) exit !Exit main loop after a successful decode + if(idec.gt.0) go to 100 !Successful decode, we're done enddo ! No single-transmission decode. diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index 6c94c868e..3b46f10cb 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -1,4 +1,4 @@ -subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) +subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) ! Accumulate Q65 spectra s3(LL,63) and associated parameters for ! message averaging. @@ -47,7 +47,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) endif do i=1,MAXAVE !Don't save info more than once for same UTC and freq - if(nutc.eq.iutc(i) .and. abs(nfreq-f0save(i)).le.ntol) go to 10 + if(nutc.eq.iutc(i) .and. abs(nfqso-f0save(i)).le.ntol) go to 10 enddo ! Save data for message averaging diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index d734af305..ef5504040 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,5 +1,5 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & - xdt0,f0,width,iaptype,xdt1,f1,snr2,dat4,id2) + xdt0,f0,iaptype,xdt1,f1,snr2,dat4,idec) use packjt77 use timer_module, only: timer @@ -19,16 +19,18 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & 51,51,42,42,50,25,31,35,57,30, 1,54,54,10,10,22,44,58,57,40, & 21,21,19/ - id2=-1 + idec=-1 ircbest=9999 allocate(c0(0:npts2-1)) irc=-99 s3lim=20. + baud=6000.0/nsps idfmax=3 idtmax=3 - ibwmin=1 - ibwmax=3 + ibwa=max(1,int(1.8*log(baud*mode_q65)) + 2) + ibwb=min(10,ibwa+4) + ibw0=(ibwa+ibwb)/2 maxdist=5 if(iand(ndepth,3).ge.2) then idfmax=5 @@ -36,11 +38,14 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & maxdist=15 endif if(iand(ndepth,3).eq.3) then - ibwmax=5 + maxdist=25 + ibwa=max(1,ibwa-1) + ibwb=min(10,ibwb+1) endif + + LL=64*(mode_q65+2) napmin=99 - baud=6000.0/nsps xdt1=xdt0 f1=f0 @@ -63,13 +68,10 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & s3=s3/base where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim endif - do ibw=ibwmin,ibwmax - nbw=ibw/2 - if(mod(ibw,2).eq.0) nbw=-nbw - ndist=ndf**2 + ndt**2 + nbw**2 + do ibw=ibwa,ibwb + ndist=ndf**2 + ndt**2 + (ibw-ibw0)**2 if(ndist.gt.maxdist) cycle - xx=1.885*log(3.0*width)+nbw - b90=1.7**xx + b90=1.72**ibw if(b90.gt.345.0) cycle b90ts = b90/baud call q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) @@ -77,17 +79,13 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & ! -1 = invalid params ! -2 = decode failed ! -3 = CRC mismatch -! irc=-99 !### TEMPORARY ### - if(irc.ge.0) then - id2=iaptype+2 -! print*,'B dec2 ',ibw,irc,id2,decoded - go to 100 - endif + if(irc.ge.0) go to 100 enddo ! ibw (b90 loop) enddo ! idt (DT loop) enddo ! idf (f0 loop) 100 if(irc.ge.0) then + idec=iaptype+2 snr2=esnodb - db(2500.0/baud) xdt1=xdt0 + nsps*ndt/(16.0*6000.0) f1=f0 + 0.5*baud*ndf diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 600a5c86a..e2b35098c 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -250,7 +250,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & enddo ! write(*,3002) 'B',xdt,f0,sum(s3) !3002 format(a1,f7.2,2f8.1) - call q65_avg(nutc,ntrperiod,LL,ntol,lclearave,xdt,f0,snr1,s3) + call q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) endif endif diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 2c06d6dd6..86f85d343 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3498,6 +3498,8 @@ void MainWindow::readFromStdout() //readFromStdout if(SpecOp::FOX==m_config.special_op_id() and for_us and (audioFreq<1000)) bDisplayRight=true; if(SpecOp::FOX!=m_config.special_op_id() and (for_us or (abs(audioFreq - m_wideGraph->rxFreq()) <= 10))) bDisplayRight=true; } + if((abs(audioFreq - m_wideGraph->rxFreq()) <= ui->sbFtol->value()) and + m_mode=="Q65") bDisplayRight=true; } else { if((abs(audioFreq - m_wideGraph->rxFreq()) <= 10) and !m_config.enable_VHF_features()) bDisplayRight=true; From e07ba410c219b1c15c869e587490bb0bb5fbe844 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 30 Dec 2020 15:40:06 -0500 Subject: [PATCH 293/426] Fix a problem with access to apsym0() array. --- lib/q65_decode.f90 | 1 - lib/qra/q65/q65.f90 | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index d7c75c6ea..89c5cfd49 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -55,7 +55,6 @@ contains integer*2 iwave(NMAX) !Raw data real, allocatable :: dd(:) !Raw data integer dat4(13) !Decoded message as 12 6-bit integers - integer apsym0(58),aph10(10) integer apmask1(78),apsymbols1(78) integer dgen(13) integer codewords(63,206) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index fcc023d11..d56ae6f4c 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -6,6 +6,7 @@ module q65 integer iutc(MAXAVE) integer iseq(MAXAVE) integer listutc(10) + integer apsym0(58),aph10(10) integer apmask(13),apsymbols(13) integer navg real f0save(MAXAVE) From bcbca02c08f6ec2cd0cd1b204c840d465886f892 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 30 Dec 2020 16:12:02 -0500 Subject: [PATCH 294/426] Enforce Q65 ClrAvg action when requested. Tweak somne logic. --- lib/q65_decode.f90 | 4 ++-- lib/qra/q65/q65_avg.f90 | 4 +--- lib/qra/q65/q65_sync.f90 | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 89c5cfd49..1d83d4868 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -62,12 +62,12 @@ contains complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s - id1=0 - id2=0 + idec=-1 mode65=2**nsubmode npts=ntrperiod*12000 nfft1=ntrperiod*12000 nfft2=ntrperiod*6000 + if(lclearave) nsave=0 allocate(dd(npts)) allocate (c00(0:nfft1-1)) allocate (c0(0:nfft1-1)) diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index 3b46f10cb..d3ac60246 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -123,12 +123,10 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) b90=1.72**ibw b90ts=b90/baud call q65_dec1(s3avg,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,avemsg) -! irc=-99 !### TEMPORARY ### - if(irc.ge.0 .and. plog.ge.PLOG_MIN) then + if(irc.ge.0) then snr2=esnodb - 0.5*db(2500.0/baud) + 3.0 !Empirical adjustment snr2=snr2 - db(float(navg)) !Is this right? idec=100+navg -! print*,'C dec1 ',ibw,irc,idec,avemsg go to 900 endif enddo diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index e2b35098c..7721d2cd5 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -180,9 +180,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & b90=1.72**ibw b90ts=b90/baud call q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) -! irc=-99 !### TEMPORARY ### if(irc.ge.0) then -! print*,'A dec1 ',ibw,irc,esnodb,baud,trim(decoded) snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment idec=1 ic=ia2/4; @@ -290,6 +288,8 @@ subroutine q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) 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 + else + irc=-1 endif return From 8a4b5b6a40d0936a414d69a5262015dd40524faa Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 31 Dec 2020 15:52:06 -0500 Subject: [PATCH 295/426] Various tweaks to the Q65 decoder; new numbering of the end-of-line decoding flags. --- lib/decoder.f90 | 5 +++++ lib/qra/q65/q65_avg.f90 | 1 + lib/qra/q65/q65_sync.f90 | 2 +- lib/test_q65.f90 | 6 +++++- widgets/mainwindow.cpp | 6 +++--- widgets/plotter.cpp | 2 +- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 15ce66989..32ffb9e38 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -785,6 +785,11 @@ contains iaptype=idec/100 navg=mod(idec,100) endif + if(iaptype.eq.1) then + iaptype=3 + else + iaptype=iaptype-2 + endif cflags='q ' write(cflags(2:2),'(i1)') iaptype if(navg.ge.2) write(cflags(3:3),'(i1)') navg diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index d3ac60246..1c2ee8702 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -70,6 +70,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) entry q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & ibwa,ibwb,codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) + if(nsave.lt.2) go to 900 mode_q65=2**nsubmode ibwa=1.8*log(baud*mode_q65) + 2 ibwb=min(10,ibwa+4) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 7721d2cd5..4ea4e5e15 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -291,7 +291,7 @@ subroutine q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) else irc=-1 endif - + return end subroutine q65_dec1 diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 49a17c678..cf557591d 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -137,7 +137,11 @@ program test_q65 iavg=0 i0=23 if(ntrperiod.le.30) i0=25 - if(line(i0:i0).ne.' ') read(line(60:),*) idec + if(line(i0:i0).ne.' ') then + i1=index(line,'q') + idec=-1 + read(line(i1+1:),*) idec + endif if(idec.lt.0) cycle if(idec.ge.12) then iavg=idec-10 diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 86f85d343..bbdbf50d0 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3481,11 +3481,13 @@ void MainWindow::readFromStdout() //readFromStdout bool bDisplayRight=bAvgMsg; int audioFreq=decodedtext.frequencyOffset(); if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="FST4" or m_mode=="Q65") { + int ftol=10; + if(m_mode=="Q65") ftol=ui->sbFtol->value(); auto const& parts = decodedtext.string().remove("<").remove(">") .split (' ', SkipEmptyParts); if (parts.size() > 6) { auto for_us = parts[5].contains (m_baseCall) - || ("DE" == parts[5] && qAbs (ui->RxFreqSpinBox->value () - audioFreq) <= 10); + || ("DE" == parts[5] && qAbs (ui->RxFreqSpinBox->value () - audioFreq) <= ftol); if(m_baseCall==m_config.my_callsign() and m_baseCall!=parts[5]) for_us=false; if(m_bCallingCQ && !m_bAutoReply && for_us && ui->cbFirst->isChecked() and SpecOp::FOX > m_config.special_op_id()) { @@ -3498,8 +3500,6 @@ void MainWindow::readFromStdout() //readFromStdout if(SpecOp::FOX==m_config.special_op_id() and for_us and (audioFreq<1000)) bDisplayRight=true; if(SpecOp::FOX!=m_config.special_op_id() and (for_us or (abs(audioFreq - m_wideGraph->rxFreq()) <= 10))) bDisplayRight=true; } - if((abs(audioFreq - m_wideGraph->rxFreq()) <= ui->sbFtol->value()) and - m_mode=="Q65") bDisplayRight=true; } else { if((abs(audioFreq - m_wideGraph->rxFreq()) <= 10) and !m_config.enable_VHF_features()) bDisplayRight=true; diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index a68ca80b3..9b6b3856b 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -261,7 +261,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) Font.setWeight(QFont::Bold); painter2D.setFont(Font); int x1=XfromFreq(m_rxFreq); - y=0.2*m_h2; + y=0.25*m_h2; painter2D.drawText(x1-4,y,"T"); x1=XfromFreq(m_rxFreq+250); painter2D.drawText(x1-4,y,"M"); From 6b351165f2f0e5f6d41babbf12b7feffcb9918c2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 1 Jan 2021 10:47:22 -0500 Subject: [PATCH 296/426] Re-introduce Q65 timer calls. Extend red sync baseline to minimum +/- 100 Hz. --- lib/qra/q65/q65_avg.f90 | 5 +++++ lib/qra/q65/q65_loops.f90 | 2 ++ lib/qra/q65/q65_sync.f90 | 5 ++++- widgets/mainwindow.cpp | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index 1c2ee8702..85fda7fc7 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -5,6 +5,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) use q65 use packjt77 + use timer_module, only: timer character*78 c78 character*37 avemsg character*1 cused(MAXAVE) @@ -123,7 +124,9 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) do ibw=ibwa,ibwb b90=1.72**ibw b90ts=b90/baud + call timer('dec1avg ',0) call q65_dec1(s3avg,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,avemsg) + call timer('dec1avg ',1) if(irc.ge.0) then snr2=esnodb - 0.5*db(2500.0/baud) + 3.0 !Empirical adjustment snr2=snr2 - db(float(navg)) !Is this right? @@ -166,7 +169,9 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) do ibw=ibwa,ibwb b90=1.72**ibw b90ts=b90/baud + call timer('dec2avg ',0) call q65_dec2(s3avg,nsubmode,b90ts,esnodb,irc,dat4,avemsg) + call timer('dec2avg ',1) if(irc.ge.0) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment snr2=snr2 - 0.5*db(float(navg)) !Is this right? diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index ef5504040..b3c4eece8 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -74,7 +74,9 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & b90=1.72**ibw if(b90.gt.345.0) cycle b90ts = b90/baud + call timer('dec2 ',0) call q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) + call timer('dec2 ',1) ! irc > 0 ==> number of iterations required to decode ! -1 = invalid params ! -2 = decode failed diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 4ea4e5e15..75e055ea9 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -14,6 +14,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & ! snr1 Relative SNR of sync signal use packjt77 + use timer_module, only: timer parameter (NSTEP=8) !Step size nsps/NSTEP parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 parameter (PLOG_MIN=-240.0) !List decoding threshold @@ -47,7 +48,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & jz=(txt+1.0)*12000.0/istep !Number of quarter-symbol steps if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher ia=ntol/df - ia2=max(ia,10*mode_q65) + ia2=max(ia,10*mode_q65,nint(100.0/df)) nsmo=int(0.7*mode_q65*mode_q65) if(nsmo.lt.1) nsmo=1 @@ -179,7 +180,9 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & do ibw=ibwa,ibwb b90=1.72**ibw b90ts=b90/baud + call timer('dec1 ',0) call q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) + call timer('dec1 ',1) if(irc.ge.0) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment idec=1 diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index bbdbf50d0..ad8837b57 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -7508,7 +7508,7 @@ void MainWindow::on_sbFtol_valueChanged(int value) void::MainWindow::VHF_features_enabled(bool b) { if(m_mode!="JT4" and m_mode!="JT65" and m_mode!="Q65") b=false; - if(b and (ui->actionInclude_averaging->isChecked() or + if(b and m_mode!="Q65" and (ui->actionInclude_averaging->isChecked() or ui->actionInclude_correlation->isChecked())) { ui->actionDeepestDecode->setChecked (true); } From e5a8e80c495e7eddf78bfdaa2746a977bc050152 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 1 Jan 2021 15:28:41 -0500 Subject: [PATCH 297/426] Minor tweaks to Q^% decoding scheme. --- lib/qra/q65/q65_avg.f90 | 8 ++++---- lib/qra/q65/q65_loops.f90 | 28 ++++++++++++++-------------- lib/test_q65.f90 | 6 +++--- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index 85fda7fc7..b90a7a227 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -23,7 +23,6 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) iutc=-1 iseq=-1 f0save=0.0 - dtdiff=0.2 nsave=0 LL0=LL first=.false. @@ -73,6 +72,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) if(nsave.lt.2) go to 900 mode_q65=2**nsubmode + f0diff=baud*mode_q65 ibwa=1.8*log(baud*mode_q65) + 2 ibwb=min(10,ibwa+4) snr1sum=0. @@ -87,7 +87,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) if(iutc(i).lt.0) cycle if(iseq(i).ne.iseq(nsave)) cycle !Sequence must match if(abs(xdt-xdtsave(i)).gt.dtdiff) cycle !DT must be close - if(abs(f0-f0save(i)).gt.float(ntol)) cycle !Freq must match + if(abs(f0-f0save(i)).gt.f0diff) cycle !Freq must match cused(i)='$' !Flag for "use this one" s3avg=s3avg + s3save(:,:,i) !Add this spectrum snr1sum=snr1sum + snr1save(i) @@ -97,7 +97,6 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) iused(navg)=i enddo if(navg.lt.MAXAVE) iused(navg+1)=0 - if(navg.lt.2) go to 900 ! Find averages of snr1, xdt, and f0 used in this decoding attempt. snr1ave=0. @@ -118,6 +117,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) xdtsave(i),f0save(i) 1001 format(a1,i5.4,f6.1,f6.2,f7.1) enddo + if(navg.lt.2) go to 900 s3avg=s3avg/navg nFadingModel=1 @@ -129,7 +129,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) call timer('dec1avg ',1) if(irc.ge.0) then snr2=esnodb - 0.5*db(2500.0/baud) + 3.0 !Empirical adjustment - snr2=snr2 - db(float(navg)) !Is this right? + snr2=snr2 - db(float(navg)) !Is this right? idec=100+navg go to 900 endif diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index b3c4eece8..ce476c0fb 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -35,14 +35,13 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & if(iand(ndepth,3).ge.2) then idfmax=5 idtmax=5 - maxdist=15 + maxdist=10 endif if(iand(ndepth,3).eq.3) then - maxdist=25 + maxdist=15 ibwa=max(1,ibwa-1) ibwb=min(10,ibwb+1) endif - LL=64*(mode_q65+2) napmin=99 @@ -57,17 +56,15 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & call twkfreq(c00,c0,npts2,6000.0,a) do idt=1,idtmax ndt=idt/2 - if(iaptype.eq.0) then - if(mod(idt,2).eq.0) ndt=-ndt - jpk=jpk0 + nsps*ndt/16 !tsym/16 - if(jpk.lt.0) jpk=0 - call timer('spec64 ',0) - call spec64(c0,nsps,65,mode_q65,jpk,s3,LL,NN) - call timer('spec64 ',1) - call pctile(s3,LL*NN,40,base) - s3=s3/base - where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim - endif + if(mod(idt,2).eq.0) ndt=-ndt + jpk=jpk0 + nsps*ndt/16 !tsym/16 + if(jpk.lt.0) jpk=0 + call timer('spec64 ',0) + call spec64(c0,nsps,65,mode_q65,jpk,s3,LL,NN) + call timer('spec64 ',1) + call pctile(s3,LL*NN,40,base) + s3=s3/base + where(s3(1:LL*NN)>s3lim) s3(1:LL*NN)=s3lim do ibw=ibwa,ibwb ndist=ndf**2 + ndt**2 + (ibw-ibw0)**2 if(ndist.gt.maxdist) cycle @@ -87,6 +84,9 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & enddo ! idf (f0 loop) 100 if(irc.ge.0) then +! write(55,3055) ndepth,iaptype,idf,idt,ibw,ndist,irc,sum(s3(1:LL*NN)), & +! trim(decoded) +!3055 format(7i4,f10.1,1x,a) idec=iaptype+2 snr2=esnodb - db(2500.0/baud) xdt1=xdt0 + nsps*ndt/(16.0*6000.0) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index cf557591d..710220a8a 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -4,7 +4,7 @@ program test_q65 character*22 msg character*8 arg character*1 csubmode - integer naptype(1:6) + integer naptype(0:5) logical decok nargs=iargc() @@ -93,8 +93,8 @@ program test_q65 !1000 format(/'Depth:',i2,' AP:',i2,' df:',i3,' dt:',i3,' bw1:',i3,' bw2:',i3, & ! ' dist:',i3) - write(*,1010) (j,j=1,6) - write(12,1010) (j,j=1,6) + write(*,1010) (j,j=0,5) + write(12,1010) (j,j=0,5) 1010 format(' SNR Mode d Dop Sync Avg Dec Bad',6i4,' tdec avg rms'/ & 75('-')) From ebb6e5b69771770a683eed6d8bf4f0e866630d9c Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Sat, 2 Jan 2021 10:09:44 -0600 Subject: [PATCH 298/426] Fix a conflict between noise baseline percentile level and noise blanker percentage. Both were using the npct variable. Add an option for an FST4 pass when in FST4W mode. --- lib/fst4_decode.f90 | 681 +++++++++++++++++++++++--------------------- 1 file changed, 354 insertions(+), 327 deletions(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index d3ecd371b..9a1aa0f16 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -43,6 +43,7 @@ contains procedure(fst4_decode_callback) :: callback character*37 decodes(100) character*37 msg,msgsent + character*8 s_nfa_nfb character*20 wcalls(MAXWCALLS), wpart character*77 c77 character*12 mycall,hiscall @@ -58,7 +59,6 @@ contains logical lagain,lapcqonly integer itone(NN) integer hmod - integer ipct(0:7) integer*1 apmask(240),cw(240),hdec(240) integer*1 message101(101),message74(74),message77(77) integer*1 rvec(77) @@ -74,7 +74,6 @@ contains integer*2 iwave(30*60*12000) - data ipct/0,8,14,4,12,2,10,6/ 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/ @@ -247,6 +246,7 @@ contains do_k50_decode=.false. endif +! Noise blanker setup ndropmax=1 single_decode=iand(nexp_decode,32).ne.0 npct=0 @@ -262,278 +262,260 @@ contains inb2=1 !Try NB = 0, 1, 2,... 20% else inb1=0 !Fixed NB value, 0 to 25% - ipct(0)=npct endif - if(iwspr.eq.1) then !FST4W - !300 Hz wide noise-fit window - nfa=max(100,nint(nfqso+1.5*baud-150)) - nfb=min(4800,nint(nfqso+1.5*baud+150)) - fa=max(100,nint(nfqso+1.5*baud-ntol)) ! signal search window - fb=min(4800,nint(nfqso+1.5*baud+ntol)) - else if(single_decode) then - fa=max(100,nint(nfa+1.5*baud)) - fb=min(4800,nint(nfb+1.5*baud)) - ! extend noise fit 100 Hz outside of search window - nfa=max(100,nfa-100) - nfb=min(4800,nfb+100) - else - fa=max(100,nint(nfa+1.5*baud)) - fb=min(4800,nint(nfb+1.5*baud)) - ! extend noise fit 100 Hz outside of search window - nfa=max(100,nfa-100) - nfb=min(4800,nfb+100) +! If environment variable FST4W_ALSO_FST4 exists then, when in FST4W mode, +! do a second pass for FST4 decodes. The value of FST4W_ALSO_FST4 +! is of the form xxxxyyyy where nfa=xxxx and nfb=yyyy are the +! search limits for the FST4 decoding pass, e.g. +! FST4W_ALSO_FST4=08001700 will set FST4 search window to [800Hz,1700Hz] +! + nmode=1 + call get_environment_variable("FST4W_ALSO_FST4",s_nfa_nfb,nlength) + if(iwspr.eq.1 .and. nlength.eq.8) then + read(s_nfa_nfb,"(i4.4,i4.4)") nfa_mode2,nfb_mode2 + nmode=2 endif - ndecodes=0 - decodes=' ' - new_callsign=.false. - do inb=0,inb1,inb2 - if(nb.lt.0) npct=inb - call blanker(iwave,nfft1,ndropmax,npct,c_bigfft) + do imode=1,nmode + if(imode.eq.1) iwspr=1 + if(imode.eq.2) then ! this is FST4 after a FST4W pass + iwspr=0 + nfa=nfa_mode2 + nfb=nfb_mode2 + endif + +! nfa,nfb: define the noise-baseline analysis window +! fa, fb: define the signal search window +! We usually make nfafb so that noise baseline analysis +! window extends outside of the [fa,fb] window where we think the signals are. +! + if(iwspr.eq.1) then !FST4W + nfa=max(100,nfqso-ntol-100) + nfb=min(4800,nfqso+ntol+100) + fa=max(100,nint(nfqso+1.5*baud-ntol)) ! signal search window + fb=min(4800,nint(nfqso+1.5*baud+ntol)) + else if(iwspr.eq.0) then + if(imode.eq.1 .and. single_decode) then + fa=max(100,nint(nfa+1.5*baud)) + fb=min(4800,nint(nfb+1.5*baud)) + ! extend noise fit 100 Hz outside of search window + nfa=max(100,nfa-100) + nfb=min(4800,nfb+100) + else + fa=max(100,nint(nfa+1.5*baud)) + fb=min(4800,nint(nfb+1.5*baud)) + ! extend noise fit 100 Hz outside of search window + nfa=max(100,nfa-100) + nfb=min(4800,nfb+100) + endif + endif + + ndecodes=0 + decodes=' ' + new_callsign=.false. + do inb=0,inb1,inb2 + if(nb.lt.0) npct=inb ! we are looping over blanker settings + call blanker(iwave,nfft1,ndropmax,npct,c_bigfft) ! The big fft is done once and is used for calculating the smoothed spectrum ! and also for downconverting/downsampling each candidate. - call four2a(c_bigfft,nfft1,1,-1,0) !r2c - nhicoh=1 - nsyncoh=8 - minsync=1.20 - if(ntrperiod.eq.15) minsync=1.15 + call four2a(c_bigfft,nfft1,1,-1,0) !r2c + nhicoh=1 + nsyncoh=8 + minsync=1.20 + if(ntrperiod.eq.15) minsync=1.15 ! Get first approximation of candidate frequencies - call get_candidates_fst4(c_bigfft,nfft1,nsps,hmod,fs,fa,fb,nfa,nfb, & - minsync,ncand,candidates0) - isbest=0 - fc2=0. - do icand=1,ncand - fc0=candidates0(icand,1) - if(iwspr.eq.0 .and. nb.lt.0 .and. npct.ne.0 .and. & - abs(fc0-(nfqso+1.5*baud)).gt.ntol) cycle - detmet=candidates0(icand,2) + call get_candidates_fst4(c_bigfft,nfft1,nsps,hmod,fs,fa,fb,nfa,nfb, & + minsync,ncand,candidates0) + isbest=0 + fc2=0. + do icand=1,ncand + fc0=candidates0(icand,1) + if(iwspr.eq.0 .and. nb.lt.0 .and. npct.ne.0 .and. & + abs(fc0-(nfqso+1.5*baud)).gt.ntol) cycle ! blanker loop only near nfqso + detmet=candidates0(icand,2) ! Downconvert and downsample a slice of the spectrum centered on the ! rough estimate of the candidates frequency. ! Output array c2 is complex baseband sampled at 12000/ndown Sa/sec. ! The size of the downsampled c2 array is nfft2=nfft1/ndown + call timer('dwnsmpl ',0) + call fst4_downsample(c_bigfft,nfft1,ndown,fc0,sigbw,c2) + call timer('dwnsmpl ',1) - call timer('dwnsmpl ',0) - call fst4_downsample(c_bigfft,nfft1,ndown,fc0,sigbw,c2) - call timer('dwnsmpl ',1) + call timer('sync240 ',0) + call fst4_sync_search(c2,nfft2,hmod,fs2,nss,ntrperiod,nsyncoh,emedelay,sbest,fcbest,isbest) + call timer('sync240 ',1) - call timer('sync240 ',0) - call fst4_sync_search(c2,nfft2,hmod,fs2,nss,ntrperiod,nsyncoh,emedelay,sbest,fcbest,isbest) - call timer('sync240 ',1) - - fc_synced = fc0 + fcbest - dt_synced = (isbest-fs2)*dt2 !nominal dt is 1 second so frame starts at sample fs2 - candidates0(icand,3)=fc_synced - candidates0(icand,4)=isbest - enddo + fc_synced = fc0 + fcbest + dt_synced = (isbest-fs2)*dt2 !nominal dt is 1 second so frame starts at sample fs2 + candidates0(icand,3)=fc_synced + candidates0(icand,4)=isbest + enddo ! remove duplicate candidates - do icand=1,ncand - fc=candidates0(icand,3) - isbest=nint(candidates0(icand,4)) - do ic2=icand+1,ncand - fc2=candidates0(ic2,3) - isbest2=nint(candidates0(ic2,4)) - if(fc2.gt.0.0) then - if(abs(fc2-fc).lt.0.10*baud) then ! same frequency - if(abs(isbest2-isbest).le.2) then - candidates0(ic2,3)=-1 - endif - endif - endif - enddo - enddo - ic=0 - do icand=1,ncand - if(candidates0(icand,3).gt.0) then - ic=ic+1 - candidates0(ic,:)=candidates0(icand,:) - endif - enddo - ncand=ic - -! If FST4 and Single Decode is not checked, then find candidates within -! 20 Hz of nfqso and put them at the top of the list - if(iwspr.eq.0 .and. .not.single_decode) then - nclose=count(abs(candidates0(:,3)-(nfqso+1.5*baud)).le.20) - k=0 - do i=1,ncand - if(abs(candidates0(i,3)-(nfqso+1.5*baud)).le.20) then - k=k+1 - candidates(k,:)=candidates0(i,:) - endif - enddo - do i=1,ncand - if(abs(candidates0(i,3)-(nfqso+1.5*baud)).gt.20) then - k=k+1 - candidates(k,:)=candidates0(i,:) - endif - enddo - else - candidates=candidates0 - endif - - xsnr=0. - do icand=1,ncand - sync=candidates(icand,2) - fc_synced=candidates(icand,3) - isbest=nint(candidates(icand,4)) - xdt=(isbest-nspsec)/fs2 - if(ntrperiod.eq.15) xdt=(isbest-real(nspsec)/2.0)/fs2 - call timer('dwnsmpl ',0) - call fst4_downsample(c_bigfft,nfft1,ndown,fc_synced,sigbw,c2) - call timer('dwnsmpl ',1) - - do ijitter=0,jittermax - if(ijitter.eq.0) ioffset=0 - if(ijitter.eq.1) ioffset=1 - if(ijitter.eq.2) ioffset=-1 - is0=isbest+ioffset - iend=is0+160*nss-1 - if( is0.lt.0 .or. iend.gt.(nfft2-1) ) cycle - cframe=c2(is0:iend) - bitmetrics=0 - call timer('bitmetrc',0) - call get_fst4_bitmetrics(cframe,nss,nblock,nhicoh,bitmetrics, & - s4,nsync_qual,badsync) - call timer('bitmetrc',1) - if(badsync) cycle - - do il=1,4 - llrs( 1: 60,il)=bitmetrics( 17: 76, il) - llrs( 61:120,il)=bitmetrics( 93:152, il) - llrs(121:180,il)=bitmetrics(169:228, il) - llrs(181:240,il)=bitmetrics(245:304, il) - enddo - - apmag=maxval(abs(llrs(:,4)))*1.1 - ntmax=nblock+nappasses(nQSOProgress) - if(lapcqonly) ntmax=nblock+1 - if(ndepth.eq.1) ntmax=nblock ! no ap for ndepth=1 - apmask=0 - - if(iwspr.eq.1) then ! 50-bit msgs, no ap decoding - nblock=4 - ntmax=nblock - endif - - do itry=1,ntmax - if(itry.eq.1) llr=llrs(:,1) - if(itry.eq.2.and.itry.le.nblock) llr=llrs(:,2) - if(itry.eq.3.and.itry.le.nblock) llr=llrs(:,3) - if(itry.eq.4.and.itry.le.nblock) llr=llrs(:,4) - if(itry.le.nblock) then - apmask=0 - iaptype=0 - endif - - if(itry.gt.nblock .and. iwspr.eq.0) then ! do ap passes - llr=llrs(:,nblock) ! Use largest blocksize as the basis for AP passes - iaptype=naptypes(nQSOProgress,itry-nblock) - if(lapcqonly) iaptype=1 - 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 - apmask=0 - apmask(1:29)=1 - llr(1:29)=apmag*mcq(1:29) - endif - - if(iaptype.eq.2) then ! MyCall ??? ??? - apmask=0 - apmask(1:29)=1 - llr(1:29)=apmag*apbits(1:29) - endif - - if(iaptype.eq.3) then ! MyCall DxCall ??? - apmask=0 - apmask(1:58)=1 - llr(1:58)=apmag*apbits(1:58) - endif - - if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype .eq.6) then - apmask=0 - apmask(1:77)=1 - llr(1:58)=apmag*apbits(1:58) - if(iaptype.eq.4) llr(59:77)=apmag*mrrr(1:19) - if(iaptype.eq.5) llr(59:77)=apmag*m73(1:19) - if(iaptype.eq.6) llr(59:77)=apmag*mrr73(1:19) - endif - endif - - dmin=0.0 - nharderrors=-1 - unpk77_success=.false. - if(iwspr.eq.0) then - maxosd=2 - Keff=91 - norder=3 - call timer('d240_101',0) - call decode240_101(llr,Keff,maxosd,norder,apmask,message101, & - cw,ntype,nharderrors,dmin) - call timer('d240_101',1) - if(count(cw.eq.1).eq.0) then - nharderrors=-nharderrors - cycle - endif - write(c77,'(77i1)') mod(message101(1:77)+rvec,2) - call unpack77(c77,1,msg,unpk77_success) - elseif(iwspr.eq.1) then -! Try decoding with Keff=66 - maxosd=2 - call timer('d240_74 ',0) - Keff=66 - norder=3 - call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & - ntype,nharderrors,dmin) - call timer('d240_74 ',1) - if(nharderrors.lt.0) goto 3465 - if(count(cw.eq.1).eq.0) then - nharderrors=-nharderrors - cycle - endif - write(c77,'(50i1)') message74(1:50) - c77(51:77)='000000000000000000000110000' - call unpack77(c77,1,msg,unpk77_success) - if(unpk77_success .and. do_k50_decode) then -! If decode was obtained with Keff=66, save call/grid in fst4w_calls.txt if not there already. - i1=index(msg,' ') - i2=i1+index(msg(i1+1:),' ') - wpart=trim(msg(1:i2)) -! Only save callsigns/grids from type 1 messages - if(index(wpart,'/').eq.0 .and. index(wpart,'<').eq.0) then - ifound=0 - do i=1,nwcalls - if(index(wcalls(i),wpart).ne.0) ifound=1 - enddo - - if(ifound.eq.0) then ! This is a new callsign - new_callsign=.true. - if(nwcalls.lt.MAXWCALLS) then - nwcalls=nwcalls+1 - wcalls(nwcalls)=wpart - else - wcalls(1:nwcalls-1)=wcalls(2:nwcalls) - wcalls(nwcalls)=wpart - endif - endif + do icand=1,ncand + fc=candidates0(icand,3) + isbest=nint(candidates0(icand,4)) + do ic2=icand+1,ncand + fc2=candidates0(ic2,3) + isbest2=nint(candidates0(ic2,4)) + if(fc2.gt.0.0) then + if(abs(fc2-fc).lt.0.10*baud) then ! same frequency + if(abs(isbest2-isbest).le.2) then + candidates0(ic2,3)=-1 endif endif -3465 continue + endif + enddo + enddo + ic=0 + do icand=1,ncand + if(candidates0(icand,3).gt.0) then + ic=ic+1 + candidates0(ic,:)=candidates0(icand,:) + endif + enddo + ncand=ic -! If no decode then try Keff=50 - iaptype=0 - if( .not. unpk77_success .and. do_k50_decode ) then - maxosd=1 +! If FST4 mode and Single Decode is not checked, then find candidates +! within 20 Hz of nfqso and put them at the top of the list + if(iwspr.eq.0 .and. .not.single_decode) then + nclose=count(abs(candidates0(:,3)-(nfqso+1.5*baud)).le.20) + k=0 + do i=1,ncand + if(abs(candidates0(i,3)-(nfqso+1.5*baud)).le.20) then + k=k+1 + candidates(k,:)=candidates0(i,:) + endif + enddo + do i=1,ncand + if(abs(candidates0(i,3)-(nfqso+1.5*baud)).gt.20) then + k=k+1 + candidates(k,:)=candidates0(i,:) + endif + enddo + else + candidates=candidates0 + endif + + xsnr=0. + do icand=1,ncand + sync=candidates(icand,2) + fc_synced=candidates(icand,3) + isbest=nint(candidates(icand,4)) + xdt=(isbest-nspsec)/fs2 + if(ntrperiod.eq.15) xdt=(isbest-real(nspsec)/2.0)/fs2 + call timer('dwnsmpl ',0) + call fst4_downsample(c_bigfft,nfft1,ndown,fc_synced,sigbw,c2) + call timer('dwnsmpl ',1) + + do ijitter=0,jittermax + if(ijitter.eq.0) ioffset=0 + if(ijitter.eq.1) ioffset=1 + if(ijitter.eq.2) ioffset=-1 + is0=isbest+ioffset + iend=is0+160*nss-1 + if( is0.lt.0 .or. iend.gt.(nfft2-1) ) cycle + cframe=c2(is0:iend) + bitmetrics=0 + call timer('bitmetrc',0) + call get_fst4_bitmetrics(cframe,nss,nblock,nhicoh,bitmetrics, & + s4,nsync_qual,badsync) + call timer('bitmetrc',1) + if(badsync) cycle + + do il=1,4 + llrs( 1: 60,il)=bitmetrics( 17: 76, il) + llrs( 61:120,il)=bitmetrics( 93:152, il) + llrs(121:180,il)=bitmetrics(169:228, il) + llrs(181:240,il)=bitmetrics(245:304, il) + enddo + + apmag=maxval(abs(llrs(:,4)))*1.1 + ntmax=nblock+nappasses(nQSOProgress) + if(lapcqonly) ntmax=nblock+1 + if(ndepth.eq.1) ntmax=nblock ! no ap for ndepth=1 + apmask=0 + + if(iwspr.eq.1) then ! 50-bit msgs, no ap decoding + nblock=4 + ntmax=nblock + endif + + do itry=1,ntmax + if(itry.eq.1) llr=llrs(:,1) + if(itry.eq.2.and.itry.le.nblock) llr=llrs(:,2) + if(itry.eq.3.and.itry.le.nblock) llr=llrs(:,3) + if(itry.eq.4.and.itry.le.nblock) llr=llrs(:,4) + if(itry.le.nblock) then + apmask=0 + iaptype=0 + endif + + if(itry.gt.nblock .and. iwspr.eq.0) then ! do ap passes + llr=llrs(:,nblock) ! Use largest blocksize as the basis for AP passes + iaptype=naptypes(nQSOProgress,itry-nblock) + if(lapcqonly) iaptype=1 + 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 + apmask=0 + apmask(1:29)=1 + llr(1:29)=apmag*mcq(1:29) + endif + + if(iaptype.eq.2) then ! MyCall ??? ??? + apmask=0 + apmask(1:29)=1 + llr(1:29)=apmag*apbits(1:29) + endif + + if(iaptype.eq.3) then ! MyCall DxCall ??? + apmask=0 + apmask(1:58)=1 + llr(1:58)=apmag*apbits(1:58) + endif + + if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype .eq.6) then + apmask=0 + apmask(1:77)=1 + llr(1:58)=apmag*apbits(1:58) + if(iaptype.eq.4) llr(59:77)=apmag*mrrr(1:19) + if(iaptype.eq.5) llr(59:77)=apmag*m73(1:19) + if(iaptype.eq.6) llr(59:77)=apmag*mrr73(1:19) + endif + endif + + dmin=0.0 + nharderrors=-1 + unpk77_success=.false. + if(iwspr.eq.0) then + maxosd=2 + Keff=91 + norder=3 + call timer('d240_101',0) + call decode240_101(llr,Keff,maxosd,norder,apmask,message101, & + cw,ntype,nharderrors,dmin) + call timer('d240_101',1) + if(count(cw.eq.1).eq.0) then + nharderrors=-nharderrors + cycle + endif + write(c77,'(77i1)') mod(message101(1:77)+rvec,2) + call unpack77(c77,1,msg,unpk77_success) + elseif(iwspr.eq.1) then +! Try decoding with Keff=66 + maxosd=2 call timer('d240_74 ',0) - Keff=50 - norder=4 + Keff=66 + norder=3 call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & ntype,nharderrors,dmin) call timer('d240_74 ',1) + if(nharderrors.lt.0) goto 3465 if(count(cw.eq.1).eq.0) then nharderrors=-nharderrors cycle @@ -541,89 +523,134 @@ contains write(c77,'(50i1)') message74(1:50) c77(51:77)='000000000000000000000110000' call unpack77(c77,1,msg,unpk77_success) -! No CRC in this mode, so only accept the decode if call/grid have been seen before - if(unpk77_success) then - unpk77_success=.false. - do i=1,nwcalls - if(index(msg,trim(wcalls(i))).gt.0) then - unpk77_success=.true. + if(unpk77_success .and. do_k50_decode) then +! If decode was obtained with Keff=66, save call/grid in fst4w_calls.txt if not there already. + i1=index(msg,' ') + i2=i1+index(msg(i1+1:),' ') + wpart=trim(msg(1:i2)) +! Only save callsigns/grids from type 1 messages + if(index(wpart,'/').eq.0 .and. index(wpart,'<').eq.0) then + ifound=0 + do i=1,nwcalls + if(index(wcalls(i),wpart).ne.0) ifound=1 + enddo + + if(ifound.eq.0) then ! This is a new callsign + new_callsign=.true. + if(nwcalls.lt.MAXWCALLS) then + nwcalls=nwcalls+1 + wcalls(nwcalls)=wpart + else + wcalls(1:nwcalls-1)=wcalls(2:nwcalls) + wcalls(nwcalls)=wpart + endif endif - enddo + endif endif +3465 continue + +! If no decode then try Keff=50 + iaptype=0 + if( .not. unpk77_success .and. do_k50_decode ) then + maxosd=1 + call timer('d240_74 ',0) + Keff=50 + norder=4 + call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & + ntype,nharderrors,dmin) + call timer('d240_74 ',1) + if(count(cw.eq.1).eq.0) then + nharderrors=-nharderrors + cycle + endif + write(c77,'(50i1)') message74(1:50) + c77(51:77)='000000000000000000000110000' + call unpack77(c77,1,msg,unpk77_success) +! No CRC in this mode, so only accept the decode if call/grid have been seen before + if(unpk77_success) then + unpk77_success=.false. + do i=1,nwcalls + if(index(msg,trim(wcalls(i))).gt.0) then + unpk77_success=.true. + endif + enddo + endif + endif + endif - endif + if(nharderrors .ge.0 .and. unpk77_success) then + idupe=0 + do i=1,ndecodes + if(decodes(i).eq.msg) idupe=1 + enddo + if(idupe.eq.1) goto 800 + ndecodes=ndecodes+1 + decodes(ndecodes)=msg - if(nharderrors .ge.0 .and. unpk77_success) then - idupe=0 - do i=1,ndecodes - if(decodes(i).eq.msg) idupe=1 - enddo - if(idupe.eq.1) goto 800 - ndecodes=ndecodes+1 - decodes(ndecodes)=msg + if(iwspr.eq.0) then + call get_fst4_tones_from_bits(message101,itone,0) + else + call get_fst4_tones_from_bits(message74,itone,1) + endif + inquire(file='plotspec',exist=plotspec_exists) + fmid=-999.0 + call timer('dopsprd ',0) + if(plotspec_exists) then + call dopspread(itone,iwave,nsps,nmax,ndown,hmod, & + isbest,fc_synced,fmid,w50) + endif + call timer('dopsprd ',1) + xsig=0 + do i=1,NN + xsig=xsig+s4(itone(i),i) + enddo + base=candidates(icand,5) + arg=600.0*(xsig/base)-1.0 + if(arg.gt.0.0) then + xsnr=10*log10(arg)-35.5-12.5*log10(nsps/8200.0) + if(ntrperiod.eq. 15) xsnr=xsnr+2 + if(ntrperiod.eq. 30) xsnr=xsnr+1 + if(ntrperiod.eq. 900) xsnr=xsnr+1 + if(ntrperiod.eq.1800) xsnr=xsnr+2 + else + xsnr=-99.9 + endif + nsnr=nint(xsnr) + qual=0.0 + fsig=fc_synced - 1.5*baud + inquire(file=trim(data_dir)//'/decdata',exist=decdata_exists) + if(decdata_exists) then + hdec=0 + where(llrs(:,1).ge.0.0) hdec=1 + nhp=count(hdec.ne.cw) ! # hard errors wrt N=1 soft symbols + hd=sum(ieor(hdec,cw)*abs(llrs(:,1))) ! weighted distance wrt N=1 symbols + open(21,file=trim(data_dir)//'/fst4_decodes.dat',status='unknown',position='append') + write(21,3021) nutc,icand,itry,nsyncoh,iaptype, & + ijitter,npct,ntype,Keff,nsync_qual,nharderrors,dmin,nhp,hd, & + sync,xsnr,xdt,fsig,w50,trim(msg) +3021 format(i6.6,i4,6i3,3i4,f6.1,i4,f6.1,f9.2,f6.1,f6.2,f7.1,f7.3,1x,a) + close(21) + endif + call this%callback(nutc,smax1,nsnr,xdt,fsig,msg, & + iaptype,qual,ntrperiod,lwspr,fmid,w50) + if(iwspr.eq.0 .and. nb.lt.0 .and. imode.eq.1) go to 900 + goto 800 + endif + enddo ! metrics + enddo ! istart jitter +800 enddo !candidate list + enddo ! noise blanker loop - if(iwspr.eq.0) then - call get_fst4_tones_from_bits(message101,itone,0) - else - call get_fst4_tones_from_bits(message74,itone,1) - endif - inquire(file='plotspec',exist=plotspec_exists) - fmid=-999.0 - call timer('dopsprd ',0) - if(plotspec_exists) then - call dopspread(itone,iwave,nsps,nmax,ndown,hmod, & - isbest,fc_synced,fmid,w50) - endif - call timer('dopsprd ',1) - xsig=0 - do i=1,NN - xsig=xsig+s4(itone(i),i) - enddo - base=candidates(icand,5) - arg=600.0*(xsig/base)-1.0 - if(arg.gt.0.0) then - xsnr=10*log10(arg)-35.5-12.5*log10(nsps/8200.0) - if(ntrperiod.eq. 15) xsnr=xsnr+2 - if(ntrperiod.eq. 30) xsnr=xsnr+1 - if(ntrperiod.eq. 900) xsnr=xsnr+1 - if(ntrperiod.eq.1800) xsnr=xsnr+2 - else - xsnr=-99.9 - endif - nsnr=nint(xsnr) - qual=0.0 - fsig=fc_synced - 1.5*baud - inquire(file=trim(data_dir)//'/decdata',exist=decdata_exists) - if(decdata_exists) then - hdec=0 - where(llrs(:,1).ge.0.0) hdec=1 - nhp=count(hdec.ne.cw) ! # hard errors wrt N=1 soft symbols - hd=sum(ieor(hdec,cw)*abs(llrs(:,1))) ! weighted distance wrt N=1 symbols - open(21,file=trim(data_dir)//'/fst4_decodes.dat',status='unknown',position='append') - write(21,3021) nutc,icand,itry,nsyncoh,iaptype, & - ijitter,ntype,Keff,nsync_qual,nharderrors,dmin,nhp,hd, & - sync,xsnr,xdt,fsig,w50,trim(msg) -3021 format(i6.6,i4,5i3,3i4,f6.1,i4,f6.1,f9.2,f6.1,f6.2,f7.1,f7.3,1x,a) - close(21) - endif - call this%callback(nutc,smax1,nsnr,xdt,fsig,msg, & - iaptype,qual,ntrperiod,lwspr,fmid,w50) - if(iwspr.eq.0 .and. nb.lt.0) go to 900 - goto 800 - endif - enddo ! metrics - enddo ! istart jitter -800 enddo !candidate list - enddo ! noise blanker loop + if(new_callsign .and. do_k50_decode) then ! re-write the fst4w_calls.txt file + open(42,file=trim(data_dir)//'/fst4w_calls.txt',status='unknown') + do i=1,nwcalls + write(42,'(a20)') trim(wcalls(i)) + enddo + close(42) + endif - if(new_callsign .and. do_k50_decode) then ! re-write the fst4w_calls.txt file - open(42,file=trim(data_dir)//'/fst4w_calls.txt',status='unknown') - do i=1,nwcalls - write(42,'(a20)') trim(wcalls(i)) - enddo - close(42) - endif + enddo ! mode loop 900 return end subroutine decode @@ -818,8 +845,8 @@ contains do i=ina,inb !Compute CCF of s() and 4 tones s2(i)=s(i-hmod*3) + s(i-hmod) +s(i+hmod) +s(i+hmod*3) enddo - npct=30 - call fst4_baseline(s2,nnw,ina+hmod*3,inb-hmod*3,npct,sbase) + npctile=30 + call fst4_baseline(s2,nnw,ina+hmod*3,inb-hmod*3,npctile,sbase) if(any(sbase(ina:inb).le.0.0)) return s2(ina:inb)=s2(ina:inb)/sbase(ina:inb) !Normalize wrt noise level From 41258a5ddc2661f218176abe6e6276a6e3864ad0 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Sat, 2 Jan 2021 10:25:29 -0600 Subject: [PATCH 299/426] Add rudimentary sanity checks to the values parsed from FST4W_ALSO_FST4. --- lib/fst4_decode.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index 9a1aa0f16..9b066e61f 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -275,6 +275,7 @@ contains if(iwspr.eq.1 .and. nlength.eq.8) then read(s_nfa_nfb,"(i4.4,i4.4)") nfa_mode2,nfb_mode2 nmode=2 + if(nfa_mode2.lt.100 .or. nfb_mode2.gt.4910 .or. nfb_mode2.le.nfa_mode2) nmode=1 endif do imode=1,nmode From e2c2228d365c7daed5f43f6428eebba2b5317cd2 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Sat, 2 Jan 2021 11:37:16 -0600 Subject: [PATCH 300/426] Remove some debug code. --- lib/fst4_decode.f90 | 638 +++++++++++++++++++++----------------------- 1 file changed, 307 insertions(+), 331 deletions(-) diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index 9b066e61f..5fb591ffa 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -43,7 +43,6 @@ contains procedure(fst4_decode_callback) :: callback character*37 decodes(100) character*37 msg,msgsent - character*8 s_nfa_nfb character*20 wcalls(MAXWCALLS), wpart character*77 c77 character*12 mycall,hiscall @@ -264,259 +263,281 @@ contains inb1=0 !Fixed NB value, 0 to 25% endif -! If environment variable FST4W_ALSO_FST4 exists then, when in FST4W mode, -! do a second pass for FST4 decodes. The value of FST4W_ALSO_FST4 -! is of the form xxxxyyyy where nfa=xxxx and nfb=yyyy are the -! search limits for the FST4 decoding pass, e.g. -! FST4W_ALSO_FST4=08001700 will set FST4 search window to [800Hz,1700Hz] -! - nmode=1 - call get_environment_variable("FST4W_ALSO_FST4",s_nfa_nfb,nlength) - if(iwspr.eq.1 .and. nlength.eq.8) then - read(s_nfa_nfb,"(i4.4,i4.4)") nfa_mode2,nfb_mode2 - nmode=2 - if(nfa_mode2.lt.100 .or. nfb_mode2.gt.4910 .or. nfb_mode2.le.nfa_mode2) nmode=1 - endif - - do imode=1,nmode - if(imode.eq.1) iwspr=1 - if(imode.eq.2) then ! this is FST4 after a FST4W pass - iwspr=0 - nfa=nfa_mode2 - nfb=nfb_mode2 - endif ! nfa,nfb: define the noise-baseline analysis window ! fa, fb: define the signal search window ! We usually make nfafb so that noise baseline analysis ! window extends outside of the [fa,fb] window where we think the signals are. ! - if(iwspr.eq.1) then !FST4W - nfa=max(100,nfqso-ntol-100) - nfb=min(4800,nfqso+ntol+100) - fa=max(100,nint(nfqso+1.5*baud-ntol)) ! signal search window - fb=min(4800,nint(nfqso+1.5*baud+ntol)) - else if(iwspr.eq.0) then - if(imode.eq.1 .and. single_decode) then - fa=max(100,nint(nfa+1.5*baud)) - fb=min(4800,nint(nfb+1.5*baud)) - ! extend noise fit 100 Hz outside of search window - nfa=max(100,nfa-100) - nfb=min(4800,nfb+100) - else - fa=max(100,nint(nfa+1.5*baud)) - fb=min(4800,nint(nfb+1.5*baud)) - ! extend noise fit 100 Hz outside of search window - nfa=max(100,nfa-100) - nfb=min(4800,nfb+100) - endif + if(iwspr.eq.1) then !FST4W + nfa=max(100,nfqso-ntol-100) + nfb=min(4800,nfqso+ntol+100) + fa=max(100,nint(nfqso+1.5*baud-ntol)) ! signal search window + fb=min(4800,nint(nfqso+1.5*baud+ntol)) + else if(iwspr.eq.0) then + if(single_decode) then + fa=max(100,nint(nfa+1.5*baud)) + fb=min(4800,nint(nfb+1.5*baud)) + ! extend noise fit 100 Hz outside of search window + nfa=max(100,nfa-100) + nfb=min(4800,nfb+100) + else + fa=max(100,nint(nfa+1.5*baud)) + fb=min(4800,nint(nfb+1.5*baud)) + ! extend noise fit 100 Hz outside of search window + nfa=max(100,nfa-100) + nfb=min(4800,nfb+100) endif + endif - ndecodes=0 - decodes=' ' - new_callsign=.false. - do inb=0,inb1,inb2 - if(nb.lt.0) npct=inb ! we are looping over blanker settings - call blanker(iwave,nfft1,ndropmax,npct,c_bigfft) + ndecodes=0 + decodes=' ' + new_callsign=.false. + do inb=0,inb1,inb2 + if(nb.lt.0) npct=inb ! we are looping over blanker settings + call blanker(iwave,nfft1,ndropmax,npct,c_bigfft) ! The big fft is done once and is used for calculating the smoothed spectrum ! and also for downconverting/downsampling each candidate. - call four2a(c_bigfft,nfft1,1,-1,0) !r2c - nhicoh=1 - nsyncoh=8 - minsync=1.20 - if(ntrperiod.eq.15) minsync=1.15 + call four2a(c_bigfft,nfft1,1,-1,0) !r2c + nhicoh=1 + nsyncoh=8 + minsync=1.20 + if(ntrperiod.eq.15) minsync=1.15 ! Get first approximation of candidate frequencies - call get_candidates_fst4(c_bigfft,nfft1,nsps,hmod,fs,fa,fb,nfa,nfb, & - minsync,ncand,candidates0) - isbest=0 - fc2=0. - do icand=1,ncand - fc0=candidates0(icand,1) - if(iwspr.eq.0 .and. nb.lt.0 .and. npct.ne.0 .and. & - abs(fc0-(nfqso+1.5*baud)).gt.ntol) cycle ! blanker loop only near nfqso - detmet=candidates0(icand,2) + call get_candidates_fst4(c_bigfft,nfft1,nsps,hmod,fs,fa,fb,nfa,nfb, & + minsync,ncand,candidates0) + isbest=0 + fc2=0. + do icand=1,ncand + fc0=candidates0(icand,1) + if(iwspr.eq.0 .and. nb.lt.0 .and. npct.ne.0 .and. & + abs(fc0-(nfqso+1.5*baud)).gt.ntol) cycle ! blanker loop only near nfqso + detmet=candidates0(icand,2) ! Downconvert and downsample a slice of the spectrum centered on the ! rough estimate of the candidates frequency. ! Output array c2 is complex baseband sampled at 12000/ndown Sa/sec. ! The size of the downsampled c2 array is nfft2=nfft1/ndown - call timer('dwnsmpl ',0) - call fst4_downsample(c_bigfft,nfft1,ndown,fc0,sigbw,c2) - call timer('dwnsmpl ',1) + call timer('dwnsmpl ',0) + call fst4_downsample(c_bigfft,nfft1,ndown,fc0,sigbw,c2) + call timer('dwnsmpl ',1) - call timer('sync240 ',0) - call fst4_sync_search(c2,nfft2,hmod,fs2,nss,ntrperiod,nsyncoh,emedelay,sbest,fcbest,isbest) - call timer('sync240 ',1) + call timer('sync240 ',0) + call fst4_sync_search(c2,nfft2,hmod,fs2,nss,ntrperiod,nsyncoh,emedelay,sbest,fcbest,isbest) + call timer('sync240 ',1) - fc_synced = fc0 + fcbest - dt_synced = (isbest-fs2)*dt2 !nominal dt is 1 second so frame starts at sample fs2 - candidates0(icand,3)=fc_synced - candidates0(icand,4)=isbest - enddo + fc_synced = fc0 + fcbest + dt_synced = (isbest-fs2)*dt2 !nominal dt is 1 second so frame starts at sample fs2 + candidates0(icand,3)=fc_synced + candidates0(icand,4)=isbest + enddo ! remove duplicate candidates - do icand=1,ncand - fc=candidates0(icand,3) - isbest=nint(candidates0(icand,4)) - do ic2=icand+1,ncand - fc2=candidates0(ic2,3) - isbest2=nint(candidates0(ic2,4)) - if(fc2.gt.0.0) then - if(abs(fc2-fc).lt.0.10*baud) then ! same frequency - if(abs(isbest2-isbest).le.2) then - candidates0(ic2,3)=-1 - endif + do icand=1,ncand + fc=candidates0(icand,3) + isbest=nint(candidates0(icand,4)) + do ic2=icand+1,ncand + fc2=candidates0(ic2,3) + isbest2=nint(candidates0(ic2,4)) + if(fc2.gt.0.0) then + if(abs(fc2-fc).lt.0.10*baud) then ! same frequency + if(abs(isbest2-isbest).le.2) then + candidates0(ic2,3)=-1 endif endif - enddo - enddo - ic=0 - do icand=1,ncand - if(candidates0(icand,3).gt.0) then - ic=ic+1 - candidates0(ic,:)=candidates0(icand,:) endif enddo - ncand=ic - -! If FST4 mode and Single Decode is not checked, then find candidates -! within 20 Hz of nfqso and put them at the top of the list - if(iwspr.eq.0 .and. .not.single_decode) then - nclose=count(abs(candidates0(:,3)-(nfqso+1.5*baud)).le.20) - k=0 - do i=1,ncand - if(abs(candidates0(i,3)-(nfqso+1.5*baud)).le.20) then - k=k+1 - candidates(k,:)=candidates0(i,:) - endif - enddo - do i=1,ncand - if(abs(candidates0(i,3)-(nfqso+1.5*baud)).gt.20) then - k=k+1 - candidates(k,:)=candidates0(i,:) - endif - enddo - else - candidates=candidates0 + enddo + ic=0 + do icand=1,ncand + if(candidates0(icand,3).gt.0) then + ic=ic+1 + candidates0(ic,:)=candidates0(icand,:) endif + enddo + ncand=ic - xsnr=0. - do icand=1,ncand - sync=candidates(icand,2) - fc_synced=candidates(icand,3) - isbest=nint(candidates(icand,4)) - xdt=(isbest-nspsec)/fs2 - if(ntrperiod.eq.15) xdt=(isbest-real(nspsec)/2.0)/fs2 - call timer('dwnsmpl ',0) - call fst4_downsample(c_bigfft,nfft1,ndown,fc_synced,sigbw,c2) - call timer('dwnsmpl ',1) +! If FST4 mode and Single Decode is not checked, then find candidates +! within 20 Hz of nfqso and put them at the top of the list + if(iwspr.eq.0 .and. .not.single_decode) then + nclose=count(abs(candidates0(:,3)-(nfqso+1.5*baud)).le.20) + k=0 + do i=1,ncand + if(abs(candidates0(i,3)-(nfqso+1.5*baud)).le.20) then + k=k+1 + candidates(k,:)=candidates0(i,:) + endif + enddo + do i=1,ncand + if(abs(candidates0(i,3)-(nfqso+1.5*baud)).gt.20) then + k=k+1 + candidates(k,:)=candidates0(i,:) + endif + enddo + else + candidates=candidates0 + endif - do ijitter=0,jittermax - if(ijitter.eq.0) ioffset=0 - if(ijitter.eq.1) ioffset=1 - if(ijitter.eq.2) ioffset=-1 - is0=isbest+ioffset - iend=is0+160*nss-1 - if( is0.lt.0 .or. iend.gt.(nfft2-1) ) cycle - cframe=c2(is0:iend) - bitmetrics=0 - call timer('bitmetrc',0) - call get_fst4_bitmetrics(cframe,nss,nblock,nhicoh,bitmetrics, & - s4,nsync_qual,badsync) - call timer('bitmetrc',1) - if(badsync) cycle + xsnr=0. + do icand=1,ncand + sync=candidates(icand,2) + fc_synced=candidates(icand,3) + isbest=nint(candidates(icand,4)) + xdt=(isbest-nspsec)/fs2 + if(ntrperiod.eq.15) xdt=(isbest-real(nspsec)/2.0)/fs2 + call timer('dwnsmpl ',0) + call fst4_downsample(c_bigfft,nfft1,ndown,fc_synced,sigbw,c2) + call timer('dwnsmpl ',1) - do il=1,4 - llrs( 1: 60,il)=bitmetrics( 17: 76, il) - llrs( 61:120,il)=bitmetrics( 93:152, il) - llrs(121:180,il)=bitmetrics(169:228, il) - llrs(181:240,il)=bitmetrics(245:304, il) - enddo + do ijitter=0,jittermax + if(ijitter.eq.0) ioffset=0 + if(ijitter.eq.1) ioffset=1 + if(ijitter.eq.2) ioffset=-1 + is0=isbest+ioffset + iend=is0+160*nss-1 + if( is0.lt.0 .or. iend.gt.(nfft2-1) ) cycle + cframe=c2(is0:iend) + bitmetrics=0 + call timer('bitmetrc',0) + call get_fst4_bitmetrics(cframe,nss,nblock,nhicoh,bitmetrics, & + s4,nsync_qual,badsync) + call timer('bitmetrc',1) + if(badsync) cycle - apmag=maxval(abs(llrs(:,4)))*1.1 - ntmax=nblock+nappasses(nQSOProgress) - if(lapcqonly) ntmax=nblock+1 - if(ndepth.eq.1) ntmax=nblock ! no ap for ndepth=1 - apmask=0 + do il=1,4 + llrs( 1: 60,il)=bitmetrics( 17: 76, il) + llrs( 61:120,il)=bitmetrics( 93:152, il) + llrs(121:180,il)=bitmetrics(169:228, il) + llrs(181:240,il)=bitmetrics(245:304, il) + enddo - if(iwspr.eq.1) then ! 50-bit msgs, no ap decoding - nblock=4 - ntmax=nblock + apmag=maxval(abs(llrs(:,4)))*1.1 + ntmax=nblock+nappasses(nQSOProgress) + if(lapcqonly) ntmax=nblock+1 + if(ndepth.eq.1) ntmax=nblock ! no ap for ndepth=1 + apmask=0 + + if(iwspr.eq.1) then ! 50-bit msgs, no ap decoding + nblock=4 + ntmax=nblock + endif + + do itry=1,ntmax + if(itry.eq.1) llr=llrs(:,1) + if(itry.eq.2.and.itry.le.nblock) llr=llrs(:,2) + if(itry.eq.3.and.itry.le.nblock) llr=llrs(:,3) + if(itry.eq.4.and.itry.le.nblock) llr=llrs(:,4) + if(itry.le.nblock) then + apmask=0 + iaptype=0 endif - do itry=1,ntmax - if(itry.eq.1) llr=llrs(:,1) - if(itry.eq.2.and.itry.le.nblock) llr=llrs(:,2) - if(itry.eq.3.and.itry.le.nblock) llr=llrs(:,3) - if(itry.eq.4.and.itry.le.nblock) llr=llrs(:,4) - if(itry.le.nblock) then + if(itry.gt.nblock .and. iwspr.eq.0) then ! do ap passes + llr=llrs(:,nblock) ! Use largest blocksize as the basis for AP passes + iaptype=naptypes(nQSOProgress,itry-nblock) + if(lapcqonly) iaptype=1 + 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 apmask=0 - iaptype=0 + apmask(1:29)=1 + llr(1:29)=apmag*mcq(1:29) endif - if(itry.gt.nblock .and. iwspr.eq.0) then ! do ap passes - llr=llrs(:,nblock) ! Use largest blocksize as the basis for AP passes - iaptype=naptypes(nQSOProgress,itry-nblock) - if(lapcqonly) iaptype=1 - 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 - apmask=0 - apmask(1:29)=1 - llr(1:29)=apmag*mcq(1:29) - endif - - if(iaptype.eq.2) then ! MyCall ??? ??? - apmask=0 - apmask(1:29)=1 - llr(1:29)=apmag*apbits(1:29) - endif - - if(iaptype.eq.3) then ! MyCall DxCall ??? - apmask=0 - apmask(1:58)=1 - llr(1:58)=apmag*apbits(1:58) - endif - - if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype .eq.6) then - apmask=0 - apmask(1:77)=1 - llr(1:58)=apmag*apbits(1:58) - if(iaptype.eq.4) llr(59:77)=apmag*mrrr(1:19) - if(iaptype.eq.5) llr(59:77)=apmag*m73(1:19) - if(iaptype.eq.6) llr(59:77)=apmag*mrr73(1:19) - endif + if(iaptype.eq.2) then ! MyCall ??? ??? + apmask=0 + apmask(1:29)=1 + llr(1:29)=apmag*apbits(1:29) endif - dmin=0.0 - nharderrors=-1 - unpk77_success=.false. - if(iwspr.eq.0) then - maxosd=2 - Keff=91 - norder=3 - call timer('d240_101',0) - call decode240_101(llr,Keff,maxosd,norder,apmask,message101, & - cw,ntype,nharderrors,dmin) - call timer('d240_101',1) - if(count(cw.eq.1).eq.0) then - nharderrors=-nharderrors - cycle - endif - write(c77,'(77i1)') mod(message101(1:77)+rvec,2) - call unpack77(c77,1,msg,unpk77_success) - elseif(iwspr.eq.1) then + if(iaptype.eq.3) then ! MyCall DxCall ??? + apmask=0 + apmask(1:58)=1 + llr(1:58)=apmag*apbits(1:58) + endif + + if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype .eq.6) then + apmask=0 + apmask(1:77)=1 + llr(1:58)=apmag*apbits(1:58) + if(iaptype.eq.4) llr(59:77)=apmag*mrrr(1:19) + if(iaptype.eq.5) llr(59:77)=apmag*m73(1:19) + if(iaptype.eq.6) llr(59:77)=apmag*mrr73(1:19) + endif + endif + + dmin=0.0 + nharderrors=-1 + unpk77_success=.false. + if(iwspr.eq.0) then + maxosd=2 + Keff=91 + norder=3 + call timer('d240_101',0) + call decode240_101(llr,Keff,maxosd,norder,apmask,message101, & + cw,ntype,nharderrors,dmin) + call timer('d240_101',1) + if(count(cw.eq.1).eq.0) then + nharderrors=-nharderrors + cycle + endif + write(c77,'(77i1)') mod(message101(1:77)+rvec,2) + call unpack77(c77,1,msg,unpk77_success) + elseif(iwspr.eq.1) then ! Try decoding with Keff=66 - maxosd=2 + maxosd=2 + call timer('d240_74 ',0) + Keff=66 + norder=3 + call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & + ntype,nharderrors,dmin) + call timer('d240_74 ',1) + if(nharderrors.lt.0) goto 3465 + if(count(cw.eq.1).eq.0) then + nharderrors=-nharderrors + cycle + endif + write(c77,'(50i1)') message74(1:50) + c77(51:77)='000000000000000000000110000' + call unpack77(c77,1,msg,unpk77_success) + if(unpk77_success .and. do_k50_decode) then +! If decode was obtained with Keff=66, save call/grid in fst4w_calls.txt if not there already. + i1=index(msg,' ') + i2=i1+index(msg(i1+1:),' ') + wpart=trim(msg(1:i2)) +! Only save callsigns/grids from type 1 messages + if(index(wpart,'/').eq.0 .and. index(wpart,'<').eq.0) then + ifound=0 + do i=1,nwcalls + if(index(wcalls(i),wpart).ne.0) ifound=1 + enddo + + if(ifound.eq.0) then ! This is a new callsign + new_callsign=.true. + if(nwcalls.lt.MAXWCALLS) then + nwcalls=nwcalls+1 + wcalls(nwcalls)=wpart + else + wcalls(1:nwcalls-1)=wcalls(2:nwcalls) + wcalls(nwcalls)=wpart + endif + endif + endif + endif +3465 continue + +! If no decode then try Keff=50 + iaptype=0 + if( .not. unpk77_success .and. do_k50_decode ) then + maxosd=1 call timer('d240_74 ',0) - Keff=66 - norder=3 + Keff=50 + norder=4 call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & ntype,nharderrors,dmin) call timer('d240_74 ',1) - if(nharderrors.lt.0) goto 3465 if(count(cw.eq.1).eq.0) then nharderrors=-nharderrors cycle @@ -524,134 +545,89 @@ contains write(c77,'(50i1)') message74(1:50) c77(51:77)='000000000000000000000110000' call unpack77(c77,1,msg,unpk77_success) - if(unpk77_success .and. do_k50_decode) then -! If decode was obtained with Keff=66, save call/grid in fst4w_calls.txt if not there already. - i1=index(msg,' ') - i2=i1+index(msg(i1+1:),' ') - wpart=trim(msg(1:i2)) -! Only save callsigns/grids from type 1 messages - if(index(wpart,'/').eq.0 .and. index(wpart,'<').eq.0) then - ifound=0 - do i=1,nwcalls - if(index(wcalls(i),wpart).ne.0) ifound=1 - enddo - - if(ifound.eq.0) then ! This is a new callsign - new_callsign=.true. - if(nwcalls.lt.MAXWCALLS) then - nwcalls=nwcalls+1 - wcalls(nwcalls)=wpart - else - wcalls(1:nwcalls-1)=wcalls(2:nwcalls) - wcalls(nwcalls)=wpart - endif - endif - endif - endif -3465 continue - -! If no decode then try Keff=50 - iaptype=0 - if( .not. unpk77_success .and. do_k50_decode ) then - maxosd=1 - call timer('d240_74 ',0) - Keff=50 - norder=4 - call decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw, & - ntype,nharderrors,dmin) - call timer('d240_74 ',1) - if(count(cw.eq.1).eq.0) then - nharderrors=-nharderrors - cycle - endif - write(c77,'(50i1)') message74(1:50) - c77(51:77)='000000000000000000000110000' - call unpack77(c77,1,msg,unpk77_success) ! No CRC in this mode, so only accept the decode if call/grid have been seen before - if(unpk77_success) then - unpk77_success=.false. - do i=1,nwcalls - if(index(msg,trim(wcalls(i))).gt.0) then - unpk77_success=.true. - endif - enddo - endif + if(unpk77_success) then + unpk77_success=.false. + do i=1,nwcalls + if(index(msg,trim(wcalls(i))).gt.0) then + unpk77_success=.true. + endif + enddo endif - endif - if(nharderrors .ge.0 .and. unpk77_success) then - idupe=0 - do i=1,ndecodes - if(decodes(i).eq.msg) idupe=1 - enddo - if(idupe.eq.1) goto 800 - ndecodes=ndecodes+1 - decodes(ndecodes)=msg + endif - if(iwspr.eq.0) then - call get_fst4_tones_from_bits(message101,itone,0) - else - call get_fst4_tones_from_bits(message74,itone,1) - endif - inquire(file='plotspec',exist=plotspec_exists) - fmid=-999.0 - call timer('dopsprd ',0) - if(plotspec_exists) then - call dopspread(itone,iwave,nsps,nmax,ndown,hmod, & - isbest,fc_synced,fmid,w50) - endif - call timer('dopsprd ',1) - xsig=0 - do i=1,NN - xsig=xsig+s4(itone(i),i) - enddo - base=candidates(icand,5) - arg=600.0*(xsig/base)-1.0 - if(arg.gt.0.0) then - xsnr=10*log10(arg)-35.5-12.5*log10(nsps/8200.0) - if(ntrperiod.eq. 15) xsnr=xsnr+2 - if(ntrperiod.eq. 30) xsnr=xsnr+1 - if(ntrperiod.eq. 900) xsnr=xsnr+1 - if(ntrperiod.eq.1800) xsnr=xsnr+2 - else - xsnr=-99.9 - endif - nsnr=nint(xsnr) - qual=0.0 - fsig=fc_synced - 1.5*baud - inquire(file=trim(data_dir)//'/decdata',exist=decdata_exists) - if(decdata_exists) then - hdec=0 - where(llrs(:,1).ge.0.0) hdec=1 - nhp=count(hdec.ne.cw) ! # hard errors wrt N=1 soft symbols - hd=sum(ieor(hdec,cw)*abs(llrs(:,1))) ! weighted distance wrt N=1 symbols - open(21,file=trim(data_dir)//'/fst4_decodes.dat',status='unknown',position='append') - write(21,3021) nutc,icand,itry,nsyncoh,iaptype, & - ijitter,npct,ntype,Keff,nsync_qual,nharderrors,dmin,nhp,hd, & - sync,xsnr,xdt,fsig,w50,trim(msg) -3021 format(i6.6,i4,6i3,3i4,f6.1,i4,f6.1,f9.2,f6.1,f6.2,f7.1,f7.3,1x,a) - close(21) - endif - call this%callback(nutc,smax1,nsnr,xdt,fsig,msg, & - iaptype,qual,ntrperiod,lwspr,fmid,w50) - if(iwspr.eq.0 .and. nb.lt.0 .and. imode.eq.1) go to 900 - goto 800 + if(nharderrors .ge.0 .and. unpk77_success) then + idupe=0 + do i=1,ndecodes + if(decodes(i).eq.msg) idupe=1 + enddo + if(idupe.eq.1) goto 800 + ndecodes=ndecodes+1 + decodes(ndecodes)=msg + + if(iwspr.eq.0) then + call get_fst4_tones_from_bits(message101,itone,0) + else + call get_fst4_tones_from_bits(message74,itone,1) endif - enddo ! metrics - enddo ! istart jitter -800 enddo !candidate list - enddo ! noise blanker loop + inquire(file='plotspec',exist=plotspec_exists) + fmid=-999.0 + call timer('dopsprd ',0) + if(plotspec_exists) then + call dopspread(itone,iwave,nsps,nmax,ndown,hmod, & + isbest,fc_synced,fmid,w50) + endif + call timer('dopsprd ',1) + xsig=0 + do i=1,NN + xsig=xsig+s4(itone(i),i) + enddo + base=candidates(icand,5) + arg=600.0*(xsig/base)-1.0 + if(arg.gt.0.0) then + xsnr=10*log10(arg)-35.5-12.5*log10(nsps/8200.0) + if(ntrperiod.eq. 15) xsnr=xsnr+2 + if(ntrperiod.eq. 30) xsnr=xsnr+1 + if(ntrperiod.eq. 900) xsnr=xsnr+1 + if(ntrperiod.eq.1800) xsnr=xsnr+2 + else + xsnr=-99.9 + endif + nsnr=nint(xsnr) + qual=0.0 + fsig=fc_synced - 1.5*baud + inquire(file=trim(data_dir)//'/decdata',exist=decdata_exists) + if(decdata_exists) then + hdec=0 + where(llrs(:,1).ge.0.0) hdec=1 + nhp=count(hdec.ne.cw) ! # hard errors wrt N=1 soft symbols + hd=sum(ieor(hdec,cw)*abs(llrs(:,1))) ! weighted distance wrt N=1 symbols + open(21,file=trim(data_dir)//'/fst4_decodes.dat',status='unknown',position='append') + write(21,3021) nutc,icand,itry,nsyncoh,iaptype, & + ijitter,npct,ntype,Keff,nsync_qual,nharderrors,dmin,nhp,hd, & + sync,xsnr,xdt,fsig,w50,trim(msg) +3021 format(i6.6,i4,6i3,3i4,f6.1,i4,f6.1,f9.2,f6.1,f6.2,f7.1,f7.3,1x,a) + close(21) + endif + call this%callback(nutc,smax1,nsnr,xdt,fsig,msg, & + iaptype,qual,ntrperiod,lwspr,fmid,w50) + if(iwspr.eq.0 .and. nb.lt.0) go to 900 + goto 800 + endif + enddo ! metrics + enddo ! istart jitter +800 enddo !candidate list + enddo ! noise blanker loop - if(new_callsign .and. do_k50_decode) then ! re-write the fst4w_calls.txt file - open(42,file=trim(data_dir)//'/fst4w_calls.txt',status='unknown') - do i=1,nwcalls - write(42,'(a20)') trim(wcalls(i)) - enddo - close(42) - endif - - enddo ! mode loop + if(new_callsign .and. do_k50_decode) then ! re-write the fst4w_calls.txt file + open(42,file=trim(data_dir)//'/fst4w_calls.txt',status='unknown') + do i=1,nwcalls + write(42,'(a20)') trim(wcalls(i)) + enddo + close(42) + endif 900 return end subroutine decode From bc58604d4257e47b540797c98f9fc6ee9c975181 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 2 Jan 2021 13:09:47 -0500 Subject: [PATCH 301/426] Allow optional frequency drift of simulated Q65 signals. --- lib/qra/q65/q65sim.f90 | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/qra/q65/q65sim.f90 b/lib/qra/q65/q65sim.f90 index c3881d673..230535965 100644 --- a/lib/qra/q65/q65sim.f90 +++ b/lib/qra/q65/q65sim.f90 @@ -15,13 +15,14 @@ program q65sim complex cspread(0:NMAX-1) !Complex amplitude for Rayleigh fading complex z real*8 f0,dt,twopi,phi,dphi,baud,fsample,freq - character msg*37,fname*17,csubmode*1,arg*12,cd*1 + character msg*37,fname*17,csubmode*1,arg*12 character msgsent*37 nargs=iargc() - if(nargs.ne.8) then - print *, 'Usage: q65sim "msg" A-E freq fDop DT TRp Nfiles SNR' - print *, 'Example: q65sim "K1ABC W9XYZ EN37" A 1500 0.0 0.0 60 1 -26' + if(nargs.ne.9) then + print*,'Usage: q65sim "msg" A-E freq fDop DT f1 TRp Nfile SNR' + print*,'Example: q65sim "K1ABC W9XYZ EN37" A 1500 0.0 0.0 0.0 60 1 -26' + print*,' fDop is Doppler spread; f1 is drift rate (Hz/min)' go to 999 endif call getarg(1,msg) @@ -34,10 +35,12 @@ program q65sim call getarg(5,arg) read(arg,*) xdt call getarg(6,arg) - read(arg,*) ntrperiod + read(arg,*) f1 call getarg(7,arg) - read(arg,*) nfiles + read(arg,*) ntrperiod call getarg(8,arg) + read(arg,*) nfiles + call getarg(9,arg) read(arg,*) snrdb if(ntrperiod.eq.15) then @@ -86,7 +89,7 @@ program q65sim h=default_header(12000,npts) write(*,1004) -1004 format('File TR Freq Mode S/N DT Dop Message'/60('-')) +1004 format('File TR Freq Mode S/N Dop DT f1 Message'/66('-')) do ifile=1,nfiles !Loop over requested number of files if(ntrperiod.lt.60) then @@ -109,8 +112,8 @@ program q65sim 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,f0,csubmode,snrdb,xdt,fspread,trim(msgsent) -1020 format(i4,i6,f7.1,2x,a1,2x,f5.1,f6.2,f6.1,2x,a) + write(*,1020) ifile,ntrperiod,f0,csubmode,snrdb,fspread,xdt,f1,trim(msgsent) +1020 format(i4,i6,f7.1,2x,a1,2x,f5.1,f6.2,2f6.1,2x,a) phi=0.d0 dphi=0.d0 k=(xdt+0.5)*12000 !Start audio at t=xdt+0.5 s (TR=15 and 30 s) @@ -120,7 +123,8 @@ program q65sim isym=i/nsps + 1 if(isym.gt.nsym) exit if(isym.ne.isym0) then - freq=f0 + itone(isym)*baud*mode65 +! Drift term + freq = f0 + itone(isym)*baud*mode65 + f1*i*dt/60.0 dphi=twopi*freq*dt isym0=isym endif From 04b22ce250b567cf4b0d1c0f447ca74dd0230c6c Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 2 Jan 2021 14:21:14 -0500 Subject: [PATCH 302/426] Preparing to add solution for frequendy drift to q65_sync.f90. --- lib/qra/q65/q65_sync.f90 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 75e055ea9..e04069689 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -130,6 +130,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & ! Compute 2D ccf using all 85 symbols in the list message ccf=0. iia=200.0/df + do lag=lag1,lag2 do k=1,85 j=j0 + NSTEP*(k-1) + 1 + lag @@ -141,6 +142,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & endif enddo enddo + ccfmax=maxval(ccf(-ia:ia,:)) if(ccfmax.gt.ccf_best) then ccf_best=ccfmax @@ -164,7 +166,11 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & cycle endif n=n+1 - if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) + if(j.ge.1 .and. j.le.jz) then + do i=0,LL + s3(i-64,n)=s1(i+i1,j) + enddo + endif enddo nsubmode=0 From a02cbabb994edc7fad8461dd78fc8ea9f0f0eef7 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 3 Jan 2021 11:18:14 -0500 Subject: [PATCH 303/426] Fix an off-by-1 bug introduced in the previous commit. --- lib/qra/q65/q65_sync.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index e04069689..48ca0a727 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -167,7 +167,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & endif n=n+1 if(j.ge.1 .and. j.le.jz) then - do i=0,LL + do i=0,LL-1 s3(i-64,n)=s1(i+i1,j) enddo endif From 6074e4e3afa250652225c35f86e3892bf2c92a73 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 3 Jan 2021 11:19:39 -0500 Subject: [PATCH 304/426] Options for introducing frequency drift and frequency steps (e.g. 10 Hz) in Q65 simulator. --- lib/qra/q65/q65sim.f90 | 22 ++++++++++++---------- lib/test_q65.f90 | 30 ++++++++++++++++++------------ 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/lib/qra/q65/q65sim.f90 b/lib/qra/q65/q65sim.f90 index 230535965..7fea0ed99 100644 --- a/lib/qra/q65/q65sim.f90 +++ b/lib/qra/q65/q65sim.f90 @@ -19,10 +19,10 @@ program q65sim character msgsent*37 nargs=iargc() - if(nargs.ne.9) then - print*,'Usage: q65sim "msg" A-E freq fDop DT f1 TRp Nfile SNR' - print*,'Example: q65sim "K1ABC W9XYZ EN37" A 1500 0.0 0.0 0.0 60 1 -26' - print*,' fDop is Doppler spread; f1 is drift rate (Hz/min)' + if(nargs.ne.10) then + print*,'Usage: q65sim "msg" A-E freq fDop DT f1 Stp TRp Nfile SNR' + print*,'Example: q65sim "K1ABC W9XYZ EN37" A 1500 0.0 0.0 0.0 1 60 1 -26' + print*,' fDop: Doppler spread; f1: drift rate (Hz/min); Stp: step size (Hz)' go to 999 endif call getarg(1,msg) @@ -37,10 +37,12 @@ program q65sim call getarg(6,arg) read(arg,*) f1 call getarg(7,arg) - read(arg,*) ntrperiod + read(arg,*) nstp call getarg(8,arg) - read(arg,*) nfiles + read(arg,*) ntrperiod call getarg(9,arg) + read(arg,*) nfiles + call getarg(10,arg) read(arg,*) snrdb if(ntrperiod.eq.15) then @@ -89,7 +91,7 @@ program q65sim h=default_header(12000,npts) write(*,1004) -1004 format('File TR Freq Mode S/N Dop DT f1 Message'/66('-')) +1004 format('File TR Freq Mode S/N Dop DT f1 Stp Message'/70('-')) do ifile=1,nfiles !Loop over requested number of files if(ntrperiod.lt.60) then @@ -112,8 +114,8 @@ program q65sim 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,f0,csubmode,snrdb,fspread,xdt,f1,trim(msgsent) -1020 format(i4,i6,f7.1,2x,a1,2x,f5.1,f6.2,2f6.1,2x,a) + write(*,1020) ifile,ntrperiod,f0,csubmode,snrdb,fspread,xdt,f1,nstp,trim(msgsent) +1020 format(i4,i6,f7.1,2x,a1,2x,f5.1,f6.2,2f6.1,i4,2x,a) phi=0.d0 dphi=0.d0 k=(xdt+0.5)*12000 !Start audio at t=xdt+0.5 s (TR=15 and 30 s) @@ -123,8 +125,8 @@ program q65sim isym=i/nsps + 1 if(isym.gt.nsym) exit if(isym.ne.isym0) then -! Drift term freq = f0 + itone(isym)*baud*mode65 + f1*i*dt/60.0 + if(nstp.ne.0) freq=nstp*nint(freq/nstp) dphi=twopi*freq*dt isym0=isym endif diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index 710220a8a..d5c7cb4f5 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -1,6 +1,6 @@ program test_q65 - character*75 cmd1,cmd2,line + character*82 cmd1,cmd2,line character*22 msg character*8 arg character*1 csubmode @@ -8,9 +8,9 @@ program test_q65 logical decok nargs=iargc() - if(nargs.ne.10) then - print*,'Usage: test_q65 "msg" A-D depth freq DT fDop TRp Q nfiles SNR' - print*,'Example: test_q65 "K1ABC W9XYZ EN37" A 3 1500 0.0 5.0 60 3 100 -20' + if(nargs.ne.12) then + print*,'Usage: test_q65 "msg" A-D depth freq DT fDop f1 Stp TRp Q nfiles SNR' + print*,'Example: test_q65 "K1ABC W9XYZ EN37" A 3 1500 0.0 5.0 0.0 1 60 3 100 -20' print*,'Use SNR = 0 to loop over all relevant SNRs' print*,'Use MyCall=K1ABC, HisCall=W9XYZ, HisGrid="EN37" for AP decodes' print*,'Option Q sets QSOprogress (0-5) for AP decoding.' @@ -28,12 +28,16 @@ program test_q65 call getarg(6,arg) read(arg,*) fDop call getarg(7,arg) - read(arg,*) ntrperiod + read(arg,*) f1 call getarg(8,arg) - read(arg,*) nQSOprogress + read(arg,*) nstp call getarg(9,arg) - read(arg,*) nfiles + read(arg,*) ntrperiod call getarg(10,arg) + read(arg,*) nQSOprogress + call getarg(11,arg) + read(arg,*) nfiles + call getarg(12,arg) read(arg,*) snr if(ntrperiod.eq.15) then @@ -68,7 +72,7 @@ program test_q65 ! 1 2 3 4 5 6 7 ! 123456789012345678901234567890123456789012345678901234567890123456789012345' - cmd1='q65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 60 100 -10.0 > junk0' + cmd1='q65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 0.0 1 60 100 -10.0 > junk0' cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A -Q 3 -f 1500 *.wav > junk' write(cmd1(10:33),'(a)') '"'//msg//'"' @@ -76,15 +80,17 @@ program test_q65 write(cmd1(37:40),'(i4)') nf0 write(cmd1(41:45),'(f5.0)') fDop write(cmd1(46:50),'(f5.2)') dt - write(cmd1(51:54),'(i4)') ntrperiod - write(cmd1(55:59),'(i5)') nfiles + write(cmd1(51:54),'(f4.0)') f1 + write(cmd1(55:57),'(i3)') nstp + write(cmd1(58:61),'(i4)') ntrperiod + write(cmd1(62:66),'(i5)') nfiles write(cmd2(11:13),'(i3)') ntrperiod write(cmd2(33:34),'(i2)') ndepth write(cmd2(44:44),'(i1)') nQSOprogress write(cmd2(49:52),'(i4)') nf0 cmd2(39:39)=csubmode - + call system('rm -f *.wav') ! call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist) @@ -110,7 +116,7 @@ program test_q65 nfalse=0 naptype=0 ndecn=0 - write(cmd1(63:67),'(f5.1)') snr1 + write(cmd1(70:74),'(f5.1)') snr1 call system(cmd1) call sec0(0,tdec) call system(cmd2) From 662a43d3dd9f88d7be32f4d5b79fe5efdf2e5b43 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 4 Jan 2021 09:07:03 -0500 Subject: [PATCH 305/426] Corrections to the frequency-drift feature in q65sim. --- lib/qra/q65/q65sim.f90 | 3 ++- lib/test_q65.f90 | 26 +++++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/lib/qra/q65/q65sim.f90 b/lib/qra/q65/q65sim.f90 index 7fea0ed99..6ceeb60fa 100644 --- a/lib/qra/q65/q65sim.f90 +++ b/lib/qra/q65/q65sim.f90 @@ -125,8 +125,9 @@ program q65sim isym=i/nsps + 1 if(isym.gt.nsym) exit if(isym.ne.isym0) then - freq = f0 + itone(isym)*baud*mode65 + f1*i*dt/60.0 + freq = f0 + f1*i*dt/60.0 if(nstp.ne.0) freq=nstp*nint(freq/nstp) + freq=freq + itone(isym)*baud*mode65 dphi=twopi*freq*dt isym0=isym endif diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index d5c7cb4f5..dc54f0ad0 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -93,16 +93,12 @@ program test_q65 call system('rm -f *.wav') -! call qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist) -! write(*,1000) ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist -! write(12,1000) ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist -!1000 format(/'Depth:',i2,' AP:',i2,' df:',i3,' dt:',i3,' bw1:',i3,' bw2:',i3, & -! ' dist:',i3) - + write(*,1008) ntrperiod,csubmode,ndepth,fDop,f1,nstp +1008 format('Mode:',i4,a1,' Depth:',i3,' fDop:',f6.1,' Drift:',f6.1, & + ' Steps:',i3) write(*,1010) (j,j=0,5) write(12,1010) (j,j=0,5) -1010 format(' SNR Mode d Dop Sync Avg Dec Bad',6i4,' tdec avg rms'/ & - 75('-')) +1010 format(' SNR Sync Avg Dec Bad',6i4,' tdec avg rms'/64('-')) dterr=tsym/4.0 nferr=max(1,nint(0.5*baud),nint(fdop/3.0)) @@ -169,17 +165,17 @@ program test_q65 snr_avg=snrsum/nsum snr_rms=sqrt(snrsq/nsum - snr_avg**2) endif - write(*,1100) snr1,ntrperiod,csubmode,ndepth,nint(fDop),nsync,ndecn, & - ndec1,nfalse,naptype,tdec/nfiles,snr_avg,snr_rms - write(12,1100) snr1,ntrperiod,csubmode,ndepth,nint(fDop),nsync,ndecn, & - ndec1,nfalse,naptype,tdec/nfiles,snr_avg,snr_rms -1100 format(f5.1,i4,a1,i3,5i4,i5,5i4,f6.2,f6.1,f5.1) + write(*,1100) snr1,nsync,ndecn,ndec1,nfalse,naptype,tdec/nfiles, & + snr_avg,snr_rms + write(12,1100) snr1,nsync,ndecn,ndec1,nfalse,naptype,tdec/nfiles, & + snr_avg,snr_rms +1100 format(f5.1,4i4,i5,5i4,f6.2,f6.1,f5.1) if(ndec1.lt.nfiles/2 .and. ndec1z.ge.nfiles/2) then snr_thresh=snr1 + float(nfiles/2 - ndec1)/(ndec1z-ndec1) open(13,file='snr_thresh.out',status='unknown',position='append') write(13,1200) ntrperiod,csubmode,ndepth,nQSOprogress,nfiles, & - fdop,snr_thresh,trim(msg) -1200 format(i3,a1,2i3,i5,2f7.1,2x,a) + fdop,f1,nstp,nfalse,snr_thresh,trim(msg) +1200 format(i3,a1,2i3,i5,2f7.1,2i3,f7.1,2x,a) close(13) endif flush(6) From ea271152b8b34da798e85c9aa50ad6c91f21feee Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 4 Jan 2021 14:38:02 -0500 Subject: [PATCH 306/426] Implement "orange sync curve", useful for multi-signal situation. --- lib/qra/q65/q65_sync.f90 | 21 +++++++++++++++++++-- widgets/plotter.cpp | 15 ++++++++------- widgets/plotter.h | 4 +++- widgets/widegraph.cpp | 9 ++++++++- widgets/widegraph.ui | 5 +++++ 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 48ca0a727..57e5fb7c6 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -30,6 +30,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) real, allocatable :: ccf(:,:) !CCF(freq,lag) real, allocatable :: ccf1(:) !CCF(freq) at best lag + real, allocatable :: ccf2(:) !CCF(freq) at any lag real sync(85) !sync vector complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ @@ -57,6 +58,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & allocate(c0(0:nfft-1)) allocate(ccf(-ia2:ia2,-53:214)) allocate(ccf1(-ia2:ia2)) + allocate(ccf2(-ia2:ia2)) if(sync(1).eq.99.0) then !Generate the sync vector sync=-22.0/63.0 !Sync tone OFF @@ -156,6 +158,10 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & endif enddo ! imsg + do i=-ia2,ia2 + ccf2(i)=maxval(ccf(i,:)) + enddo + i1=i0+ipk-64 i2=i1+LL-1 j=j0+jpk-7 @@ -197,6 +203,10 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & ccf1=ccf1-base smax=maxval(ccf1) if(smax.gt.10.0) ccf1=10.0*ccf1/smax + base=(sum(ccf2(-ia2:-ia2+ic)) + sum(ccf2(ia2-ic:ia2)))/(2.0+2.0*ic); + ccf2=ccf2-base + smax=maxval(ccf2) + if(smax.gt.10.0) ccf2=10.0*ccf2/smax go to 200 endif enddo @@ -223,6 +233,11 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & jpk=ijpk(2)-53-1 f0=nfqso + ipk*df xdt=jpk*dtstep + + do i=-ia2,ia2 + ccf2(i)=maxval(ccf(i,:)) + enddo + sq=0. nsq=0 jd=(lag2-lag1)/4 @@ -239,6 +254,8 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & snr1=smax/rms ccf1=ccf(:,jpk)/rms if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 + ccf2=ccf2/rms + if(snr1.gt.10.0) ccf2=(10.0/snr1)*ccf2 if(iand(ndepth,16).eq.16) then ! Fill s3() from s1() here, then call q65_avg(). @@ -270,8 +287,8 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & enddo do i=-ia2,ia2 freq=nfqso + i*df - write(17,1100) freq,ccf1(i),xdt -1100 format(3f10.3) + write(17,1100) freq,ccf1(i),xdt,ccf2(i) +1100 format(4f10.3) enddo close(17) width=df*(i2-i1) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 9b6b3856b..fd7cb173c 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -141,7 +141,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) //move current data down one line (must do this before attaching a QPainter object) if(bScroll and !m_bReplot) m_WaterfallPixmap.scroll(0,1,0,0,m_w,m_h1); QPainter painter1(&m_WaterfallPixmap); - if(m_bFirst or bRed or !m_bQ65_Sync or m_mode!=m_mode0 or m_bResized) { + if(m_bFirst or bRed or (!m_bQ65_Sync and !m_bQ65_MultiSync) or m_mode!=m_mode0 or m_bResized) { m_2DPixmap = m_OverlayPixmap.copy(0,0,m_w,m_h2); m_bFirst=false; m_bResized=false; @@ -227,7 +227,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) } - if(i==iz-1 and !m_bQ65_Sync) { + if(i==iz-1 and !m_bQ65_Sync and !m_bQ65_MultiSync) { painter2D.drawPolyline(LineBuf,j); } LineBuf[j].setX(i); @@ -271,25 +271,26 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) painter2D.drawText(x1-4,y,"73"); } - if(bRed and m_bQ65_Sync) { + if(bRed and (m_bQ65_Sync or m_bQ65_MultiSync)) { //Plot the Q65 red or orange sync curve int k=0; std::ifstream f; f.open(m_redFile.toLatin1()); if(f) { int x,y; - float freq,sync,xdt; + float freq,sync,xdt,sync2; for(int i=0; i<99999; i++) { - f >> freq >> sync >> xdt; + f >> freq >> sync >> xdt >> sync2; if(f.eof()) break; x=XfromFreq(freq); + if(m_bQ65_MultiSync) sync=sync2; y=m_h2*(0.9 - 0.09*gain2d*sync) - m_plot2dZero; LineBuf2[k].setX(x); LineBuf2[k].setY(y); - k++; } f.close(); - QPen pen0(Qt::red,2); + QPen pen0(Qt::red,2); + if(m_bQ65_MultiSync) pen0.setColor("orange"); painter2D.setPen(pen0); painter2D.drawPolyline(LineBuf2,k); QString t; diff --git a/widgets/plotter.h b/widgets/plotter.h index 3f8f00574..c3627eb00 100644 --- a/widgets/plotter.h +++ b/widgets/plotter.h @@ -82,7 +82,8 @@ public: bool Reference() const {return m_bReference;} void setQ65_Sync(bool b) {m_bQ65_Sync = b;} bool Q65_Sync() const {return m_bQ65_Sync;} - void drawRed(int ia, int ib, float swide[]); + void setQ65_MultiSync(bool b) {m_bQ65_MultiSync = b;} + bool Q65_MultiSync() const {return m_bQ65_MultiSync;} void drawRed(int ia, int ib, float swide[]); void setVHF(bool bVHF); void setRedFile(QString fRed); void setFST4_FreqRange(int fLow,int fHigh); @@ -116,6 +117,7 @@ private: bool m_bReference; bool m_bReference0; bool m_bQ65_Sync; + bool m_bQ65_MultiSync; bool m_bVHF; bool m_bSingleDecode; bool m_bFirst=true; diff --git a/widgets/widegraph.cpp b/widgets/widegraph.cpp index 2bcb5936a..6f6736029 100644 --- a/widgets/widegraph.cpp +++ b/widgets/widegraph.cpp @@ -71,11 +71,13 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) : ui->widePlot->setLinearAvg(m_settings->value("LinearAvg",false).toBool()); ui->widePlot->setReference(m_settings->value("Reference",false).toBool()); ui->widePlot->setQ65_Sync(m_settings->value("Q65_Sync",false).toBool()); + ui->widePlot->setQ65_MultiSync(m_settings->value("Q65_MultiSync",false).toBool()); if(ui->widePlot->current()) ui->spec2dComboBox->setCurrentIndex(0); if(ui->widePlot->cumulative()) ui->spec2dComboBox->setCurrentIndex(1); if(ui->widePlot->linearAvg()) ui->spec2dComboBox->setCurrentIndex(2); if(ui->widePlot->Reference()) ui->spec2dComboBox->setCurrentIndex(3); if(ui->widePlot->Q65_Sync()) ui->spec2dComboBox->setCurrentIndex(4); + if(ui->widePlot->Q65_MultiSync()) ui->spec2dComboBox->setCurrentIndex(5); int nbpp=m_settings->value("BinsPerPixel",2).toInt(); ui->widePlot->setBinsPerPixel(nbpp); ui->sbPercent2dPlot->setValue(m_Percent2DScreen); @@ -133,6 +135,7 @@ void WideGraph::saveSettings() //saveS m_settings->setValue ("LinearAvg", ui->widePlot->linearAvg()); m_settings->setValue ("Reference", ui->widePlot->Reference()); m_settings->setValue ("Q65_Sync", ui->widePlot->Q65_Sync()); + m_settings->setValue ("Q65_MultiSync", ui->widePlot->Q65_MultiSync()); m_settings->setValue ("BinsPerPixel", ui->widePlot->binsPerPixel ()); m_settings->setValue ("StartFreq", ui->widePlot->startFreq ()); m_settings->setValue ("WaterfallPalette", m_waterfallPalette); @@ -320,6 +323,7 @@ void WideGraph::on_spec2dComboBox_currentIndexChanged(int index) ui->widePlot->setLinearAvg(false); ui->widePlot->setReference(false); ui->widePlot->setQ65_Sync(false); + ui->widePlot->setQ65_MultiSync(false); ui->smoSpinBox->setEnabled(false); switch (index) { @@ -339,7 +343,10 @@ void WideGraph::on_spec2dComboBox_currentIndexChanged(int index) case 4: ui->widePlot->setQ65_Sync(true); break; - } + case 5: + ui->widePlot->setQ65_MultiSync(true); + break; + } replot(); } diff --git a/widgets/widegraph.ui b/widgets/widegraph.ui index d78757444..84fbf7f5b 100644 --- a/widgets/widegraph.ui +++ b/widgets/widegraph.ui @@ -340,6 +340,11 @@ Q65_Sync + + + Q65_MultiSync + + From 40cf752fe652f867e4ef59883040136b90f882e2 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Mon, 4 Jan 2021 23:01:04 +0000 Subject: [PATCH 307/426] Fix missed merge conflict --- widgets/mainwindow.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index f9a9d241d..8ea5a2821 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -1034,7 +1034,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, void MainWindow::not_GA_warning_message () { -<<<<<<< HEAD // MessageBox::critical_message (this, // "This is a pre-release version of WSJT-X 2.3.0 made\n" // "available for testing purposes. By design it will\n" @@ -1043,16 +1042,6 @@ void MainWindow::not_GA_warning_message () // if (now >= QDateTime {{2021, 1, 19}, {0, 0}, Qt::UTC}) { // Q_EMIT finished (); // } -======= - MessageBox::critical_message (this, - "This is a pre-release version of WSJT-X 2.3.0 made\n" - "available for testing purposes. By design it will\n" - "be nonfunctional after 0000 UTC on Jan 26, 2021."); - auto now = QDateTime::currentDateTimeUtc (); - if (now >= QDateTime {{2021, 1, 26}, {0, 0}, Qt::UTC}) { - Q_EMIT finished (); - } ->>>>>>> master } void MainWindow::initialize_fonts () From f82356e199a7a498c58b34053c7a0972fe652565 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 5 Jan 2021 10:42:19 -0500 Subject: [PATCH 308/426] q65sim: nonzero step size ==> Doppler tracking; otherwise, linear drift. --- lib/qra/q65/q65sim.f90 | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/qra/q65/q65sim.f90 b/lib/qra/q65/q65sim.f90 index 6ceeb60fa..5c7efb1a4 100644 --- a/lib/qra/q65/q65sim.f90 +++ b/lib/qra/q65/q65sim.f90 @@ -22,7 +22,10 @@ program q65sim if(nargs.ne.10) then print*,'Usage: q65sim "msg" A-E freq fDop DT f1 Stp TRp Nfile SNR' print*,'Example: q65sim "K1ABC W9XYZ EN37" A 1500 0.0 0.0 0.0 1 60 1 -26' - print*,' fDop: Doppler spread; f1: drift rate (Hz/min); Stp: step size (Hz)' + print*,' fDop = Doppler spread' + print*,' f1 = Drift or Doppler rate (Hz/min)' + print*,' Stp = Step size (Hz)' + print*,' Stp = 0 implies no Doppler tracking' go to 999 endif call getarg(1,msg) @@ -125,9 +128,9 @@ program q65sim isym=i/nsps + 1 if(isym.gt.nsym) exit if(isym.ne.isym0) then - freq = f0 + f1*i*dt/60.0 - if(nstp.ne.0) freq=nstp*nint(freq/nstp) - freq=freq + itone(isym)*baud*mode65 + freq_drift=f1*i*dt/60.0 + if(nstp.ne.0) freq_drift=freq_drift - nstp*nint(freq_drift/nstp) + freq = f0 + freq_drift + itone(isym)*baud*mode65 dphi=twopi*freq*dt isym0=isym endif From 4fb90e336ee1a9c3f8448dd4f180d241f1aa6c53 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 5 Jan 2021 11:30:22 -0500 Subject: [PATCH 309/426] Better orange sync curve. Clear message averaging after a successful decode. --- lib/q65_decode.f90 | 4 +-- lib/qra/q65/q65_avg.f90 | 24 ++++++++++++---- lib/qra/q65/q65_sync.f90 | 62 +++++++++++++++++++--------------------- 3 files changed, 50 insertions(+), 40 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 1d83d4868..6068e3ef3 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -183,8 +183,8 @@ contains 1000 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent nsnr=nint(snr2) - call this%callback(nutc,sync,nsnr,xdt1,f1,decoded, & - idec,ntrperiod) + call this%callback(nutc,sync,nsnr,xdt1,f1,decoded,idec,ntrperiod) + call q65_clravg else ! Report sync, even if no decode. nsnr=db(snr1) - 35.0 diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index b90a7a227..a1f748792 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -70,7 +70,6 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) entry q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & ibwa,ibwb,codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) - if(nsave.lt.2) go to 900 mode_q65=2**nsubmode f0diff=baud*mode_q65 ibwa=1.8*log(baud*mode_q65) + 2 @@ -117,7 +116,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) xdtsave(i),f0save(i) 1001 format(a1,i5.4,f6.1,f6.2,f7.1) enddo - if(navg.lt.2) go to 900 + if(navg.lt.2) go to 100 s3avg=s3avg/navg nFadingModel=1 @@ -131,7 +130,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) snr2=esnodb - 0.5*db(2500.0/baud) + 3.0 !Empirical adjustment snr2=snr2 - db(float(navg)) !Is this right? idec=100+navg - go to 900 + go to 100 endif enddo @@ -176,11 +175,24 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment snr2=snr2 - 0.5*db(float(navg)) !Is this right? idec=100*(iaptype+2) + navg -! print*,'D dec2 ',ibw,dec,snr2,avemsg - go to 900 + go to 100 endif enddo ! ibw enddo ! ipass -900 return +100 return + + entry q65_clravg + + iutc=-1 + iseq=-1 + snr1save=0. + xdtsave=0. + f0save=0.0 + nsave=0 + if(allocated(s3save)) s3save=0. + if(allocated(s3avg)) s3avg=0. + + return + end subroutine q65_avg diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 57e5fb7c6..2c1d2e8f0 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -158,10 +158,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & endif enddo ! imsg - do i=-ia2,ia2 - ccf2(i)=maxval(ccf(i,:)) - enddo - i1=i0+ipk-64 i2=i1+LL-1 j=j0+jpk-7 @@ -207,15 +203,15 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & ccf2=ccf2-base smax=maxval(ccf2) if(smax.gt.10.0) ccf2=10.0*ccf2/smax - go to 200 + go to 100 endif enddo - -!###################################################################### -! Establish xdt, f0, and snr1 using sync symbols (and perhaps some AP symbols) -100 ccf=0. irc=-2 dat4=0 + +!###################################################################### +! Compute the 2D CCF using sync symbols only +100 ccf=0. do lag=lag1,lag2 do k=1,85 n=NSTEP*(k-1) + 1 @@ -228,11 +224,10 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & endif enddo enddo + ijpk=maxloc(ccf(-ia:ia,:)) ipk=ijpk(1)-ia-1 jpk=ijpk(2)-53-1 - f0=nfqso + ipk*df - xdt=jpk*dtstep do i=-ia2,ia2 ccf2(i)=maxval(ccf(i,:)) @@ -252,46 +247,49 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & rms=sqrt(sq/nsq) smax=ccf(ipk,jpk) snr1=smax/rms - ccf1=ccf(:,jpk)/rms - if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 ccf2=ccf2/rms if(snr1.gt.10.0) ccf2=(10.0/snr1)*ccf2 - if(iand(ndepth,16).eq.16) then + if(irc.le.0) then + f0=nfqso + ipk*df + xdt=jpk*dtstep + ccf1=ccf(:,jpk)/rms + if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 + if(iand(ndepth,16).eq.16) then ! Fill s3() from s1() here, then call q65_avg(). - i1=i0+ipk-64 - i2=i1+LL-1 - if(snr1.ge.2.8 .and. i1.ge.1 .and. i2.le.iz) then - j=j0+jpk-7 - n=0 - do k=1,85 - j=j+8 - if(sync(k).gt.0.0) then - cycle - endif - n=n+1 - if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) - enddo -! write(*,3002) 'B',xdt,f0,sum(s3) -!3002 format(a1,f7.2,2f8.1) - call q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) + i1=i0+ipk-64 + i2=i1+LL-1 + if(snr1.ge.2.8 .and. i1.ge.1 .and. i2.le.iz) then + j=j0+jpk-7 + n=0 + do k=1,85 + j=j+8 + if(sync(k).gt.0.0) then + cycle + endif + n=n+1 + if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) + enddo + call q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) + endif endif endif -200 smax=maxval(ccf1) + smax=maxval(ccf1) i1=-9999 i2=-9999 do i=-ia,ia if(i1.eq.-9999 .and. ccf1(i).ge.0.5*smax) i1=i if(i2.eq.-9999 .and. ccf1(-i).ge.0.5*smax) i2=-i enddo + width=df*(i2-i1) + do i=-ia2,ia2 freq=nfqso + i*df write(17,1100) freq,ccf1(i),xdt,ccf2(i) 1100 format(4f10.3) enddo close(17) - width=df*(i2-i1) 900 return end subroutine q65_sync From ea1510d837d4aee5b35c879a729d5c15d51886f2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 5 Jan 2021 12:01:20 -0500 Subject: [PATCH 310/426] Don't display DT on waterfall when orange sync curve is used. --- widgets/plotter.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index fd7cb173c..52bb97be7 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -293,12 +293,14 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) if(m_bQ65_MultiSync) pen0.setColor("orange"); painter2D.setPen(pen0); painter2D.drawPolyline(LineBuf2,k); - QString t; - t = t.asprintf("DT = %6.2f",xdt); - painter2D.setPen(Qt::white); - Font.setWeight(QFont::Bold); - painter2D.setFont(Font); - painter2D.drawText(m_w-100,m_h2/2,t); + if(m_bQ65_Sync) { + QString t; + t = t.asprintf("DT = %6.2f",xdt); + painter2D.setPen(Qt::white); + Font.setWeight(QFont::Bold); + painter2D.setFont(Font); + painter2D.drawText(m_w-100,m_h2/2,t); + } } } update(); //trigger a new paintEvent From 47c8286db80416f9318763a895cfb09cf7d0ee27 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 5 Jan 2021 19:45:00 -0500 Subject: [PATCH 311/426] Disallow Q65 decodes with sum(dat4)=0: all 78 bita are zero. --- lib/qra/q65/q65_sync.f90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 2c1d2e8f0..616c7ba86 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -308,6 +308,7 @@ subroutine q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) decoded=' ' call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) + if(sum(dat4).le.0) irc=-2 if(irc.ge.0 .and. plog.gt.PLOG_MIN) then write(c77,1000) dat4(1:12),dat4(13)/2 1000 format(12b6.6,b5.5) @@ -332,6 +333,7 @@ subroutine q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) decoded=' ' call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) + if(sum(dat4).le.0) irc=-2 if(irc.ge.0) then write(c77,1000) dat4(1:12),dat4(13)/2 1000 format(12b6.6,b5.5) From fb6fcdf35e5db16893a850c9e1641e80b934a5e5 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 6 Jan 2021 10:24:08 -0500 Subject: [PATCH 312/426] Mostly(?) temporary diagnostic tests. --- lib/q65_decode.f90 | 4 ++-- lib/qra/q65/q65_set_list.f90 | 1 + lib/qra/q65/q65_sync.f90 | 12 ++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 6068e3ef3..193ba6afc 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -89,10 +89,10 @@ contains df1=12000.0/nfft1 this%callback => callback nFadingModel=1 -! Set up the codewords for full-AP list decoding - call q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) dgen=0 call q65_enc(dgen,codewords) !Initialize the Q65 codec +! Set up the codewords for full-AP list decoding + call q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) call timer('sync_q65',0) call q65_sync(nutc,iwave,ntrperiod,mode65,codewords,ncw,nsps, & nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4, & diff --git a/lib/qra/q65/q65_set_list.f90 b/lib/qra/q65/q65_set_list.f90 index 44b2e12cb..266659d60 100644 --- a/lib/qra/q65/q65_set_list.f90 +++ b/lib/qra/q65/q65_set_list.f90 @@ -10,6 +10,7 @@ subroutine q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ ncw=0 + codewords=0 if(hiscall(1:1).eq. ' ') return ncw=MAX_NCW diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 616c7ba86..1ed8fdbe6 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -53,6 +53,11 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & nsmo=int(0.7*mode_q65*mode_q65) if(nsmo.lt.1) nsmo=1 +!### TEMPORARY + f0=-99 + xdt=-99 +!### + allocate(s1(iz,jz)) allocate(s3(-64:LL-65,63)) allocate(c0(0:nfft-1)) @@ -158,6 +163,13 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & endif enddo ! imsg +!### TEMPORARY + if(f0.le.0.0 .or. xdt.lt.-90.) then + write(71) iz,jz,ia2,ccf_best,ipk,jpk,nfqso,mode_q65,df,xdt,f0, & + imsg_best,ccf,ccf1 + endif +!### + i1=i0+ipk-64 i2=i1+LL-1 j=j0+jpk-7 From 5fe0bd350855f8a435c1ae3efa9ea7380ab87b86 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 6 Jan 2021 10:30:05 -0500 Subject: [PATCH 313/426] Add s1 to the diagnostic output. --- lib/qra/q65/q65_sync.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 1ed8fdbe6..07f7a7d97 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -166,7 +166,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & !### TEMPORARY if(f0.le.0.0 .or. xdt.lt.-90.) then write(71) iz,jz,ia2,ccf_best,ipk,jpk,nfqso,mode_q65,df,xdt,f0, & - imsg_best,ccf,ccf1 + imsg_best,ccf,ccf1,s1 endif !### From 9b35eeaeadf68189fa2764427b8b4684000dd4f7 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Wed, 6 Jan 2021 15:48:07 +0000 Subject: [PATCH 314/426] Revert "Add s1 to the diagnostic output." This reverts commit 5fe0bd350855f8a435c1ae3efa9ea7380ab87b86. --- lib/qra/q65/q65_sync.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 07f7a7d97..1ed8fdbe6 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -166,7 +166,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & !### TEMPORARY if(f0.le.0.0 .or. xdt.lt.-90.) then write(71) iz,jz,ia2,ccf_best,ipk,jpk,nfqso,mode_q65,df,xdt,f0, & - imsg_best,ccf,ccf1,s1 + imsg_best,ccf,ccf1 endif !### From c407bc00f92eacb2ac69e92a56256a0cf15d047e Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Wed, 6 Jan 2021 15:48:21 +0000 Subject: [PATCH 315/426] Revert "Mostly(?) temporary diagnostic tests." This reverts commit fb6fcdf35e5db16893a850c9e1641e80b934a5e5. --- lib/q65_decode.f90 | 4 ++-- lib/qra/q65/q65_set_list.f90 | 1 - lib/qra/q65/q65_sync.f90 | 12 ------------ 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 193ba6afc..6068e3ef3 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -89,10 +89,10 @@ contains df1=12000.0/nfft1 this%callback => callback nFadingModel=1 +! Set up the codewords for full-AP list decoding + call q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) dgen=0 call q65_enc(dgen,codewords) !Initialize the Q65 codec -! Set up the codewords for full-AP list decoding - call q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) call timer('sync_q65',0) call q65_sync(nutc,iwave,ntrperiod,mode65,codewords,ncw,nsps, & nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4, & diff --git a/lib/qra/q65/q65_set_list.f90 b/lib/qra/q65/q65_set_list.f90 index 266659d60..44b2e12cb 100644 --- a/lib/qra/q65/q65_set_list.f90 +++ b/lib/qra/q65/q65_set_list.f90 @@ -10,7 +10,6 @@ subroutine q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ ncw=0 - codewords=0 if(hiscall(1:1).eq. ' ') return ncw=MAX_NCW diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 1ed8fdbe6..616c7ba86 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -53,11 +53,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & nsmo=int(0.7*mode_q65*mode_q65) if(nsmo.lt.1) nsmo=1 -!### TEMPORARY - f0=-99 - xdt=-99 -!### - allocate(s1(iz,jz)) allocate(s3(-64:LL-65,63)) allocate(c0(0:nfft-1)) @@ -163,13 +158,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & endif enddo ! imsg -!### TEMPORARY - if(f0.le.0.0 .or. xdt.lt.-90.) then - write(71) iz,jz,ia2,ccf_best,ipk,jpk,nfqso,mode_q65,df,xdt,f0, & - imsg_best,ccf,ccf1 - endif -!### - i1=i0+ipk-64 i2=i1+LL-1 j=j0+jpk-7 From c7f2be4ba73900c63be4ddc2116cbf3d56226e18 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Wed, 6 Jan 2021 15:49:51 +0000 Subject: [PATCH 316/426] Initialize a variable --- lib/qra/q65/q65_sync.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 616c7ba86..892f60c78 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -37,6 +37,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & data sync(1)/99.0/ save sync + irc=-2 idec=-1 snr1=0. dat4=0 From aee0a1e851f54d4ea454840dcc7bd0161d57d9a9 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 6 Jan 2021 10:58:25 -0500 Subject: [PATCH 317/426] Silence a compiler warning. --- lib/qra/q65/q65_sync.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 892f60c78..ee0e28794 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -299,6 +299,7 @@ subroutine q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) use q65 use packjt77 + real s3(1,1) !Silence compiler warning that wants to see a 2D array real s3prob(0:63,63) !Symbol-value probabilities integer codewords(63,206) integer dat4(13) From bc8d134135dd946a5e6f1dc242d6ca2d2db5a912 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 6 Jan 2021 12:44:49 -0500 Subject: [PATCH 318/426] Silence several compiler warnings. --- lib/jt65_decode.f90 | 2 +- lib/pctile.f90 | 1 - lib/symspec.f90 | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/jt65_decode.f90 b/lib/jt65_decode.f90 index 90459add1..8ac539e94 100644 --- a/lib/jt65_decode.f90 +++ b/lib/jt65_decode.f90 @@ -94,7 +94,7 @@ contains save this%callback => callback - first_time=nrobust !Silence compiler warning + first_time=nrobust .and. (emedelay.eq.-999.9) !Silence compiler warning first_time=newdat dd=dd0 ndecoded=0 diff --git a/lib/pctile.f90 b/lib/pctile.f90 index c0a6cdb72..0c16aaba3 100644 --- a/lib/pctile.f90 +++ b/lib/pctile.f90 @@ -12,6 +12,5 @@ subroutine pctile(x,npts,npct,xpct) if(j.gt.npts) j=npts xpct=tmp(j) -900 continue return end subroutine pctile diff --git a/lib/symspec.f90 b/lib/symspec.f90 index 64c88815e..707a6368e 100644 --- a/lib/symspec.f90 +++ b/lib/symspec.f90 @@ -37,7 +37,7 @@ subroutine symspec(shared_data,k,TRperiod,nsps,ingain,bLowSidelobes, & equivalence (xc,cx) save - if(TRperiod.lt.0.d0) stop !Silence compiler warning + if(TRperiod+npct.eq.-999.9) stop !Silence compiler warning nfft3=16384 !df=12000.0/16384 = 0.732422 jstep=nsps/2 !Step size = half-symbol in id2() if(k.gt.NMAX) go to 900 From 2194f3096a833329924ca94537a2c45c03c7d99d Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 6 Jan 2021 21:44:49 -0500 Subject: [PATCH 319/426] Don't count lines with blank message in "ndecoded". --- lib/decoder.f90 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 32ffb9e38..1de956ecc 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -370,9 +370,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) close(13) if(ncontest.eq.6) close(19) if(params%nmode.eq.4 .or. params%nmode.eq.65 .or. params%nmode.eq.66) close(14) - return - contains subroutine jt4_decoded(this,snr,dt,freq,have_sync,sync,is_deep, & @@ -812,7 +810,7 @@ contains select type(this) type is (counting_q65_decoder) - this%decoded = this%decoded + 1 + if(idec.gt.0) this%decoded = this%decoded + 1 end select return From b9d33bc998b80d704ed442e0f6c505171e533310 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Thu, 7 Jan 2021 10:05:53 -0600 Subject: [PATCH 320/426] Address compiler warnings in fst4/w code. --- lib/fst4/encode240_101.f90 | 2 -- lib/fst4/encode240_74.f90 | 2 -- lib/fst4/fastosd240_74.f90 | 1 + lib/fst4/genfst4.f90 | 2 +- lib/fst4/get_fst4_bitmetrics.f90 | 5 +++-- lib/fst4/get_fst4_bitmetrics2.f90 | 1 - lib/fst4/ldpcsim240_74.f90 | 4 ++-- lib/fst4/osd240_101.f90 | 1 + lib/fst4/osd240_74.f90 | 1 + lib/fst4_decode.f90 | 3 ++- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/fst4/encode240_101.f90 b/lib/fst4/encode240_101.f90 index da0021df3..bd16b3ca9 100644 --- a/lib/fst4/encode240_101.f90 +++ b/lib/fst4/encode240_101.f90 @@ -4,12 +4,10 @@ subroutine encode240_101(message,codeword) use crc integer, parameter:: N=240, K=101, M=N-K - character*24 c24 integer*1 codeword(N) integer*1 gen(M,K) integer*1 message(K) integer*1 pchecks(M) - integer*4 ncrc24 include "ldpc_240_101_generator.f90" logical first data first/.true./ diff --git a/lib/fst4/encode240_74.f90 b/lib/fst4/encode240_74.f90 index 1d555d17f..459041407 100644 --- a/lib/fst4/encode240_74.f90 +++ b/lib/fst4/encode240_74.f90 @@ -4,12 +4,10 @@ subroutine encode240_74(message,codeword) use crc integer, parameter:: N=240, K=74, M=N-K - character*24 c24 integer*1 codeword(N) integer*1 gen(M,K) integer*1 message(K) integer*1 pchecks(M) - integer*4 ncrc24 include "ldpc_240_74_generator.f90" logical first data first/.true./ diff --git a/lib/fst4/fastosd240_74.f90 b/lib/fst4/fastosd240_74.f90 index f4bb61d60..aa14506a7 100644 --- a/lib/fst4/fastosd240_74.f90 +++ b/lib/fst4/fastosd240_74.f90 @@ -164,6 +164,7 @@ subroutine fastosd240_74(llr,k,apmask,ndeep,message74,cw,nhardmin,dmin) cw=c0 ntotal=0 nrejected=0 + xlambda=0.0 if(ndeep.eq.0) goto 998 ! norder=0 if(ndeep.gt.4) ndeep=4 diff --git a/lib/fst4/genfst4.f90 b/lib/fst4/genfst4.f90 index 650062a03..429505541 100644 --- a/lib/fst4/genfst4.f90 +++ b/lib/fst4/genfst4.f90 @@ -68,7 +68,7 @@ subroutine genfst4(msg0,ichk,msgsent,msgbits,i4tone,iwspr) if(ichk.eq.1) go to 999 if(unpk77_success) go to 2 -1 msgbits=0 + msgbits=0 itone=0 msgsent='*** bad message *** ' go to 999 diff --git a/lib/fst4/get_fst4_bitmetrics.f90 b/lib/fst4/get_fst4_bitmetrics.f90 index 9d3b0ba83..0ae747c5f 100644 --- a/lib/fst4/get_fst4_bitmetrics.f90 +++ b/lib/fst4/get_fst4_bitmetrics.f90 @@ -1,4 +1,4 @@ -subroutine get_fst4_bitmetrics(cd,nss,nmax,nhicoh,bitmetrics,s4,nsync_qual,badsync) +subroutine get_fst4_bitmetrics(cd,nss,bitmetrics,s4,nsync_qual,badsync) use timer_module, only: timer include 'fst4_params.f90' @@ -21,9 +21,10 @@ subroutine get_fst4_bitmetrics(cd,nss,nmax,nhicoh,bitmetrics,s4,nsync_qual,badsy data isyncword2/2,3,1,0,3,2,0,1/ data graymap/0,1,3,2/ data first/.true./,nss0/-1/ - save first,one,cp,nss0 + save first,one,nss0 if(nss.ne.nss0 .and. allocated(ci)) deallocate(ci) + if(first .or. nss.ne.nss0) then allocate(ci(nss,0:3)) one=.false. diff --git a/lib/fst4/get_fst4_bitmetrics2.f90 b/lib/fst4/get_fst4_bitmetrics2.f90 index 9badef231..96557d2ef 100644 --- a/lib/fst4/get_fst4_bitmetrics2.f90 +++ b/lib/fst4/get_fst4_bitmetrics2.f90 @@ -5,7 +5,6 @@ subroutine get_fst4_bitmetrics2(cd,nss,hmod,nsizes,bitmetrics,s4snr,badsync) complex csymb(nss) complex, allocatable, save :: c1(:,:) ! ideal waveforms, 4 tones complex cp(0:3) ! accumulated phase shift over symbol types 0:3 - complex csum,cterm integer isyncword1(0:7),isyncword2(0:7) integer graymap(0:3) integer ip(1) diff --git a/lib/fst4/ldpcsim240_74.f90 b/lib/fst4/ldpcsim240_74.f90 index 78e8e6b5f..de3ffa8b6 100644 --- a/lib/fst4/ldpcsim240_74.f90 +++ b/lib/fst4/ldpcsim240_74.f90 @@ -6,7 +6,7 @@ program ldpcsim240_74 parameter(N=240, K=74, M=N-K) character*8 arg - character*37 msg0,msg + character*37 msg0 character*77 c77 character*24 c24 integer*1 msgbits(74) @@ -15,7 +15,7 @@ program ldpcsim240_74 integer*1 codeword(N),message74(74) integer ncrc24 real rxdata(N),llr(N) - logical first,unpk77_success + logical first data first/.true./ nargs=iargc() diff --git a/lib/fst4/osd240_101.f90 b/lib/fst4/osd240_101.f90 index 5e9f5d195..9d97e8b61 100644 --- a/lib/fst4/osd240_101.f90 +++ b/lib/fst4/osd240_101.f90 @@ -128,6 +128,7 @@ subroutine osd240_101(llr,k,apmask,ndeep,message101,cw,nhardmin,dmin) nrejected=0 npre1=0 npre2=0 + nt=0 if(ndeep.eq.0) goto 998 ! norder=0 if(ndeep.gt.6) ndeep=6 diff --git a/lib/fst4/osd240_74.f90 b/lib/fst4/osd240_74.f90 index a44b69867..a54e767bb 100644 --- a/lib/fst4/osd240_74.f90 +++ b/lib/fst4/osd240_74.f90 @@ -128,6 +128,7 @@ subroutine osd240_74(llr,k,apmask,ndeep,message74,cw,nhardmin,dmin) nrejected=0 npre1=0 npre2=0 + nt=0 if(ndeep.eq.0) goto 998 ! norder=0 if(ndeep.gt.6) ndeep=6 diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index 5fb591ffa..b58b270e7 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -89,6 +89,7 @@ contains mycall13=mycall if(iwspr.ne.0.and.iwspr.ne.1) return + if(lagain) continue ! use lagain to keep compiler happy if(first) then ! read the fst4_calls.txt file @@ -401,7 +402,7 @@ contains cframe=c2(is0:iend) bitmetrics=0 call timer('bitmetrc',0) - call get_fst4_bitmetrics(cframe,nss,nblock,nhicoh,bitmetrics, & + call get_fst4_bitmetrics(cframe,nss,bitmetrics, & s4,nsync_qual,badsync) call timer('bitmetrc',1) if(badsync) cycle From 5b0975bf45c2a3777323c1dcb0e9e15bcf7688c3 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 7 Jan 2021 11:13:43 -0500 Subject: [PATCH 321/426] Correct several flaws in message averaging. Still may want to handle MAXAVE better? --- lib/q65_decode.f90 | 3 ++- lib/qra/q65/q65_avg.f90 | 28 +++++++++++++++------------- lib/qra/q65/q65_sync.f90 | 2 +- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 6068e3ef3..896a52ac7 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -157,8 +157,9 @@ contains if(idec.gt.0) go to 100 !Successful decode, we're done enddo -! No single-transmission decode. +! There was no single-transmission decode. if(iand(ndepth,16).eq.16) then +! Try for an average decode. call q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & ibwa,ibwb,codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) endif diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index a1f748792..d6dd07de2 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -11,7 +11,6 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) character*1 cused(MAXAVE) character*6 cutc real s3(-64:LL-65,63) !Symbol spectra - integer iused(MAXAVE) integer dat4(13) integer codewords(63,206) integer apmask1(78),apsymbols1(78) @@ -19,13 +18,8 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) data first/.true./ save - if(first .or. LL.ne.LL0 .or.lclearave) then - iutc=-1 - iseq=-1 - f0save=0.0 - nsave=0 + if(LL.ne.LL0) then LL0=LL - first=.false. if(allocated(s3save)) deallocate(s3save) if(allocated(s3avg)) deallocate(s3avg) allocate(s3save(-64:LL-65,63,MAXAVE)) @@ -33,6 +27,15 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) s3save=0. s3avg=0. endif + if(first .or. lclearave) then + iutc=-1 + iseq=-1 + f0save=0.0 + nsave=0 + first=.false. + s3save=0. + s3avg=0. + endif if(ntrperiod.eq.15) then dtdiff=0.038 @@ -52,7 +55,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) ! Save data for message averaging nsave=nsave+1 - nsave=mod(nsave-1,MAXAVE)+1 + if(nsave.gt.MAXAVE) nsave=nsave-MAXAVE n=nutc if(ntrperiod.ge.60) n=100*n write(cutc,'(i6.6)') n @@ -62,7 +65,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) iutc(nsave)=nutc !UTC, hhmm or hhmmss snr1save(nsave)=snr1 !SNR from sync xdtsave(nsave)=xdt !DT - f0save(nsave)=f0 !f0 + f0save(nsave)=f0 !f0 s3save(:,:,nsave)=s3(:,:) !Symbol spectra 10 return @@ -93,9 +96,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) xdtsum=xdtsum + xdtsave(i) fsum=fsum + f0save(i) navg=navg+1 - iused(navg)=i enddo - if(navg.lt.MAXAVE) iused(navg+1)=0 ! Find averages of snr1, xdt, and f0 used in this decoding attempt. snr1ave=0. @@ -108,7 +109,8 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) endif ! Write parameters for display to User in the Message Averaging (F7) window. - do i=1,nsave + do i=1,MAXAVE + if(iutc(i).lt.0) cycle if(ntrperiod.le.30) write(14,1000) cused(i),iutc(i),snr1save(i), & xdtsave(i),f0save(i) 1000 format(a1,i7.6,f6.1,f6.2,f7.1) @@ -183,7 +185,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) 100 return entry q65_clravg - + iutc=-1 iseq=-1 snr1save=0. diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index ee0e28794..ed718a664 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -60,7 +60,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & allocate(ccf(-ia2:ia2,-53:214)) allocate(ccf1(-ia2:ia2)) allocate(ccf2(-ia2:ia2)) - + s3=0. if(sync(1).eq.99.0) then !Generate the sync vector sync=-22.0/63.0 !Sync tone OFF do k=1,22 From 353edc4718b6191fcdf9e1a26d899412e6c5132f Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Thu, 7 Jan 2021 10:18:38 -0600 Subject: [PATCH 322/426] Fix one more compiler warning. --- lib/pctile.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pctile.f90 b/lib/pctile.f90 index c0a6cdb72..f30b27ff0 100644 --- a/lib/pctile.f90 +++ b/lib/pctile.f90 @@ -12,6 +12,6 @@ subroutine pctile(x,npts,npct,xpct) if(j.gt.npts) j=npts xpct=tmp(j) -900 continue + continue return end subroutine pctile From 08ef142cb99f82b8ad10b52f972741b1cdff0e0d Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 7 Jan 2021 11:46:36 -0500 Subject: [PATCH 323/426] Trim the Tx message sent to the right panel. --- widgets/mainwindow.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 8ea5a2821..ad7f9bd2b 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -4192,7 +4192,7 @@ void MainWindow::guiUpdate() if(m_restart) { write_all("Tx",m_currentMessage); if (m_config.TX_messages ()) { - ui->decodedTextBrowser2->displayTransmittedText(m_currentMessage,m_modeTx, + ui->decodedTextBrowser2->displayTransmittedText(m_currentMessage.trimmed(),m_modeTx, ui->TxFreqSpinBox->value(),m_bFastMode,m_TRperiod); } } @@ -4287,8 +4287,8 @@ void MainWindow::guiUpdate() 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,m_TRperiod); + ui->decodedTextBrowser2->displayTransmittedText(current_message.trimmed(), + m_modeTx,ui->TxFreqSpinBox->value(),m_bFastMode,m_TRperiod); } } From f003ae653c6ae52619ee84534ea374680102a3de Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 7 Jan 2021 12:00:11 -0500 Subject: [PATCH 324/426] Silence compiler warning. --- lib/qra/q65/q65_sync.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index ed718a664..f83fa4806 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -326,6 +326,7 @@ subroutine q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) use q65 use packjt77 + real s3(1,1) !Silence compiler warning that wants to see a 2D array real s3prob(0:63,63) !Symbol-value probabilities integer dat4(13) character c77*77,decoded*37 From b21477b3ba4be87cf89977c9a9c7ced8a5a92484 Mon Sep 17 00:00:00 2001 From: k9an Date: Thu, 7 Jan 2021 11:38:59 -0600 Subject: [PATCH 325/426] Fix a compiler warning. --- lib/ft8/ft8sim.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ft8/ft8sim.f90 b/lib/ft8/ft8sim.f90 index 42931635b..fd18dff4f 100644 --- a/lib/ft8/ft8sim.f90 +++ b/lib/ft8/ft8sim.f90 @@ -91,7 +91,6 @@ program ft8sim_gfsk call sgran() - msg0=msg do ifile=1,nfiles c0=0. c0(0:NWAVE-1)=cwave From 3b54196206ae1a1cde2ea6a766ef9e18a4adba92 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 7 Jan 2021 13:37:12 -0500 Subject: [PATCH 326/426] Remove two disused lines of code. --- lib/blanker.f90 | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/blanker.f90 b/lib/blanker.f90 index 65c8f8fe6..fe43931b5 100644 --- a/lib/blanker.f90 +++ b/lib/blanker.f90 @@ -48,10 +48,5 @@ subroutine blanker(iwave,nz,ndropmax,npct,c_bigfft) endif enddo - fblanked=fblanked + 0.1*(float(ndropped)/nz - fblanked) - fblanked=float(ndropped)/nz -! write(*,3001) npct,nthresh,fblanked -!3001 format(2i5,f7.3) - return end subroutine blanker From f5d2c5cd90cb858e297529ad2e4f11fff41236cd Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 7 Jan 2021 15:51:52 -0500 Subject: [PATCH 327/426] Erase old T M R 73 when RxFreq has changed. --- widgets/plotter.cpp | 4 +++- widgets/plotter.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 52bb97be7..2181bc619 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -141,10 +141,12 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) //move current data down one line (must do this before attaching a QPainter object) if(bScroll and !m_bReplot) m_WaterfallPixmap.scroll(0,1,0,0,m_w,m_h1); QPainter painter1(&m_WaterfallPixmap); - if(m_bFirst or bRed or (!m_bQ65_Sync and !m_bQ65_MultiSync) or m_mode!=m_mode0 or m_bResized) { + if(m_bFirst or bRed or (!m_bQ65_Sync and !m_bQ65_MultiSync) or m_mode!=m_mode0 + or m_bResized or m_rxFreq!=m_rxFreq0) { m_2DPixmap = m_OverlayPixmap.copy(0,0,m_w,m_h2); m_bFirst=false; m_bResized=false; + m_rxFreq0=m_rxFreq; } m_mode0=m_mode; QPainter painter2D(&m_2DPixmap); diff --git a/widgets/plotter.h b/widgets/plotter.h index c3627eb00..a4a8d6e04 100644 --- a/widgets/plotter.h +++ b/widgets/plotter.h @@ -179,6 +179,7 @@ private: qint32 m_h1; qint32 m_h2; qint32 m_rxFreq; + qint32 m_rxFreq0=0; qint32 m_txFreq; qint32 m_fMin; qint32 m_fMax; From 57b6b2dca146cad94dfe1b926bb6f1eb4e86759c Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 8 Jan 2021 09:42:07 -0500 Subject: [PATCH 328/426] Set the "b90" search range once (in q65_decocd.f90) and share valies through q65.mod. --- lib/q65_decode.f90 | 12 ++++++++++-- lib/qra/q65/q65.f90 | 2 +- lib/qra/q65/q65_avg.f90 | 4 +--- lib/qra/q65/q65_loops.f90 | 12 ++++-------- lib/qra/q65/q65_sync.f90 | 5 ++--- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 896a52ac7..f1fb0364d 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -85,10 +85,18 @@ contains else stop 'Invalid TR period' endif + baud=12000.0/nsps df1=12000.0/nfft1 this%callback => callback nFadingModel=1 + ibwa=max(1,int(1.8*log(baud*mode65)) + 2) + ibwb=min(10,ibwa+4) + if(iand(ndepth,3).eq.3) then + ibwa=max(1,ibwa-1) + ibwb=min(10,ibwb+1) + endif + ! Set up the codewords for full-AP list decoding call q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) dgen=0 @@ -103,7 +111,7 @@ contains f1=f0 go to 100 endif - + if(snr1.lt.2.8) then xdt1=0. !No reliable sync, abandon decoding attempt f1=0. @@ -161,7 +169,7 @@ contains if(iand(ndepth,16).eq.16) then ! Try for an average decode. call q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & - ibwa,ibwb,codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) + codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) endif 100 decoded=' ' diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index d56ae6f4c..056608ba8 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -8,7 +8,7 @@ module q65 integer listutc(10) integer apsym0(58),aph10(10) integer apmask(13),apsymbols(13) - integer navg + integer navg,ibwa,ibwb real f0save(MAXAVE) real xdtsave(MAXAVE) real snr1save(MAXAVE) diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index d6dd07de2..c3e1d386f 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -71,12 +71,10 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) 10 return entry q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & - ibwa,ibwb,codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) + codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) mode_q65=2**nsubmode f0diff=baud*mode_q65 - ibwa=1.8*log(baud*mode_q65) + 2 - ibwb=min(10,ibwa+4) snr1sum=0. xdtsum=0. fsum=0. diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index ce476c0fb..22dbd5196 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -3,6 +3,8 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & use packjt77 use timer_module, only: timer + use q65 + parameter (NN=63) parameter (LN=1152*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 complex c00(0:npts2-1) !Analytic representation of dd(), 6000 Hz @@ -28,8 +30,6 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & idfmax=3 idtmax=3 - ibwa=max(1,int(1.8*log(baud*mode_q65)) + 2) - ibwb=min(10,ibwa+4) ibw0=(ibwa+ibwb)/2 maxdist=5 if(iand(ndepth,3).ge.2) then @@ -37,12 +37,8 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & idtmax=5 maxdist=10 endif - if(iand(ndepth,3).eq.3) then - maxdist=15 - ibwa=max(1,ibwa-1) - ibwb=min(10,ibwb+1) - endif - + if(iand(ndepth,3).eq.3) maxdist=15 + LL=64*(mode_q65+2) napmin=99 xdt1=xdt0 diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index f83fa4806..ed85b03ef 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -15,9 +15,10 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & use packjt77 use timer_module, only: timer + use q65 + parameter (NSTEP=8) !Step size nsps/NSTEP parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 - parameter (PLOG_MIN=-240.0) !List decoding threshold integer*2 iwave(0:12000*ntrperiod-1) !Raw data integer isync(22) !Indices of sync symbols integer itone(85) @@ -183,8 +184,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & if(mode_q65.eq.16) nsubmode=4 nFadingModel=1 baud=12000.0/nsps - ibwa=1.8*log(baud*mode_q65) + 2 - ibwb=min(10,ibwa+4) do ibw=ibwa,ibwb b90=1.72**ibw From 146ca19b976fcf615d8ffeae56eb928afa12585b Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 9 Jan 2021 11:02:54 -0500 Subject: [PATCH 329/426] Add some additional calls to timer(). --- lib/q65_decode.f90 | 5 ++++- lib/qra/q65/q65_sync.f90 | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index f1fb0364d..807e90e21 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -167,9 +167,12 @@ contains ! There was no single-transmission decode. if(iand(ndepth,16).eq.16) then -! Try for an average decode. + ! Try for an average decode. + + call timer('q65_avg2',0) call q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) + call timer('q65_avg2',1) endif 100 decoded=' ' diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index ed85b03ef..a891218d1 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -69,6 +69,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & enddo endif + call timer('s1 ',0) fac=1/32767.0 do j=1,jz !Compute symbol spectra at step size i1=(j-1)*istep @@ -90,6 +91,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & call smo121(s1(1:iz,j),iz) enddo enddo + call timer('s1 ',1) i0=nint(nfqso/df) !Target QSO frequency if(i0-64.lt.1 .or. i0-65+LL.gt.iz) go to 900 @@ -115,6 +117,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & !###################################################################### ! Try list decoding via "Deep Likelihood". + call timer('list_dec',0) ipk=0 jpk=0 ccf_best=0. @@ -159,6 +162,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & ccf1=ccf(:,jpk) endif enddo ! imsg + call timer('list_dec',1) i1=i0+ipk-64 i2=i1+LL-1 @@ -212,6 +216,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & !###################################################################### ! Compute the 2D CCF using sync symbols only 100 ccf=0. + call timer('2dccf ',0) do lag=lag1,lag2 do k=1,85 n=NSTEP*(k-1) + 1 @@ -249,6 +254,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & snr1=smax/rms ccf2=ccf2/rms if(snr1.gt.10.0) ccf2=(10.0/snr1)*ccf2 + call timer('2dccf ',1) if(irc.le.0) then f0=nfqso + ipk*df @@ -270,7 +276,9 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & n=n+1 if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) enddo + call timer('q65_avg ',0) call q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) + call timer('q65_avg ',1) endif endif endif From d60a97634bb74d0b7fa775207decd18b19e2707e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 9 Jan 2021 15:03:41 -0500 Subject: [PATCH 330/426] Improve the trace behavior of timer(). --- lib/timer_impl.f90 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/timer_impl.f90 b/lib/timer_impl.f90 index 4e35162cb..221db9e0a 100644 --- a/lib/timer_impl.f90 +++ b/lib/timer_impl.f90 @@ -6,6 +6,7 @@ module timer_impl public :: init_timer, fini_timer integer, public :: limtrace=0 +! integer, public :: limtrace=10000000 private @@ -146,8 +147,11 @@ contains endif ntrace=ntrace+1 - if(ntrace.lt.limtrace) write(lu,1020) ntrace,tname,k,level,nparent(n) -1020 format(i8,': ',a8,3i5) + tname='TopLevel' + if(nparent(n).ge.1 .and. nparent(n).le.MAXCALL) tname=name(nparent(n)) + if(ntrace.lt.limtrace) write(lu,1020) ntrace,dname,k,level,nparent(n),tname +1020 format(i8,': ',a8,3i5,2x,a8) + flush(lu) go to 998 ! Write out the timer statistics From 7f4876cee01a0da510423e645ac9c28ba54ece82 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 9 Jan 2021 16:35:04 -0500 Subject: [PATCH 331/426] Revert "Improve the trace behavior of timer()." This reverts commit d60a97634bb74d0b7fa775207decd18b19e2707e. --- lib/timer_impl.f90 | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/timer_impl.f90 b/lib/timer_impl.f90 index 221db9e0a..4e35162cb 100644 --- a/lib/timer_impl.f90 +++ b/lib/timer_impl.f90 @@ -6,7 +6,6 @@ module timer_impl public :: init_timer, fini_timer integer, public :: limtrace=0 -! integer, public :: limtrace=10000000 private @@ -147,11 +146,8 @@ contains endif ntrace=ntrace+1 - tname='TopLevel' - if(nparent(n).ge.1 .and. nparent(n).le.MAXCALL) tname=name(nparent(n)) - if(ntrace.lt.limtrace) write(lu,1020) ntrace,dname,k,level,nparent(n),tname -1020 format(i8,': ',a8,3i5,2x,a8) - flush(lu) + if(ntrace.lt.limtrace) write(lu,1020) ntrace,tname,k,level,nparent(n) +1020 format(i8,': ',a8,3i5) go to 998 ! Write out the timer statistics From c3010f1d142caed735492e0c1ff11fc818c35028 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 9 Jan 2021 18:41:41 -0500 Subject: [PATCH 332/426] Revert "Revert "Improve the trace behavior of timer()."" This reverts commit 7f4876cee01a0da510423e645ac9c28ba54ece82. --- lib/timer_impl.f90 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/timer_impl.f90 b/lib/timer_impl.f90 index 4e35162cb..221db9e0a 100644 --- a/lib/timer_impl.f90 +++ b/lib/timer_impl.f90 @@ -6,6 +6,7 @@ module timer_impl public :: init_timer, fini_timer integer, public :: limtrace=0 +! integer, public :: limtrace=10000000 private @@ -146,8 +147,11 @@ contains endif ntrace=ntrace+1 - if(ntrace.lt.limtrace) write(lu,1020) ntrace,tname,k,level,nparent(n) -1020 format(i8,': ',a8,3i5) + tname='TopLevel' + if(nparent(n).ge.1 .and. nparent(n).le.MAXCALL) tname=name(nparent(n)) + if(ntrace.lt.limtrace) write(lu,1020) ntrace,dname,k,level,nparent(n),tname +1020 format(i8,': ',a8,3i5,2x,a8) + flush(lu) go to 998 ! Write out the timer statistics From e8ba69099a69cdeba5d2e7cb6f6b4021937d2554 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 9 Jan 2021 19:58:34 -0500 Subject: [PATCH 333/426] Re-organize module q65.f90. Can still be improved! --- CMakeLists.txt | 1 - lib/qra/q65/q65.f90 | 206 ++++++++++++++++++++++++++++++++++++++++ lib/qra/q65/q65_avg.f90 | 24 ++++- 3 files changed, 229 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ff8f2080..73fc9dcd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -497,7 +497,6 @@ set (wsjt_FSRCS lib/polyfit.f90 lib/prog_args.f90 lib/ps4.f90 - lib/qra/q65/q65_avg.f90 lib/qra/q65/q65_sync.f90 lib/qra/q65/q65_ap.f90 lib/qra/q65/q65_loops.f90 diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 056608ba8..de1c715dc 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -9,10 +9,216 @@ module q65 integer apsym0(58),aph10(10) integer apmask(13),apsymbols(13) integer navg,ibwa,ibwb + real dtdiff real f0save(MAXAVE) real xdtsave(MAXAVE) real snr1save(MAXAVE) real,allocatable :: s3save(:,:,:) real,allocatable :: s3avg(:,:) +contains + +subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) + +! Accumulate Q65 spectra s3(LL,63) and associated parameters for +! message averaging. + + character*6 cutc + real s3(-64:LL-65,63) !Symbol spectra + logical first,lclearave,lapcqonly + data first/.true./ + save + + if(LL.ne.LL0) then + LL0=LL + if(allocated(s3save)) deallocate(s3save) + if(allocated(s3avg)) deallocate(s3avg) + allocate(s3save(-64:LL-65,63,MAXAVE)) + allocate(s3avg(-64:LL-65,63)) + s3save=0. + s3avg=0. + endif + if(first .or. lclearave) then + iutc=-1 + iseq=-1 + f0save=0.0 + nsave=0 + first=.false. + s3save=0. + s3avg=0. + endif + + if(ntrperiod.eq.15) then + dtdiff=0.038 + else if(ntrperiod.eq.30) then + dtdiff=0.08 + else if(ntrperiod.eq.60) then + dtdiff=0.16 + else if(ntrperiod.eq.120) then + dtdiff=0.4 + else if(ntrperiod.eq.300) then + dtdiff=0.9 + endif + dtdiff=2.5*dtdiff + + do i=1,MAXAVE !Don't save info more than once for same UTC and freq + if(nutc.eq.iutc(i) .and. abs(nfqso-f0save(i)).le.ntol) go to 10 + enddo + +! Save data for message averaging + nsave=nsave+1 + if(nsave.gt.MAXAVE) nsave=nsave-MAXAVE + n=nutc + if(ntrperiod.ge.60) n=100*n + write(cutc,'(i6.6)') n + read(cutc,'(3i2)') ih,im,is + nsec=3600*ih + 60*im + is + iseq(nsave)=mod(nsec/ntrperiod,2) !T/R sequence: 0 (even) or 1 (odd) + iutc(nsave)=nutc !UTC, hhmm or hhmmss + snr1save(nsave)=snr1 !SNR from sync + xdtsave(nsave)=xdt !DT + f0save(nsave)=f0 !f0 + s3save(:,:,nsave)=s3(:,:) !Symbol spectra + +10 return +end subroutine q65_avg + +subroutine q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & + codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) + + use packjt77 + use timer_module, only: timer + character*78 c78 + character*1 cused(MAXAVE) + character*37 avemsg + integer dat4(13) + integer codewords(63,206) + integer apmask1(78),apsymbols1(78) + logical lapcqonly + + mode_q65=2**nsubmode + f0diff=1.5*baud*mode_q65 + snr1sum=0. + xdtsum=0. + fsum=0. + navg=0 + s3avg=0. + +! Find previously saved spectra that should be averaged with this one + do i=1,MAXAVE + cused(i)='.' !Flag for "not used" + if(iutc(i).lt.0) cycle + if(iseq(i).ne.iseq(nsave)) cycle !Sequence must match + if(abs(xdt-xdtsave(i)).gt.dtdiff) cycle !DT must be close + if(abs(f0-f0save(i)).gt.f0diff) cycle !Freq must match + cused(i)='$' !Flag for "use this one" + s3avg=s3avg + s3save(:,:,i) !Add this spectrum + snr1sum=snr1sum + snr1save(i) + xdtsum=xdtsum + xdtsave(i) + fsum=fsum + f0save(i) + navg=navg+1 + enddo + +! Find averages of snr1, xdt, and f0 used in this decoding attempt. + snr1ave=0. + xdtave=0. + f0ave=0. + if(navg.gt.0) then + snr1ave=snr1sum/navg + xdtave=xdtsum/navg + f0ave=fsum/navg + endif + +! Write parameters for display to User in the Message Averaging (F7) window. + do i=1,MAXAVE + if(iutc(i).lt.0) cycle + if(ntrperiod.le.30) write(14,1000) cused(i),iutc(i),snr1save(i), & + xdtsave(i),f0save(i) +1000 format(a1,i7.6,f6.1,f6.2,f7.1) + if(ntrperiod.ge.60) write(14,1001) cused(i),iutc(i),snr1save(i), & + xdtsave(i),f0save(i) +1001 format(a1,i5.4,f6.1,f6.2,f7.1) + enddo + if(navg.lt.2) go to 100 + + s3avg=s3avg/navg + do ibw=ibwa,ibwb + b90=1.72**ibw + b90ts=b90/baud + call timer('dec1avg ',0) + call q65_dec1(s3avg,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,avemsg) + call timer('dec1avg ',1) + if(irc.ge.0) then + snr2=esnodb - 0.5*db(2500.0/baud) + 3.0 !Empirical adjustment + snr2=snr2 - db(float(navg)) !Is this right? + idec=100+navg + go to 100 + endif + enddo + +! Loop over full range of available AP +! APmask=0 +! APsymbols=0 + npasses=2 + if(nQSOprogress.eq.5) npasses=3 + if(lapcqonly) npasses=1 + iaptype=0 + ncontest=0 !### ??? ### + do ipass=0,npasses + 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 +! if(iaptype.eq.4) then +! do j=1,3 +! ng15=32401+j +! write(c78(60:74),'(b15.15)') ng15 +! read(c78,1060) dgen +! call q65_enc(dgen,codewords(1,j)) +! enddo +! endif +! print*,'AAA',ncw,iaptype + endif + + do ibw=ibwa,ibwb + b90=1.72**ibw + b90ts=b90/baud + call timer('dec2avg ',0) + call q65_dec2(s3avg,nsubmode,b90ts,esnodb,irc,dat4,avemsg) + call timer('dec2avg ',1) + if(irc.ge.0) then + snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment + snr2=snr2 - 0.5*db(float(navg)) !Is this right? + idec=100*(iaptype+2) + navg + go to 100 + endif + enddo ! ibw + enddo ! ipass + +100 return +end subroutine q65_avg2 + +subroutine q65_clravg + + iutc=-1 + iseq=-1 + snr1save=0. + xdtsave=0. + f0save=0.0 + nsave=0 + if(allocated(s3save)) s3save=0. + if(allocated(s3avg)) s3avg=0. + + return +end subroutine q65_clravg + end module q65 diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 index c3e1d386f..8121b58c6 100644 --- a/lib/qra/q65/q65_avg.f90 +++ b/lib/qra/q65/q65_avg.f90 @@ -1,3 +1,22 @@ +module q65 + + parameter (MAXAVE=64) + parameter (PLOG_MIN=-240.0) !List decoding threshold + integer nsave,nlist,LL0 + integer iutc(MAXAVE) + integer iseq(MAXAVE) + integer listutc(10) + integer apsym0(58),aph10(10) + integer apmask(13),apsymbols(13) + integer navg,ibwa,ibwb + real f0save(MAXAVE) + real xdtsave(MAXAVE) + real snr1save(MAXAVE) + real,allocatable :: s3save(:,:,:) + real,allocatable :: s3avg(:,:) + +contains + subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) ! Accumulate Q65 spectra s3(LL,63) and associated parameters for @@ -69,8 +88,9 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) s3save(:,:,nsave)=s3(:,:) !Symbol spectra 10 return +end subroutine q65_avg - entry q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & +subroutine q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) mode_q65=2**nsubmode @@ -196,3 +216,5 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) return end subroutine q65_avg + +end module q65 From f55ac6741b9228fece62edb8f420ed7e6059b883 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 10 Jan 2021 16:28:52 -0500 Subject: [PATCH 334/426] Post single-transmission Q65 decodes to the left panel. --- widgets/mainwindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index ad7f9bd2b..5245a55a0 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3480,7 +3480,8 @@ void MainWindow::readFromStdout() //readFromStdout //Right (Rx Frequency) window bool bDisplayRight=bAvgMsg; int audioFreq=decodedtext.frequencyOffset(); - if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="FST4" or m_mode=="Q65") { +// if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="FST4" or m_mode=="Q65") { + if(m_mode=="FT8" or m_mode=="FT4" or m_mode=="FST4") { int ftol=10; if(m_mode=="Q65") ftol=ui->sbFtol->value(); auto const& parts = decodedtext.string().remove("<").remove(">") From fe1aefe93a37330ea0a7da70290f503d014ed11c Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 10 Jan 2021 16:30:07 -0500 Subject: [PATCH 335/426] More code cleanup for module q65.f90. --- lib/q65_decode.f90 | 4 +- lib/qra/q65/q65.f90 | 62 ++++------- lib/qra/q65/q65_avg.f90 | 220 ---------------------------------------- 3 files changed, 19 insertions(+), 267 deletions(-) delete mode 100644 lib/qra/q65/q65_avg.f90 diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 807e90e21..b36f03099 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -170,8 +170,8 @@ contains ! Try for an average decode. call timer('q65_avg2',0) - call q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & - codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) + call q65_avg2(ntrperiod,baud,nsubmode,nQSOprogress,lapcqonly, & + codewords,ncw,xdt,f0,snr2,dat4,idec) call timer('q65_avg2',1) endif diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index de1c715dc..6a100e256 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -9,7 +9,6 @@ module q65 integer apsym0(58),aph10(10) integer apmask(13),apsymbols(13) integer navg,ibwa,ibwb - real dtdiff real f0save(MAXAVE) real xdtsave(MAXAVE) real snr1save(MAXAVE) @@ -25,7 +24,7 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) character*6 cutc real s3(-64:LL-65,63) !Symbol spectra - logical first,lclearave,lapcqonly + logical first,lclearave data first/.true./ save @@ -48,21 +47,8 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) s3avg=0. endif - if(ntrperiod.eq.15) then - dtdiff=0.038 - else if(ntrperiod.eq.30) then - dtdiff=0.08 - else if(ntrperiod.eq.60) then - dtdiff=0.16 - else if(ntrperiod.eq.120) then - dtdiff=0.4 - else if(ntrperiod.eq.300) then - dtdiff=0.9 - endif - dtdiff=2.5*dtdiff - do i=1,MAXAVE !Don't save info more than once for same UTC and freq - if(nutc.eq.iutc(i) .and. abs(nfqso-f0save(i)).le.ntol) go to 10 + if(nutc.eq.iutc(i) .and. abs(nfqso-f0save(i)).le.ntol) go to 900 enddo ! Save data for message averaging @@ -80,11 +66,11 @@ subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) f0save(nsave)=f0 !f0 s3save(:,:,nsave)=s3(:,:) !Symbol spectra -10 return +900 return end subroutine q65_avg -subroutine q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & - codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) +subroutine q65_avg2(ntrperiod,baud,nsubmode,nQSOprogress,lapcqonly, & + codewords,ncw,xdt,f0,snr2,dat4,idec) use packjt77 use timer_module, only: timer @@ -97,10 +83,18 @@ subroutine q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & logical lapcqonly mode_q65=2**nsubmode - f0diff=1.5*baud*mode_q65 - snr1sum=0. - xdtsum=0. - fsum=0. + dtdiff=0.038 + if(ntrperiod.eq.30) then + dtdiff=0.08 + else if(ntrperiod.eq.60) then + dtdiff=0.16 + else if(ntrperiod.eq.120) then + dtdiff=0.4 + else if(ntrperiod.eq.300) then + dtdiff=0.9 + endif + dtdiff=3.5*dtdiff + f0diff=2.5*baud*mode_q65 navg=0 s3avg=0. @@ -113,21 +107,8 @@ subroutine q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & if(abs(f0-f0save(i)).gt.f0diff) cycle !Freq must match cused(i)='$' !Flag for "use this one" s3avg=s3avg + s3save(:,:,i) !Add this spectrum - snr1sum=snr1sum + snr1save(i) - xdtsum=xdtsum + xdtsave(i) - fsum=fsum + f0save(i) navg=navg+1 enddo - -! Find averages of snr1, xdt, and f0 used in this decoding attempt. - snr1ave=0. - xdtave=0. - f0ave=0. - if(navg.gt.0) then - snr1ave=snr1sum/navg - xdtave=xdtsum/navg - f0ave=fsum/navg - endif ! Write parameters for display to User in the Message Averaging (F7) window. do i=1,MAXAVE @@ -178,15 +159,6 @@ subroutine q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & 1060 format(13b6.6) write(c78,1050) apsymbols1 read(c78,1060) apsymbols -! if(iaptype.eq.4) then -! do j=1,3 -! ng15=32401+j -! write(c78(60:74),'(b15.15)') ng15 -! read(c78,1060) dgen -! call q65_enc(dgen,codewords(1,j)) -! enddo -! endif -! print*,'AAA',ncw,iaptype endif do ibw=ibwa,ibwb diff --git a/lib/qra/q65/q65_avg.f90 b/lib/qra/q65/q65_avg.f90 deleted file mode 100644 index 8121b58c6..000000000 --- a/lib/qra/q65/q65_avg.f90 +++ /dev/null @@ -1,220 +0,0 @@ -module q65 - - parameter (MAXAVE=64) - parameter (PLOG_MIN=-240.0) !List decoding threshold - integer nsave,nlist,LL0 - integer iutc(MAXAVE) - integer iseq(MAXAVE) - integer listutc(10) - integer apsym0(58),aph10(10) - integer apmask(13),apsymbols(13) - integer navg,ibwa,ibwb - real f0save(MAXAVE) - real xdtsave(MAXAVE) - real snr1save(MAXAVE) - real,allocatable :: s3save(:,:,:) - real,allocatable :: s3avg(:,:) - -contains - -subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) - -! Accumulate Q65 spectra s3(LL,63) and associated parameters for -! message averaging. - - use q65 - use packjt77 - use timer_module, only: timer - character*78 c78 - character*37 avemsg - character*1 cused(MAXAVE) - character*6 cutc - real s3(-64:LL-65,63) !Symbol spectra - integer dat4(13) - integer codewords(63,206) - integer apmask1(78),apsymbols1(78) - logical first,lclearave,lapcqonly - data first/.true./ - save - - if(LL.ne.LL0) then - LL0=LL - if(allocated(s3save)) deallocate(s3save) - if(allocated(s3avg)) deallocate(s3avg) - allocate(s3save(-64:LL-65,63,MAXAVE)) - allocate(s3avg(-64:LL-65,63)) - s3save=0. - s3avg=0. - endif - if(first .or. lclearave) then - iutc=-1 - iseq=-1 - f0save=0.0 - nsave=0 - first=.false. - s3save=0. - s3avg=0. - endif - - if(ntrperiod.eq.15) then - dtdiff=0.038 - else if(ntrperiod.eq.30) then - dtdiff=0.08 - else if(ntrperiod.eq.60) then - dtdiff=0.16 - else if(ntrperiod.eq.120) then - dtdiff=0.4 - else if(ntrperiod.eq.300) then - dtdiff=0.9 - endif - - do i=1,MAXAVE !Don't save info more than once for same UTC and freq - if(nutc.eq.iutc(i) .and. abs(nfqso-f0save(i)).le.ntol) go to 10 - enddo - -! Save data for message averaging - nsave=nsave+1 - if(nsave.gt.MAXAVE) nsave=nsave-MAXAVE - n=nutc - if(ntrperiod.ge.60) n=100*n - write(cutc,'(i6.6)') n - read(cutc,'(3i2)') ih,im,is - nsec=3600*ih + 60*im + is - iseq(nsave)=mod(nsec/ntrperiod,2) !T/R sequence: 0 (even) or 1 (odd) - iutc(nsave)=nutc !UTC, hhmm or hhmmss - snr1save(nsave)=snr1 !SNR from sync - xdtsave(nsave)=xdt !DT - f0save(nsave)=f0 !f0 - s3save(:,:,nsave)=s3(:,:) !Symbol spectra - -10 return -end subroutine q65_avg - -subroutine q65_avg2(ntrperiod,ntol,baud,nsubmode,nQSOprogress,lapcqonly, & - codewords,ncw,xdt,f0,snr1,snr2,dat4,idec) - - mode_q65=2**nsubmode - f0diff=baud*mode_q65 - snr1sum=0. - xdtsum=0. - fsum=0. - navg=0 - s3avg=0. - -! Find previously saved spectra that should be averaged with this one - do i=1,MAXAVE - cused(i)='.' !Flag for "not used" - if(iutc(i).lt.0) cycle - if(iseq(i).ne.iseq(nsave)) cycle !Sequence must match - if(abs(xdt-xdtsave(i)).gt.dtdiff) cycle !DT must be close - if(abs(f0-f0save(i)).gt.f0diff) cycle !Freq must match - cused(i)='$' !Flag for "use this one" - s3avg=s3avg + s3save(:,:,i) !Add this spectrum - snr1sum=snr1sum + snr1save(i) - xdtsum=xdtsum + xdtsave(i) - fsum=fsum + f0save(i) - navg=navg+1 - enddo - -! Find averages of snr1, xdt, and f0 used in this decoding attempt. - snr1ave=0. - xdtave=0. - f0ave=0. - if(navg.gt.0) then - snr1ave=snr1sum/navg - xdtave=xdtsum/navg - f0ave=fsum/navg - endif - -! Write parameters for display to User in the Message Averaging (F7) window. - do i=1,MAXAVE - if(iutc(i).lt.0) cycle - if(ntrperiod.le.30) write(14,1000) cused(i),iutc(i),snr1save(i), & - xdtsave(i),f0save(i) -1000 format(a1,i7.6,f6.1,f6.2,f7.1) - if(ntrperiod.ge.60) write(14,1001) cused(i),iutc(i),snr1save(i), & - xdtsave(i),f0save(i) -1001 format(a1,i5.4,f6.1,f6.2,f7.1) - enddo - if(navg.lt.2) go to 100 - - s3avg=s3avg/navg - nFadingModel=1 - do ibw=ibwa,ibwb - b90=1.72**ibw - b90ts=b90/baud - call timer('dec1avg ',0) - call q65_dec1(s3avg,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,avemsg) - call timer('dec1avg ',1) - if(irc.ge.0) then - snr2=esnodb - 0.5*db(2500.0/baud) + 3.0 !Empirical adjustment - snr2=snr2 - db(float(navg)) !Is this right? - idec=100+navg - go to 100 - endif - enddo - -! Loop over full range of available AP -! APmask=0 -! APsymbols=0 - npasses=2 - if(nQSOprogress.eq.5) npasses=3 - if(lapcqonly) npasses=1 - iaptype=0 - do ipass=0,npasses - 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 - if(iaptype.eq.4) then - do j=1,3 - ng15=32401+j - write(c78(60:74),'(b15.15)') ng15 - read(c78,1060) dgen - call q65_enc(dgen,codewords(1,j)) - enddo - endif - endif - - do ibw=ibwa,ibwb - b90=1.72**ibw - b90ts=b90/baud - call timer('dec2avg ',0) - call q65_dec2(s3avg,nsubmode,b90ts,esnodb,irc,dat4,avemsg) - call timer('dec2avg ',1) - if(irc.ge.0) then - snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment - snr2=snr2 - 0.5*db(float(navg)) !Is this right? - idec=100*(iaptype+2) + navg - go to 100 - endif - enddo ! ibw - enddo ! ipass - -100 return - - entry q65_clravg - - iutc=-1 - iseq=-1 - snr1save=0. - xdtsave=0. - f0save=0.0 - nsave=0 - if(allocated(s3save)) s3save=0. - if(allocated(s3avg)) s3avg=0. - - return - -end subroutine q65_avg - -end module q65 From cc2ca0f7e75d393c33357d2a3b47e18f43afc717 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 11 Jan 2021 15:09:47 -0500 Subject: [PATCH 336/426] Fix an "off by one" times mode_q65 error, and some code cleanup. --- lib/q65_decode.f90 | 9 --------- lib/qra/q65/q65_loops.f90 | 5 ----- lib/qra/q65/q65_sync.f90 | 6 +++--- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index b36f03099..f5ba6e26e 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -148,14 +148,6 @@ contains 1060 format(13b6.6) write(c78,1050) apsymbols1 read(c78,1060) apsymbols - if(iaptype.eq.4) then - do j=1,3 - ng15=32401+j - write(c78(60:74),'(b15.15)') ng15 - read(c78,1060) dgen - call q65_enc(dgen,codewords(1,j)) - enddo - endif endif call timer('q65loops',0) @@ -168,7 +160,6 @@ contains ! There was no single-transmission decode. if(iand(ndepth,16).eq.16) then ! Try for an average decode. - call timer('q65_avg2',0) call q65_avg2(ntrperiod,baud,nsubmode,nQSOprogress,lapcqonly, & codewords,ncw,xdt,f0,snr2,dat4,idec) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 22dbd5196..2754c50ab 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -12,14 +12,9 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & character decoded*37 real a(3) !twkfreq params f,f1,f2 real s3(LN) !Symbol spectra - integer cw4(63) integer dat4(13) !Decoded message (as 13 six-bit integers) integer nap(0:11) !AP return codes data nap/0,2,3,2,3,4,2,3,6,4,6,6/ - data cw4/0, 0, 0, 0, 8, 4,60,35,17,48,33,25,34,43,43,43,35,15,46,30, & - 54,24,26,26,57,57,42, 3,23,11,49,49,16, 2, 6, 6,55,21,39,51, & - 51,51,42,42,50,25,31,35,57,30, 1,54,54,10,10,22,44,58,57,40, & - 21,21,19/ idec=-1 ircbest=9999 diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index a891218d1..7b02a7b88 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -176,7 +176,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & n=n+1 if(j.ge.1 .and. j.le.jz) then do i=0,LL-1 - s3(i-64,n)=s1(i+i1,j) + s3(i-64,n)=s1(i+i1,j) !Copy from s1 into s3 enddo endif enddo @@ -262,8 +262,8 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & ccf1=ccf(:,jpk)/rms if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 if(iand(ndepth,16).eq.16) then -! Fill s3() from s1() here, then call q65_avg(). - i1=i0+ipk-64 +! Copy from s1 into s3, then call q65_avg(). + i1=i0+ipk-64 + mode_q65 i2=i1+LL-1 if(snr1.ge.2.8 .and. i1.ge.1 .and. i2.le.iz) then j=j0+jpk-7 From ef478717ea9016b234fd37823e144e8c81bee942 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 12 Jan 2021 08:18:17 -0500 Subject: [PATCH 337/426] Don't forget to do something about nsmo = ??. --- lib/qra/q65/q65_sync.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 7b02a7b88..0ad8b425c 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -54,6 +54,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & ia2=max(ia,10*mode_q65,nint(100.0/df)) nsmo=int(0.7*mode_q65*mode_q65) if(nsmo.lt.1) nsmo=1 +! nsmo=1 !### TEMPORARY ### allocate(s1(iz,jz)) allocate(s3(-64:LL-65,63)) From d25e188dca95e7628b1a1384106ade7768682d37 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 12 Jan 2021 11:28:46 -0500 Subject: [PATCH 338/426] Code cleanup. --- lib/q65_decode.f90 | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index f5ba6e26e..bba1eed69 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -1,27 +1,27 @@ module q65_decode - type :: q65_decoder - procedure(q65_decode_callback), pointer :: callback + type :: q65_decoder + procedure(q65_decode_callback), pointer :: callback contains - procedure :: decode - end type q65_decoder + procedure :: decode + end type q65_decoder - abstract interface - subroutine q65_decode_callback (this,nutc,sync,nsnr,dt,freq, & - decoded,nap,ntrperiod) - import q65_decoder - implicit none - class(q65_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 - integer, intent(in) :: ntrperiod - end subroutine q65_decode_callback - end interface + abstract interface + subroutine q65_decode_callback (this,nutc,sync,nsnr,dt,freq, & + decoded,nap,ntrperiod) + import q65_decoder + implicit none + class(q65_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 + integer, intent(in) :: ntrperiod + end subroutine q65_decode_callback + end interface contains From 0c13a698234cba9148ae6dc2dad85d5bc003e2ff Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 12 Jan 2021 11:29:18 -0500 Subject: [PATCH 339/426] Save a description of useful Unit Tests for several Q65 submodes. --- UnitTests.txt | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 UnitTests.txt diff --git a/UnitTests.txt b/UnitTests.txt new file mode 100644 index 000000000..26a3f119e --- /dev/null +++ b/UnitTests.txt @@ -0,0 +1,49 @@ +Mode: Q65-30A +Data: 30A_K1JT_6m_Ionoscatter (62 files, DT 0.1 to 0.4) +Message: "K1JT K9AN R-16" +RxFreq: 1000/10 + +Commit No AP MyCall BothCalls +----------------------------------- +ef4787: 3 10 6 14 30 33 + + +Mode: Q65-30A +Data: 30A_N0AN_6m_Ionoscatter (69 files, DT=0 to 0.1) +Message: "N0AN K1JT -19" +RxFreq: 1500/10 + +Commit No AP MyCall BothCalls +----------------------------------- +ef4787: 7 14 16 22 38 40 + + +Mode: Q65-60B +Data: 60B_1296_Troposcatter (75 files) +Message: "VK7MO VK7PD QE38" +RxFreq: 1000/10 + +Commit No AP MyCall BothCalls +----------------------------------- +ef4787: 1 2 11 23 64 67 + + +Mode: Q65-60D +Data: MsgAvg (22 files, simulated fDop = 50 Hz) +Message: "K1ABC W9XYZ EN37" +RxFreq: 1000/10 + +Commit No AP MyCall BothCalls +----------------------------------- +ef4787: 0 10 21 21 22 22 + + +Mode: Q65-60D +Data: Nil Average (40 files, simulated fDop = 0? Hz ) +Message: "VK7MO VK3WE QF32" +RxFreq: 1200/20 +Requires nsmo=1 ### + +Commit No AP MyCall BothCalls +----------------------------------- +ef4787: 1 16 24 28 39 39 From 63beab393b20a8dc9b443c529427bb9676dbf239 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 12 Jan 2021 11:39:26 -0500 Subject: [PATCH 340/426] Compute s1() in a separate subroutine, q65_symspec.f90. --- lib/qra/q65/q65_sync.f90 | 60 +++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 0ad8b425c..2e8146004 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -33,7 +33,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & real, allocatable :: ccf1(:) !CCF(freq) at best lag real, allocatable :: ccf2(:) !CCF(freq) at any lag real sync(85) !sync vector - complex, allocatable :: c0(:) !Complex spectrum of symbol data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ data sync(1)/99.0/ save sync @@ -58,7 +57,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & allocate(s1(iz,jz)) allocate(s3(-64:LL-65,63)) - allocate(c0(0:nfft-1)) allocate(ccf(-ia2:ia2,-53:214)) allocate(ccf1(-ia2:ia2)) allocate(ccf2(-ia2:ia2)) @@ -71,27 +69,8 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & endif call timer('s1 ',0) - fac=1/32767.0 - do j=1,jz !Compute symbol spectra at step size - i1=(j-1)*istep - i2=i1+nsps-1 - k=-1 - do i=i1,i2,2 !Load iwave data into complex array c0, for r2c FFT - xx=iwave(i) - yy=iwave(i+1) - k=k+1 - c0(k)=fac*cmplx(xx,yy) - enddo - c0(k+1:)=0. - call four2a(c0,nfft,1,-1,0) !r2c FFT - do i=1,iz - s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 - enddo -! For large Doppler spreads, should we smooth the spectra here? - do i=1,nsmo - call smo121(s1(1:iz,j),iz) - enddo - enddo + nmax=ntrperiod*12000 + call q65_symspec(iwave,nmax,nsps,iz,jz,istep,nsmo,s1) call timer('s1 ',1) i0=nint(nfqso/df) !Target QSO frequency @@ -303,6 +282,41 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & 900 return end subroutine q65_sync +subroutine q65_symspec(iwave,nmax,nsps,iz,jz,istep,nsmo,s1) + + integer*2 iwave(0:nmax-1) !Raw data + real s1(iz,jz) + complex, allocatable :: c0(:) !Complex spectrum of symbol + + allocate(c0(0:nsps-1)) + + nfft=nsps + fac=1/32767.0 + do j=1,jz !Compute symbol spectra at step size + i1=(j-1)*istep + i2=i1+nsps-1 + k=-1 + do i=i1,i2,2 !Load iwave data into complex array c0, for r2c FFT + xx=iwave(i) + yy=iwave(i+1) + k=k+1 + c0(k)=fac*cmplx(xx,yy) + enddo + c0(k+1:)=0. + call four2a(c0,nfft,1,-1,0) !r2c FFT + do i=1,iz + s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 + enddo +! For large Doppler spreads, should we smooth the spectra here? + do i=1,nsmo + call smo121(s1(1:iz,j),iz) + enddo + enddo + + return +end subroutine q65_symspec + + subroutine q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) use q65 From a75037d053ba392345b1cd743da24c999ca54bcb Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 12 Jan 2021 13:41:39 -0500 Subject: [PATCH 341/426] Accumulate the symbol spectra s1() in array s1a(). --- lib/qra/q65/q65.f90 | 1 + lib/qra/q65/q65_sync.f90 | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 6a100e256..79d5b062b 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -12,6 +12,7 @@ module q65 real f0save(MAXAVE) real xdtsave(MAXAVE) real snr1save(MAXAVE) + real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra real,allocatable :: s3save(:,:,:) real,allocatable :: s3avg(:,:) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 2e8146004..be0c090ea 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -60,6 +60,14 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & allocate(ccf(-ia2:ia2,-53:214)) allocate(ccf1(-ia2:ia2)) allocate(ccf2(-ia2:ia2)) + if(LL.ne.LL0 .or. lclearave) then + if(allocated(s1a)) deallocate(s1a) + allocate(s1a(iz,jz)) + s1a=0. + navg=0 + LL0=LL + endif + s3=0. if(sync(1).eq.99.0) then !Generate the sync vector sync=-22.0/63.0 !Sync tone OFF @@ -93,11 +101,13 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & if(nsps.ge.7200) j0=1.0/dtstep !Nominal start-signal index if(ncw.lt.1) go to 100 - + !###################################################################### ! Try list decoding via "Deep Likelihood". call timer('list_dec',0) +! call q65_dec_q3(codewords,ncw,isync,df + ipk=0 jpk=0 ccf_best=0. @@ -190,6 +200,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & go to 100 endif enddo + irc=-2 dat4=0 @@ -256,9 +267,9 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & n=n+1 if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) enddo - call timer('q65_avg ',0) - call q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) - call timer('q65_avg ',1) +! call timer('q65_avg ',0) +! call q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) +! call timer('q65_avg ',1) endif endif endif @@ -284,12 +295,12 @@ end subroutine q65_sync subroutine q65_symspec(iwave,nmax,nsps,iz,jz,istep,nsmo,s1) + use q65 integer*2 iwave(0:nmax-1) !Raw data real s1(iz,jz) complex, allocatable :: c0(:) !Complex spectrum of symbol allocate(c0(0:nsps-1)) - nfft=nsps fac=1/32767.0 do j=1,jz !Compute symbol spectra at step size @@ -312,6 +323,8 @@ subroutine q65_symspec(iwave,nmax,nsps,iz,jz,istep,nsmo,s1) call smo121(s1(1:iz,j),iz) enddo enddo + s1a=s1a+s1 + navg=navg+1 return end subroutine q65_symspec From 60a053aa94a73fbccdd42f4a0460a12960c2cfbd Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 12 Jan 2021 14:34:41 -0500 Subject: [PATCH 342/426] Working toward some new, separate, Q65 subroutines. --- lib/qra/q65/q65_sync.f90 | 216 +++++++++++++++++++++------------------ 1 file changed, 117 insertions(+), 99 deletions(-) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index be0c090ea..96ce49129 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -21,12 +21,11 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 integer*2 iwave(0:12000*ntrperiod-1) !Raw data integer isync(22) !Indices of sync symbols - integer itone(85) integer codewords(63,206) integer dat4(13) integer ijpk(2) - logical lclearave character*37 decoded + logical lclearave real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) real, allocatable :: ccf(:,:) !CCF(freq,lag) @@ -104,105 +103,15 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & !###################################################################### ! Try list decoding via "Deep Likelihood". - call timer('list_dec',0) -! call q65_dec_q3(codewords,ncw,isync,df - - ipk=0 - jpk=0 - ccf_best=0. - imsg_best=-1 - do imsg=1,ncw - i=1 - k=0 - do j=1,85 - if(j.eq.isync(i)) then - i=i+1 - itone(j)=-1 - else - k=k+1 - itone(j)=codewords(k,imsg) - endif - enddo -! Compute 2D ccf using all 85 symbols in the list message - ccf=0. - iia=200.0/df - - do lag=lag1,lag2 - do k=1,85 - j=j0 + NSTEP*(k-1) + 1 + lag - if(j.ge.1 .and. j.le.jz) then - do i=-ia2,ia2 - ii=i0+mode_q65*itone(k)+i - if(ii.ge.iia .and. ii.le.iz) ccf(i,lag)=ccf(i,lag) + s1(ii,j) - enddo - endif - enddo - enddo - - ccfmax=maxval(ccf(-ia:ia,:)) - if(ccfmax.gt.ccf_best) then - ccf_best=ccfmax - ijpk=maxloc(ccf(-ia:ia,:)) - ipk=ijpk(1)-ia-1 - jpk=ijpk(2)-53-1 - f0=nfqso + (ipk-mode_q65)*df - xdt=jpk*dtstep - imsg_best=imsg - ccf1=ccf(:,jpk) - endif - enddo ! imsg + call q65_dec_q3(codewords,ncw,isync,sync,df,s1,iz,jz,ia,ibwa,ibwb, & + nstep,nsps,mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & + dat4,idec,decoded) call timer('list_dec',1) - - i1=i0+ipk-64 - i2=i1+LL-1 - j=j0+jpk-7 - n=0 - do k=1,85 - j=j+8 - if(sync(k).gt.0.0) then - cycle - endif - n=n+1 - if(j.ge.1 .and. j.le.jz) then - do i=0,LL-1 - s3(i-64,n)=s1(i+i1,j) !Copy from s1 into s3 - enddo - endif - enddo - - nsubmode=0 - if(mode_q65.eq.2) nsubmode=1 - if(mode_q65.eq.4) nsubmode=2 - if(mode_q65.eq.8) nsubmode=3 - if(mode_q65.eq.16) nsubmode=4 - nFadingModel=1 - baud=12000.0/nsps - - do ibw=ibwa,ibwb - b90=1.72**ibw - b90ts=b90/baud - call timer('dec1 ',0) - call q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) - call timer('dec1 ',1) - if(irc.ge.0) then - snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment - idec=1 - ic=ia2/4; - base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); - ccf1=ccf1-base - smax=maxval(ccf1) - if(smax.gt.10.0) ccf1=10.0*ccf1/smax - base=(sum(ccf2(-ia2:-ia2+ic)) + sum(ccf2(ia2-ic:ia2)))/(2.0+2.0*ic); - ccf2=ccf2-base - smax=maxval(ccf2) - if(smax.gt.10.0) ccf2=10.0*ccf2/smax - go to 100 - endif - enddo - - irc=-2 - dat4=0 + if(idec.lt.0) then + irc=-2 + dat4=0 + endif !###################################################################### ! Compute the 2D CCF using sync symbols only @@ -329,6 +238,115 @@ subroutine q65_symspec(iwave,nmax,nsps,iz,jz,istep,nsmo,s1) return end subroutine q65_symspec +subroutine q65_dec_q3(codewords,ncw,isync,sync,df,s1,iz,jz,ia,ibwa,ibwb, & + nstep,nsps,mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & + dat4,idec,decoded) + + character*37 decoded + integer isync(22) !Indices of sync symbols + integer itone(85) + integer ijpk(2) + integer codewords(63,206) + integer dat4(13) + real ccf(-ia2:ia2,-53:214) + real ccf1(-ia2:ia2) + real ccf2(-ia2:ia2) + real s1(iz,jz) + real s3(-64:LL-65,63) + real sync(85) !sync vector + + ipk=0 + jpk=0 + ccf_best=0. + imsg_best=-1 + do imsg=1,ncw + i=1 + k=0 + do j=1,85 + if(j.eq.isync(i)) then + i=i+1 + itone(j)=-1 + else + k=k+1 + itone(j)=codewords(k,imsg) + endif + enddo +! Compute 2D ccf using all 85 symbols in the list message + ccf=0. + iia=200.0/df + + do lag=lag1,lag2 + do k=1,85 + j=j0 + NSTEP*(k-1) + 1 + lag + if(j.ge.1 .and. j.le.jz) then + do i=-ia2,ia2 + ii=i0+mode_q65*itone(k)+i + if(ii.ge.iia .and. ii.le.iz) ccf(i,lag)=ccf(i,lag) + s1(ii,j) + enddo + endif + enddo + enddo + + ccfmax=maxval(ccf(-ia:ia,:)) + if(ccfmax.gt.ccf_best) then + ccf_best=ccfmax + ijpk=maxloc(ccf(-ia:ia,:)) + ipk=ijpk(1)-ia-1 + jpk=ijpk(2)-53-1 + f0=nfqso + (ipk-mode_q65)*df + xdt=jpk*dtstep + imsg_best=imsg + ccf1=ccf(:,jpk) + endif + enddo ! imsg + + i1=i0+ipk-64 + i2=i1+LL-1 + j=j0+jpk-7 + n=0 + do k=1,85 + j=j+8 + if(sync(k).gt.0.0) then + cycle + endif + n=n+1 + if(j.ge.1 .and. j.le.jz) then + do i=0,LL-1 + s3(i-64,n)=s1(i+i1,j) !Copy from s1 into s3 + enddo + endif + enddo + + nsubmode=0 + if(mode_q65.eq.2) nsubmode=1 + if(mode_q65.eq.4) nsubmode=2 + if(mode_q65.eq.8) nsubmode=3 + if(mode_q65.eq.16) nsubmode=4 + nFadingModel=1 + baud=12000.0/nsps + + do ibw=ibwa,ibwb + b90=1.72**ibw + b90ts=b90/baud + call q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) + if(irc.ge.0) then + snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment + idec=1 + ic=ia2/4; + base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); + ccf1=ccf1-base + smax=maxval(ccf1) + if(smax.gt.10.0) ccf1=10.0*ccf1/smax + base=(sum(ccf2(-ia2:-ia2+ic)) + sum(ccf2(ia2-ic:ia2)))/(2.0+2.0*ic); + ccf2=ccf2-base + smax=maxval(ccf2) + if(smax.gt.10.0) ccf2=10.0*ccf2/smax + exit + endif + enddo + + return +end subroutine q65_dec_q3 subroutine q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) From c9ce452500a2de66b5c5d71eac83277059ebd1db Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 12 Jan 2021 14:42:00 -0500 Subject: [PATCH 343/426] Code cleanup. --- lib/qra/q65/q65_sync.f90 | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 96ce49129..968889ddd 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -99,23 +99,22 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & j0=0.5/dtstep if(nsps.ge.7200) j0=1.0/dtstep !Nominal start-signal index - if(ncw.lt.1) go to 100 + irc=-2 + idec=-1 + dat4=0 -!###################################################################### + if(ncw.gt.0) then ! Try list decoding via "Deep Likelihood". - call timer('list_dec',0) - call q65_dec_q3(codewords,ncw,isync,sync,df,s1,iz,jz,ia,ibwa,ibwb, & - nstep,nsps,mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & - dat4,idec,decoded) - call timer('list_dec',1) - if(idec.lt.0) then - irc=-2 - dat4=0 + call timer('list_dec',0) + call q65_dec_q3(codewords,ncw,isync,sync,df,s1,iz,jz,ia,ibwa,ibwb, & + nstep,nsps,mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & + dat4,idec,decoded) + call timer('list_dec',1) endif !###################################################################### ! Compute the 2D CCF using sync symbols only -100 ccf=0. + ccf=0. call timer('2dccf ',0) do lag=lag1,lag2 do k=1,85 From 98a52accbbc1563c42f2864fb976010424c3cffa Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 12 Jan 2021 15:42:24 -0500 Subject: [PATCH 344/426] More refactoring of Q65 decoder. --- lib/qra/q65/q65_sync.f90 | 66 ++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 968889ddd..08c43b28f 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -36,6 +36,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & data sync(1)/99.0/ save sync + if(nutc+ndepth.eq.-999) stop irc=-2 idec=-1 snr1=0. @@ -81,7 +82,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & call timer('s1 ',1) i0=nint(nfqso/df) !Target QSO frequency - if(i0-64.lt.1 .or. i0-65+LL.gt.iz) go to 900 + if(i0-64.lt.1 .or. i0-65+LL.gt.iz) go to 900 !Frequency out of range call pctile(s1(i0-64:i0-65+LL,1:jz),LL*jz,40,base) s1=s1/base @@ -99,7 +100,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & j0=0.5/dtstep if(nsps.ge.7200) j0=1.0/dtstep !Nominal start-signal index - irc=-2 idec=-1 dat4=0 @@ -113,7 +113,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & endif !###################################################################### -! Compute the 2D CCF using sync symbols only +! Get 2d CCF and ccf2 using sync symbols only ccf=0. call timer('2dccf ',0) do lag=lag1,lag2 @@ -128,15 +128,14 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & endif enddo enddo - - ijpk=maxloc(ccf(-ia:ia,:)) - ipk=ijpk(1)-ia-1 - jpk=ijpk(2)-53-1 - do i=-ia2,ia2 ccf2(i)=maxval(ccf(i,:)) enddo +! Estimate rms on ccf baseline + ijpk=maxloc(ccf(-ia:ia,:)) + ipk=ijpk(1)-ia-1 + jpk=ijpk(2)-53-1 sq=0. nsq=0 jd=(lag2-lag1)/4 @@ -155,31 +154,13 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & if(snr1.gt.10.0) ccf2=(10.0/snr1)*ccf2 call timer('2dccf ',1) - if(irc.le.0) then + if(idec.le.0) then +! The q3 decode attempt failed, so we'll try a more general decode. f0=nfqso + ipk*df xdt=jpk*dtstep ccf1=ccf(:,jpk)/rms if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 - if(iand(ndepth,16).eq.16) then -! Copy from s1 into s3, then call q65_avg(). - i1=i0+ipk-64 + mode_q65 - i2=i1+LL-1 - if(snr1.ge.2.8 .and. i1.ge.1 .and. i2.le.iz) then - j=j0+jpk-7 - n=0 - do k=1,85 - j=j+8 - if(sync(k).gt.0.0) then - cycle - endif - n=n+1 - if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) - enddo -! call timer('q65_avg ',0) -! call q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) -! call timer('q65_avg ',1) - endif - endif + call q65_s1_to_s3(s1,iz,jz,i0,j0,ipk,jpk,LL,mode_q65,sync,s3) endif smax=maxval(ccf1) @@ -191,6 +172,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & enddo width=df*(i2-i1) +! Write data for the red and orange sync curves. do i=-ia2,ia2 freq=nfqso + i*df write(17,1100) freq,ccf1(i),xdt,ccf2(i) @@ -347,6 +329,32 @@ subroutine q65_dec_q3(codewords,ncw,isync,sync,df,s1,iz,jz,ia,ibwa,ibwb, & return end subroutine q65_dec_q3 +subroutine q65_s1_to_s3(s1,iz,jz,i0,j0,ipk,jpk,LL,mode_q65,sync,s3) + +! Copy from s1a into s3, then call the dec_q* routines + + real s1(iz,jz) + real s3(-64:LL-65,63) + real sync(85) !sync vector + + i1=i0+ipk-64 + mode_q65 + i2=i1+LL-1 + if(i1.ge.1 .and. i2.le.iz) then + j=j0+jpk-7 + n=0 + do k=1,85 + j=j+8 + if(sync(k).gt.0.0) then + cycle + endif + n=n+1 + if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) + enddo + endif + + return +end subroutine q65_s1_to_s3 + subroutine q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) use q65 From 304ca4a55d96349fb9dcee3b937c692f851e2fba Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 09:46:14 -0500 Subject: [PATCH 345/426] Move some routines into the module q65.f90. --- lib/qra/q65/q65.f90 | 49 +++++++++++++++++++++++++++++++++++++ lib/qra/q65/q65_sync.f90 | 52 +--------------------------------------- lib/timer_impl.f90 | 4 ++-- 3 files changed, 52 insertions(+), 53 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 79d5b062b..8c121ff70 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -194,4 +194,53 @@ subroutine q65_clravg return end subroutine q65_clravg +subroutine q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) + + use packjt77 + real s3(1,1) !Silence compiler warning that wants to see a 2D array + real s3prob(0:63,63) !Symbol-value probabilities + integer dat4(13) + character c77*77,decoded*37 + logical unpk77_success + + nFadingModel=1 + decoded=' ' + call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) + call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) + if(sum(dat4).le.0) irc=-2 + if(irc.ge.0) then + 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 + endif + + return +end subroutine q65_dec2 + +subroutine q65_s1_to_s3(s1,iz,jz,i0,j0,ipk,jpk,LL,mode_q65,sync,s3) + +! Copy from s1a into s3, then call the dec_q* routines + + real s1(iz,jz) + real s3(-64:LL-65,63) + real sync(85) !sync vector + + i1=i0+ipk-64 + mode_q65 + i2=i1+LL-1 + if(i1.ge.1 .and. i2.le.iz) then + j=j0+jpk-7 + n=0 + do k=1,85 + j=j+8 + if(sync(k).gt.0.0) then + cycle + endif + n=n+1 + if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) + enddo + endif + + return +end subroutine q65_s1_to_s3 + end module q65 diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 08c43b28f..cea6862e4 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -329,36 +329,10 @@ subroutine q65_dec_q3(codewords,ncw,isync,sync,df,s1,iz,jz,ia,ibwa,ibwb, & return end subroutine q65_dec_q3 -subroutine q65_s1_to_s3(s1,iz,jz,i0,j0,ipk,jpk,LL,mode_q65,sync,s3) - -! Copy from s1a into s3, then call the dec_q* routines - - real s1(iz,jz) - real s3(-64:LL-65,63) - real sync(85) !sync vector - - i1=i0+ipk-64 + mode_q65 - i2=i1+LL-1 - if(i1.ge.1 .and. i2.le.iz) then - j=j0+jpk-7 - n=0 - do k=1,85 - j=j+8 - if(sync(k).gt.0.0) then - cycle - endif - n=n+1 - if(j.ge.1 .and. j.le.jz) s3(-64:LL-65,n)=s1(i1:i2,j) - enddo - endif - - return -end subroutine q65_s1_to_s3 - subroutine q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) - use q65 use packjt77 + parameter (PLOG_MIN=-240.0) !List decoding threshold real s3(1,1) !Silence compiler warning that wants to see a 2D array real s3prob(0:63,63) !Symbol-value probabilities integer codewords(63,206) @@ -381,27 +355,3 @@ subroutine q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) return end subroutine q65_dec1 - -subroutine q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) - - use q65 - use packjt77 - real s3(1,1) !Silence compiler warning that wants to see a 2D array - real s3prob(0:63,63) !Symbol-value probabilities - integer dat4(13) - character c77*77,decoded*37 - logical unpk77_success - - nFadingModel=1 - decoded=' ' - call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) - call q65_dec(s3,s3prob,APmask,APsymbols,esnodb,dat4,irc) - if(sum(dat4).le.0) irc=-2 - if(irc.ge.0) then - 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 - endif - - return -end subroutine q65_dec2 diff --git a/lib/timer_impl.f90 b/lib/timer_impl.f90 index 221db9e0a..0c6eadfc6 100644 --- a/lib/timer_impl.f90 +++ b/lib/timer_impl.f90 @@ -5,8 +5,8 @@ module timer_impl implicit none public :: init_timer, fini_timer - integer, public :: limtrace=0 -! integer, public :: limtrace=10000000 +! integer, public :: limtrace=0 + integer, public :: limtrace=10000000 private From daed54db2df338bcb64221a246a8194d67891656 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 09:52:00 -0500 Subject: [PATCH 346/426] Remove unused routines q65_avg() and q65_avg2(). --- lib/q65_decode.f90 | 4 +- lib/qra/q65/q65.f90 | 163 +------------------------------------------- 2 files changed, 3 insertions(+), 164 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index bba1eed69..9a8046b5d 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -161,8 +161,8 @@ contains if(iand(ndepth,16).eq.16) then ! Try for an average decode. call timer('q65_avg2',0) - call q65_avg2(ntrperiod,baud,nsubmode,nQSOprogress,lapcqonly, & - codewords,ncw,xdt,f0,snr2,dat4,idec) +! call q65_avg2(ntrperiod,baud,nsubmode,nQSOprogress,lapcqonly, & +! codewords,ncw,xdt,f0,snr2,dat4,idec) call timer('q65_avg2',1) endif diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 8c121ff70..170c26f7d 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -8,7 +8,7 @@ module q65 integer listutc(10) integer apsym0(58),aph10(10) integer apmask(13),apsymbols(13) - integer navg,ibwa,ibwb + integer navg,ibwa,ibwb,LL real f0save(MAXAVE) real xdtsave(MAXAVE) real snr1save(MAXAVE) @@ -18,167 +18,6 @@ module q65 contains -subroutine q65_avg(nutc,ntrperiod,LL,nfqso,ntol,lclearave,xdt,f0,snr1,s3) - -! Accumulate Q65 spectra s3(LL,63) and associated parameters for -! message averaging. - - character*6 cutc - real s3(-64:LL-65,63) !Symbol spectra - logical first,lclearave - data first/.true./ - save - - if(LL.ne.LL0) then - LL0=LL - if(allocated(s3save)) deallocate(s3save) - if(allocated(s3avg)) deallocate(s3avg) - allocate(s3save(-64:LL-65,63,MAXAVE)) - allocate(s3avg(-64:LL-65,63)) - s3save=0. - s3avg=0. - endif - if(first .or. lclearave) then - iutc=-1 - iseq=-1 - f0save=0.0 - nsave=0 - first=.false. - s3save=0. - s3avg=0. - endif - - do i=1,MAXAVE !Don't save info more than once for same UTC and freq - if(nutc.eq.iutc(i) .and. abs(nfqso-f0save(i)).le.ntol) go to 900 - enddo - -! Save data for message averaging - nsave=nsave+1 - if(nsave.gt.MAXAVE) nsave=nsave-MAXAVE - n=nutc - if(ntrperiod.ge.60) n=100*n - write(cutc,'(i6.6)') n - read(cutc,'(3i2)') ih,im,is - nsec=3600*ih + 60*im + is - iseq(nsave)=mod(nsec/ntrperiod,2) !T/R sequence: 0 (even) or 1 (odd) - iutc(nsave)=nutc !UTC, hhmm or hhmmss - snr1save(nsave)=snr1 !SNR from sync - xdtsave(nsave)=xdt !DT - f0save(nsave)=f0 !f0 - s3save(:,:,nsave)=s3(:,:) !Symbol spectra - -900 return -end subroutine q65_avg - -subroutine q65_avg2(ntrperiod,baud,nsubmode,nQSOprogress,lapcqonly, & - codewords,ncw,xdt,f0,snr2,dat4,idec) - - use packjt77 - use timer_module, only: timer - character*78 c78 - character*1 cused(MAXAVE) - character*37 avemsg - integer dat4(13) - integer codewords(63,206) - integer apmask1(78),apsymbols1(78) - logical lapcqonly - - mode_q65=2**nsubmode - dtdiff=0.038 - if(ntrperiod.eq.30) then - dtdiff=0.08 - else if(ntrperiod.eq.60) then - dtdiff=0.16 - else if(ntrperiod.eq.120) then - dtdiff=0.4 - else if(ntrperiod.eq.300) then - dtdiff=0.9 - endif - dtdiff=3.5*dtdiff - f0diff=2.5*baud*mode_q65 - navg=0 - s3avg=0. - -! Find previously saved spectra that should be averaged with this one - do i=1,MAXAVE - cused(i)='.' !Flag for "not used" - if(iutc(i).lt.0) cycle - if(iseq(i).ne.iseq(nsave)) cycle !Sequence must match - if(abs(xdt-xdtsave(i)).gt.dtdiff) cycle !DT must be close - if(abs(f0-f0save(i)).gt.f0diff) cycle !Freq must match - cused(i)='$' !Flag for "use this one" - s3avg=s3avg + s3save(:,:,i) !Add this spectrum - navg=navg+1 - enddo - -! Write parameters for display to User in the Message Averaging (F7) window. - do i=1,MAXAVE - if(iutc(i).lt.0) cycle - if(ntrperiod.le.30) write(14,1000) cused(i),iutc(i),snr1save(i), & - xdtsave(i),f0save(i) -1000 format(a1,i7.6,f6.1,f6.2,f7.1) - if(ntrperiod.ge.60) write(14,1001) cused(i),iutc(i),snr1save(i), & - xdtsave(i),f0save(i) -1001 format(a1,i5.4,f6.1,f6.2,f7.1) - enddo - if(navg.lt.2) go to 100 - - s3avg=s3avg/navg - do ibw=ibwa,ibwb - b90=1.72**ibw - b90ts=b90/baud - call timer('dec1avg ',0) - call q65_dec1(s3avg,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,avemsg) - call timer('dec1avg ',1) - if(irc.ge.0) then - snr2=esnodb - 0.5*db(2500.0/baud) + 3.0 !Empirical adjustment - snr2=snr2 - db(float(navg)) !Is this right? - idec=100+navg - go to 100 - endif - enddo - -! Loop over full range of available AP -! APmask=0 -! APsymbols=0 - npasses=2 - if(nQSOprogress.eq.5) npasses=3 - if(lapcqonly) npasses=1 - iaptype=0 - ncontest=0 !### ??? ### - do ipass=0,npasses - 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 - - do ibw=ibwa,ibwb - b90=1.72**ibw - b90ts=b90/baud - call timer('dec2avg ',0) - call q65_dec2(s3avg,nsubmode,b90ts,esnodb,irc,dat4,avemsg) - call timer('dec2avg ',1) - if(irc.ge.0) then - snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment - snr2=snr2 - 0.5*db(float(navg)) !Is this right? - idec=100*(iaptype+2) + navg - go to 100 - endif - enddo ! ibw - enddo ! ipass - -100 return -end subroutine q65_avg2 subroutine q65_clravg From 71385aa20a3c5493478f041e7fcc9ab31a372fa2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 10:23:38 -0500 Subject: [PATCH 347/426] Move some variables into the q65 module. --- lib/q65_decode.f90 | 3 +-- lib/qra/q65/q65.f90 | 24 ++++++------------------ lib/qra/q65/q65_sync.f90 | 4 +--- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 9a8046b5d..e6a1baab3 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -57,7 +57,6 @@ contains integer dat4(13) !Decoded message as 12 6-bit integers integer apmask1(78),apsymbols1(78) integer dgen(13) - integer codewords(63,206) logical lclearave,lapcqonly,unpk77_success complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s @@ -102,7 +101,7 @@ contains dgen=0 call q65_enc(dgen,codewords) !Initialize the Q65 codec call timer('sync_q65',0) - call q65_sync(nutc,iwave,ntrperiod,mode65,codewords,ncw,nsps, & + call q65_sync(nutc,iwave,ntrperiod,mode65,nsps, & nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4, & snr2,idec) call timer('sync_q65',1) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 170c26f7d..c21eeb5da 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -1,34 +1,22 @@ module q65 - parameter (MAXAVE=64) - parameter (PLOG_MIN=-240.0) !List decoding threshold + parameter (NSTEP=8) !Time bins per symbol, in s1() and s1a() + parameter (PLOG_MIN=-240.0) !List decoding threshold integer nsave,nlist,LL0 - integer iutc(MAXAVE) - integer iseq(MAXAVE) integer listutc(10) integer apsym0(58),aph10(10) integer apmask(13),apsymbols(13) - integer navg,ibwa,ibwb,LL - real f0save(MAXAVE) - real xdtsave(MAXAVE) - real snr1save(MAXAVE) + integer codewords(63,206) + integer navg,ibwa,ibwb,ncw real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra - real,allocatable :: s3save(:,:,:) - real,allocatable :: s3avg(:,:) contains subroutine q65_clravg - iutc=-1 - iseq=-1 - snr1save=0. - xdtsave=0. - f0save=0.0 - nsave=0 - if(allocated(s3save)) s3save=0. - if(allocated(s3avg)) s3avg=0. + s1a=0. + navg=0 return end subroutine q65_clravg diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index cea6862e4..83b0ee127 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -1,4 +1,4 @@ -subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & +subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,nsps, & nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,idec) ! Detect and align with the Q65 sync vector, returning time and frequency @@ -17,11 +17,9 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,codewords,ncw,nsps, & use timer_module, only: timer use q65 - parameter (NSTEP=8) !Step size nsps/NSTEP parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 integer*2 iwave(0:12000*ntrperiod-1) !Raw data integer isync(22) !Indices of sync symbols - integer codewords(63,206) integer dat4(13) integer ijpk(2) character*37 decoded From dc4c3e87ebb1adadc403603f9de437470b6581e1 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 10:44:19 -0500 Subject: [PATCH 348/426] Move more things into q65 module. --- lib/qra/q65/q65.f90 | 6 ++++-- lib/qra/q65/q65_sync.f90 | 13 +++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index c21eeb5da..95181c15d 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -6,9 +6,11 @@ module q65 integer listutc(10) integer apsym0(58),aph10(10) integer apmask(13),apsymbols(13) + integer,dimension(22) :: isync = (/1,9,12,13,15,22,23,26,27,33,35, & + 38,46,50,55,60,62,66,69,74,76,85/) integer codewords(63,206) integer navg,ibwa,ibwb,ncw - real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra + real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra contains @@ -46,7 +48,7 @@ end subroutine q65_dec2 subroutine q65_s1_to_s3(s1,iz,jz,i0,j0,ipk,jpk,LL,mode_q65,sync,s3) -! Copy from s1a into s3, then call the dec_q* routines +! Copy from s1 or s1a into s3 real s1(iz,jz) real s3(-64:LL-65,63) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 83b0ee127..2aaab4d3f 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -19,7 +19,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,nsps, & parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 integer*2 iwave(0:12000*ntrperiod-1) !Raw data - integer isync(22) !Indices of sync symbols integer dat4(13) integer ijpk(2) character*37 decoded @@ -30,7 +29,6 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,nsps, & real, allocatable :: ccf1(:) !CCF(freq) at best lag real, allocatable :: ccf2(:) !CCF(freq) at any lag real sync(85) !sync vector - data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ data sync(1)/99.0/ save sync @@ -104,8 +102,8 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,nsps, & if(ncw.gt.0) then ! Try list decoding via "Deep Likelihood". call timer('list_dec',0) - call q65_dec_q3(codewords,ncw,isync,sync,df,s1,iz,jz,ia,ibwa,ibwb, & - nstep,nsps,mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & + call q65_dec_q3(sync,df,s1,iz,jz,ia, & + nsps,mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & dat4,idec,decoded) call timer('list_dec',1) endif @@ -217,15 +215,14 @@ subroutine q65_symspec(iwave,nmax,nsps,iz,jz,istep,nsmo,s1) return end subroutine q65_symspec -subroutine q65_dec_q3(codewords,ncw,isync,sync,df,s1,iz,jz,ia,ibwa,ibwb, & - nstep,nsps,mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & +subroutine q65_dec_q3(sync,df,s1,iz,jz,ia, & + nsps,mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & dat4,idec,decoded) + use q65 character*37 decoded - integer isync(22) !Indices of sync symbols integer itone(85) integer ijpk(2) - integer codewords(63,206) integer dat4(13) real ccf(-ia2:ia2,-53:214) real ccf1(-ia2:ia2) From 201004a47d21b56a826285c9ac75ff9f4e9312a2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 10:55:01 -0500 Subject: [PATCH 349/426] Move more into q65 module. --- lib/decoder.f90 | 8 ++++---- lib/q65_decode.f90 | 10 +++++----- lib/qra/q65/q65.f90 | 1 + lib/qra/q65/q65_sync.f90 | 14 ++++++-------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 1de956ecc..3cc58d3a7 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -758,14 +758,14 @@ contains return end subroutine fst4_decoded - subroutine q65_decoded (this,nutc,sync,nsnr,dt,freq,decoded,idec,ntrperiod) + subroutine q65_decoded (this,nutc,snr1,nsnr,dt,freq,decoded,idec,ntrperiod) use q65_decode implicit none class(q65_decoder), intent(inout) :: this integer, intent(in) :: nutc - real, intent(in) :: sync + real, intent(in) :: snr1 integer, intent(in) :: nsnr real, intent(in) :: dt real, intent(in) :: freq @@ -796,12 +796,12 @@ contains if(ntrperiod.lt.60) then write(*,1001) nutc,nsnr,dt,nint(freq),decoded,cflags 1001 format(i6.6,i4,f5.1,i5,' : ',1x,a37,1x,a3) - write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded + write(13,1002) nutc,nint(snr1),nsnr,dt,freq,0,decoded 1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') else write(*,1003) nutc,nsnr,dt,nint(freq),decoded,cflags 1003 format(i4.4,i4,f5.1,i5,' : ',1x,a37,1x,a3) - write(13,1004) nutc,nint(sync),nsnr,dt,freq,0,decoded + write(13,1004) nutc,nint(snr1),nsnr,dt,freq,0,decoded 1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' Q65') endif diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index e6a1baab3..b337e058d 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -7,13 +7,13 @@ module q65_decode end type q65_decoder abstract interface - subroutine q65_decode_callback (this,nutc,sync,nsnr,dt,freq, & + subroutine q65_decode_callback (this,nutc,snr1,nsnr,dt,freq, & decoded,nap,ntrperiod) import q65_decoder implicit none class(q65_decoder), intent(inout) :: this integer, intent(in) :: nutc - real, intent(in) :: sync + real, intent(in) :: snr1 integer, intent(in) :: nsnr real, intent(in) :: dt real, intent(in) :: freq @@ -185,13 +185,13 @@ contains 1000 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent nsnr=nint(snr2) - call this%callback(nutc,sync,nsnr,xdt1,f1,decoded,idec,ntrperiod) + call this%callback(nutc,snr1,nsnr,xdt1,f1,decoded,idec,ntrperiod) call q65_clravg else -! Report sync, even if no decode. +! Report snr1, even if no decode. nsnr=db(snr1) - 35.0 idec=-1 - call this%callback(nutc,sync,nsnr,xdt1,f1,decoded, & + call this%callback(nutc,snr1,nsnr,xdt1,f1,decoded, & idec,ntrperiod) endif diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 95181c15d..0ce9527cd 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -11,6 +11,7 @@ module q65 integer codewords(63,206) integer navg,ibwa,ibwb,ncw real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra + real sync(85) !sync vector contains diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 2aaab4d3f..57d62c769 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -22,15 +22,14 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,nsps, & integer dat4(13) integer ijpk(2) character*37 decoded - logical lclearave + logical first,lclearave real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) real, allocatable :: ccf(:,:) !CCF(freq,lag) real, allocatable :: ccf1(:) !CCF(freq) at best lag real, allocatable :: ccf2(:) !CCF(freq) at any lag - real sync(85) !sync vector - data sync(1)/99.0/ - save sync + data first/.true./ + save first if(nutc+ndepth.eq.-999) stop irc=-2 @@ -65,7 +64,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,nsps, & endif s3=0. - if(sync(1).eq.99.0) then !Generate the sync vector + if(first) then !Generate the sync vector sync=-22.0/63.0 !Sync tone OFF do k=1,22 sync(isync(k))=1.0 !Sync tone ON @@ -102,7 +101,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,nsps, & if(ncw.gt.0) then ! Try list decoding via "Deep Likelihood". call timer('list_dec',0) - call q65_dec_q3(sync,df,s1,iz,jz,ia, & + call q65_dec_q3(df,s1,iz,jz,ia, & nsps,mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & dat4,idec,decoded) call timer('list_dec',1) @@ -215,7 +214,7 @@ subroutine q65_symspec(iwave,nmax,nsps,iz,jz,istep,nsmo,s1) return end subroutine q65_symspec -subroutine q65_dec_q3(sync,df,s1,iz,jz,ia, & +subroutine q65_dec_q3(df,s1,iz,jz,ia, & nsps,mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & dat4,idec,decoded) @@ -229,7 +228,6 @@ subroutine q65_dec_q3(sync,df,s1,iz,jz,ia, & real ccf2(-ia2:ia2) real s1(iz,jz) real s3(-64:LL-65,63) - real sync(85) !sync vector ipk=0 jpk=0 From df8ad7a8d99616fc7c30d998180461d32a3cff4d Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 11:07:20 -0500 Subject: [PATCH 350/426] More into q65 module. --- lib/q65_decode.f90 | 2 +- lib/qra/q65/q65.f90 | 2 +- lib/qra/q65/q65_loops.f90 | 10 +++++----- lib/qra/q65/q65_sync.f90 | 10 +++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index b337e058d..cb97a10a7 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -101,7 +101,7 @@ contains dgen=0 call q65_enc(dgen,codewords) !Initialize the Q65 codec call timer('sync_q65',0) - call q65_sync(nutc,iwave,ntrperiod,mode65,nsps, & + call q65_sync(nutc,iwave,ntrperiod,mode65, & nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4, & snr2,idec) call timer('sync_q65',1) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 0ce9527cd..870a0f155 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -9,7 +9,7 @@ module q65 integer,dimension(22) :: isync = (/1,9,12,13,15,22,23,26,27,33,35, & 38,46,50,55,60,62,66,69,74,76,85/) integer codewords(63,206) - integer navg,ibwa,ibwb,ncw + integer navg,ibwa,ibwb,ncw,nsps real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra real sync(85) !sync vector diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 2754c50ab..92bed63df 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,4 +1,4 @@ -subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & +subroutine q65_loops(c00,npts2,nsps2,mode_q65,nsubmode,ndepth,jpk0, & xdt0,f0,iaptype,xdt1,f1,snr2,dat4,idec) use packjt77 @@ -21,7 +21,7 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & allocate(c0(0:npts2-1)) irc=-99 s3lim=20. - baud=6000.0/nsps + baud=6000.0/nsps2 idfmax=3 idtmax=3 @@ -48,10 +48,10 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & do idt=1,idtmax ndt=idt/2 if(mod(idt,2).eq.0) ndt=-ndt - jpk=jpk0 + nsps*ndt/16 !tsym/16 + jpk=jpk0 + nsps2*ndt/16 !tsym/16 if(jpk.lt.0) jpk=0 call timer('spec64 ',0) - call spec64(c0,nsps,65,mode_q65,jpk,s3,LL,NN) + call spec64(c0,nsps2,65,mode_q65,jpk,s3,LL,NN) call timer('spec64 ',1) call pctile(s3,LL*NN,40,base) s3=s3/base @@ -80,7 +80,7 @@ subroutine q65_loops(c00,npts2,nsps,mode_q65,nsubmode,ndepth,jpk0, & !3055 format(7i4,f10.1,1x,a) idec=iaptype+2 snr2=esnodb - db(2500.0/baud) - xdt1=xdt0 + nsps*ndt/(16.0*6000.0) + xdt1=xdt0 + nsps2*ndt/(16.0*6000.0) f1=f0 + 0.5*baud*ndf endif diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 57d62c769..f5f6b97dc 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -1,4 +1,4 @@ -subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,nsps, & +subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65, & nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,idec) ! Detect and align with the Q65 sync vector, returning time and frequency @@ -73,7 +73,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,nsps, & call timer('s1 ',0) nmax=ntrperiod*12000 - call q65_symspec(iwave,nmax,nsps,iz,jz,istep,nsmo,s1) + call q65_symspec(iwave,nmax,iz,jz,istep,nsmo,s1) call timer('s1 ',1) i0=nint(nfqso/df) !Target QSO frequency @@ -102,7 +102,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,nsps, & ! Try list decoding via "Deep Likelihood". call timer('list_dec',0) call q65_dec_q3(df,s1,iz,jz,ia, & - nsps,mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & + mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & dat4,idec,decoded) call timer('list_dec',1) endif @@ -178,7 +178,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65,nsps, & 900 return end subroutine q65_sync -subroutine q65_symspec(iwave,nmax,nsps,iz,jz,istep,nsmo,s1) +subroutine q65_symspec(iwave,nmax,iz,jz,istep,nsmo,s1) use q65 integer*2 iwave(0:nmax-1) !Raw data @@ -215,7 +215,7 @@ subroutine q65_symspec(iwave,nmax,nsps,iz,jz,istep,nsmo,s1) end subroutine q65_symspec subroutine q65_dec_q3(df,s1,iz,jz,ia, & - nsps,mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & + mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & dat4,idec,decoded) use q65 From 18100fd2595e8dd1378b0ef13aa200964883b7d6 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 11:21:59 -0500 Subject: [PATCH 351/426] More into q65 module. --- lib/q65_decode.f90 | 8 ++++---- lib/qra/q65/q65.f90 | 2 +- lib/qra/q65/q65_loops.f90 | 2 +- lib/qra/q65/q65_sync.f90 | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index cb97a10a7..596cb0b90 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -62,7 +62,7 @@ contains complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s idec=-1 - mode65=2**nsubmode + mode_q65=2**nsubmode npts=ntrperiod*12000 nfft1=ntrperiod*12000 nfft2=ntrperiod*6000 @@ -89,7 +89,7 @@ contains df1=12000.0/nfft1 this%callback => callback nFadingModel=1 - ibwa=max(1,int(1.8*log(baud*mode65)) + 2) + ibwa=max(1,int(1.8*log(baud*mode_q65)) + 2) ibwb=min(10,ibwa+4) if(iand(ndepth,3).eq.3) then ibwa=max(1,ibwa-1) @@ -101,7 +101,7 @@ contains dgen=0 call q65_enc(dgen,codewords) !Initialize the Q65 codec call timer('sync_q65',0) - call q65_sync(nutc,iwave,ntrperiod,mode65, & + call q65_sync(nutc,iwave,ntrperiod, & nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4, & snr2,idec) call timer('sync_q65',1) @@ -150,7 +150,7 @@ contains endif call timer('q65loops',0) - call q65_loops(c00,npts/2,nsps/2,mode65,nsubmode,ndepth,jpk0, & + 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.gt.0) go to 100 !Successful decode, we're done diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 870a0f155..7d115a251 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -9,7 +9,7 @@ module q65 integer,dimension(22) :: isync = (/1,9,12,13,15,22,23,26,27,33,35, & 38,46,50,55,60,62,66,69,74,76,85/) integer codewords(63,206) - integer navg,ibwa,ibwb,ncw,nsps + integer navg,ibwa,ibwb,ncw,nsps,mode_q65 real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra real sync(85) !sync vector diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 92bed63df..d2ef86ae9 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -1,4 +1,4 @@ -subroutine q65_loops(c00,npts2,nsps2,mode_q65,nsubmode,ndepth,jpk0, & +subroutine q65_loops(c00,npts2,nsps2,nsubmode,ndepth,jpk0, & xdt0,f0,iaptype,xdt1,f1,snr2,dat4,idec) use packjt77 diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index f5f6b97dc..76dbc6b22 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -1,4 +1,4 @@ -subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65, & +subroutine q65_sync(nutc,iwave,ntrperiod, & nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,idec) ! Detect and align with the Q65 sync vector, returning time and frequency @@ -102,7 +102,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,mode_q65, & ! Try list decoding via "Deep Likelihood". call timer('list_dec',0) call q65_dec_q3(df,s1,iz,jz,ia, & - mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & + lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & dat4,idec,decoded) call timer('list_dec',1) endif @@ -215,7 +215,7 @@ subroutine q65_symspec(iwave,nmax,iz,jz,istep,nsmo,s1) end subroutine q65_symspec subroutine q65_dec_q3(df,s1,iz,jz,ia, & - mode_q65,lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & + lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & dat4,idec,decoded) use q65 From a5a1eed6811992527b6d1f5660c45ca6d4b55415 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 11:29:33 -0500 Subject: [PATCH 352/426] More into q65 module. --- lib/qra/q65/q65.f90 | 2 +- lib/qra/q65/q65_sync.f90 | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 7d115a251..f030f18a8 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -9,7 +9,7 @@ module q65 integer,dimension(22) :: isync = (/1,9,12,13,15,22,23,26,27,33,35, & 38,46,50,55,60,62,66,69,74,76,85/) integer codewords(63,206) - integer navg,ibwa,ibwb,ncw,nsps,mode_q65 + integer navg,ibwa,ibwb,ncw,nsps,mode_q65,istep,nsmo real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra real sync(85) !sync vector diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 76dbc6b22..ca2a1efad 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -1,5 +1,5 @@ -subroutine q65_sync(nutc,iwave,ntrperiod, & - nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4,snr2,idec) +subroutine q65_sync(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & + emedelay,xdt,f0,snr1,width,dat4,snr2,idec) ! Detect and align with the Q65 sync vector, returning time and frequency ! offsets and SNR estimate. @@ -73,7 +73,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod, & call timer('s1 ',0) nmax=ntrperiod*12000 - call q65_symspec(iwave,nmax,iz,jz,istep,nsmo,s1) + call q65_symspec(iwave,nmax,iz,jz,s1) call timer('s1 ',1) i0=nint(nfqso/df) !Target QSO frequency @@ -178,7 +178,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod, & 900 return end subroutine q65_sync -subroutine q65_symspec(iwave,nmax,iz,jz,istep,nsmo,s1) +subroutine q65_symspec(iwave,nmax,iz,jz,s1) use q65 integer*2 iwave(0:nmax-1) !Raw data From f05654e19ddde9c2353f8095da95df4320a940e6 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 11:38:49 -0500 Subject: [PATCH 353/426] More into q65 module. --- lib/qra/q65/q65.f90 | 25 +++++++++++++++++++++++++ lib/qra/q65/q65_sync.f90 | 38 +++++--------------------------------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index f030f18a8..d5052c2e0 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -24,6 +24,31 @@ subroutine q65_clravg return end subroutine q65_clravg +subroutine q65_dec1(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) + + use packjt77 + real s3(1,1) !Silence compiler warning that wants to see a 2D array + real s3prob(0:63,63) !Symbol-value probabilities + integer dat4(13) + character c77*77,decoded*37 + logical unpk77_success + + nFadingModel=1 + decoded=' ' + call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) + call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) + if(sum(dat4).le.0) irc=-2 + if(irc.ge.0 .and. plog.gt.PLOG_MIN) then + 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 + else + irc=-1 + endif + + return +end subroutine q65_dec1 + subroutine q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) use packjt77 diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index ca2a1efad..26c2967ba 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -72,8 +72,8 @@ subroutine q65_sync(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & endif call timer('s1 ',0) - nmax=ntrperiod*12000 - call q65_symspec(iwave,nmax,iz,jz,s1) +! Compute spectra with symbol length and NSTEP time bins per symbol. + call q65_symspec(iwave,ntrperiod*12000,iz,jz,s1) call timer('s1 ',1) i0=nint(nfqso/df) !Target QSO frequency @@ -101,9 +101,8 @@ subroutine q65_sync(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & if(ncw.gt.0) then ! Try list decoding via "Deep Likelihood". call timer('list_dec',0) - call q65_dec_q3(df,s1,iz,jz,ia, & - lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & - dat4,idec,decoded) + call q65_dec_q3(df,s1,iz,jz,ia,lag1,lag2,i0,j0,ccf,ccf1,ccf2, & + ia2,s3,LL,snr2,dat4,idec,decoded) call timer('list_dec',1) endif @@ -302,7 +301,7 @@ subroutine q65_dec_q3(df,s1,iz,jz,ia, & do ibw=ibwa,ibwb b90=1.72**ibw b90ts=b90/baud - call q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) + call q65_dec1(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) if(irc.ge.0) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment idec=1 @@ -321,30 +320,3 @@ subroutine q65_dec_q3(df,s1,iz,jz,ia, & return end subroutine q65_dec_q3 - -subroutine q65_dec1(s3,nsubmode,b90ts,codewords,ncw,esnodb,irc,dat4,decoded) - - use packjt77 - parameter (PLOG_MIN=-240.0) !List decoding threshold - real s3(1,1) !Silence compiler warning that wants to see a 2D array - real s3prob(0:63,63) !Symbol-value probabilities - integer codewords(63,206) - integer dat4(13) - character c77*77,decoded*37 - logical unpk77_success - - nFadingModel=1 - decoded=' ' - call q65_intrinsics_ff(s3,nsubmode,b90ts,nFadingModel,s3prob) - call q65_dec_fullaplist(s3,s3prob,codewords,ncw,esnodb,dat4,plog,irc) - if(sum(dat4).le.0) irc=-2 - if(irc.ge.0 .and. plog.gt.PLOG_MIN) then - 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 - else - irc=-1 - endif - - return -end subroutine q65_dec1 From 80a7b8c8af4a2bce76044cce0cd5894a3fb54fb9 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 11:44:29 -0500 Subject: [PATCH 354/426] More into q65 module. --- lib/qra/q65/q65.f90 | 141 ++++++++++++++++++++++++++++++++++++++ lib/qra/q65/q65_sync.f90 | 144 --------------------------------------- 2 files changed, 141 insertions(+), 144 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index d5052c2e0..60ade8776 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -24,6 +24,147 @@ subroutine q65_clravg return end subroutine q65_clravg +subroutine q65_symspec(iwave,nmax,iz,jz,s1) + + integer*2 iwave(0:nmax-1) !Raw data + real s1(iz,jz) + complex, allocatable :: c0(:) !Complex spectrum of symbol + + allocate(c0(0:nsps-1)) + nfft=nsps + fac=1/32767.0 + do j=1,jz !Compute symbol spectra at step size + i1=(j-1)*istep + i2=i1+nsps-1 + k=-1 + do i=i1,i2,2 !Load iwave data into complex array c0, for r2c FFT + xx=iwave(i) + yy=iwave(i+1) + k=k+1 + c0(k)=fac*cmplx(xx,yy) + enddo + c0(k+1:)=0. + call four2a(c0,nfft,1,-1,0) !r2c FFT + do i=1,iz + s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 + enddo +! For large Doppler spreads, should we smooth the spectra here? + do i=1,nsmo + call smo121(s1(1:iz,j),iz) + enddo + enddo + s1a=s1a+s1 + navg=navg+1 + + return +end subroutine q65_symspec + +subroutine q65_dec_q3(df,s1,iz,jz,ia,lag1,lag2,i0,j0,ccf,ccf1,ccf2, & + ia2,s3,LL,snr2,dat4,idec,decoded) + + character*37 decoded + integer itone(85) + integer ijpk(2) + integer dat4(13) + real ccf(-ia2:ia2,-53:214) + real ccf1(-ia2:ia2) + real ccf2(-ia2:ia2) + real s1(iz,jz) + real s3(-64:LL-65,63) + + ipk=0 + jpk=0 + ccf_best=0. + imsg_best=-1 + do imsg=1,ncw + i=1 + k=0 + do j=1,85 + if(j.eq.isync(i)) then + i=i+1 + itone(j)=-1 + else + k=k+1 + itone(j)=codewords(k,imsg) + endif + enddo +! Compute 2D ccf using all 85 symbols in the list message + ccf=0. + iia=200.0/df + + do lag=lag1,lag2 + do k=1,85 + j=j0 + NSTEP*(k-1) + 1 + lag + if(j.ge.1 .and. j.le.jz) then + do i=-ia2,ia2 + ii=i0+mode_q65*itone(k)+i + if(ii.ge.iia .and. ii.le.iz) ccf(i,lag)=ccf(i,lag) + s1(ii,j) + enddo + endif + enddo + enddo + + ccfmax=maxval(ccf(-ia:ia,:)) + if(ccfmax.gt.ccf_best) then + ccf_best=ccfmax + ijpk=maxloc(ccf(-ia:ia,:)) + ipk=ijpk(1)-ia-1 + jpk=ijpk(2)-53-1 + f0=nfqso + (ipk-mode_q65)*df + xdt=jpk*dtstep + imsg_best=imsg + ccf1=ccf(:,jpk) + endif + enddo ! imsg + + i1=i0+ipk-64 + i2=i1+LL-1 + j=j0+jpk-7 + n=0 + do k=1,85 + j=j+8 + if(sync(k).gt.0.0) then + cycle + endif + n=n+1 + if(j.ge.1 .and. j.le.jz) then + do i=0,LL-1 + s3(i-64,n)=s1(i+i1,j) !Copy from s1 into s3 + enddo + endif + enddo + + nsubmode=0 + if(mode_q65.eq.2) nsubmode=1 + if(mode_q65.eq.4) nsubmode=2 + if(mode_q65.eq.8) nsubmode=3 + if(mode_q65.eq.16) nsubmode=4 + nFadingModel=1 + baud=12000.0/nsps + + do ibw=ibwa,ibwb + b90=1.72**ibw + b90ts=b90/baud + call q65_dec1(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) + if(irc.ge.0) then + snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment + idec=1 + ic=ia2/4; + base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); + ccf1=ccf1-base + smax=maxval(ccf1) + if(smax.gt.10.0) ccf1=10.0*ccf1/smax + base=(sum(ccf2(-ia2:-ia2+ic)) + sum(ccf2(ia2-ic:ia2)))/(2.0+2.0*ic); + ccf2=ccf2-base + smax=maxval(ccf2) + if(smax.gt.10.0) ccf2=10.0*ccf2/smax + exit + endif + enddo + + return +end subroutine q65_dec_q3 + subroutine q65_dec1(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) use packjt77 diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 26c2967ba..0f8c534c2 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -176,147 +176,3 @@ subroutine q65_sync(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & 900 return end subroutine q65_sync - -subroutine q65_symspec(iwave,nmax,iz,jz,s1) - - use q65 - integer*2 iwave(0:nmax-1) !Raw data - real s1(iz,jz) - complex, allocatable :: c0(:) !Complex spectrum of symbol - - allocate(c0(0:nsps-1)) - nfft=nsps - fac=1/32767.0 - do j=1,jz !Compute symbol spectra at step size - i1=(j-1)*istep - i2=i1+nsps-1 - k=-1 - do i=i1,i2,2 !Load iwave data into complex array c0, for r2c FFT - xx=iwave(i) - yy=iwave(i+1) - k=k+1 - c0(k)=fac*cmplx(xx,yy) - enddo - c0(k+1:)=0. - call four2a(c0,nfft,1,-1,0) !r2c FFT - do i=1,iz - s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 - enddo -! For large Doppler spreads, should we smooth the spectra here? - do i=1,nsmo - call smo121(s1(1:iz,j),iz) - enddo - enddo - s1a=s1a+s1 - navg=navg+1 - - return -end subroutine q65_symspec - -subroutine q65_dec_q3(df,s1,iz,jz,ia, & - lag1,lag2,i0,j0,ccf,ccf1,ccf2,ia2,s3,LL,snr2, & - dat4,idec,decoded) - - use q65 - character*37 decoded - integer itone(85) - integer ijpk(2) - integer dat4(13) - real ccf(-ia2:ia2,-53:214) - real ccf1(-ia2:ia2) - real ccf2(-ia2:ia2) - real s1(iz,jz) - real s3(-64:LL-65,63) - - ipk=0 - jpk=0 - ccf_best=0. - imsg_best=-1 - do imsg=1,ncw - i=1 - k=0 - do j=1,85 - if(j.eq.isync(i)) then - i=i+1 - itone(j)=-1 - else - k=k+1 - itone(j)=codewords(k,imsg) - endif - enddo -! Compute 2D ccf using all 85 symbols in the list message - ccf=0. - iia=200.0/df - - do lag=lag1,lag2 - do k=1,85 - j=j0 + NSTEP*(k-1) + 1 + lag - if(j.ge.1 .and. j.le.jz) then - do i=-ia2,ia2 - ii=i0+mode_q65*itone(k)+i - if(ii.ge.iia .and. ii.le.iz) ccf(i,lag)=ccf(i,lag) + s1(ii,j) - enddo - endif - enddo - enddo - - ccfmax=maxval(ccf(-ia:ia,:)) - if(ccfmax.gt.ccf_best) then - ccf_best=ccfmax - ijpk=maxloc(ccf(-ia:ia,:)) - ipk=ijpk(1)-ia-1 - jpk=ijpk(2)-53-1 - f0=nfqso + (ipk-mode_q65)*df - xdt=jpk*dtstep - imsg_best=imsg - ccf1=ccf(:,jpk) - endif - enddo ! imsg - - i1=i0+ipk-64 - i2=i1+LL-1 - j=j0+jpk-7 - n=0 - do k=1,85 - j=j+8 - if(sync(k).gt.0.0) then - cycle - endif - n=n+1 - if(j.ge.1 .and. j.le.jz) then - do i=0,LL-1 - s3(i-64,n)=s1(i+i1,j) !Copy from s1 into s3 - enddo - endif - enddo - - nsubmode=0 - if(mode_q65.eq.2) nsubmode=1 - if(mode_q65.eq.4) nsubmode=2 - if(mode_q65.eq.8) nsubmode=3 - if(mode_q65.eq.16) nsubmode=4 - nFadingModel=1 - baud=12000.0/nsps - - do ibw=ibwa,ibwb - b90=1.72**ibw - b90ts=b90/baud - call q65_dec1(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) - if(irc.ge.0) then - snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment - idec=1 - ic=ia2/4; - base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); - ccf1=ccf1-base - smax=maxval(ccf1) - if(smax.gt.10.0) ccf1=10.0*ccf1/smax - base=(sum(ccf2(-ia2:-ia2+ic)) + sum(ccf2(ia2-ic:ia2)))/(2.0+2.0*ic); - ccf2=ccf2-base - smax=maxval(ccf2) - if(smax.gt.10.0) ccf2=10.0*ccf2/smax - exit - endif - enddo - - return -end subroutine q65_dec_q3 From 0922a6ca6ef8bdd329c77730730d81cda08489f1 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 14:23:50 -0500 Subject: [PATCH 355/426] Fix several undefined variables. --- lib/qra/q65/q65.f90 | 2 +- lib/qra/q65/q65_sync.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 60ade8776..cb0cf8b6b 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -60,7 +60,7 @@ subroutine q65_symspec(iwave,nmax,iz,jz,s1) end subroutine q65_symspec subroutine q65_dec_q3(df,s1,iz,jz,ia,lag1,lag2,i0,j0,ccf,ccf1,ccf2, & - ia2,s3,LL,snr2,dat4,idec,decoded) + ia2,s3,LL,nfqso,dtstep,xdt,f0,snr2,dat4,idec,decoded) character*37 decoded integer itone(85) diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_sync.f90 index 0f8c534c2..a3d5d7c83 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_sync.f90 @@ -102,7 +102,7 @@ subroutine q65_sync(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & ! Try list decoding via "Deep Likelihood". call timer('list_dec',0) call q65_dec_q3(df,s1,iz,jz,ia,lag1,lag2,i0,j0,ccf,ccf1,ccf2, & - ia2,s3,LL,snr2,dat4,idec,decoded) + ia2,s3,LL,nfqso,dtstep,xdt,f0,snr2,dat4,idec,decoded) call timer('list_dec',1) endif From bfca40aa1c42160dffd19c272918955428c4163c Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 14:34:20 -0500 Subject: [PATCH 356/426] Rename q65_sync to q65_dec0. --- CMakeLists.txt | 2 +- lib/q65_decode.f90 | 6 +++--- lib/qra/q65/q65.f90 | 2 -- lib/qra/q65/{q65_sync.f90 => q65_dec0.f90} | 4 ++-- 4 files changed, 6 insertions(+), 8 deletions(-) rename lib/qra/q65/{q65_sync.f90 => q65_dec0.f90} (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 73fc9dcd2..cbbb08df7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -497,8 +497,8 @@ set (wsjt_FSRCS lib/polyfit.f90 lib/prog_args.f90 lib/ps4.f90 - lib/qra/q65/q65_sync.f90 lib/qra/q65/q65_ap.f90 + lib/qra/q65/q65_dec0.f90 lib/qra/q65/q65_loops.f90 lib/qra/q65/q65_set_list.f90 lib/refspectrum.f90 diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 596cb0b90..573bc02d7 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -100,11 +100,11 @@ contains call q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) dgen=0 call q65_enc(dgen,codewords) !Initialize the Q65 codec - call timer('sync_q65',0) - call q65_sync(nutc,iwave,ntrperiod, & + call timer('q65_dec0',0) + call q65_dec0(nutc,iwave,ntrperiod, & nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4, & snr2,idec) - call timer('sync_q65',1) + call timer('q65_dec0',1) if(idec.ge.0) then xdt1=xdt !We have a list-decode result f1=f0 diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index cb0cf8b6b..743753a25 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -15,7 +15,6 @@ module q65 contains - subroutine q65_clravg s1a=0. @@ -139,7 +138,6 @@ subroutine q65_dec_q3(df,s1,iz,jz,ia,lag1,lag2,i0,j0,ccf,ccf1,ccf2, & if(mode_q65.eq.4) nsubmode=2 if(mode_q65.eq.8) nsubmode=3 if(mode_q65.eq.16) nsubmode=4 - nFadingModel=1 baud=12000.0/nsps do ibw=ibwa,ibwb diff --git a/lib/qra/q65/q65_sync.f90 b/lib/qra/q65/q65_dec0.f90 similarity index 98% rename from lib/qra/q65/q65_sync.f90 rename to lib/qra/q65/q65_dec0.f90 index a3d5d7c83..0ee6e474b 100644 --- a/lib/qra/q65/q65_sync.f90 +++ b/lib/qra/q65/q65_dec0.f90 @@ -1,4 +1,4 @@ -subroutine q65_sync(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & +subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & emedelay,xdt,f0,snr1,width,dat4,snr2,idec) ! Detect and align with the Q65 sync vector, returning time and frequency @@ -175,4 +175,4 @@ subroutine q65_sync(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & close(17) 900 return -end subroutine q65_sync +end subroutine q65_dec0 From f0808942e52ccf4c161437a93682efd867994923 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 14:50:07 -0500 Subject: [PATCH 357/426] Move q65_dec0 into q65 module. --- CMakeLists.txt | 1 - lib/qra/q65/q65.f90 | 178 +++++++++++++++++++++++++++++++++++++++ lib/qra/q65/q65_dec0.f90 | 178 --------------------------------------- 3 files changed, 178 insertions(+), 179 deletions(-) delete mode 100644 lib/qra/q65/q65_dec0.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index cbbb08df7..535f1f68f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -498,7 +498,6 @@ set (wsjt_FSRCS lib/prog_args.f90 lib/ps4.f90 lib/qra/q65/q65_ap.f90 - lib/qra/q65/q65_dec0.f90 lib/qra/q65/q65_loops.f90 lib/qra/q65/q65_set_list.f90 lib/refspectrum.f90 diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 743753a25..3d9f9b1fa 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -15,6 +15,184 @@ module q65 contains +subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & + emedelay,xdt,f0,snr1,width,dat4,snr2,idec) + +! Detect and align with the Q65 sync vector, returning time and frequency +! offsets and SNR estimate. + +! Input: iwave(0:nmax-1) Raw data +! mode_q65 Tone spacing 1 2 4 8 16 (A-E) +! nsps Samples per symbol at 12000 Sa/s +! nfqso Target frequency (Hz) +! ntol Search range around nfqso (Hz) +! Output: xdt Time offset from nominal (s) +! f0 Frequency of sync tone +! snr1 Relative SNR of sync signal + + use packjt77 + use timer_module, only: timer + + parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 + integer*2 iwave(0:12000*ntrperiod-1) !Raw data + integer dat4(13) + integer ijpk(2) + character*37 decoded + logical first,lclearave + real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps + real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) + real, allocatable :: ccf(:,:) !CCF(freq,lag) + real, allocatable :: ccf1(:) !CCF(freq) at best lag + real, allocatable :: ccf2(:) !CCF(freq) at any lag + data first/.true./ + save first + + if(nutc+ndepth.eq.-999) stop + irc=-2 + idec=-1 + snr1=0. + dat4=0 + LL=64*(2+mode_q65) + nfft=nsps + df=12000.0/nfft !Freq resolution = baud + istep=nsps/NSTEP + iz=5000.0/df !Uppermost frequency bin, at 5000 Hz + txt=85.0*nsps/12000.0 + jz=(txt+1.0)*12000.0/istep !Number of quarter-symbol steps + if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher + ia=ntol/df + ia2=max(ia,10*mode_q65,nint(100.0/df)) + nsmo=int(0.7*mode_q65*mode_q65) + if(nsmo.lt.1) nsmo=1 +! nsmo=1 !### TEMPORARY ### + + allocate(s1(iz,jz)) + allocate(s3(-64:LL-65,63)) + allocate(ccf(-ia2:ia2,-53:214)) + allocate(ccf1(-ia2:ia2)) + allocate(ccf2(-ia2:ia2)) + if(LL.ne.LL0 .or. lclearave) then + if(allocated(s1a)) deallocate(s1a) + allocate(s1a(iz,jz)) + s1a=0. + navg=0 + LL0=LL + endif + + s3=0. + if(first) then !Generate the sync vector + sync=-22.0/63.0 !Sync tone OFF + do k=1,22 + sync(isync(k))=1.0 !Sync tone ON + enddo + endif + + call timer('s1 ',0) +! Compute spectra with symbol length and NSTEP time bins per symbol. + call q65_symspec(iwave,ntrperiod*12000,iz,jz,s1) + call timer('s1 ',1) + + i0=nint(nfqso/df) !Target QSO frequency + if(i0-64.lt.1 .or. i0-65+LL.gt.iz) go to 900 !Frequency out of range + call pctile(s1(i0-64:i0-65+LL,1:jz),LL*jz,40,base) + s1=s1/base + +! Apply fast AGC + s1max=20.0 !Empirical choice + do j=1,jz !### Maybe wrong way? ### + smax=maxval(s1(i0-64:i0-65+LL,j)) + if(smax.gt.s1max) s1(i0-64:i0-65+LL,j)=s1(i0-64:i0-65+LL,j)*s1max/smax + enddo + + dtstep=nsps/(NSTEP*12000.0) !Step size in seconds + lag1=-1.0/dtstep + lag2=1.0/dtstep + 0.9999 + if(nsps.ge.3600 .and. emedelay.gt.0) lag2=4.0/dtstep + 0.9999 !Include EME + j0=0.5/dtstep + if(nsps.ge.7200) j0=1.0/dtstep !Nominal start-signal index + + idec=-1 + dat4=0 + + if(ncw.gt.0) then +! Try list decoding via "Deep Likelihood". + call timer('list_dec',0) + call q65_dec_q3(df,s1,iz,jz,ia,lag1,lag2,i0,j0,ccf,ccf1,ccf2, & + ia2,s3,LL,nfqso,dtstep,xdt,f0,snr2,dat4,idec,decoded) + call timer('list_dec',1) + endif + +!###################################################################### +! Get 2d CCF and ccf2 using sync symbols only + ccf=0. + call timer('2dccf ',0) + do lag=lag1,lag2 + do k=1,85 + n=NSTEP*(k-1) + 1 + j=n+lag+j0 + if(j.ge.1 .and. j.le.jz) then + do i=-ia2,ia2 + if(i0+i.lt.1 .or. i0+i.gt.iz) cycle + ccf(i,lag)=ccf(i,lag) + sync(k)*s1(i0+i,j) + enddo + endif + enddo + enddo + do i=-ia2,ia2 + ccf2(i)=maxval(ccf(i,:)) + enddo + +! Estimate rms on ccf baseline + ijpk=maxloc(ccf(-ia:ia,:)) + ipk=ijpk(1)-ia-1 + jpk=ijpk(2)-53-1 + sq=0. + nsq=0 + jd=(lag2-lag1)/4 + do i=-ia2,ia2 + do j=lag1,lag2 + if(abs(j-jpk).gt.jd .and. abs(i-ipk).gt.ia/2) then + sq=sq + ccf(i,j)**2 + nsq=nsq+1 + endif + enddo + enddo + rms=sqrt(sq/nsq) + smax=ccf(ipk,jpk) + snr1=smax/rms + ccf2=ccf2/rms + if(snr1.gt.10.0) ccf2=(10.0/snr1)*ccf2 + call timer('2dccf ',1) + + if(idec.le.0) then +! The q3 decode attempt failed, so we'll try a more general decode. + f0=nfqso + ipk*df + xdt=jpk*dtstep + ccf1=ccf(:,jpk)/rms + if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 + call q65_s1_to_s3(s1,iz,jz,i0,j0,ipk,jpk,LL,mode_q65,sync,s3) + endif + + smax=maxval(ccf1) + i1=-9999 + i2=-9999 + do i=-ia,ia + if(i1.eq.-9999 .and. ccf1(i).ge.0.5*smax) i1=i + if(i2.eq.-9999 .and. ccf1(-i).ge.0.5*smax) i2=-i + enddo + width=df*(i2-i1) + +! Write data for the red and orange sync curves. + do i=-ia2,ia2 + freq=nfqso + i*df + write(17,1100) freq,ccf1(i),xdt,ccf2(i) +1100 format(4f10.3) + enddo + close(17) + +900 return +end subroutine q65_dec0 + subroutine q65_clravg s1a=0. diff --git a/lib/qra/q65/q65_dec0.f90 b/lib/qra/q65/q65_dec0.f90 deleted file mode 100644 index 0ee6e474b..000000000 --- a/lib/qra/q65/q65_dec0.f90 +++ /dev/null @@ -1,178 +0,0 @@ -subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & - emedelay,xdt,f0,snr1,width,dat4,snr2,idec) - -! Detect and align with the Q65 sync vector, returning time and frequency -! offsets and SNR estimate. - -! Input: iwave(0:nmax-1) Raw data -! mode_q65 Tone spacing 1 2 4 8 16 (A-E) -! nsps Samples per symbol at 12000 Sa/s -! nfqso Target frequency (Hz) -! ntol Search range around nfqso (Hz) -! Output: xdt Time offset from nominal (s) -! f0 Frequency of sync tone -! snr1 Relative SNR of sync signal - - use packjt77 - use timer_module, only: timer - use q65 - - parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 - integer*2 iwave(0:12000*ntrperiod-1) !Raw data - integer dat4(13) - integer ijpk(2) - character*37 decoded - logical first,lclearave - real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps - real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) - real, allocatable :: ccf(:,:) !CCF(freq,lag) - real, allocatable :: ccf1(:) !CCF(freq) at best lag - real, allocatable :: ccf2(:) !CCF(freq) at any lag - data first/.true./ - save first - - if(nutc+ndepth.eq.-999) stop - irc=-2 - idec=-1 - snr1=0. - dat4=0 - LL=64*(2+mode_q65) - nfft=nsps - df=12000.0/nfft !Freq resolution = baud - istep=nsps/NSTEP - iz=5000.0/df !Uppermost frequency bin, at 5000 Hz - txt=85.0*nsps/12000.0 - jz=(txt+1.0)*12000.0/istep !Number of quarter-symbol steps - if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher - ia=ntol/df - ia2=max(ia,10*mode_q65,nint(100.0/df)) - nsmo=int(0.7*mode_q65*mode_q65) - if(nsmo.lt.1) nsmo=1 -! nsmo=1 !### TEMPORARY ### - - allocate(s1(iz,jz)) - allocate(s3(-64:LL-65,63)) - allocate(ccf(-ia2:ia2,-53:214)) - allocate(ccf1(-ia2:ia2)) - allocate(ccf2(-ia2:ia2)) - if(LL.ne.LL0 .or. lclearave) then - if(allocated(s1a)) deallocate(s1a) - allocate(s1a(iz,jz)) - s1a=0. - navg=0 - LL0=LL - endif - - s3=0. - if(first) then !Generate the sync vector - sync=-22.0/63.0 !Sync tone OFF - do k=1,22 - sync(isync(k))=1.0 !Sync tone ON - enddo - endif - - call timer('s1 ',0) -! Compute spectra with symbol length and NSTEP time bins per symbol. - call q65_symspec(iwave,ntrperiod*12000,iz,jz,s1) - call timer('s1 ',1) - - i0=nint(nfqso/df) !Target QSO frequency - if(i0-64.lt.1 .or. i0-65+LL.gt.iz) go to 900 !Frequency out of range - call pctile(s1(i0-64:i0-65+LL,1:jz),LL*jz,40,base) - s1=s1/base - -! Apply fast AGC - s1max=20.0 !Empirical choice - do j=1,jz !### Maybe wrong way? ### - smax=maxval(s1(i0-64:i0-65+LL,j)) - if(smax.gt.s1max) s1(i0-64:i0-65+LL,j)=s1(i0-64:i0-65+LL,j)*s1max/smax - enddo - - dtstep=nsps/(NSTEP*12000.0) !Step size in seconds - lag1=-1.0/dtstep - lag2=1.0/dtstep + 0.9999 - if(nsps.ge.3600 .and. emedelay.gt.0) lag2=4.0/dtstep + 0.9999 !Include EME - j0=0.5/dtstep - if(nsps.ge.7200) j0=1.0/dtstep !Nominal start-signal index - - idec=-1 - dat4=0 - - if(ncw.gt.0) then -! Try list decoding via "Deep Likelihood". - call timer('list_dec',0) - call q65_dec_q3(df,s1,iz,jz,ia,lag1,lag2,i0,j0,ccf,ccf1,ccf2, & - ia2,s3,LL,nfqso,dtstep,xdt,f0,snr2,dat4,idec,decoded) - call timer('list_dec',1) - endif - -!###################################################################### -! Get 2d CCF and ccf2 using sync symbols only - ccf=0. - call timer('2dccf ',0) - do lag=lag1,lag2 - do k=1,85 - n=NSTEP*(k-1) + 1 - j=n+lag+j0 - if(j.ge.1 .and. j.le.jz) then - do i=-ia2,ia2 - if(i0+i.lt.1 .or. i0+i.gt.iz) cycle - ccf(i,lag)=ccf(i,lag) + sync(k)*s1(i0+i,j) - enddo - endif - enddo - enddo - do i=-ia2,ia2 - ccf2(i)=maxval(ccf(i,:)) - enddo - -! Estimate rms on ccf baseline - ijpk=maxloc(ccf(-ia:ia,:)) - ipk=ijpk(1)-ia-1 - jpk=ijpk(2)-53-1 - sq=0. - nsq=0 - jd=(lag2-lag1)/4 - do i=-ia2,ia2 - do j=lag1,lag2 - if(abs(j-jpk).gt.jd .and. abs(i-ipk).gt.ia/2) then - sq=sq + ccf(i,j)**2 - nsq=nsq+1 - endif - enddo - enddo - rms=sqrt(sq/nsq) - smax=ccf(ipk,jpk) - snr1=smax/rms - ccf2=ccf2/rms - if(snr1.gt.10.0) ccf2=(10.0/snr1)*ccf2 - call timer('2dccf ',1) - - if(idec.le.0) then -! The q3 decode attempt failed, so we'll try a more general decode. - f0=nfqso + ipk*df - xdt=jpk*dtstep - ccf1=ccf(:,jpk)/rms - if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 - call q65_s1_to_s3(s1,iz,jz,i0,j0,ipk,jpk,LL,mode_q65,sync,s3) - endif - - smax=maxval(ccf1) - i1=-9999 - i2=-9999 - do i=-ia,ia - if(i1.eq.-9999 .and. ccf1(i).ge.0.5*smax) i1=i - if(i2.eq.-9999 .and. ccf1(-i).ge.0.5*smax) i2=-i - enddo - width=df*(i2-i1) - -! Write data for the red and orange sync curves. - do i=-ia2,ia2 - freq=nfqso + i*df - write(17,1100) freq,ccf1(i),xdt,ccf2(i) -1100 format(4f10.3) - enddo - close(17) - -900 return -end subroutine q65_dec0 From 121fd2c3dd5481733afa9f4913d4254bd2ec865e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 13 Jan 2021 15:29:57 -0500 Subject: [PATCH 358/426] Fix a logical test on idec. --- lib/q65_decode.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 573bc02d7..14b5fafa5 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -153,7 +153,7 @@ contains 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.gt.0) go to 100 !Successful decode, we're done + if(idec.ge.0) go to 100 !Successful decode, we're done enddo ! There was no single-transmission decode. From 32a9f0bc315861329d75736d84a5a55ee95ed398 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 14 Jan 2021 10:25:35 -0500 Subject: [PATCH 359/426] Move more variables into public q65 module space. --- lib/qra/q65/q65.f90 | 122 +++++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 53 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 3d9f9b1fa..9577bc4fa 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -9,9 +9,11 @@ module q65 integer,dimension(22) :: isync = (/1,9,12,13,15,22,23,26,27,33,35, & 38,46,50,55,60,62,66,69,74,76,85/) integer codewords(63,206) - integer navg,ibwa,ibwb,ncw,nsps,mode_q65,istep,nsmo + integer navg,ibwa,ibwb,ncw,nsps,mode_q65,istep,nsmo,lag1,lag2 + integer i0,j0 real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra real sync(85) !sync vector + real df,dtstep contains @@ -117,8 +119,8 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & if(ncw.gt.0) then ! Try list decoding via "Deep Likelihood". call timer('list_dec',0) - call q65_dec_q3(df,s1,iz,jz,ia,lag1,lag2,i0,j0,ccf,ccf1,ccf2, & - ia2,s3,LL,nfqso,dtstep,xdt,f0,snr2,dat4,idec,decoded) + call q65_dec_q3(s1,iz,jz,ia,ia2,ccf,ccf1,ccf2,s3,LL,nfqso,xdt,f0, & + snr2,dat4,idec,decoded) call timer('list_dec',1) endif @@ -170,7 +172,7 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & xdt=jpk*dtstep ccf1=ccf(:,jpk)/rms if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 - call q65_s1_to_s3(s1,iz,jz,i0,j0,ipk,jpk,LL,mode_q65,sync,s3) + call q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) endif smax=maxval(ccf1) @@ -236,12 +238,10 @@ subroutine q65_symspec(iwave,nmax,iz,jz,s1) return end subroutine q65_symspec -subroutine q65_dec_q3(df,s1,iz,jz,ia,lag1,lag2,i0,j0,ccf,ccf1,ccf2, & - ia2,s3,LL,nfqso,dtstep,xdt,f0,snr2,dat4,idec,decoded) +subroutine q65_dec_q3(s1,iz,jz,ia,ia2,ccf,ccf1,ccf2,s3,LL,nfqso,xdt,f0, & + snr2,dat4,idec,decoded) character*37 decoded - integer itone(85) - integer ijpk(2) integer dat4(13) real ccf(-ia2:ia2,-53:214) real ccf1(-ia2:ia2) @@ -249,50 +249,8 @@ subroutine q65_dec_q3(df,s1,iz,jz,ia,lag1,lag2,i0,j0,ccf,ccf1,ccf2, & real s1(iz,jz) real s3(-64:LL-65,63) - ipk=0 - jpk=0 - ccf_best=0. - imsg_best=-1 - do imsg=1,ncw - i=1 - k=0 - do j=1,85 - if(j.eq.isync(i)) then - i=i+1 - itone(j)=-1 - else - k=k+1 - itone(j)=codewords(k,imsg) - endif - enddo -! Compute 2D ccf using all 85 symbols in the list message - ccf=0. - iia=200.0/df - - do lag=lag1,lag2 - do k=1,85 - j=j0 + NSTEP*(k-1) + 1 + lag - if(j.ge.1 .and. j.le.jz) then - do i=-ia2,ia2 - ii=i0+mode_q65*itone(k)+i - if(ii.ge.iia .and. ii.le.iz) ccf(i,lag)=ccf(i,lag) + s1(ii,j) - enddo - endif - enddo - enddo - - ccfmax=maxval(ccf(-ia:ia,:)) - if(ccfmax.gt.ccf_best) then - ccf_best=ccfmax - ijpk=maxloc(ccf(-ia:ia,:)) - ipk=ijpk(1)-ia-1 - jpk=ijpk(2)-53-1 - f0=nfqso + (ipk-mode_q65)*df - xdt=jpk*dtstep - imsg_best=imsg - ccf1=ccf(:,jpk) - endif - enddo ! imsg + call q65_ccf_85(s1,iz,jz,nfqso,ia,ia2, & + ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) i1=i0+ipk-64 i2=i1+LL-1 @@ -341,6 +299,64 @@ subroutine q65_dec_q3(df,s1,iz,jz,ia,lag1,lag2,i0,j0,ccf,ccf1,ccf2, & return end subroutine q65_dec_q3 +subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2, & + ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) + + real s1(iz,jz) + real ccf(-ia2:ia2,-53:214) + real ccf1(-ia2:ia2) + integer ijpk(2) + integer itone(85) + + ipk=0 + jpk=0 + ccf_best=0. + imsg_best=-1 + do imsg=1,ncw + i=1 + k=0 + do j=1,85 + if(j.eq.isync(i)) then + i=i+1 + itone(j)=-1 + else + k=k+1 + itone(j)=codewords(k,imsg) + endif + enddo +! Compute 2D ccf using all 85 symbols in the list message + ccf=0. + iia=200.0/df + + do lag=lag1,lag2 + do k=1,85 + j=j0 + NSTEP*(k-1) + 1 + lag + if(j.ge.1 .and. j.le.jz) then + do i=-ia2,ia2 + ii=i0+mode_q65*itone(k)+i + if(ii.ge.iia .and. ii.le.iz) ccf(i,lag)=ccf(i,lag) + s1(ii,j) + enddo + endif + enddo + enddo + + ccfmax=maxval(ccf(-ia:ia,:)) + if(ccfmax.gt.ccf_best) then + ccf_best=ccfmax + ijpk=maxloc(ccf(-ia:ia,:)) + ipk=ijpk(1)-ia-1 + jpk=ijpk(2)-53-1 + f0=nfqso + (ipk-mode_q65)*df + xdt=jpk*dtstep + imsg_best=imsg + ccf1=ccf(:,jpk) + endif + enddo ! imsg + + return +end subroutine q65_ccf_85 + + subroutine q65_dec1(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) use packjt77 @@ -389,7 +405,7 @@ subroutine q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) return end subroutine q65_dec2 -subroutine q65_s1_to_s3(s1,iz,jz,i0,j0,ipk,jpk,LL,mode_q65,sync,s3) +subroutine q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) ! Copy from s1 or s1a into s3 From e0d6e362e8955e378c9f68fdb36a7bc37e554669 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 14 Jan 2021 10:43:50 -0500 Subject: [PATCH 360/426] Make separate sync subroutines q65_ccf_85() (all 85 symbols) and q65_ccf_22() (sync symbols only). --- lib/qra/q65/q65.f90 | 60 +++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 9577bc4fa..1411050df 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -38,7 +38,6 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 integer*2 iwave(0:12000*ntrperiod-1) !Raw data integer dat4(13) - integer ijpk(2) character*37 decoded logical first,lclearave real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps @@ -124,30 +123,10 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & call timer('list_dec',1) endif -!###################################################################### ! Get 2d CCF and ccf2 using sync symbols only - ccf=0. - call timer('2dccf ',0) - do lag=lag1,lag2 - do k=1,85 - n=NSTEP*(k-1) + 1 - j=n+lag+j0 - if(j.ge.1 .and. j.le.jz) then - do i=-ia2,ia2 - if(i0+i.lt.1 .or. i0+i.gt.iz) cycle - ccf(i,lag)=ccf(i,lag) + sync(k)*s1(i0+i,j) - enddo - endif - enddo - enddo - do i=-ia2,ia2 - ccf2(i)=maxval(ccf(i,:)) - enddo + call q65_ccf_22(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,ccf,ccf2) ! Estimate rms on ccf baseline - ijpk=maxloc(ccf(-ia:ia,:)) - ipk=ijpk(1)-ia-1 - jpk=ijpk(2)-53-1 sq=0. nsq=0 jd=(lag2-lag1)/4 @@ -164,12 +143,9 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & snr1=smax/rms ccf2=ccf2/rms if(snr1.gt.10.0) ccf2=(10.0/snr1)*ccf2 - call timer('2dccf ',1) if(idec.le.0) then ! The q3 decode attempt failed, so we'll try a more general decode. - f0=nfqso + ipk*df - xdt=jpk*dtstep ccf1=ccf(:,jpk)/rms if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 call q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) @@ -249,8 +225,7 @@ subroutine q65_dec_q3(s1,iz,jz,ia,ia2,ccf,ccf1,ccf2,s3,LL,nfqso,xdt,f0, & real s1(iz,jz) real s3(-64:LL-65,63) - call q65_ccf_85(s1,iz,jz,nfqso,ia,ia2, & - ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) + call q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) i1=i0+ipk-64 i2=i1+LL-1 @@ -356,6 +331,37 @@ subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2, & return end subroutine q65_ccf_85 +subroutine q65_ccf_22(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,ccf,ccf2) + + real s1(iz,jz) + real ccf(-ia2:ia2,-53:214) + real ccf2(-ia2:ia2) + integer ijpk(2) + + ccf=0. + do lag=lag1,lag2 + do k=1,85 + n=NSTEP*(k-1) + 1 + j=n+lag+j0 + if(j.ge.1 .and. j.le.jz) then + do i=-ia2,ia2 + if(i0+i.lt.1 .or. i0+i.gt.iz) cycle + ccf(i,lag)=ccf(i,lag) + sync(k)*s1(i0+i,j) + enddo + endif + enddo + enddo + do i=-ia2,ia2 + ccf2(i)=maxval(ccf(i,:)) + enddo + ijpk=maxloc(ccf(-ia:ia,:)) + ipk=ijpk(1)-ia-1 + jpk=ijpk(2)-53-1 + f0=nfqso + ipk*df + xdt=jpk*dtstep + + return +end subroutine q65_ccf_22 subroutine q65_dec1(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) From 0abdff0216ab98be4166e6c81590ef90cc56c607 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 14 Jan 2021 10:58:11 -0500 Subject: [PATCH 361/426] Clean up the usage of q65_dec_q3, q65_ccf_85, and q65_ccf_22. --- lib/qra/q65/q65.f90 | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 1411050df..d17b7e6c9 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -117,10 +117,23 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & if(ncw.gt.0) then ! Try list decoding via "Deep Likelihood". + call timer('ccf_85 ',0) + call q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) + call timer('ccf_85 ',1) + call timer('list_dec',0) - call q65_dec_q3(s1,iz,jz,ia,ia2,ccf,ccf1,ccf2,s3,LL,nfqso,xdt,f0, & - snr2,dat4,idec,decoded) + call q65_dec_q3(s1,iz,jz,s3,LL,ipk,jpk,snr2,dat4,idec,decoded) call timer('list_dec',1) + + ic=ia2/4; + base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); + ccf1=ccf1-base + smax=maxval(ccf1) + if(smax.gt.10.0) ccf1=10.0*ccf1/smax + base=(sum(ccf2(-ia2:-ia2+ic)) + sum(ccf2(ia2-ic:ia2)))/(2.0+2.0*ic); + ccf2=ccf2-base + smax=maxval(ccf2) + if(smax.gt.10.0) ccf2=10.0*ccf2/smax endif ! Get 2d CCF and ccf2 using sync symbols only @@ -214,19 +227,13 @@ subroutine q65_symspec(iwave,nmax,iz,jz,s1) return end subroutine q65_symspec -subroutine q65_dec_q3(s1,iz,jz,ia,ia2,ccf,ccf1,ccf2,s3,LL,nfqso,xdt,f0, & - snr2,dat4,idec,decoded) +subroutine q65_dec_q3(s1,iz,jz,s3,LL,ipk,jpk,snr2,dat4,idec,decoded) character*37 decoded integer dat4(13) - real ccf(-ia2:ia2,-53:214) - real ccf1(-ia2:ia2) - real ccf2(-ia2:ia2) real s1(iz,jz) real s3(-64:LL-65,63) - call q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) - i1=i0+ipk-64 i2=i1+LL-1 j=j0+jpk-7 @@ -258,15 +265,6 @@ subroutine q65_dec_q3(s1,iz,jz,ia,ia2,ccf,ccf1,ccf2,s3,LL,nfqso,xdt,f0, & if(irc.ge.0) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment idec=1 - ic=ia2/4; - base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); - ccf1=ccf1-base - smax=maxval(ccf1) - if(smax.gt.10.0) ccf1=10.0*ccf1/smax - base=(sum(ccf2(-ia2:-ia2+ic)) + sum(ccf2(ia2-ic:ia2)))/(2.0+2.0*ic); - ccf2=ccf2-base - smax=maxval(ccf2) - if(smax.gt.10.0) ccf2=10.0*ccf2/smax exit endif enddo From fca6d482f7ae328cb8c6cf6f3646ec24b93805d3 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 14 Jan 2021 13:13:40 -0500 Subject: [PATCH 362/426] Radionalize the reporting of idec values. --- lib/decoder.f90 | 7 +-- lib/q65_decode.f90 | 20 ++++++-- lib/qra/q65/q65.f90 | 102 +++++++++++++++++++++----------------- lib/qra/q65/q65_loops.f90 | 2 +- 4 files changed, 73 insertions(+), 58 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 3cc58d3a7..ced405b29 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -776,18 +776,13 @@ contains character*3 cflags cflags=' ' - if(idec.gt.0) then + if(idec.ge.0) then iaptype=idec navg=0 if(idec.ge.100) then iaptype=idec/100 navg=mod(idec,100) endif - if(iaptype.eq.1) then - iaptype=3 - else - iaptype=iaptype-2 - endif cflags='q ' write(cflags(2:2),'(i1)') iaptype if(navg.ge.2) write(cflags(3:3),'(i1)') navg diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 14b5fafa5..26115a086 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -29,7 +29,7 @@ contains ntol,ndepth,lclearave,emedelay,mycall,hiscall,hisgrid,nQSOprogress, & ncontest,lapcqonly) -! Decodes Q65 signals +! Top-level routine that organizes the decoding of Q65 signals ! Input: iwave Raw data, i*2 ! nutc UTC for time-tagging the decode ! ntrperiod T/R sequence length (s) @@ -37,6 +37,11 @@ contains ! 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 ! Output: sent to the callback routine for display to user use timer_module, only: timer @@ -61,6 +66,7 @@ contains 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 idec=-1 mode_q65=2**nsubmode npts=ntrperiod*12000 @@ -101,10 +107,13 @@ contains dgen=0 call q65_enc(dgen,codewords) !Initialize the Q65 codec call timer('q65_dec0',0) - call q65_dec0(nutc,iwave,ntrperiod, & - nfqso,ntol,ndepth,lclearave,emedelay,xdt,f0,snr1,width,dat4, & - snr2,idec) +! Call top-level routine in q65 module: establish sync and try for a q3 decode. + call q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & + emedelay,xdt,f0,snr1,width,dat4,snr2,idec) call timer('q65_dec0',1) + + print*,'AAA',idec + if(idec.ge.0) then xdt1=xdt !We have a list-decode result f1=f0 @@ -166,7 +175,8 @@ contains endif 100 decoded=' ' - if(idec.gt.0) then + print*,'BBB',idec + if(idec.ge.0) then ! ------------------------------------------------------ ! idec Meaning diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index d17b7e6c9..4dfae41c4 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -20,17 +20,27 @@ contains subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & emedelay,xdt,f0,snr1,width,dat4,snr2,idec) -! Detect and align with the Q65 sync vector, returning time and frequency -! offsets and SNR estimate. +! Top-level routine in q65 module ! Input: iwave(0:nmax-1) Raw data -! mode_q65 Tone spacing 1 2 4 8 16 (A-E) -! nsps Samples per symbol at 12000 Sa/s +! ntrperiod T/R sequence length (s) ! nfqso Target frequency (Hz) ! ntol Search range around nfqso (Hz) +! ndepth Requested decoding depth +! lclearave Flag to clear the accumulating array +! emedelay Extra delay for EME signals ! Output: xdt Time offset from nominal (s) ! f0 Frequency of sync tone ! snr1 Relative SNR of sync signal +! width Estimated Doppler spread +! dat4(13) Decoded message as 13 six-bit integers +! snr2 Estimated SNR of decoded signal +! idec Flag for decing results +! -1 No decode +! 0 No AP +! 1 "CQ ? ?" +! 2 "Mycall ? ?" +! 3 "MyCall HisCall ?" use packjt77 use timer_module, only: timer @@ -48,7 +58,9 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & data first/.true./ save first - if(nutc+ndepth.eq.-999) stop + if(nutc+ndepth.eq.-999) stop !Silence compiler warnings + +! Set seom parameters and allocate storage for large arrays irc=-2 idec=-1 snr1=0. @@ -65,7 +77,12 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & ia2=max(ia,10*mode_q65,nint(100.0/df)) nsmo=int(0.7*mode_q65*mode_q65) if(nsmo.lt.1) nsmo=1 -! nsmo=1 !### TEMPORARY ### + if(first) then !Generate the sync vector + sync=-22.0/63.0 !Sync tone OFF + do k=1,22 + sync(isync(k))=1.0 !Sync tone ON + enddo + endif allocate(s1(iz,jz)) allocate(s3(-64:LL-65,63)) @@ -79,32 +96,6 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & navg=0 LL0=LL endif - - s3=0. - if(first) then !Generate the sync vector - sync=-22.0/63.0 !Sync tone OFF - do k=1,22 - sync(isync(k))=1.0 !Sync tone ON - enddo - endif - - call timer('s1 ',0) -! Compute spectra with symbol length and NSTEP time bins per symbol. - call q65_symspec(iwave,ntrperiod*12000,iz,jz,s1) - call timer('s1 ',1) - - i0=nint(nfqso/df) !Target QSO frequency - if(i0-64.lt.1 .or. i0-65+LL.gt.iz) go to 900 !Frequency out of range - call pctile(s1(i0-64:i0-65+LL,1:jz),LL*jz,40,base) - s1=s1/base - -! Apply fast AGC - s1max=20.0 !Empirical choice - do j=1,jz !### Maybe wrong way? ### - smax=maxval(s1(i0-64:i0-65+LL,j)) - if(smax.gt.s1max) s1(i0-64:i0-65+LL,j)=s1(i0-64:i0-65+LL,j)*s1max/smax - enddo - dtstep=nsps/(NSTEP*12000.0) !Step size in seconds lag1=-1.0/dtstep lag2=1.0/dtstep + 0.9999 @@ -112,28 +103,46 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & j0=0.5/dtstep if(nsps.ge.7200) j0=1.0/dtstep !Nominal start-signal index - idec=-1 - dat4=0 + s3=0. + call timer('s1 ',0) +! Compute symbol spectra with NSTEP time bins per symbol + call q65_symspec(iwave,ntrperiod*12000,iz,jz,s1) + call timer('s1 ',1) + i0=nint(nfqso/df) !Target QSO frequency + if(i0-64.lt.1 .or. i0-65+LL.gt.iz) go to 900 !Frequency out of range + call pctile(s1(i0-64:i0-65+LL,1:jz),LL*jz,40,base) + s1=s1/base + +! Apply fast AGC to the symbol spectra + s1max=20.0 !Empirical choice + do j=1,jz !### Maybe wrong way? ### + smax=maxval(s1(i0-64:i0-65+LL,j)) + if(smax.gt.s1max) s1(i0-64:i0-65+LL,j)=s1(i0-64:i0-65+LL,j)*s1max/smax + enddo + + dat4=0 if(ncw.gt.0) then ! Try list decoding via "Deep Likelihood". call timer('ccf_85 ',0) +! Try to synchronize using all 85 symbols call q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) call timer('ccf_85 ',1) call timer('list_dec',0) call q65_dec_q3(s1,iz,jz,s3,LL,ipk,jpk,snr2,dat4,idec,decoded) call timer('list_dec',1) - - ic=ia2/4; - base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); - ccf1=ccf1-base - smax=maxval(ccf1) - if(smax.gt.10.0) ccf1=10.0*ccf1/smax - base=(sum(ccf2(-ia2:-ia2+ic)) + sum(ccf2(ia2-ic:ia2)))/(2.0+2.0*ic); - ccf2=ccf2-base - smax=maxval(ccf2) - if(smax.gt.10.0) ccf2=10.0*ccf2/smax + if(idec.ne.0) then + ic=ia2/4; + base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); + ccf1=ccf1-base + smax=maxval(ccf1) + if(smax.gt.10.0) ccf1=10.0*ccf1/smax + base=(sum(ccf2(-ia2:-ia2+ic)) + sum(ccf2(ia2-ic:ia2)))/(2.0+2.0*ic); + ccf2=ccf2-base + smax=maxval(ccf2) + if(smax.gt.10.0) ccf2=10.0*ccf2/smax + endif endif ! Get 2d CCF and ccf2 using sync symbols only @@ -158,7 +167,8 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & if(snr1.gt.10.0) ccf2=(10.0/snr1)*ccf2 if(idec.le.0) then -! The q3 decode attempt failed, so we'll try a more general decode. +! The q3 decode attempt failed. Copy synchronied symbol spectra from s1 +! into s3 and prepare to try a more general decode. ccf1=ccf(:,jpk)/rms if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 call q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) @@ -264,7 +274,7 @@ subroutine q65_dec_q3(s1,iz,jz,s3,LL,ipk,jpk,snr2,dat4,idec,decoded) call q65_dec1(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) if(irc.ge.0) then snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment - idec=1 + idec=3 exit endif enddo diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index d2ef86ae9..808f45283 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -78,7 +78,7 @@ subroutine q65_loops(c00,npts2,nsps2,nsubmode,ndepth,jpk0, & ! write(55,3055) ndepth,iaptype,idf,idt,ibw,ndist,irc,sum(s3(1:LL*NN)), & ! trim(decoded) !3055 format(7i4,f10.1,1x,a) - idec=iaptype+2 + idec=iaptype snr2=esnodb - db(2500.0/baud) xdt1=xdt0 + nsps2*ndt/(16.0*6000.0) f1=f0 + 0.5*baud*ndf From 02d410d4f7d5c3d29e381060488ca9869428d579 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 14 Jan 2021 13:23:09 -0500 Subject: [PATCH 363/426] Send both idec and navg to the q65 callback routine. --- lib/decoder.f90 | 15 +++++---------- lib/q65_decode.f90 | 12 +++++------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index ced405b29..582865a4e 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -758,7 +758,8 @@ contains return end subroutine fst4_decoded - subroutine q65_decoded (this,nutc,snr1,nsnr,dt,freq,decoded,idec,ntrperiod) + subroutine q65_decoded (this,nutc,snr1,nsnr,dt,freq,decoded,idec, & + navg,ntrperiod) use q65_decode implicit none @@ -771,20 +772,14 @@ contains real, intent(in) :: freq character(len=37), intent(in) :: decoded integer, intent(in) :: idec + integer, intent(in) :: navg integer, intent(in) :: ntrperiod - integer iaptype,navg character*3 cflags - + cflags=' ' if(idec.ge.0) then - iaptype=idec - navg=0 - if(idec.ge.100) then - iaptype=idec/100 - navg=mod(idec,100) - endif cflags='q ' - write(cflags(2:2),'(i1)') iaptype + write(cflags(2:2),'(i1)') idec if(navg.ge.2) write(cflags(3:3),'(i1)') navg endif diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 26115a086..45d2c1798 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -8,7 +8,7 @@ module q65_decode abstract interface subroutine q65_decode_callback (this,nutc,snr1,nsnr,dt,freq, & - decoded,nap,ntrperiod) + decoded,idec,navg,ntrperiod) import q65_decoder implicit none class(q65_decoder), intent(inout) :: this @@ -18,7 +18,8 @@ module q65_decode real, intent(in) :: dt real, intent(in) :: freq character(len=37), intent(in) :: decoded - integer, intent(in) :: nap + integer, intent(in) :: idec + integer, intent(in) :: navg integer, intent(in) :: ntrperiod end subroutine q65_decode_callback end interface @@ -112,8 +113,6 @@ contains emedelay,xdt,f0,snr1,width,dat4,snr2,idec) call timer('q65_dec0',1) - print*,'AAA',idec - if(idec.ge.0) then xdt1=xdt !We have a list-decode result f1=f0 @@ -175,7 +174,6 @@ contains endif 100 decoded=' ' - print*,'BBB',idec if(idec.ge.0) then ! ------------------------------------------------------ @@ -195,14 +193,14 @@ contains 1000 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent nsnr=nint(snr2) - call this%callback(nutc,snr1,nsnr,xdt1,f1,decoded,idec,ntrperiod) + call this%callback(nutc,snr1,nsnr,xdt1,f1,decoded,idec,navg,ntrperiod) call q65_clravg else ! Report snr1, even if no decode. nsnr=db(snr1) - 35.0 idec=-1 call this%callback(nutc,snr1,nsnr,xdt1,f1,decoded, & - idec,ntrperiod) + idec,navg,ntrperiod) endif return From 921831119cb19bd6f1ac33db8e895fc4a272fcd6 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 14 Jan 2021 14:56:37 -0500 Subject: [PATCH 364/426] Message averaging now working for 'q3n' decodes. --- lib/q65_decode.f90 | 47 ++++++++++++++++++++------------------------- lib/qra/q65/q65.f90 | 25 ++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 45d2c1798..0447eaa62 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -73,7 +73,7 @@ contains npts=ntrperiod*12000 nfft1=ntrperiod*12000 nfft2=ntrperiod*6000 - if(lclearave) nsave=0 + if(lclearave) call q65_clravg allocate(dd(npts)) allocate (c00(0:nfft1-1)) allocate (c0(0:nfft1-1)) @@ -93,7 +93,6 @@ contains endif baud=12000.0/nsps - df1=12000.0/nfft1 this%callback => callback nFadingModel=1 ibwa=max(1,int(1.8*log(baud*mode_q65)) + 2) @@ -103,14 +102,17 @@ contains ibwb=min(10,ibwb+1) endif -! Set up the codewords for full-AP list decoding +! Generate codewords for full-AP list decoding call q65_set_list(mycall,hiscall,hisgrid,codewords,ncw) dgen=0 call q65_enc(dgen,codewords) !Initialize the Q65 codec + + nused=1 call timer('q65_dec0',0) ! Call top-level routine in q65 module: establish sync and try for a q3 decode. call q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & emedelay,xdt,f0,snr1,width,dat4,snr2,idec) +! idec=-1 !### TEMPORARY ### call timer('q65_dec0',1) if(idec.ge.0) then @@ -124,24 +126,22 @@ contains f1=0. go to 100 endif - + +! Prepare for a single-period decode woth iaptype = 0, 1, or 2 (also 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 fac=1.0/32767.0 dd=fac*iwave(1:npts) call ana64(dd,npts,c00) !Convert to complex c00() at 6000 Sa/s - -! Generate ap symbols as in FT8 - call ft8apset(mycall,hiscall,ncontest,apsym0,aph10) + call ft8apset(mycall,hiscall,ncontest,apsym0,aph10) ! Generate ap symbols where(apsym0.eq.-1) apsym0=0 -! Main decoding loop starts here npasses=2 if(nQSOprogress.eq.5) npasses=3 if(lapcqonly) npasses=1 iaptype=0 - do ipass=0,npasses + do ipass=0,npasses !Loop over AP passes apmask=0 !Try first with no AP information apsymbols=0 @@ -160,41 +160,36 @@ contains call timer('q65loops',0) call q65_loops(c00,npts/2,nsps/2,nsubmode,ndepth,jpk0, & xdt,f0,iaptype,xdt1,f1,snr2,dat4,idec) +! idec=-1 !### TEMPORARY ### call timer('q65loops',1) if(idec.ge.0) go to 100 !Successful decode, we're done enddo -! There was no single-transmission decode. if(iand(ndepth,16).eq.16) then - ! Try for an average decode. - call timer('q65_avg2',0) -! call q65_avg2(ntrperiod,baud,nsubmode,nQSOprogress,lapcqonly, & -! codewords,ncw,xdt,f0,snr2,dat4,idec) - call timer('q65_avg2',1) +! There was no single-transmission decode. Try for an average decode. + call timer('list_avg',0) + call q65_q3a(xdt,f0,nfqso,nsps,snr2,dat4,idec,decoded) + call timer('list_avg',1) + nused=navg endif 100 decoded=' ' if(idec.ge.0) then - -! ------------------------------------------------------ ! idec Meaning ! ------------------------------------------------------ ! -1: No decode -! 1: Decode with AP for "MyCall DxCall ?" -! 2: Decode without AP information -! 3: Decode with AP for "CQ ? ?" -! 4: Decode with AP for "MyCall ? ?" -! ------------------------------------------------------ -! Second digit (if any) is number of sequences averaged. -! ------------------------------------------------------ +! 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 nsnr=nint(snr2) - call this%callback(nutc,snr1,nsnr,xdt1,f1,decoded,idec,navg,ntrperiod) - call q65_clravg + call this%callback(nutc,snr1,nsnr,xdt1,f1,decoded,idec,nused,ntrperiod) + call q65_clravg !Automatic ClrAvg after a decode else ! Report snr1, even if no decode. nsnr=db(snr1) - 35.0 diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 4dfae41c4..bfd596d4d 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -71,7 +71,7 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & istep=nsps/NSTEP iz=5000.0/df !Uppermost frequency bin, at 5000 Hz txt=85.0*nsps/12000.0 - jz=(txt+1.0)*12000.0/istep !Number of quarter-symbol steps + jz=(txt+1.0)*12000.0/istep !Number of symbol/NSTEP bins if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher ia=ntol/df ia2=max(ia,10*mode_q65,nint(100.0/df)) @@ -196,7 +196,7 @@ end subroutine q65_dec0 subroutine q65_clravg - s1a=0. + if(allocated(s1a)) s1a=0. navg=0 return @@ -282,6 +282,27 @@ subroutine q65_dec_q3(s1,iz,jz,s3,LL,ipk,jpk,snr2,dat4,idec,decoded) return end subroutine q65_dec_q3 +subroutine q65_q3a(xdt,f0,nfqso,nsps,snr2,dat4,idec,decoded) + + integer dat4(13) + character*37 decoded + real, allocatable :: s3a(:,:) !Symbol energies for avg s3a(LL,63) + + df=12000.0/nsps + dtstep=float(nsps)/(NSTEP*12000.0) + iz=5000.0/df !Uppermost frequency bin, at 5000 Hz + txt=85.0*nsps/12000.0 + istep=nsps/NSTEP + jz=(txt+1.0)*12000.0/istep !Number of symbol/NSTEP bins + LL=64*(2+mode_q65) + allocate(s3a(-64:LL-65,63)) + ipk=nint((f0-nfqso)/df) + mode_q65 + jpk=nint(xdt/dtstep) + call q65_dec_q3(s1a,iz,jz,s3a,LL,ipk,jpk,snr2,dat4,idec,decoded) + + return +end subroutine q65_q3a + subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2, & ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) From cbb939453f09db5167d67d394fd467ca744ba689 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 14 Jan 2021 15:19:33 -0500 Subject: [PATCH 365/426] Code cleanup. --- lib/q65_decode.f90 | 10 ++++++++-- lib/qra/q65/q65.f90 | 6 +++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 0447eaa62..189984c1e 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -166,11 +166,17 @@ contains enddo if(iand(ndepth,16).eq.16) then -! There was no single-transmission decode. Try for an average decode. +! There was no single-transmission decode. Try for an average 'q3n' decode. call timer('list_avg',0) call q65_q3a(xdt,f0,nfqso,nsps,snr2,dat4,idec,decoded) call timer('list_avg',1) - nused=navg + if(idec.ge.0) then + nused=navg + go to 100 + endif + +! There was no 'q3n' decode. Try for a 'q[012]n' decode. +! call q65_q012a() endif 100 decoded=' ' diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index bfd596d4d..ee8a71292 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -60,7 +60,7 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & if(nutc+ndepth.eq.-999) stop !Silence compiler warnings -! Set seom parameters and allocate storage for large arrays +! Set some parameters and allocate storage for large arrays irc=-2 idec=-1 snr1=0. @@ -104,10 +104,10 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & if(nsps.ge.7200) j0=1.0/dtstep !Nominal start-signal index s3=0. - call timer('s1 ',0) + call timer('q65_syms',0) ! Compute symbol spectra with NSTEP time bins per symbol call q65_symspec(iwave,ntrperiod*12000,iz,jz,s1) - call timer('s1 ',1) + call timer('q65_syms',1) i0=nint(nfqso/df) !Target QSO frequency if(i0-64.lt.1 .or. i0-65+LL.gt.iz) go to 900 !Frequency out of range From 70a3481a4fa147214ba378e0d809e9da06804839 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 14 Jan 2021 15:39:48 -0500 Subject: [PATCH 366/426] Improved organization for obtaining 'q3n' decodes. --- lib/q65_decode.f90 | 8 ++++++-- lib/qra/q65/q65.f90 | 36 ++++++++++-------------------------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 189984c1e..b078529d3 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -108,9 +108,10 @@ contains call q65_enc(dgen,codewords) !Initialize the Q65 codec nused=1 + iavg=0 call timer('q65_dec0',0) ! Call top-level routine in q65 module: establish sync and try for a q3 decode. - call q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & + call q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & emedelay,xdt,f0,snr1,width,dat4,snr2,idec) ! idec=-1 !### TEMPORARY ### call timer('q65_dec0',1) @@ -168,7 +169,10 @@ contains if(iand(ndepth,16).eq.16) then ! There was no single-transmission decode. Try for an average 'q3n' decode. call timer('list_avg',0) - call q65_q3a(xdt,f0,nfqso,nsps,snr2,dat4,idec,decoded) +! Call top-level routine in q65 module: establish sync and try for a q3 decode. + 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 nused=navg diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index ee8a71292..bfc8b2229 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -17,12 +17,13 @@ module q65 contains -subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & +subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & emedelay,xdt,f0,snr1,width,dat4,snr2,idec) ! Top-level routine in q65 module -! Input: iwave(0:nmax-1) Raw data +! Input: iavg 0 for single-period decode, 1 for average +! iwave(0:nmax-1) Raw data ! ntrperiod T/R sequence length (s) ! nfqso Target frequency (Hz) ! ntol Search range around nfqso (Hz) @@ -104,10 +105,14 @@ subroutine q65_dec0(nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & if(nsps.ge.7200) j0=1.0/dtstep !Nominal start-signal index s3=0. - call timer('q65_syms',0) + if(iavg.eq.0) then + call timer('q65_syms',0) ! Compute symbol spectra with NSTEP time bins per symbol - call q65_symspec(iwave,ntrperiod*12000,iz,jz,s1) - call timer('q65_syms',1) + call q65_symspec(iwave,ntrperiod*12000,iz,jz,s1) + call timer('q65_syms',1) + else + s1=s1a + endif i0=nint(nfqso/df) !Target QSO frequency if(i0-64.lt.1 .or. i0-65+LL.gt.iz) go to 900 !Frequency out of range @@ -282,27 +287,6 @@ subroutine q65_dec_q3(s1,iz,jz,s3,LL,ipk,jpk,snr2,dat4,idec,decoded) return end subroutine q65_dec_q3 -subroutine q65_q3a(xdt,f0,nfqso,nsps,snr2,dat4,idec,decoded) - - integer dat4(13) - character*37 decoded - real, allocatable :: s3a(:,:) !Symbol energies for avg s3a(LL,63) - - df=12000.0/nsps - dtstep=float(nsps)/(NSTEP*12000.0) - iz=5000.0/df !Uppermost frequency bin, at 5000 Hz - txt=85.0*nsps/12000.0 - istep=nsps/NSTEP - jz=(txt+1.0)*12000.0/istep !Number of symbol/NSTEP bins - LL=64*(2+mode_q65) - allocate(s3a(-64:LL-65,63)) - ipk=nint((f0-nfqso)/df) + mode_q65 - jpk=nint(xdt/dtstep) - call q65_dec_q3(s1a,iz,jz,s3a,LL,ipk,jpk,snr2,dat4,idec,decoded) - - return -end subroutine q65_q3a - subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2, & ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) From b64c1faba4c2481bf119436a56a4ac8b8c10da4f Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 14 Jan 2021 15:52:51 -0500 Subject: [PATCH 367/426] Prepare for doing AP=012 decodes from s1a. --- lib/q65_decode.f90 | 22 ++++++++++++++++++---- lib/timer_impl.f90 | 4 ++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index b078529d3..f6fc455ef 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -145,7 +145,6 @@ contains 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, & @@ -164,12 +163,13 @@ contains ! idec=-1 !### TEMPORARY ### call timer('q65loops',1) if(idec.ge.0) go to 100 !Successful decode, we're done - enddo + enddo ! ipass if(iand(ndepth,16).eq.16) then ! There was no single-transmission decode. Try for an average 'q3n' decode. call timer('list_avg',0) -! Call top-level routine in q65 module: establish sync and try for a q3 decode. +! 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) @@ -180,7 +180,21 @@ contains endif ! There was no 'q3n' decode. Try for a 'q[012]n' decode. -! call q65_q012a() + 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 q65_dec012() + if(idec.ge.0) go to 100 !Successful decode, we're done + enddo endif 100 decoded=' ' diff --git a/lib/timer_impl.f90 b/lib/timer_impl.f90 index 0c6eadfc6..221db9e0a 100644 --- a/lib/timer_impl.f90 +++ b/lib/timer_impl.f90 @@ -5,8 +5,8 @@ module timer_impl implicit none public :: init_timer, fini_timer -! integer, public :: limtrace=0 - integer, public :: limtrace=10000000 + integer, public :: limtrace=0 +! integer, public :: limtrace=10000000 private From ada5a60124b5a3c9cb0216f31e8be7ea4e53d3c8 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 15 Jan 2021 12:40:38 -0500 Subject: [PATCH 368/426] All types of Q65 message averaging are now implemented and functional. --- lib/q65_decode.f90 | 49 +++++++++++++++-------------------- lib/qra/q65/q65.f90 | 63 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 31 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index f6fc455ef..e33497417 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -61,7 +61,6 @@ contains integer*2 iwave(NMAX) !Raw data real, allocatable :: dd(:) !Raw data integer dat4(13) !Decoded message as 12 6-bit integers - integer apmask1(78),apsymbols1(78) integer dgen(13) logical lclearave,lapcqonly,unpk77_success complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s @@ -165,38 +164,31 @@ contains if(idec.ge.0) go to 100 !Successful decode, we're done enddo ! ipass - if(iand(ndepth,16).eq.16) then + if(iand(ndepth,16).eq.0 .or. navg.lt.2) go to 100 ! There was no single-transmission decode. Try for an average 'q3n' decode. - call timer('list_avg',0) + 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 - nused=navg - go to 100 - endif + 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 + nused=navg + go to 100 + endif ! There was no 'q3n' decode. Try for a 'q[012]n' decode. - 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 q65_dec012() - if(idec.ge.0) go to 100 !Successful decode, we're done - enddo - endif - +! 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) nused=navg + 100 decoded=' ' if(idec.ge.0) then ! idec Meaning @@ -217,6 +209,7 @@ contains else ! Report snr1, even if no decode. nsnr=db(snr1) - 35.0 + if(nsnr.lt.-35) nsnr=-35 idec=-1 call this%callback(nutc,snr1,nsnr,xdt1,f1,decoded, & idec,navg,ntrperiod) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index bfc8b2229..9338ab8b7 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -5,11 +5,13 @@ module q65 integer nsave,nlist,LL0 integer listutc(10) integer apsym0(58),aph10(10) + integer apmask1(78),apsymbols1(78) integer apmask(13),apsymbols(13) integer,dimension(22) :: isync = (/1,9,12,13,15,22,23,26,27,33,35, & 38,46,50,55,60,62,66,69,74,76,85/) integer codewords(63,206) - integer navg,ibwa,ibwb,ncw,nsps,mode_q65,istep,nsmo,lag1,lag2 + integer navg,ibwa,ibwb,ncw,nsps,mode_q65 + integer istep,nsmo,lag1,lag2,npasses,nused integer i0,j0 real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra real sync(85) !sync vector @@ -96,6 +98,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & s1a=0. navg=0 LL0=LL + lclearave=.false. endif dtstep=nsps/(NSTEP*12000.0) !Step size in seconds lag1=-1.0/dtstep @@ -113,7 +116,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & else s1=s1a endif - + i0=nint(nfqso/df) !Target QSO frequency if(i0-64.lt.1 .or. i0-65+LL.gt.iz) go to 900 !Frequency out of range call pctile(s1(i0-64:i0-65+LL,1:jz),LL*jz,40,base) @@ -127,7 +130,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & enddo dat4=0 - if(ncw.gt.0) then + if(ncw.gt.0 .and. iavg.lt.2) then ! Try list decoding via "Deep Likelihood". call timer('ccf_85 ',0) ! Try to synchronize using all 85 symbols @@ -196,6 +199,10 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & enddo close(17) + if(iavg.eq.2) then + call q65_dec_q012(s3,LL,snr2,dat4,idec,decoded) + endif + 900 return end subroutine q65_dec0 @@ -287,6 +294,56 @@ subroutine q65_dec_q3(s1,iz,jz,s3,LL,ipk,jpk,snr2,dat4,idec,decoded) return end subroutine q65_dec_q3 +subroutine q65_dec_q012(s3,LL,snr2,dat4,idec,decoded) + + character*37 decoded + character*78 c78 + integer dat4(13) + real s3(-64:LL-65,63) + logical lapcqonly + + nsubmode=0 + if(mode_q65.eq.2) nsubmode=1 + if(mode_q65.eq.4) nsubmode=2 + if(mode_q65.eq.8) nsubmode=3 + if(mode_q65.eq.16) nsubmode=4 + + baud=12000.0/nsps + iaptype=0 + nQSOprogress=0 !### TEMPORARY ? ### + ncontest=0 + lapcqonly=.false. + + 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 + + do ibw=ibwa,ibwb + b90=1.72**ibw + b90ts=b90/baud + call q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) + if(irc.ge.0) then + snr2=esnodb - db(2500.0/baud) + 3.0 !Empirical adjustment + idec=iaptype + go to 100 + endif + enddo + enddo + +100 return +end subroutine q65_dec_q012 + subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2, & ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) From 1f9eeb52ccc7780e5bf0327d63232ac8641e4bc9 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 15 Jan 2021 14:12:52 -0500 Subject: [PATCH 369/426] Add user option for Auto_Clear_Avg after a decode. --- UnitTests.txt | 17 +++++++------ displayWidgets.txt | 43 +++++++++++++++---------------- lib/q65_decode.f90 | 2 +- widgets/mainwindow.cpp | 57 +++++++++++++++++++++++++----------------- widgets/mainwindow.h | 1 + widgets/mainwindow.ui | 9 +++++++ 6 files changed, 76 insertions(+), 53 deletions(-) diff --git a/UnitTests.txt b/UnitTests.txt index 26a3f119e..84e07a371 100644 --- a/UnitTests.txt +++ b/UnitTests.txt @@ -3,37 +3,38 @@ Data: 30A_K1JT_6m_Ionoscatter (62 files, DT 0.1 to 0.4) Message: "K1JT K9AN R-16" RxFreq: 1000/10 -Commit No AP MyCall BothCalls +Commit No_AP MyCall BothCalls ----------------------------------- ef4787: 3 10 6 14 30 33 - +ada5a6: 3 6 6 10 29 36 Mode: Q65-30A Data: 30A_N0AN_6m_Ionoscatter (69 files, DT=0 to 0.1) Message: "N0AN K1JT -19" RxFreq: 1500/10 -Commit No AP MyCall BothCalls +Commit No_AP MyCall BothCalls ----------------------------------- ef4787: 7 14 16 22 38 40 - +70a348: 38 43 +ada5a6: 10 17 11 23 40 46 Mode: Q65-60B Data: 60B_1296_Troposcatter (75 files) Message: "VK7MO VK7PD QE38" RxFreq: 1000/10 -Commit No AP MyCall BothCalls +Commit No_AP MyCall BothCalls ----------------------------------- ef4787: 1 2 11 23 64 67 - +ada5a6: 1 5 14 28 64 67 Mode: Q65-60D Data: MsgAvg (22 files, simulated fDop = 50 Hz) Message: "K1ABC W9XYZ EN37" RxFreq: 1000/10 -Commit No AP MyCall BothCalls +Commit No_AP MyCall BothCalls ----------------------------------- ef4787: 0 10 21 21 22 22 @@ -44,6 +45,6 @@ Message: "VK7MO VK3WE QF32" RxFreq: 1200/20 Requires nsmo=1 ### -Commit No AP MyCall BothCalls +Commit No_AP MyCall BothCalls ----------------------------------- ef4787: 1 16 24 28 39 39 diff --git a/displayWidgets.txt b/displayWidgets.txt index 607f2daca..8f7378b01 100644 --- a/displayWidgets.txt +++ b/displayWidgets.txt @@ -1,28 +1,28 @@ Here are the "displayWidgets()" strings for WSJT-X modes 1 2 3 - 012345678901234567890123456789012345 + 0123456789012345678901234567890123456 ------------------------------------------------ -JT4 111010000000110000110000000000000000 -JT4/VHF 111110010010110110111100000000000000 -JT9 111010000000111000010000000000001000 -JT9/VHF 111110101000111110010000000000000000 -JT9+JT65 111010000001111000010000000000001000 -JT65 111010000000111000010000000000001000 -JT65/VHF 111110010000110110101100010000000000 -QRA64 111110010110110110000000001000000000 -Q65 111111010110110100111000001100000000 -ISCAT 100111000000000110000000000000000000 -MSK144 101111110100000000010001000000000000 -WSPR 000000000000000001010000000000000000 -FST4 111111000100111000010000000100000011 -FST4W 000000000000000001010000000000000100 -Echo 000000000000000000000010000000000000 -FCal 001101000000000000000000000001000000 -FT8 111010000100111000010000100110001000 -FT8/VHF 111010000100111000010000100110001000 -FT8/Fox 111010000100111000010000000000100000 -FT8/Hound 111010000100111000010000000000110000 +JT4 1110100000001100001100000000000000000 +JT4/VHF 1111100100101101101111000000000000000 +JT9 1110100000001110000100000000000010000 +JT9/VHF 1111101010001111100100000000000000000 +JT9+JT65 1110100000011110000100000000000010000 +JT65 1110100000001110000100000000000010000 +JT65/VHF 1111100100001101101011000100000000000 +QRA64 1111100101101101100000000010000000000 +Q65 1111110101101101001110000011000000001 +ISCAT 1001110000000001100000000000000000000 +MSK144 1011111101000000000100010000000000000 +WSPR 0000000000000000010100000000000000000 +FST4 1111110001001110000100000001000000110 +FST4W 0000000000000000010100000000000001000 +Echo 0000000000000000000000100000000000000 +FCal 0011010000000000000000000000010000000 +FT8 1110100001001110000100001001100010000 +FT8/VHF 1110100001001110000100001001100010000 +FT8/Fox 1110100001001110000100000000001000000 +FT8/Hound 1110100001001110000100000000001100000 ---------------------------------------------- 1 2 3 012345678901234567890123456789012 @@ -66,3 +66,4 @@ Mapping of column numbers to widgets 33. sbTR_FST4W 34. sbF_Low 35. sbF_High +36. AutoClrAvg diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index e33497417..c0d842690 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -205,7 +205,7 @@ contains call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent nsnr=nint(snr2) call this%callback(nutc,snr1,nsnr,xdt1,f1,decoded,idec,nused,ntrperiod) - call q65_clravg !Automatic ClrAvg after a decode + if(iand(ndepth,128).ne.0) call q65_clravg !AutoClrAvg after decode else ! Report snr1, even if no decode. nsnr=db(snr1) - 35.0 diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 5245a55a0..fb2d6784c 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -214,7 +214,7 @@ namespace // grid exact match excluding RR73 QRegularExpression grid_regexp {"\\A(?![Rr]{2}73)[A-Ra-r]{2}[0-9]{2}([A-Xa-x]{2}){0,1}\\z"}; auto quint32_max = std::numeric_limits::max (); - constexpr int N_WIDGETS {36}; + constexpr int N_WIDGETS {37}; constexpr int default_rx_audio_buffer_frames {-1}; // lets Qt decide constexpr int default_tx_audio_buffer_frames {-1}; // lets Qt decide @@ -987,6 +987,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, ui->actionInclude_averaging->setChecked(m_ndepth&16); ui->actionInclude_correlation->setChecked(m_ndepth&32); ui->actionEnable_AP_DXcall->setChecked(m_ndepth&64); + ui->actionAuto_Clear_Avg->setChecked(m_ndepth&128); m_UTCdisk=-1; m_fCPUmskrtd=0.0; @@ -1185,6 +1186,7 @@ void MainWindow::writeSettings() m_settings->setValue("pwrBandTuneMemory",m_pwrBandTuneMemory); m_settings->setValue ("FT8AP", ui->actionEnable_AP_FT8->isChecked ()); m_settings->setValue ("JT65AP", ui->actionEnable_AP_JT65->isChecked ()); + m_settings->setValue ("AutoClearAvg", ui->actionAuto_Clear_Avg->isChecked ()); m_settings->setValue("SplitterState",ui->decodes_splitter->saveState()); m_settings->setValue("Blanker",ui->sbNB->value()); @@ -1297,6 +1299,7 @@ void MainWindow::readSettings() m_pwrBandTuneMemory=m_settings->value("pwrBandTuneMemory").toHash(); ui->actionEnable_AP_FT8->setChecked (m_settings->value ("FT8AP", false).toBool()); ui->actionEnable_AP_JT65->setChecked (m_settings->value ("JT65AP", false).toBool()); + ui->actionAuto_Clear_Avg->setChecked (m_settings->value ("AutoClearAvg", false).toBool()); ui->decodes_splitter->restoreState(m_settings->value("SplitterState").toByteArray()); ui->sbNB->setValue(m_settings->value("Blanker",0).toInt()); { @@ -1896,6 +1899,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog ui->actionInclude_averaging->setChecked(false); ui->actionInclude_correlation->setChecked(false); ui->actionEnable_AP_JT65->setVisible(false); + ui->actionAuto_Clear_Avg->setVisible(false); } if(m_config.special_op_id()!=nContest0) ui->tx1->setEnabled(true); chkFT4(); @@ -3102,6 +3106,7 @@ void MainWindow::decode() //decode() if (!ui->actionInclude_averaging->isVisible ()) depth &= ~16; if (!ui->actionInclude_correlation->isVisible ()) depth &= ~32; if (!ui->actionEnable_AP_DXcall->isVisible ()) depth &= ~64; + if (!ui->actionAuto_Clear_Avg->isVisible()) depth &= ~128; dec_data.params.ndepth=depth; dec_data.params.n2pass=1; if(m_config.twoPass()) dec_data.params.n2pass=2; @@ -5988,6 +5993,7 @@ void MainWindow::displayWidgets(qint64 n) ui->sbF_Low->setVisible(b); } if(i==35) ui->sbF_High->setVisible(b); + if(i==36) ui->actionAuto_Clear_Avg->setVisible (b); j=j>>1; } ui->pbBestSP->setVisible(m_mode=="FT4"); @@ -6021,11 +6027,11 @@ void MainWindow::on_actionFST4_triggered() ui->rh_decodes_title_label->setText(tr ("Rx Frequency")); WSPR_config(false); if(m_config.single_decode()) { -// 012345678901234567890123456789012345 - displayWidgets(nWidgets("111111000100111000010000000100000000")); +// 0123456789012345678901234567890123456 + displayWidgets(nWidgets("1111110001001110000100000001000000000")); m_wideGraph->setSingleDecode(true); } else { - displayWidgets(nWidgets("111011000100111000010000000100000011")); + displayWidgets(nWidgets("1110110001001110000100000001000000110")); m_wideGraph->setSingleDecode(false); ui->sbFtol->setValue(20); } @@ -6061,8 +6067,8 @@ void MainWindow::on_actionFST4W_triggered() m_FFTSize = m_nsps / 2; Q_EMIT FFTSize(m_FFTSize); WSPR_config(true); -// 012345678901234567890123456789012345 - displayWidgets(nWidgets("000000000000000001010000000000000100")); +// 0123456789012345678901234567890123456 + displayWidgets(nWidgets("0000000000000000010100000000000001000")); setup_status_bar(false); ui->band_hopping_group_box->setChecked(false); ui->band_hopping_group_box->setVisible(false); @@ -6110,7 +6116,7 @@ void MainWindow::on_actionFT4_triggered() ui->rh_decodes_title_label->setText(tr ("Rx Frequency")); ui->lh_decodes_title_label->setText(tr ("Band Activity")); ui->lh_decodes_headings_label->setText( " UTC dB DT Freq " + tr ("Message")); - displayWidgets(nWidgets("111010000100111000010000000110001000")); + displayWidgets(nWidgets("1110100001001110000100000001100010000")); ui->txrb2->setEnabled(true); ui->txrb4->setEnabled(true); ui->txrb5->setEnabled(true); @@ -6159,7 +6165,7 @@ void MainWindow::on_actionFT8_triggered() ui->lh_decodes_title_label->setText(tr ("Band Activity")); ui->lh_decodes_headings_label->setText( " UTC dB DT Freq " + tr ("Message")); } - displayWidgets(nWidgets("111010000100111000010000100110001000")); + displayWidgets(nWidgets("1110100001001110000100001001100010000")); ui->txrb2->setEnabled(true); ui->txrb4->setEnabled(true); ui->txrb5->setEnabled(true); @@ -6177,7 +6183,7 @@ void MainWindow::on_actionFT8_triggered() ui->cbAutoSeq->setEnabled(false); ui->tabWidget->setCurrentIndex(1); ui->TxFreqSpinBox->setValue(300); - displayWidgets(nWidgets("111010000100111000010000000000100000")); + displayWidgets(nWidgets("1110100001001110000100000000001000000")); ui->labDXped->setText(tr ("Fox")); on_fox_log_action_triggered(); } @@ -6187,7 +6193,7 @@ void MainWindow::on_actionFT8_triggered() ui->cbAutoSeq->setEnabled(false); ui->tabWidget->setCurrentIndex(0); ui->cbHoldTxFreq->setChecked(true); - displayWidgets(nWidgets("111010000100110000010000000000110000")); + displayWidgets(nWidgets("1110100001001100000100000000001100000")); ui->labDXped->setText(tr ("Hound")); ui->txrb1->setChecked(true); ui->txrb2->setEnabled(false); @@ -6262,9 +6268,9 @@ void MainWindow::on_actionJT4_triggered() ui->sbSubmode->setValue(0); } if(bVHF) { - displayWidgets(nWidgets("111110010010110110111100000000000000")); + displayWidgets(nWidgets("1111100100101101101111000000000000000")); } else { - displayWidgets(nWidgets("111010000000110000110000000000000000")); + displayWidgets(nWidgets("1110100000001100001100000000000000000")); } fast_config(false); statusChanged(); @@ -6321,9 +6327,9 @@ void MainWindow::on_actionJT9_triggered() ui->lh_decodes_title_label->setText(tr ("Band Activity")); ui->rh_decodes_title_label->setText(tr ("Rx Frequency")); if(bVHF) { - displayWidgets(nWidgets("111110101000111110010000000000000000")); + displayWidgets(nWidgets("1111101010001111100100000000000000000")); } else { - displayWidgets(nWidgets("111010000000111000010000000000001000")); + displayWidgets(nWidgets("1110100000001110000100000000000010000")); } fast_config(m_bFastMode); ui->cbAutoSeq->setVisible(m_bFast9); @@ -6369,9 +6375,9 @@ void MainWindow::on_actionJT65_triggered() ui->rh_decodes_title_label->setText(tr ("Rx Frequency")); } if(bVHF) { - displayWidgets(nWidgets("111110010000110110101100010000000000")); + displayWidgets(nWidgets("1111100100001101101011000100000000000")); } else { - displayWidgets(nWidgets("111010000000111000010000000000001000")); + displayWidgets(nWidgets("1110100000001110000100000000000010000")); } fast_config(false); if(ui->cbShMsgs->isChecked()) { @@ -6409,8 +6415,8 @@ void MainWindow::on_actionQ65_triggered() m_wideGraph->setRxFreq(ui->RxFreqSpinBox->value()); m_wideGraph->setTxFreq(ui->TxFreqSpinBox->value()); switch_mode (Modes::Q65); -// 012345678901234567890123456789012345 - displayWidgets(nWidgets("111111010110110100111000000100000000")); +// 0123456789012345678901234567890123456 + displayWidgets(nWidgets("1111110101101101001110000001000000001")); ui->lh_decodes_title_label->setText(tr ("Single-Period Decodes")); ui->rh_decodes_title_label->setText(tr ("Average Decodes")); ui->lh_decodes_headings_label->setText("UTC dB DT Freq " + tr ("Message")); @@ -6449,7 +6455,7 @@ void MainWindow::on_actionISCAT_triggered() ui->sbSubmode->setMaximum(1); if(m_nSubMode==0) ui->TxFreqSpinBox->setValue(1012); if(m_nSubMode==1) ui->TxFreqSpinBox->setValue(560); - displayWidgets(nWidgets("100111000000000110000000000000000000")); + displayWidgets(nWidgets("1001110000000001100000000000000000000")); fast_config(true); statusChanged (); } @@ -6510,7 +6516,7 @@ void MainWindow::on_actionMSK144_triggered() ui->rptSpinBox->setValue(0); ui->rptSpinBox->setSingleStep(1); ui->sbFtol->values ({20, 50, 100, 200}); - displayWidgets(nWidgets("101111110100000000010001000010000000")); + displayWidgets(nWidgets("1011111101000000000100010000100000000")); fast_config(m_bFastMode); statusChanged(); @@ -6550,7 +6556,7 @@ void MainWindow::on_actionWSPR_triggered() m_bFastMode=false; m_bFast9=false; ui->TxFreqSpinBox->setValue(ui->WSPRfreqSpinBox->value()); - displayWidgets(nWidgets("000000000000000001010000000000000000")); + displayWidgets(nWidgets("0000000000000000010100000000000000000")); fast_config(false); statusChanged(); } @@ -6583,7 +6589,7 @@ void MainWindow::on_actionEcho_triggered() m_bFast9=false; WSPR_config(true); ui->lh_decodes_headings_label->setText(" UTC N Level Sig DF Width Q"); - displayWidgets(nWidgets("000000000000000000000010000000000000")); + displayWidgets(nWidgets("0000000000000000000000100000000000000")); fast_config(false); statusChanged(); } @@ -6609,7 +6615,7 @@ void MainWindow::on_actionFreqCal_triggered() // 18:15:47 0 1 1500 1550.349 0.100 3.5 10.2 ui->lh_decodes_headings_label->setText(" UTC Freq CAL Offset fMeas DF Level S/N"); ui->measure_check_box->setChecked (false); - displayWidgets(nWidgets("001101000000000000000000000001000000")); + displayWidgets(nWidgets("0011010000000000000000000000010000000")); statusChanged(); } @@ -6776,6 +6782,11 @@ void MainWindow::on_actionEnable_AP_DXcall_toggled (bool checked) m_ndepth ^= (-checked ^ m_ndepth) & 0x00000040; } +void MainWindow::on_actionAuto_Clear_Avg_toggled (bool checked) +{ + m_ndepth ^= (-checked ^ m_ndepth) & 0x00000080; +} + void MainWindow::on_actionErase_ALL_TXT_triggered() //Erase ALL.TXT { int ret = MessageBox::query_message (this, tr ("Confirm Erase"), diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index 51898842c..d71a400f4 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -264,6 +264,7 @@ private slots: void on_actionInclude_averaging_toggled (bool); void on_actionInclude_correlation_toggled (bool); void on_actionEnable_AP_DXcall_toggled (bool); + void on_actionAuto_Clear_Avg_toggled (bool); void VHF_features_enabled(bool b); void on_sbSubmode_valueChanged(int n); void on_cbShMsgs_toggled(bool b); diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index d030cd0f0..9ea45933d 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -2853,6 +2853,7 @@ Double-click to reset to the standard 73 message + @@ -3396,6 +3397,14 @@ Double-click to reset to the standard 73 message Quick-Start Guide to Q65 + + + true + + + Auto Clear Avg after decode + + From 88c94efde298d517f0a1d3f30df468266fae4586 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 15 Jan 2021 14:16:50 -0500 Subject: [PATCH 370/426] Save updated summary of Q65 Unit Tests. --- UnitTests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnitTests.txt b/UnitTests.txt index 84e07a371..cac20ff92 100644 --- a/UnitTests.txt +++ b/UnitTests.txt @@ -37,7 +37,7 @@ RxFreq: 1000/10 Commit No_AP MyCall BothCalls ----------------------------------- ef4787: 0 10 21 21 22 22 - +ada5a6 0 11 21 21 22 22 Mode: Q65-60D Data: Nil Average (40 files, simulated fDop = 0? Hz ) From b343deb8cd8538adec431ef497c0a50d143c4361 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 16 Jan 2021 10:21:34 -0500 Subject: [PATCH 371/426] Correct a problem with reporting DT and f0 for Q65 'q3' decodes. --- lib/qra/q65/q65.f90 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 9338ab8b7..3383db63e 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -154,7 +154,11 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & endif ! Get 2d CCF and ccf2 using sync symbols only - call q65_ccf_22(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,ccf,ccf2) + call q65_ccf_22(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0a,xdta,ccf,ccf2) + if(idec.lt.0) then + f0=f0a + xdt=xdta + endif ! Estimate rms on ccf baseline sq=0. From 397c31a62dd7459bc08de453d1bf22fb0ad8adf9 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 16 Jan 2021 12:21:13 -0500 Subject: [PATCH 372/426] Extend red/orange sync curves to full width. Display navg on status bar. --- lib/decoder.f90 | 10 ++++++---- lib/q65_decode.f90 | 9 ++++++--- lib/qra/q65/q65.f90 | 8 +++++--- widgets/mainwindow.cpp | 10 ++++++++-- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 582865a4e..e2c762f52 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -61,6 +61,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) type(counting_fst4_decoder) :: my_fst4 type(counting_q65_decoder) :: my_q65 + nused=0 rms=sqrt(dot_product(float(id2(1:180000)), & float(id2(1:180000)))/180000.0) if(rms.lt.3.0) go to 800 @@ -205,8 +206,9 @@ subroutine multimode_decoder(ss,id2,params,nfsample) call timer('dec_q65 ',0) call my_q65%decode(q65_decoded,id2,params%nutc,params%ntr, & params%nsubmode,params%nfqso,params%ntol,params%ndepth, & - logical(params%nclearave),params%emedelay,mycall,hiscall, & - hisgrid,params%nQSOProgress,ncontest,logical(params%lapcqonly)) + params%nfa,params%nfb,logical(params%nclearave), & + params%emedelay,mycall,hiscall,hisgrid,params%nQSOProgress, & + ncontest,logical(params%lapcqonly),navg0) call timer('dec_q65 ',1) go to 800 endif @@ -363,8 +365,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample) endif if(params%nmode.ne.8 .or. params%nzhsym.eq.50 .or. & .not.params%ndiskdat) then - write(*,1010) nsynced,ndecoded -1010 format('',2i4) + write(*,1010) nsynced,ndecoded,navg0 +1010 format('',3i4) call flush(6) endif close(13) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index c0d842690..0ac662ace 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -27,8 +27,8 @@ module q65_decode contains subroutine decode(this,callback,iwave,nutc,ntrperiod,nsubmode,nfqso, & - ntol,ndepth,lclearave,emedelay,mycall,hiscall,hisgrid,nQSOprogress, & - ncontest,lapcqonly) + ntol,ndepth,nfa0,nfb0,lclearave,emedelay,mycall,hiscall,hisgrid, & + nQSOprogress,ncontest,lapcqonly,navg0) ! Top-level routine that organizes the decoding of Q65 signals ! Input: iwave Raw data, i*2 @@ -67,6 +67,8 @@ contains complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s ! Start by setting some parameters and allocating storage for large arrays + nfa=nfa0 + nfb=nfb0 idec=-1 mode_q65=2**nsubmode npts=ntrperiod*12000 @@ -212,8 +214,9 @@ contains if(nsnr.lt.-35) nsnr=-35 idec=-1 call this%callback(nutc,snr1,nsnr,xdt1,f1,decoded, & - idec,navg,ntrperiod) + idec,0,ntrperiod) endif + navg0=navg return end subroutine decode diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 3383db63e..0da0c336a 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -10,7 +10,7 @@ module q65 integer,dimension(22) :: isync = (/1,9,12,13,15,22,23,26,27,33,35, & 38,46,50,55,60,62,66,69,74,76,85/) integer codewords(63,206) - integer navg,ibwa,ibwb,ncw,nsps,mode_q65 + integer navg,ibwa,ibwb,ncw,nsps,mode_q65,nfa,nfb integer istep,nsmo,lag1,lag2,npasses,nused integer i0,j0 real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra @@ -198,8 +198,10 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & ! Write data for the red and orange sync curves. do i=-ia2,ia2 freq=nfqso + i*df - write(17,1100) freq,ccf1(i),xdt,ccf2(i) -1100 format(4f10.3) + if(freq.ge.float(nfa) .and. freq.le.float(nfb)) then + write(17,1100) freq,ccf1(i),xdt,ccf2(i) +1100 format(4f10.3) + endif enddo close(17) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index fb2d6784c..a087a2ef5 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3373,12 +3373,18 @@ void MainWindow::readFromStdout() //readFromStdout if(line_read.indexOf("") >= 0) { m_bDecoded = line_read.mid(20).trimmed().toInt() > 0; - if(m_nDecodes==0) ndecodes_label.setText("0"); + int n=line_read.trimmed().size(); + int navg=line_read.trimmed().mid(n-2).toInt(); + if(m_mode=="Q65") { + ndecodes_label.setText(QString::number(navg)); + } else { + if(m_nDecodes==0) ndecodes_label.setText("0"); + } decodeDone (); return; } else { m_nDecodes+=1; - ndecodes_label.setText(QString::number(m_nDecodes)); + if(m_mode!="Q65") ndecodes_label.setText(QString::number(m_nDecodes)); if(m_mode=="JT4" or m_mode=="JT65" or m_mode=="Q65") { //### Do something about Q65 here ? ### int nf=line_read.indexOf("f"); From 20aa3a8e231b70b198538ac3d34d2c43e2983af8 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 16 Jan 2021 12:24:42 -0500 Subject: [PATCH 373/426] Fix ia2 definition. --- lib/qra/q65/q65.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 0da0c336a..7cfb74334 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -77,7 +77,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & jz=(txt+1.0)*12000.0/istep !Number of symbol/NSTEP bins if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher ia=ntol/df - ia2=max(ia,10*mode_q65,nint(100.0/df)) + ia2=max(ia,10*mode_q65,nint(3000.0/df)) nsmo=int(0.7*mode_q65*mode_q65) if(nsmo.lt.1) nsmo=1 if(first) then !Generate the sync vector From a53b3dc6975980092f7bc4476ae4363b6ea4b8ff Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 16 Jan 2021 14:18:15 -0500 Subject: [PATCH 374/426] Computing 2d ccf over all freqs takes too long and too much memory. There's a better way! Remove the 'nsmo' smoothing: it's not helpful. --- lib/qra/q65/q65.f90 | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 7cfb74334..a6ca34cc4 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -11,7 +11,7 @@ module q65 38,46,50,55,60,62,66,69,74,76,85/) integer codewords(63,206) integer navg,ibwa,ibwb,ncw,nsps,mode_q65,nfa,nfb - integer istep,nsmo,lag1,lag2,npasses,nused + integer istep,lag1,lag2,npasses,nused integer i0,j0 real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra real sync(85) !sync vector @@ -77,9 +77,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & jz=(txt+1.0)*12000.0/istep !Number of symbol/NSTEP bins if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher ia=ntol/df - ia2=max(ia,10*mode_q65,nint(3000.0/df)) - nsmo=int(0.7*mode_q65*mode_q65) - if(nsmo.lt.1) nsmo=1 + ia2=max(ia,10*mode_q65,nint(100.0/df)) if(first) then !Generate the sync vector sync=-22.0/63.0 !Sync tone OFF do k=1,22 @@ -244,10 +242,6 @@ subroutine q65_symspec(iwave,nmax,iz,jz,s1) do i=1,iz s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 enddo -! For large Doppler spreads, should we smooth the spectra here? - do i=1,nsmo - call smo121(s1(1:iz,j),iz) - enddo enddo s1a=s1a+s1 navg=navg+1 From 2aee8d9d621eb1b48927f35b0c998674c8c996d3 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 17 Jan 2021 14:31:56 -0500 Subject: [PATCH 375/426] Resize s1a when required. --- lib/qra/q65/q65.f90 | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index a6ca34cc4..00ff325e1 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -2,7 +2,7 @@ module q65 parameter (NSTEP=8) !Time bins per symbol, in s1() and s1a() parameter (PLOG_MIN=-240.0) !List decoding threshold - integer nsave,nlist,LL0 + integer nsave,nlist,LL0,iz0,jz0 integer listutc(10) integer apsym0(58),aph10(10) integer apmask1(78),apsymbols1(78) @@ -90,12 +90,14 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & allocate(ccf(-ia2:ia2,-53:214)) allocate(ccf1(-ia2:ia2)) allocate(ccf2(-ia2:ia2)) - if(LL.ne.LL0 .or. lclearave) then + if(LL.ne.LL0 .or. iz.ne.iz0 .or. jz.ne.jz0 .or. lclearave) then if(allocated(s1a)) deallocate(s1a) allocate(s1a(iz,jz)) s1a=0. navg=0 LL0=LL + iz0=iz + jz0=jz lclearave=.false. endif dtstep=nsps/(NSTEP*12000.0) !Step size in seconds @@ -114,7 +116,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & else s1=s1a endif - + i0=nint(nfqso/df) !Target QSO frequency if(i0-64.lt.1 .or. i0-65+LL.gt.iz) go to 900 !Frequency out of range call pctile(s1(i0-64:i0-65+LL,1:jz),LL*jz,40,base) @@ -206,7 +208,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & if(iavg.eq.2) then call q65_dec_q012(s3,LL,snr2,dat4,idec,decoded) endif - + 900 return end subroutine q65_dec0 @@ -461,7 +463,7 @@ end subroutine q65_dec1 subroutine q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) use packjt77 - real s3(1,1) !Silence compiler warning that wants to see a 2D array + real s3(iz0,jz0) !Silence compiler warning that wants to see a 2D array real s3prob(0:63,63) !Symbol-value probabilities integer dat4(13) character c77*77,decoded*37 From a9bce0dbd3c3a9ef605f2efb277fb47ca3455209 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 17 Jan 2021 19:50:58 -0500 Subject: [PATCH 376/426] Don't allow a decode attempt with previous data if TRperiod has increased. --- lib/decoder.f90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index e2c762f52..d81128040 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -51,7 +51,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) character(len=12) :: mycall, hiscall character(len=6) :: mygrid, hisgrid character*60 line - data ndec8/0/ + data ndec8/0/,ntr0/-1/ save type(counting_jt4_decoder) :: my_jt4 type(counting_jt65_decoder) :: my_jt65 @@ -61,7 +61,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample) type(counting_fst4_decoder) :: my_fst4 type(counting_q65_decoder) :: my_q65 - nused=0 + if(params%nagain .and. params%ntr.gt.ntr0) go to 800 + ntr0=params%ntr rms=sqrt(dot_product(float(id2(1:180000)), & float(id2(1:180000)))/180000.0) if(rms.lt.3.0) go to 800 From 6d220d46d24ca917b8dad56079b258a92e9afb13 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 18 Jan 2021 09:25:58 -0500 Subject: [PATCH 377/426] Do not attempt decoding after TRp is increased and Decode button clicked. --- lib/decoder.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index d81128040..2e72fc748 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -61,7 +61,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) type(counting_fst4_decoder) :: my_fst4 type(counting_q65_decoder) :: my_q65 - if(params%nagain .and. params%ntr.gt.ntr0) go to 800 + if(.not.params%newdat .and. params%ntr.gt.ntr0) go to 800 ntr0=params%ntr rms=sqrt(dot_product(float(id2(1:180000)), & float(id2(1:180000)))/180000.0) From baa029f4baa9be8fe6f25898bf7bb40946e43fc4 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 18 Jan 2021 10:18:38 -0500 Subject: [PATCH 378/426] Add some descriptive documentation to the q65.f90 module. --- lib/qra/q65/q65.f90 | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 00ff325e1..4c4773301 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -23,7 +23,10 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & emedelay,xdt,f0,snr1,width,dat4,snr2,idec) ! Top-level routine in q65 module - +! - Compute symbol spectra +! - Attempt sync and q3 decode using all 85 symbols +! - If that fails, try sync with 22 symbols and standard q[0124] decode + ! Input: iavg 0 for single-period decode, 1 for average ! iwave(0:nmax-1) Raw data ! ntrperiod T/R sequence length (s) @@ -41,8 +44,8 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & ! idec Flag for decing results ! -1 No decode ! 0 No AP -! 1 "CQ ? ?" -! 2 "Mycall ? ?" +! 1 "CQ ? ?" +! 2 "Mycall ? ?" ! 3 "MyCall HisCall ?" use packjt77 @@ -211,9 +214,11 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & 900 return end subroutine q65_dec0 - + subroutine q65_clravg +! Clear the averaging array to start a new average. + if(allocated(s1a)) s1a=0. navg=0 @@ -222,6 +227,8 @@ end subroutine q65_clravg subroutine q65_symspec(iwave,nmax,iz,jz,s1) +! Compute symbol spectra with NSTEP time-steps per symbol. + integer*2 iwave(0:nmax-1) !Raw data real s1(iz,jz) complex, allocatable :: c0(:) !Complex spectrum of symbol @@ -253,6 +260,8 @@ end subroutine q65_symspec subroutine q65_dec_q3(s1,iz,jz,s3,LL,ipk,jpk,snr2,dat4,idec,decoded) +! Copy synchronized symbol spectra from s1 into s3, then attempt a q3 decode. + character*37 decoded integer dat4(13) real s1(iz,jz) @@ -298,6 +307,8 @@ end subroutine q65_dec_q3 subroutine q65_dec_q012(s3,LL,snr2,dat4,idec,decoded) +! Do separate passes attempting q0, q1, q2 decodes. + character*37 decoded character*78 c78 integer dat4(13) @@ -346,9 +357,11 @@ subroutine q65_dec_q012(s3,LL,snr2,dat4,idec,decoded) 100 return end subroutine q65_dec_q012 -subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2, & - ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) +subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) +! Attempt synchronization using all 85 symbols, in advance of an +! attempt at q3 decoding. Return ccf1 for the "red sync curve". + real s1(iz,jz) real ccf(-ia2:ia2,-53:214) real ccf1(-ia2:ia2) @@ -371,6 +384,7 @@ subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2, & itone(j)=codewords(k,imsg) endif enddo + ! Compute 2D ccf using all 85 symbols in the list message ccf=0. iia=200.0/df @@ -405,6 +419,9 @@ end subroutine q65_ccf_85 subroutine q65_ccf_22(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,ccf,ccf2) +! Attempt synchronization using only the 22 sync symbols. Return ccf2 +! for the "orange sync curve". + real s1(iz,jz) real ccf(-ia2:ia2,-53:214) real ccf2(-ia2:ia2) @@ -437,6 +454,8 @@ end subroutine q65_ccf_22 subroutine q65_dec1(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) +! Attmpt a full-AP list decode. + use packjt77 real s3(1,1) !Silence compiler warning that wants to see a 2D array real s3prob(0:63,63) !Symbol-value probabilities @@ -462,6 +481,8 @@ end subroutine q65_dec1 subroutine q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) +! Attempt a q0, q1, or q2 decode using spcified AP information. + use packjt77 real s3(iz0,jz0) !Silence compiler warning that wants to see a 2D array real s3prob(0:63,63) !Symbol-value probabilities @@ -485,7 +506,7 @@ end subroutine q65_dec2 subroutine q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) -! Copy from s1 or s1a into s3 +! Copy synchronized symbol energies from s1 (or s1a) into s3. real s1(iz,jz) real s3(-64:LL-65,63) From 86b0affc56b9e5209bd54f149ba2ef75672fbb69 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Mon, 18 Jan 2021 13:47:54 -0600 Subject: [PATCH 379/426] FST4/W: Disable envelope shaping at start and end of transmission when environment variable FST4_NOSHAPING=1. Works for fst4sim too. --- lib/fst4/fst4sim.f90 | 2 +- lib/fst4/gen_fst4wave.f90 | 156 +++++++++++++++++++------------------- 2 files changed, 80 insertions(+), 78 deletions(-) diff --git a/lib/fst4/fst4sim.f90 b/lib/fst4/fst4sim.f90 index 83ac5baed..28caf2fe0 100644 --- a/lib/fst4/fst4sim.f90 +++ b/lib/fst4/fst4sim.f90 @@ -120,7 +120,7 @@ program fst4sim if(fspread.gt.0.0 .or. delay.ne.0.0) call watterson(c,nwave,NZ,fs,delay,fspread) if(fspread.lt.0.0) call lorentzian_fading(c,nwave,fs,-fspread) c=sig*c - wave=real(c) + wave=aimag(c) if(snrdb.lt.90) then do i=1,nmax !Add gaussian noise at specified SNR xnoise=gran() diff --git a/lib/fst4/gen_fst4wave.f90 b/lib/fst4/gen_fst4wave.f90 index ead00709b..74a9b34c9 100644 --- a/lib/fst4/gen_fst4wave.f90 +++ b/lib/fst4/gen_fst4wave.f90 @@ -1,91 +1,93 @@ subroutine gen_fst4wave(itone,nsym,nsps,nwave,fsample,hmod,f0, & - icmplx,cwave,wave) + icmplx,cwave,wave) - parameter(NTAB=65536) - real wave(nwave) - complex cwave(nwave),ctab(0:NTAB-1) - real, allocatable, save :: pulse(:) - real, allocatable :: dphi(:) - integer hmod - integer itone(nsym) - logical first - data first/.true./ - data nsps0/-99/ - save first,twopi,dt,tsym,nsps0,ctab + use prog_args + parameter(NTAB=65536) + real wave(nwave) + complex cwave(nwave),ctab(0:NTAB-1) + character(len=1) :: cvalue + real, allocatable, save :: pulse(:) + real, allocatable :: dphi(:) + integer hmod + integer itone(nsym) + logical first, lshape + data first/.true./ + data nsps0/-99/ + data lshape/.true./ + save first,twopi,dt,tsym,nsps0,ctab,lshape - if(first) then - twopi=8.0*atan(1.0) - do i=0,NTAB-1 - phi=i*twopi/NTAB - ctab(i)=cmplx(cos(phi),sin(phi)) - enddo - endif + if(first) then + twopi=8.0*atan(1.0) + do i=0,NTAB-1 + phi=i*twopi/NTAB + ctab(i)=cmplx(cos(phi),sin(phi)) + enddo + call get_environment_variable("FST4_NOSHAPING",cvalue,nlen) + if(nlen.eq.1 .and. cvalue.eq."1") lshape=.false. + endif - if(first.or.nsps.ne.nsps0) then - if(allocated(pulse)) deallocate(pulse) - allocate(pulse(1:3*nsps)) - dt=1.0/fsample - tsym=nsps/fsample + if(first.or.nsps.ne.nsps0) then + if(allocated(pulse)) deallocate(pulse) + allocate(pulse(1:3*nsps)) + dt=1.0/fsample + tsym=nsps/fsample ! Compute the smoothed frequency-deviation pulse - do i=1,3*nsps - tt=(i-1.5*nsps)/real(nsps) - pulse(i)=gfsk_pulse(2.0,tt) - enddo - first=.false. - nsps0=nsps - endif + do i=1,3*nsps + tt=(i-1.5*nsps)/real(nsps) + pulse(i)=gfsk_pulse(2.0,tt) + enddo + first=.false. + nsps0=nsps + endif ! Compute the smoothed frequency waveform. ! Length = (nsym+2)*nsps samples, zero-padded - allocate( dphi(0:(nsym+2)*nsps-1) ) - 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 + allocate( dphi(0:(nsym+2)*nsps-1) ) + 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-1.5*hmod/tsym)*dt !Shift frequency up by f0 - if(icmplx.eq.0) wave=0. - if(icmplx.eq.1) cwave=0. - k=0 - do j=0,(nsym+2)*nsps-1 - k=k+1 - i=phi*float(NTAB)/twopi - i=iand(i,NTAB-1) - if(icmplx.eq.0) then - wave(k)=real(ctab(i)) - else - cwave(k)=ctab(i) - endif - phi=phi+dphi(j) - if(phi.gt.twopi) phi=phi-twopi - enddo + phi=0.0 + dphi = dphi + twopi*(f0-1.5*hmod/tsym)*dt !Shift frequency up by f0 + if(icmplx.eq.0) wave=0. + if(icmplx.eq.1) cwave=0. + k=0 + do j=nsps,(nsym+1)*nsps-1 + k=k+1 + i=phi*float(NTAB)/twopi + i=iand(i,NTAB-1) + if(icmplx.eq.0) then + wave(k)=aimag(ctab(i)) + else + cwave(k)=ctab(i) + endif + phi=phi+dphi(j) + if(phi.gt.twopi) phi=phi-twopi + enddo ! Compute the ramp-up and ramp-down symbols - kshift=nsps - if(icmplx.eq.0) then - wave(1:nsps)=0.0 - wave(nsps+1:nsps+nsps/4)=wave(nsps+1:nsps+nsps/4) * & - (1.0-cos(twopi*(/(i,i=0,nsps/4-1)/)/real(nsps/2)))/2.0 - k1=nsym*nsps+3*nsps/4+1 - wave((nsym+1)*nsps+1:)=0.0 - wave(k1:k1+nsps/4)=wave(k1:k1+nsps/4) * & - (1.0+cos(twopi*(/(i,i=0,nsps/4)/)/real(nsps/2)))/2.0 - wave=cshift(wave,kshift) - else - cwave(1:nsps)=0.0 - cwave(nsps+1:nsps+nsps/4)=cwave(nsps+1:nsps+nsps/4) * & - (1.0-cos(twopi*(/(i,i=0,nsps/4-1)/)/real(nsps/2)))/2.0 - k1=nsym*nsps+3*nsps/4+1 - cwave((nsym+1)*nsps+1:)=0.0 - cwave(k1:k1+nsps/4)=cwave(k1:k1+nsps/4) * & - (1.0+cos(twopi*(/(i,i=0,nsps/4)/)/real(nsps/2)))/2.0 - cwave=cshift(cwave,kshift) - endif + if(icmplx.eq.0) then + if(lshape) then + wave(1:nsps/4)=wave(1:nsps/4) * & + (1.0-cos(twopi*(/(i,i=0,nsps/4-1)/)/real(nsps/2)))/2.0 + k1=(nsym-1)*nsps+3*nsps/4+1 + wave(k1:k1+nsps/4)=wave(k1:k1+nsps/4) * & + (1.0+cos(twopi*(/(i,i=0,nsps/4)/)/real(nsps/2)))/2.0 + endif + else + if(lshape) then + cwave(1:nsps/4)=cwave(1:nsps/4) * & + (1.0-cos(twopi*(/(i,i=0,nsps/4-1)/)/real(nsps/2)))/2.0 + k1=(nsym-1)*nsps+3*nsps/4+1 + cwave(k1:k1+nsps/4)=cwave(k1:k1+nsps/4) * & + (1.0+cos(twopi*(/(i,i=0,nsps/4)/)/real(nsps/2)))/2.0 + endif + endif - return + return end subroutine gen_fst4wave From d815eb759204d9012c23a1abf5aa42ea7a054e5b Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 19 Jan 2021 09:24:30 -0500 Subject: [PATCH 380/426] Replace the 'nsmo' smoothing: it is indeed useful, sometimes. (Can we have it both ways?) --- lib/qra/q65/q65.f90 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 4c4773301..68d550c1e 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -11,7 +11,7 @@ module q65 38,46,50,55,60,62,66,69,74,76,85/) integer codewords(63,206) integer navg,ibwa,ibwb,ncw,nsps,mode_q65,nfa,nfb - integer istep,lag1,lag2,npasses,nused + integer istep,nsmo,lag1,lag2,npasses,nused integer i0,j0 real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra real sync(85) !sync vector @@ -81,6 +81,8 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher ia=ntol/df ia2=max(ia,10*mode_q65,nint(100.0/df)) + nsmo=int(0.7*mode_q65*mode_q65) + if(nsmo.lt.1) nsmo=1 if(first) then !Generate the sync vector sync=-22.0/63.0 !Sync tone OFF do k=1,22 @@ -251,6 +253,10 @@ subroutine q65_symspec(iwave,nmax,iz,jz,s1) do i=1,iz s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 enddo +! For large Doppler spreads, should we smooth the spectra here? + do i=1,nsmo + call smo121(s1(1:iz,j),iz) + enddo enddo s1a=s1a+s1 navg=navg+1 From 39024253d2ad03392623914f42f7667858c94bb4 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 19 Jan 2021 14:11:21 -0500 Subject: [PATCH 381/426] Fix the oddball values of DT and f0 sometimes being displayed. --- lib/decoder.f90 | 1 + lib/q65_decode.f90 | 26 ++++++++++++++++++-------- lib/qra/q65/q65.f90 | 7 ++++--- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 2e72fc748..db66fe4ac 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -211,6 +211,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) params%emedelay,mycall,hiscall,hisgrid,params%nQSOProgress, & ncontest,logical(params%lapcqonly),navg0) call timer('dec_q65 ',1) + close(17) go to 800 endif diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 0ac662ace..dd6fd960b 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -118,14 +118,14 @@ contains call timer('q65_dec0',1) if(idec.ge.0) then - xdt1=xdt !We have a list-decode result - f1=f0 + dtdec=xdt !We have a list-decode result + f0dec=f0 go to 100 endif if(snr1.lt.2.8) then - xdt1=0. !No reliable sync, abandon decoding attempt - f1=0. + dtdec=0. !No reliable sync, abandon decoding attempt + f0dec=0. go to 100 endif @@ -163,7 +163,11 @@ contains xdt,f0,iaptype,xdt1,f1,snr2,dat4,idec) ! idec=-1 !### TEMPORARY ### call timer('q65loops',1) - if(idec.ge.0) go to 100 !Successful decode, we're done + 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.lt.2) go to 100 @@ -176,6 +180,8 @@ contains 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 go to 100 endif @@ -189,7 +195,11 @@ contains 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) nused=navg + if(idec.ge.0) then + dtdec=xdt !We have a q[012]n result + f0dec=f0 + nused=navg + endif 100 decoded=' ' if(idec.ge.0) then @@ -206,14 +216,14 @@ contains 1000 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent nsnr=nint(snr2) - call this%callback(nutc,snr1,nsnr,xdt1,f1,decoded,idec,nused,ntrperiod) + call this%callback(nutc,snr1,nsnr,dtdec,f0dec,decoded,idec,nused,ntrperiod) if(iand(ndepth,128).ne.0) call q65_clravg !AutoClrAvg after decode else ! Report snr1, even if no decode. nsnr=db(snr1) - 35.0 if(nsnr.lt.-35) nsnr=-35 idec=-1 - call this%callback(nutc,snr1,nsnr,xdt1,f1,decoded, & + call this%callback(nutc,snr1,nsnr,xdt,f0,decoded, & idec,0,ntrperiod) endif navg0=navg diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 68d550c1e..30409e8d1 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -1,7 +1,7 @@ module q65 parameter (NSTEP=8) !Time bins per symbol, in s1() and s1a() - parameter (PLOG_MIN=-240.0) !List decoding threshold + parameter (PLOG_MIN=-242.0) !List decoding threshold integer nsave,nlist,LL0,iz0,jz0 integer listutc(10) integer apsym0(58),aph10(10) @@ -15,7 +15,7 @@ module q65 integer i0,j0 real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra real sync(85) !sync vector - real df,dtstep + real df,dtstep,dtdec,f0dec contains @@ -208,7 +208,8 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & 1100 format(4f10.3) endif enddo - close(17) + rewind 17 + if(iavg.eq.2) then call q65_dec_q012(s3,LL,snr2,dat4,idec,decoded) From 0cc67bbc59e4bbf6709543f1911b77c89814742f Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 19 Jan 2021 15:30:17 -0500 Subject: [PATCH 382/426] Separate accumulations of odd/even sequences in s1a. Still need to fix navg ==> navg(0:1). --- lib/q65_decode.f90 | 17 ++++++++++++++--- lib/qra/q65/q65.f90 | 14 +++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index dd6fd960b..2275b2bfd 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -8,7 +8,7 @@ module q65_decode abstract interface subroutine q65_decode_callback (this,nutc,snr1,nsnr,dt,freq, & - decoded,idec,navg,ntrperiod) + decoded,idec,nused,ntrperiod) import q65_decoder implicit none class(q65_decoder), intent(inout) :: this @@ -19,7 +19,7 @@ module q65_decode real, intent(in) :: freq character(len=37), intent(in) :: decoded integer, intent(in) :: idec - integer, intent(in) :: navg + integer, intent(in) :: nused integer, intent(in) :: ntrperiod end subroutine q65_decode_callback end interface @@ -58,6 +58,7 @@ contains character*37 decoded !Decoded message character*77 c77 character*78 c78 + character*6 cutc integer*2 iwave(NMAX) !Raw data real, allocatable :: dd(:) !Raw data integer dat4(13) !Decoded message as 12 6-bit integers @@ -74,6 +75,15 @@ contains npts=ntrperiod*12000 nfft1=ntrperiod*12000 nfft2=ntrperiod*6000 + +! Determine the T/R sequence: iseq=0 (even), or iseq=1 (odd) + n=nutc + if(ntrperiod.ge.60) 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)) @@ -216,7 +226,8 @@ contains 1000 format(12b6.6,b5.5) call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent nsnr=nint(snr2) - call this%callback(nutc,snr1,nsnr,dtdec,f0dec,decoded,idec,nused,ntrperiod) + call this%callback(nutc,snr1,nsnr,dtdec,f0dec,decoded, & + idec,nused,ntrperiod) if(iand(ndepth,128).ne.0) call q65_clravg !AutoClrAvg after decode else ! Report snr1, even if no decode. diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 30409e8d1..e37ad852d 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -1,6 +1,6 @@ module q65 - parameter (NSTEP=8) !Time bins per symbol, in s1() and s1a() + parameter (NSTEP=8) !Number of time bins per symbol in s1, s1a, s1b parameter (PLOG_MIN=-242.0) !List decoding threshold integer nsave,nlist,LL0,iz0,jz0 integer listutc(10) @@ -11,10 +11,10 @@ module q65 38,46,50,55,60,62,66,69,74,76,85/) integer codewords(63,206) integer navg,ibwa,ibwb,ncw,nsps,mode_q65,nfa,nfb - integer istep,nsmo,lag1,lag2,npasses,nused + integer istep,nsmo,lag1,lag2,npasses,nused,iseq integer i0,j0 - real,allocatable,save :: s1a(:,:) !Cumulative symbol spectra - real sync(85) !sync vector + real,allocatable,save :: s1a(:,:,:) !Cumulative symbol spectra + real sync(85) !sync vector real df,dtstep,dtdec,f0dec contains @@ -97,7 +97,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & allocate(ccf2(-ia2:ia2)) if(LL.ne.LL0 .or. iz.ne.iz0 .or. jz.ne.jz0 .or. lclearave) then if(allocated(s1a)) deallocate(s1a) - allocate(s1a(iz,jz)) + allocate(s1a(iz,jz,0:1)) s1a=0. navg=0 LL0=LL @@ -119,7 +119,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & call q65_symspec(iwave,ntrperiod*12000,iz,jz,s1) call timer('q65_syms',1) else - s1=s1a + s1=s1a(:,:,iseq) endif i0=nint(nfqso/df) !Target QSO frequency @@ -259,7 +259,7 @@ subroutine q65_symspec(iwave,nmax,iz,jz,s1) call smo121(s1(1:iz,j),iz) enddo enddo - s1a=s1a+s1 + s1a(:,:,iseq)=s1a(:,:,iseq) + s1 navg=navg+1 return From 8a436b63dd0470d40f868f1976faf5677729fd2a Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 19 Jan 2021 16:06:10 -0500 Subject: [PATCH 383/426] Separate message averaging for Odd and Even sequences. --- lib/decoder.f90 | 9 +++++---- lib/q65_decode.f90 | 8 ++++---- lib/qra/q65/q65.f90 | 9 +++++---- widgets/mainwindow.cpp | 8 ++++++-- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index db66fe4ac..4129cd481 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -367,8 +367,9 @@ subroutine multimode_decoder(ss,id2,params,nfsample) endif if(params%nmode.ne.8 .or. params%nzhsym.eq.50 .or. & .not.params%ndiskdat) then + write(*,1010) nsynced,ndecoded,navg0 -1010 format('',3i4) +1010 format('',2i4,i9) call flush(6) endif close(13) @@ -763,7 +764,7 @@ contains end subroutine fst4_decoded subroutine q65_decoded (this,nutc,snr1,nsnr,dt,freq,decoded,idec, & - navg,ntrperiod) + nused,ntrperiod) use q65_decode implicit none @@ -776,7 +777,7 @@ contains real, intent(in) :: freq character(len=37), intent(in) :: decoded integer, intent(in) :: idec - integer, intent(in) :: navg + integer, intent(in) :: nused integer, intent(in) :: ntrperiod character*3 cflags @@ -784,7 +785,7 @@ contains if(idec.ge.0) then cflags='q ' write(cflags(2:2),'(i1)') idec - if(navg.ge.2) write(cflags(3:3),'(i1)') navg + if(nused.ge.2) write(cflags(3:3),'(i1)') nused endif if(ntrperiod.lt.60) then diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 2275b2bfd..86ecdb9d5 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -180,7 +180,7 @@ contains endif enddo ! ipass - if(iand(ndepth,16).eq.0 .or. navg.lt.2) go to 100 + if(iand(ndepth,16).eq.0 .or. navg(iseq).lt.2) go to 100 ! There was no single-transmission decode. Try for an average 'q3n' decode. call timer('list_avg',0) ! Call top-level routine in q65 module: establish sync and try for a q3 @@ -192,7 +192,7 @@ contains if(idec.ge.0) then dtdec=xdt !We have a list-decode result from averaged data f0dec=f0 - nused=navg + nused=navg(iseq) go to 100 endif @@ -208,7 +208,7 @@ contains if(idec.ge.0) then dtdec=xdt !We have a q[012]n result f0dec=f0 - nused=navg + nused=navg(iseq) endif 100 decoded=' ' @@ -237,7 +237,7 @@ contains call this%callback(nutc,snr1,nsnr,xdt,f0,decoded, & idec,0,ntrperiod) endif - navg0=navg + navg0=1000*navg(0) + navg(1) return end subroutine decode diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index e37ad852d..eb0e6bd16 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -10,9 +10,10 @@ module q65 integer,dimension(22) :: isync = (/1,9,12,13,15,22,23,26,27,33,35, & 38,46,50,55,60,62,66,69,74,76,85/) integer codewords(63,206) - integer navg,ibwa,ibwb,ncw,nsps,mode_q65,nfa,nfb + integer ibwa,ibwb,ncw,nsps,mode_q65,nfa,nfb integer istep,nsmo,lag1,lag2,npasses,nused,iseq integer i0,j0 + integer navg(0:1) real,allocatable,save :: s1a(:,:,:) !Cumulative symbol spectra real sync(85) !sync vector real df,dtstep,dtdec,f0dec @@ -222,8 +223,8 @@ subroutine q65_clravg ! Clear the averaging array to start a new average. - if(allocated(s1a)) s1a=0. - navg=0 + if(allocated(s1a)) s1a(:,:,iseq)=0. + navg(iseq)=0 return end subroutine q65_clravg @@ -260,7 +261,7 @@ subroutine q65_symspec(iwave,nmax,iz,jz,s1) enddo enddo s1a(:,:,iseq)=s1a(:,:,iseq) + s1 - navg=navg+1 + navg(iseq)=navg(iseq) + 1 return end subroutine q65_symspec diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index a087a2ef5..4a9c12831 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3374,9 +3374,13 @@ void MainWindow::readFromStdout() //readFromStdout if(line_read.indexOf("") >= 0) { m_bDecoded = line_read.mid(20).trimmed().toInt() > 0; int n=line_read.trimmed().size(); - int navg=line_read.trimmed().mid(n-2).toInt(); + int n2=line_read.trimmed().mid(n-7).toInt(); + int n0=n2/1000; + int n1=n2%1000; if(m_mode=="Q65") { - ndecodes_label.setText(QString::number(navg)); + QString t; + t.sprintf("%d %d",n0,n1); + ndecodes_label.setText(t); } else { if(m_nDecodes==0) ndecodes_label.setText("0"); } From 0715eb63960b300ee78f53d777f9fcf99d121583 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 20 Jan 2021 15:19:18 -0500 Subject: [PATCH 384/426] Add 50.211 as a Q65 frequency for Q65. This one is for EME; ionoscatter is on 50.275. --- models/FrequencyList.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/models/FrequencyList.cpp b/models/FrequencyList.cpp index e774f298b..beae8fd6d 100644 --- a/models/FrequencyList.cpp +++ b/models/FrequencyList.cpp @@ -263,6 +263,7 @@ namespace {28180000, Modes::FT4, IARURegions::ALL}, {50200000, Modes::Echo, IARURegions::ALL}, + {50211000, Modes::Q65, IARURegions::ALL}, {50275000, Modes::Q65, IARURegions::ALL}, {50276000, Modes::JT65, IARURegions::R2}, {50276000, Modes::JT65, IARURegions::R3}, From b64520591f729437be66081188348f9347d30c68 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 22 Jan 2021 15:16:21 -0500 Subject: [PATCH 385/426] Don't smooth at all if nsmo = 1: i.e., set it to nsmo=0. --- lib/qra/q65/q65.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index eb0e6bd16..bca660c43 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -256,6 +256,7 @@ subroutine q65_symspec(iwave,nmax,iz,jz,s1) s1(i,j)=real(c0(i))**2 + aimag(c0(i))**2 enddo ! For large Doppler spreads, should we smooth the spectra here? + if(nsmo.le.1) nsmo=0 do i=1,nsmo call smo121(s1(1:iz,j),iz) enddo From e222e6630254dc614d9caeb1ea0a9a3f575036a4 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 22 Jan 2021 15:32:47 -0500 Subject: [PATCH 386/426] Write the full Q65 submode name into ALL.TXT. --- widgets/mainwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 4a9c12831..e0fef05e7 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -9171,6 +9171,8 @@ void MainWindow::write_all(QString txRx, QString message) mode_string="JT65 "; } else if (message.size () > 19 && message[19]=='@') { mode_string="JT9 "; + } else if(m_mode=="Q65") { + mode_string=mode_label.text(); } else { mode_string=m_mode.leftJustified(6,' '); } From 10f574a1ec6811048f31981c3d125deda6eefe6a Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Fri, 22 Jan 2021 22:59:38 +0000 Subject: [PATCH 387/426] Qt 5.15 compatibility --- widgets/mainwindow.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 7038275b8..a8e83d05a 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3378,9 +3378,7 @@ void MainWindow::readFromStdout() //readFromStdout int n0=n2/1000; int n1=n2%1000; if(m_mode=="Q65") { - QString t; - t.sprintf("%d %d",n0,n1); - ndecodes_label.setText(t); + ndecodes_label.setText(QString {"%1 %2"}.arg (n0).arg (n1)); } else { if(m_nDecodes==0) ndecodes_label.setText("0"); } From 74a43a658c458352334576cd05afb79aa1cafcfe Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 23 Jan 2021 10:36:07 -0500 Subject: [PATCH 388/426] Clear the numbers in ndecodes_label when Clear Avg is clicked. --- widgets/mainwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index e0fef05e7..c858f26c5 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3054,6 +3054,7 @@ void MainWindow::on_ClrAvgButton_clicked() if(m_msgAvgWidget != NULL) { if(m_msgAvgWidget->isVisible()) m_msgAvgWidget->displayAvg(""); } + if(m_mode=="Q65") ndecodes_label.setText("0 0"); } void MainWindow::msgAvgDecode2() From 14895138031771e872e6a1c4c4a2fa19d4e52eb0 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 23 Jan 2021 10:58:28 -0500 Subject: [PATCH 389/426] Decodes without "newdat" should not add s1 into s1a again. --- lib/decoder.f90 | 10 +++++----- lib/q65_decode.f90 | 9 +++++---- lib/qra/q65/q65.f90 | 7 +++++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 4129cd481..fb4e80c01 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -205,11 +205,11 @@ subroutine multimode_decoder(ss,id2,params,nfsample) open(17,file=trim(temp_dir)//'/red.dat',status='unknown') open(14,file=trim(temp_dir)//'/avemsg.txt',status='unknown') call timer('dec_q65 ',0) - call my_q65%decode(q65_decoded,id2,params%nutc,params%ntr, & - params%nsubmode,params%nfqso,params%ntol,params%ndepth, & - params%nfa,params%nfb,logical(params%nclearave), & - params%emedelay,mycall,hiscall,hisgrid,params%nQSOProgress, & - ncontest,logical(params%lapcqonly),navg0) + call my_q65%decode(q65_decoded,id2,params%nutc,params%ntr, & + params%nsubmode,params%nfqso,params%ntol,params%ndepth, & + params%nfa,params%nfb,logical(params%nclearave), & + logical(params%newdat),params%emedelay,mycall,hiscall,hisgrid, & + params%nQSOProgress,ncontest,logical(params%lapcqonly),navg0) call timer('dec_q65 ',1) close(17) go to 800 diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 86ecdb9d5..d1466f63e 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -26,9 +26,9 @@ module q65_decode contains - subroutine decode(this,callback,iwave,nutc,ntrperiod,nsubmode,nfqso, & - ntol,ndepth,nfa0,nfb0,lclearave,emedelay,mycall,hiscall,hisgrid, & - nQSOprogress,ncontest,lapcqonly,navg0) + subroutine decode(this,callback,iwave,nutc,ntrperiod,nsubmode,nfqso, & + ntol,ndepth,nfa0,nfb0,lclearave,lnewdat0,emedelay,mycall, & + hiscall,hisgrid,nQSOprogress,ncontest,lapcqonly,navg0) ! Top-level routine that organizes the decoding of Q65 signals ! Input: iwave Raw data, i*2 @@ -63,13 +63,14 @@ contains real, allocatable :: dd(:) !Raw data integer dat4(13) !Decoded message as 12 6-bit integers integer dgen(13) - logical lclearave,lapcqonly,unpk77_success + logical lclearave,lnewdat0,lapcqonly,unpk77_success 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 nfa=nfa0 nfb=nfb0 + lnewdat=lnewdat0 idec=-1 mode_q65=2**nsubmode npts=ntrperiod*12000 diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index bca660c43..9e5bf9415 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -14,6 +14,7 @@ module q65 integer istep,nsmo,lag1,lag2,npasses,nused,iseq integer i0,j0 integer navg(0:1) + logical lnewdat real,allocatable,save :: s1a(:,:,:) !Cumulative symbol spectra real sync(85) !sync vector real df,dtstep,dtdec,f0dec @@ -261,8 +262,10 @@ subroutine q65_symspec(iwave,nmax,iz,jz,s1) call smo121(s1(1:iz,j),iz) enddo enddo - s1a(:,:,iseq)=s1a(:,:,iseq) + s1 - navg(iseq)=navg(iseq) + 1 + if(lnewdat) then + s1a(:,:,iseq)=s1a(:,:,iseq) + s1 + navg(iseq)=navg(iseq) + 1 + endif return end subroutine q65_symspec From fb13265f371905e052ba24a8e1173e3b7cc35125 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 23 Jan 2021 14:14:17 -0500 Subject: [PATCH 390/426] Make ibwb larger by 1. --- lib/q65_decode.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index d1466f63e..4e1e0280c 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -108,7 +108,7 @@ contains this%callback => callback nFadingModel=1 ibwa=max(1,int(1.8*log(baud*mode_q65)) + 2) - ibwb=min(10,ibwa+4) + ibwb=min(10,ibwa+5) if(iand(ndepth,3).eq.3) then ibwa=max(1,ibwa-1) ibwb=min(10,ibwb+1) From 29a113b73a64f89793933250c8a65891645524e8 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 26 Jan 2021 14:36:13 +0000 Subject: [PATCH 391/426] Add some diagnostics to PSKReporter class Sent to the severity channel log source PSKRPRT. --- Audio/soundin.cpp | 4 ++-- Logger.hpp | 14 +++++++------- Network/PSKReporter.cpp | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/Audio/soundin.cpp b/Audio/soundin.cpp index 53a29799c..d9bc78446 100644 --- a/Audio/soundin.cpp +++ b/Audio/soundin.cpp @@ -184,13 +184,13 @@ void SoundInput::reset (bool report_dropped_frames) { LOG_WARN ("Detected dropped audio source samples: " << m_stream->format ().framesForDuration (lost_usec) - << " (" << std::setprecision (4) << lost_usec / 1.e6 << " S)") + << " (" << std::setprecision (4) << lost_usec / 1.e6 << " S)"); } else if (std::abs (lost_usec) > 5 * 48000) { LOG_ERROR ("Detected excessive dropped audio source samples: " << m_stream->format ().framesForDuration (lost_usec) - << " (" << std::setprecision (4) << lost_usec / 1.e6 << " S)") + << " (" << std::setprecision (4) << lost_usec / 1.e6 << " S)"); } } cummulative_lost_usec_ = elapsed_usecs - m_stream->processedUSecs (); diff --git a/Logger.hpp b/Logger.hpp index 5d497943f..0eed8aae2 100644 --- a/Logger.hpp +++ b/Logger.hpp @@ -35,16 +35,16 @@ namespace Logger BOOST_LOG_SEV (LOGGER, boost::log::trivial::LEVEL) \ << boost::log::add_value ("Line", __LINE__) \ << boost::log::add_value ("File", __FILE__) \ - << boost::log::add_value ("Function", __FUNCTION__) << ARG; + << boost::log::add_value ("Function", __FUNCTION__) << ARG /// System Log macros. /// TRACE < DEBUG < INFO < WARN < ERROR < FATAL -#define LOG_TRACE(ARG) LOG_LOG_LOCATION (sys::get(), trace, ARG); -#define LOG_DEBUG(ARG) LOG_LOG_LOCATION (sys::get(), debug, ARG); -#define LOG_INFO(ARG) LOG_LOG_LOCATION (sys::get(), info, ARG); -#define LOG_WARN(ARG) LOG_LOG_LOCATION (sys::get(), warning, ARG); -#define LOG_ERROR(ARG) LOG_LOG_LOCATION (sys::get(), error, ARG); -#define LOG_FATAL(ARG) LOG_LOG_LOCATION (sys::get(), fatal, ARG); +#define LOG_TRACE(ARG) LOG_LOG_LOCATION (sys::get(), trace, ARG) +#define LOG_DEBUG(ARG) LOG_LOG_LOCATION (sys::get(), debug, ARG) +#define LOG_INFO(ARG) LOG_LOG_LOCATION (sys::get(), info, ARG) +#define LOG_WARN(ARG) LOG_LOG_LOCATION (sys::get(), warning, ARG) +#define LOG_ERROR(ARG) LOG_LOG_LOCATION (sys::get(), error, ARG) +#define LOG_FATAL(ARG) LOG_LOG_LOCATION (sys::get(), fatal, ARG) /// Data Log macros. Does not include LINE, FILE, FUNCTION. /// TRACE < DEBUG < INFO < WARN < ERROR < FATAL diff --git a/Network/PSKReporter.cpp b/Network/PSKReporter.cpp index 35c56f23f..c072860f8 100644 --- a/Network/PSKReporter.cpp +++ b/Network/PSKReporter.cpp @@ -22,6 +22,7 @@ #include #endif +#include "Logger.hpp" #include "Configuration.hpp" #include "pimpl_impl.hpp" @@ -46,9 +47,12 @@ class PSKReporter::impl final { Q_OBJECT + using logger_type = boost::log::sources::severity_channel_logger_mt; + public: impl (PSKReporter * self, Configuration const * config, QString const& program_info) - : self_ {self} + : logger_ {boost::log::keywords::channel = "PSKRPRT"} + , self_ {self} , config_ {config} , sequence_number_ {0u} , send_descriptors_ {0} @@ -72,6 +76,7 @@ public: if (socket_ && QAbstractSocket::UdpSocket == socket_->socketType ()) { + LOG_LOG_LOCATION (logger_, trace, "enable descriptor resend"); // send templates again send_descriptors_ = 3; // three times // send receiver data set again @@ -91,6 +96,7 @@ public: && QAbstractSocket::UnconnectedState != socket_->state () && QAbstractSocket::ClosingState != socket_->state ()) { + LOG_LOG_LOCATION (logger_, trace, "create/recreate socket"); // handle re-opening asynchronously auto connection = QSharedPointer::create (); *connection = connect (socket_.data (), &QAbstractSocket::disconnected, [this, connection] () { @@ -110,6 +116,7 @@ public: void handle_socket_error (QAbstractSocket::SocketError e) { + LOG_LOG_LOCATION (logger_, trace, "err: " << e); switch (e) { case QAbstractSocket::RemoteHostClosedError: @@ -132,12 +139,14 @@ public: // be called from the disconnected handler above. if (config_->psk_reporter_tcpip ()) { + LOG_LOG_LOCATION (logger_, trace, "create TCP/IP socket"); socket_.reset (new QTcpSocket, &QObject::deleteLater); send_descriptors_ = 1; send_receiver_data_ = 1; } else { + LOG_LOG_LOCATION (logger_, trace, "create UDP/IP socket"); socket_.reset (new QUdpSocket, &QObject::deleteLater); send_descriptors_ = 3; send_receiver_data_ = 3; @@ -154,6 +163,7 @@ public: // use this for pseudo connection with UDP, allows us to use // QIODevice::write() instead of QUDPSocket::writeDatagram() socket_->connectToHost (HOST, SERVICE_PORT, QAbstractSocket::WriteOnly); + LOG_LOG_LOCATION (logger_, debug, "remote host: " << HOST << " port: " << SERVICE_PORT); if (!report_timer_.isActive ()) { @@ -169,6 +179,7 @@ public: { if (socket_) { + LOG_LOG_LOCATION (logger_, trace, "disconnecting"); socket_->disconnectFromHost (); } descriptor_timer_.stop (); @@ -180,9 +191,12 @@ public: bool flushing () { - return FLUSH_INTERVAL && !(++flush_counter_ % FLUSH_INTERVAL); + bool flush = FLUSH_INTERVAL && !(++flush_counter_ % FLUSH_INTERVAL); + LOG_LOG_LOCATION (logger_, trace, "flush: " << flush); + return flush; } + logger_type mutable logger_; PSKReporter * self_; Configuration const * config_; QSharedPointer socket_; @@ -267,6 +281,7 @@ void PSKReporter::impl::build_preamble (QDataStream& message) << quint32 (0u) // Export Time (place-holder filled in later) << ++sequence_number_ // Sequence Number << observation_id_; // Observation Domain ID + LOG_LOG_LOCATION (logger_, trace, "#: " << sequence_number_); if (send_descriptors_) { @@ -329,6 +344,7 @@ void PSKReporter::impl::build_preamble (QDataStream& message) // insert Length set_length (out, descriptor); message.writeRawData (descriptor.constData (), descriptor.size ()); + LOG_LOG_LOCATION (logger_, debug, "sent descriptors"); } } @@ -354,11 +370,13 @@ void PSKReporter::impl::build_preamble (QDataStream& message) // insert Length and move to payload set_length (out, data); message.writeRawData (data.constData (), data.size ()); + LOG_LOG_LOCATION (logger_, debug, "sent local information"); } } void PSKReporter::impl::send_report (bool send_residue) { + LOG_LOG_LOCATION (logger_, trace, "sending residue: " << send_residue); if (QAbstractSocket::ConnectedState != socket_->state ()) return; QDataStream message {&payload_, QIODevice::WriteOnly | QIODevice::Append}; @@ -391,9 +409,11 @@ void PSKReporter::impl::send_report (bool send_residue) if (tx_residue_.size ()) { tx_out.writeRawData (tx_residue_.constData (), tx_residue_.size ()); + LOG_LOG_LOCATION (logger_, debug, "sent residue"); tx_residue_.clear (); } + LOG_LOG_LOCATION (logger_, debug, "pending spots: " << spots_.size ()); while (spots_.size () || flush) { auto tx_data_size = tx_data_.size (); @@ -452,6 +472,7 @@ void PSKReporter::impl::send_report (bool send_residue) // Send data to PSK Reporter site socket_->write (payload_); // TODO: handle errors + LOG_LOG_LOCATION (logger_, debug, "sent spots"); flush = false; // break loop message.device ()->seek (0u); payload_.clear (); // Fresh message @@ -462,29 +483,35 @@ void PSKReporter::impl::send_report (bool send_residue) break; } } + LOG_LOG_LOCATION (logger_, debug, "remaining spots: " << spots_.size ()); } } PSKReporter::PSKReporter (Configuration const * config, QString const& program_info) : m_ {this, config, program_info} { + LOG_LOG_LOCATION (m_->logger_, trace, "Started for: " << program_info); } PSKReporter::~PSKReporter () { // m_->send_report (true); // send any pending spots + LOG_LOG_LOCATION (m_->logger_, trace, "Ended"); } void PSKReporter::reconnect () { + LOG_LOG_LOCATION (m_->logger_, trace, ""); m_->reconnect (); } void PSKReporter::setLocalStation (QString const& call, QString const& gridSquare, QString const& antenna) { + LOG_LOG_LOCATION (m_->logger_, trace, "call: " << call << " grid: " << gridSquare << " ant: " << antenna); m_->check_connection (); if (call != m_->rx_call_ || gridSquare != m_->rx_grid_ || antenna != m_->rx_ant_) { + LOG_LOG_LOCATION (m_->logger_, trace, "updating information"); m_->send_receiver_data_ = m_->socket_ && QAbstractSocket::UdpSocket == m_->socket_->socketType () ? 3 : 1; m_->rx_call_ = call; @@ -496,6 +523,7 @@ void PSKReporter::setLocalStation (QString const& call, QString const& gridSquar bool PSKReporter::addRemoteStation (QString const& call, QString const& grid, Radio::Frequency freq , QString const& mode, int snr) { + LOG_LOG_LOCATION (m_->logger_, trace, "call: " << call << " grid: " << grid << " freq: " << freq << " mode: " << mode << " snr: " << snr); m_->check_connection (); if (m_->socket_ && m_->socket_->isValid ()) { @@ -511,6 +539,7 @@ bool PSKReporter::addRemoteStation (QString const& call, QString const& grid, Ra void PSKReporter::sendReport (bool last) { + LOG_LOG_LOCATION (m_->logger_, trace, "last: " << last); m_->check_connection (); if (m_->socket_ && QAbstractSocket::ConnectedState == m_->socket_->state ()) { From d087a9b8aed74d5f198fca15969df5ee7ba4be90 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 26 Jan 2021 18:22:10 +0000 Subject: [PATCH 392/426] Don't leak exceptions from ExceptionCatchingApplication::notify() --- ExceptionCatchingApplication.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ExceptionCatchingApplication.hpp b/ExceptionCatchingApplication.hpp index 8b23ab4df..04c04d62e 100644 --- a/ExceptionCatchingApplication.hpp +++ b/ExceptionCatchingApplication.hpp @@ -32,13 +32,12 @@ public: catch (std::exception const& e) { LOG_FATAL (e.what ()); - throw; } catch (...) { LOG_FATAL ("Unexpected fatal error"); - throw; } + return false; } }; From 33db1ec63e67cfa3b904cbd734b2ebf27c382006 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 26 Jan 2021 18:28:49 +0000 Subject: [PATCH 393/426] Install correctly for RelWithDebInfo build configurations --- CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f581c598..e41500fc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1669,7 +1669,7 @@ endif (NOT WIN32 AND NOT APPLE) # -# bundle fixup only done in Release or MinSizeRel configurations +# bundle fixup only done in non-Debug configurations # if (NOT is_debug_build) @@ -1716,7 +1716,7 @@ if (NOT is_debug_build) ${QT_PLUGINS_DIR}/imageformats ${QT_PLUGINS_DIR}/styles DESTINATION ${WSJT_PLUGIN_DESTINATION} - CONFIGURATIONS Release MinSizeRel + CONFIGURATIONS Release MinSizeRel RelWithDebInfo #COMPONENT runtime FILES_MATCHING PATTERN "*${CMAKE_SHARED_LIBRARY_SUFFIX}" PATTERN "*minimal*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE @@ -1730,7 +1730,7 @@ if (NOT is_debug_build) FILES ${QT_PLUGINS_DIR}/sqldrivers/libqsqlite${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION ${WSJT_PLUGIN_DESTINATION}/sqldrivers - CONFIGURATIONS Release MinSizeRel + CONFIGURATIONS Release MinSizeRel RelWithDebInfo #COMPONENT runtime ) # install ( @@ -1774,7 +1774,7 @@ if (NOT is_debug_build) ${QT_PLUGINS_DIR}/audio ${QT_PLUGINS_DIR}/imageformats DESTINATION ${WSJT_PLUGIN_DESTINATION} - CONFIGURATIONS Release MinSizeRel + CONFIGURATIONS Release MinSizeRel RelWithDebInfo #COMPONENT runtime FILES_MATCHING PATTERN "*${CMAKE_SHARED_LIBRARY_SUFFIX}" PATTERN "*minimal*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE @@ -1786,7 +1786,7 @@ if (NOT is_debug_build) FILES ${QT_PLUGINS_DIR}/sqldrivers/qsqlite${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION ${WSJT_PLUGIN_DESTINATION}/sqldrivers - CONFIGURATIONS Release MinSizeRel + CONFIGURATIONS Release MinSizeRel RelWithDebInfo #COMPONENT runtime ) # install ( From bd02e367f2881edb710a576cca738efcea60ec2d Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 26 Jan 2021 21:34:10 +0000 Subject: [PATCH 394/426] Leave global FFTW3 clean up to main programs --- lib/four2a.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/four2a.f90 b/lib/four2a.f90 index 337ca15c3..e7e0e8018 100644 --- a/lib/four2a.f90 +++ b/lib/four2a.f90 @@ -107,7 +107,6 @@ subroutine four2a(a,nfft,ndim,isign,iform) !$omp end critical(fftw) end if enddo - call fftwf_cleanup() nplan=0 !$omp end critical(four2a) From 29a2737cf7af81709a41ee83817b007b1b3b7b26 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 27 Jan 2021 11:12:57 -0500 Subject: [PATCH 395/426] Clean up spec64, now that QRA64 mode is gone. --- lib/qra/q65/q65_loops.f90 | 2 +- lib/spec64.f90 | 54 ++++++++++++++------------------------- 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 808f45283..7f7665e70 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -51,7 +51,7 @@ subroutine q65_loops(c00,npts2,nsps2,nsubmode,ndepth,jpk0, & jpk=jpk0 + nsps2*ndt/16 !tsym/16 if(jpk.lt.0) jpk=0 call timer('spec64 ',0) - call spec64(c0,nsps2,65,mode_q65,jpk,s3,LL,NN) + call spec64(c0,nsps2,mode_q65,jpk,s3,LL,NN) call timer('spec64 ',1) call pctile(s3,LL*NN,40,base) s3=s3/base diff --git a/lib/spec64.f90 b/lib/spec64.f90 index cf43994dc..dd752c692 100644 --- a/lib/spec64.f90 +++ b/lib/spec64.f90 @@ -1,4 +1,4 @@ -subroutine spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) +subroutine spec64(c0,nsps,mode_q65,jpk,s3,LL,NN) parameter (MAXFFT=20736) !### Fix this: @@ -11,41 +11,25 @@ subroutine spec64(c0,nsps,mode,mode64,jpk,s3,LL,NN) data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ nfft=nsps - - if(mode.eq.64) then - do j=1,NN - jj=j+7 !Skip first Costas array - if(j.ge.33) jj=j+14 !Skip middle Costas array - ja=jpk + (jj-1)*nfft - jb=ja+nfft-1 - cs(0:nfft-1)=c0(ja:jb) - call four2a(cs,nfft,1,-1,1) - do ii=1,LL - i=ii-65 - if(i.lt.0) i=i+nfft - s3(ii,j)=real(cs(i))**2 + aimag(cs(i))**2 - enddo + j=0 + n=1 + do k=1,84 + if(k.eq.isync(n)) then + n=n+1 + cycle + endif + j=j+1 + ja=(k-1)*nsps + jpk + jb=ja+nsps-1 + write(71,*) k,ja,jb ; flush(71) + cs(0:nfft-1)=c0(ja:jb) + call four2a(cs,nsps,1,-1,1) !c2c FFT to frequency + do ii=1,LL + i=ii-65+mode_q65 !mode_q65 = 1 2 4 8 16 for Q65 A B C D E + if(i.lt.0) i=i+nsps + s3(ii,j)=real(cs(i))**2 + aimag(cs(i))**2 enddo - else - j=0 - n=1 - do k=1,84 - if(k.eq.isync(n)) then - n=n+1 - cycle - endif - j=j+1 - ja=(k-1)*nsps + jpk - jb=ja+nsps-1 - cs(0:nfft-1)=c0(ja:jb) - call four2a(cs,nsps,1,-1,1) !c2c FFT to frequency - do ii=1,LL - i=ii-65+mode64 !mode64 = 1 2 4 8 16 for Q65 A B C D E - if(i.lt.0) i=i+nsps - s3(ii,j)=real(cs(i))**2 + aimag(cs(i))**2 - enddo - enddo - endif + enddo df=6000.0/nfft do i=1,LL From 4e27b009f097e5627bc303f9216aa6efbfd23920 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 27 Jan 2021 13:19:39 -0500 Subject: [PATCH 396/426] Code cleanup, looking toward possible implementation of -120F and -300F submodes. --- lib/qra/q65/q65.f90 | 28 +++++++--------------------- lib/qra/q65/q65_loops.f90 | 2 +- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 9e5bf9415..f01ba6d67 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -186,11 +186,11 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & if(snr1.gt.10.0) ccf2=(10.0/snr1)*ccf2 if(idec.le.0) then -! The q3 decode attempt failed. Copy synchronied symbol spectra from s1 +! The q3 decode attempt failed. Copy synchronied symbol energies from s1 ! into s3 and prepare to try a more general decode. ccf1=ccf(:,jpk)/rms if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 - call q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) + call q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,1,s3) endif smax=maxval(ccf1) @@ -272,35 +272,21 @@ end subroutine q65_symspec subroutine q65_dec_q3(s1,iz,jz,s3,LL,ipk,jpk,snr2,dat4,idec,decoded) -! Copy synchronized symbol spectra from s1 into s3, then attempt a q3 decode. +! Copy synchronized symbol energies from s1 into s3, then attempt a q3 decode. character*37 decoded integer dat4(13) real s1(iz,jz) real s3(-64:LL-65,63) - i1=i0+ipk-64 - i2=i1+LL-1 - j=j0+jpk-7 - n=0 - do k=1,85 - j=j+8 - if(sync(k).gt.0.0) then - cycle - endif - n=n+1 - if(j.ge.1 .and. j.le.jz) then - do i=0,LL-1 - s3(i-64,n)=s1(i+i1,j) !Copy from s1 into s3 - enddo - endif - enddo + call q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,0,s3) nsubmode=0 if(mode_q65.eq.2) nsubmode=1 if(mode_q65.eq.4) nsubmode=2 if(mode_q65.eq.8) nsubmode=3 if(mode_q65.eq.16) nsubmode=4 + if(mode_q65.eq.32) nsubmode=5 baud=12000.0/nsps do ibw=ibwa,ibwb @@ -516,7 +502,7 @@ subroutine q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) return end subroutine q65_dec2 -subroutine q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) +subroutine q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,itone0,s3) ! Copy synchronized symbol energies from s1 (or s1a) into s3. @@ -524,7 +510,7 @@ subroutine q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) real s3(-64:LL-65,63) real sync(85) !sync vector - i1=i0+ipk-64 + mode_q65 + i1=i0+ipk-64 + mode_q65*itone0 i2=i1+LL-1 if(i1.ge.1 .and. i2.le.iz) then j=j0+jpk-7 diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 7f7665e70..9a70916cd 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -6,7 +6,7 @@ subroutine q65_loops(c00,npts2,nsps2,nsubmode,ndepth,jpk0, & use q65 parameter (NN=63) - parameter (LN=1152*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 + parameter (LN=2176*63) !LN=LL*NN; LL=64*(mode_q65+2), NN=63 complex c00(0:npts2-1) !Analytic representation of dd(), 6000 Hz complex ,allocatable :: c0(:) !Ditto, with freq shift character decoded*37 From f056fc99ec6885d49c083eccf156db3eef8b092d Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Thu, 28 Jan 2021 13:01:52 -0500 Subject: [PATCH 397/426] Write parameters for successful decodes to file q65_decodes.dat. --- lib/q65_decode.f90 | 30 +++++++++++++++++++++++++++++- lib/qra/q65/q65.f90 | 1 + lib/qra/q65/q65_loops.f90 | 3 --- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 4e1e0280c..d76e12087 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -49,6 +49,7 @@ contains use packjt77 use, intrinsic :: iso_c_binding use q65 !Shared variables + use prog_args parameter (NMAX=300*12000) !Max TRperiod is 300 s class(q65_decoder), intent(inout) :: this @@ -59,6 +60,7 @@ contains character*77 c77 character*78 c78 character*6 cutc + character c6*6,c4*4 integer*2 iwave(NMAX) !Raw data real, allocatable :: dd(:) !Raw data integer dat4(13) !Decoded message as 12 6-bit integers @@ -68,10 +70,14 @@ contains 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 lnewdat=lnewdat0 idec=-1 + idf=0 + idt=0 + irc=0 mode_q65=2**nsubmode npts=ntrperiod*12000 nfft1=ntrperiod*12000 @@ -230,12 +236,34 @@ contains call this%callback(nutc,snr1,nsnr,dtdec,f0dec,decoded, & idec,nused,ntrperiod) if(iand(ndepth,128).ne.0) call q65_clravg !AutoClrAvg after decode + 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. + c6=hiscall(1:6) + if(c6.eq.' ') c6=' ' + c4=hisgrid(1:4) + if(c4.eq.' ') c4=' ' + if(ntrperiod.ge.60) then + write(22,1022) nutc,ntrperiod,nsubmode,nQSOprogress,idec, & + nused,iaptype,irc,idf,idt,ibw,xdt,f0,snr1,snr2, & + tdecode,mycall(1:6),c6,c4,trim(decoded) +1022 format(i6.4,10i3,f6.2,f7.1,f7.2,f6.1,f6.2,1x,a6,1x,a6,1x,a4,1x,a) + else + write(22,1023) nutc,ntrperiod,nsubmode,nQSOprogress,idec, & + nused,iaptype,irc,idf,idt,ibw,xdt,f0,snr1,snr2, & + tdecode,mycall(1:6),c6,c4,trim(decoded) +1023 format(i6.6,10i3,f6.2,f7.1,f7.2,f6.1,f6.2,1x,a6,1x,a6,1x,a4,1x,a) + endif + close(22) + endif else ! Report snr1, even if no decode. nsnr=db(snr1) - 35.0 if(nsnr.lt.-35) nsnr=-35 idec=-1 - call this%callback(nutc,snr1,nsnr,xdt,f0,decoded, & + call this%callback(nutc,snr1,nsnr,xdt,f0,decoded, & idec,0,ntrperiod) endif navg0=1000*navg(0) + navg(1) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index f01ba6d67..4714eb641 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -11,6 +11,7 @@ module q65 38,46,50,55,60,62,66,69,74,76,85/) integer codewords(63,206) integer ibwa,ibwb,ncw,nsps,mode_q65,nfa,nfb + integer idf,idt,ibw integer istep,nsmo,lag1,lag2,npasses,nused,iseq integer i0,j0 integer navg(0:1) diff --git a/lib/qra/q65/q65_loops.f90 b/lib/qra/q65/q65_loops.f90 index 9a70916cd..092997e69 100644 --- a/lib/qra/q65/q65_loops.f90 +++ b/lib/qra/q65/q65_loops.f90 @@ -75,9 +75,6 @@ subroutine q65_loops(c00,npts2,nsps2,nsubmode,ndepth,jpk0, & enddo ! idf (f0 loop) 100 if(irc.ge.0) then -! write(55,3055) ndepth,iaptype,idf,idt,ibw,ndist,irc,sum(s3(1:LL*NN)), & -! trim(decoded) -!3055 format(7i4,f10.1,1x,a) idec=iaptype snr2=esnodb - db(2500.0/baud) xdt1=xdt0 + nsps2*ndt/(16.0*6000.0) From 77d8f7ab9c2c0531907d56f6b7c1d7c331117f1a Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 29 Jan 2021 14:42:00 -0500 Subject: [PATCH 398/426] Display both red and orange sync curves. (?? Temporary ??) --- widgets/plotter.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 2181bc619..65d82c336 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -165,6 +165,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) } static QPoint LineBuf[MAX_SCREENSIZE]; static QPoint LineBuf2[MAX_SCREENSIZE]; + static QPoint LineBuf3[MAX_SCREENSIZE]; j=0; j0=int(m_startFreq/m_fftBinWidth + 0.5); int iz=XfromFreq(5000.0); @@ -284,17 +285,23 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) f >> freq >> sync >> xdt >> sync2; if(f.eof()) break; x=XfromFreq(freq); - if(m_bQ65_MultiSync) sync=sync2; +// if(m_bQ65_MultiSync) sync=sync2; y=m_h2*(0.9 - 0.09*gain2d*sync) - m_plot2dZero; LineBuf2[k].setX(x); LineBuf2[k].setY(y); + y=m_h2*(0.9 - 0.09*gain2d*sync2) - m_plot2dZero - 10; + LineBuf3[k].setX(x); + LineBuf3[k].setY(y); k++; } f.close(); QPen pen0(Qt::red,2); - if(m_bQ65_MultiSync) pen0.setColor("orange"); +// if(m_bQ65_MultiSync) pen0.setColor("orange"); painter2D.setPen(pen0); painter2D.drawPolyline(LineBuf2,k); + pen0.setColor("orange"); + painter2D.setPen(pen0); + painter2D.drawPolyline(LineBuf3,k); if(m_bQ65_Sync) { QString t; t = t.asprintf("DT = %6.2f",xdt); From 330ff0307984aff0cc81f7aa1262c81fb7194183 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 29 Jan 2021 14:43:06 -0500 Subject: [PATCH 399/426] Move writing the red/orange sync curves into a subroutine. --- lib/qra/q65/q65.f90 | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 4714eb641..7feac3b98 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -203,16 +203,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & enddo width=df*(i2-i1) -! Write data for the red and orange sync curves. - do i=-ia2,ia2 - freq=nfqso + i*df - if(freq.ge.float(nfa) .and. freq.le.float(nfb)) then - write(17,1100) freq,ccf1(i),xdt,ccf2(i) -1100 format(4f10.3) - endif - enddo - rewind 17 - + call q65_write_red(ia2,nfqso,xdt,ccf1,ccf2) if(iavg.eq.2) then call q65_dec_q012(s3,LL,snr2,dat4,idec,decoded) @@ -529,4 +520,22 @@ subroutine q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,itone0,s3) return end subroutine q65_s1_to_s3 +subroutine q65_write_red(ia2,nfqso,xdt,ccf1,ccf2) + + real ccf1(-ia2:ia2) + real ccf2(-ia2:ia2) + +! Write data for the red and orange sync curves to LU 17. + rewind 17 + do i=-ia2,ia2 + freq=nfqso + i*df + if(freq.ge.float(nfa) .and. freq.le.float(nfb)) then + write(17,1100) freq,ccf1(i),xdt,ccf2(i) +1100 format(4f10.3) + endif + enddo + + return +end subroutine q65_write_red + end module q65 From 316f41996ecca524ccc9c79de297c8d85d6866fc Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 30 Jan 2021 10:59:46 -0500 Subject: [PATCH 400/426] Correct a flaw in setting ipk. Better loop ordering in ccf_22. --- lib/qra/q65/q65.f90 | 63 +++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 7feac3b98..c7eb216a4 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -148,7 +148,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & call timer('list_dec',0) call q65_dec_q3(s1,iz,jz,s3,LL,ipk,jpk,snr2,dat4,idec,decoded) call timer('list_dec',1) - if(idec.ne.0) then + if(idec.eq.3) then ic=ia2/4; base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); ccf1=ccf1-base @@ -191,7 +191,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & ! into s3 and prepare to try a more general decode. ccf1=ccf(:,jpk)/rms if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 - call q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,1,s3) + call q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) endif smax=maxval(ccf1) @@ -271,7 +271,7 @@ subroutine q65_dec_q3(s1,iz,jz,s3,LL,ipk,jpk,snr2,dat4,idec,decoded) real s1(iz,jz) real s3(-64:LL-65,63) - call q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,0,s3) + call q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) nsubmode=0 if(mode_q65.eq.2) nsubmode=1 @@ -368,10 +368,10 @@ subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) do j=1,85 if(j.eq.isync(i)) then i=i+1 - itone(j)=-1 + itone(j)=0 else k=k+1 - itone(j)=codewords(k,imsg) + itone(j)=codewords(k,imsg) + 1 endif enddo @@ -397,7 +397,7 @@ subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) ijpk=maxloc(ccf(-ia:ia,:)) ipk=ijpk(1)-ia-1 jpk=ijpk(2)-53-1 - f0=nfqso + (ipk-mode_q65)*df + f0=nfqso + ipk*df xdt=jpk*dtstep imsg_best=imsg ccf1=ccf(:,jpk) @@ -415,27 +415,40 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,ccf,ccf2) real s1(iz,jz) real ccf(-ia2:ia2,-53:214) real ccf2(-ia2:ia2) - integer ijpk(2) ccf=0. - do lag=lag1,lag2 - do k=1,85 - n=NSTEP*(k-1) + 1 - j=n+lag+j0 - if(j.ge.1 .and. j.le.jz) then - do i=-ia2,ia2 - if(i0+i.lt.1 .or. i0+i.gt.iz) cycle - ccf(i,lag)=ccf(i,lag) + sync(k)*s1(i0+i,j) - enddo - endif - enddo - enddo + ccfbest=0. + ibest=0 + lagpk=0 + lagbest=0 do i=-ia2,ia2 - ccf2(i)=maxval(ccf(i,:)) + if(i0+i.lt.1 .or. i0+i.gt.iz) cycle + ccfmax=0. + do lag=lag1,lag2 + ccft=0. + do k=1,85 + n=NSTEP*(k-1) + 1 + j=n+lag+j0 + if(j.ge.1 .and. j.le.jz) then + ccft=ccft + sync(k)*s1(i0+i,j) + endif + enddo + if(ccft.gt.ccfmax) then + ccfmax=ccft + lagpk=lag + endif + ccf(i,lag)=ccft + enddo + ccf2(i)=ccfmax + if(ccfmax.gt.ccfbest) then + ccfbest=ccfmax + ibest=i + lagbest=lagpk + endif enddo - ijpk=maxloc(ccf(-ia:ia,:)) - ipk=ijpk(1)-ia-1 - jpk=ijpk(2)-53-1 + + ipk=ibest + jpk=lagbest f0=nfqso + ipk*df xdt=jpk*dtstep @@ -494,7 +507,7 @@ subroutine q65_dec2(s3,nsubmode,b90ts,esnodb,irc,dat4,decoded) return end subroutine q65_dec2 -subroutine q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,itone0,s3) +subroutine q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) ! Copy synchronized symbol energies from s1 (or s1a) into s3. @@ -502,7 +515,7 @@ subroutine q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,itone0,s3) real s3(-64:LL-65,63) real sync(85) !sync vector - i1=i0+ipk-64 + mode_q65*itone0 + i1=i0+ipk-64 + mode_q65 i2=i1+LL-1 if(i1.ge.1 .and. i2.le.iz) then j=j0+jpk-7 From 791b39fce5d4e1194cec6914891fc38509e12d79 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 30 Jan 2021 11:36:43 -0500 Subject: [PATCH 401/426] Estimate rms of ccf2 without using the 2-dimensional ccf(). --- lib/qra/q65/q65.f90 | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index c7eb216a4..f7f2cf665 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -169,19 +169,9 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & endif ! Estimate rms on ccf baseline - sq=0. - nsq=0 - jd=(lag2-lag1)/4 - do i=-ia2,ia2 - do j=lag1,lag2 - if(abs(j-jpk).gt.jd .and. abs(i-ipk).gt.ia/2) then - sq=sq + ccf(i,j)**2 - nsq=nsq+1 - endif - enddo - enddo - rms=sqrt(sq/nsq) - smax=ccf(ipk,jpk) + call pctile(ccf2(-ia2:ia2),2*ia2+1,40,base) + rms=0.6*base !Empirical constant 0.6 + smax=maxval(ccf2) snr1=smax/rms ccf2=ccf2/rms if(snr1.gt.10.0) ccf2=(10.0/snr1)*ccf2 From 1e7c53e0e41fc848eb0063910669b0bce74a0fab Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 30 Jan 2021 11:49:08 -0500 Subject: [PATCH 402/426] Fix several comments. --- lib/qra/q65/q65.f90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index f7f2cf665..8f2413297 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -62,8 +62,8 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) real, allocatable :: ccf(:,:) !CCF(freq,lag) - real, allocatable :: ccf1(:) !CCF(freq) at best lag - real, allocatable :: ccf2(:) !CCF(freq) at any lag + real, allocatable :: ccf1(:) !CCF(freq) at fixed lag (red) + real, allocatable :: ccf2(:) !Max CCF(freq) at any lag (orange) data first/.true./ save first @@ -185,6 +185,8 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & endif smax=maxval(ccf1) + +! Estimate frequenct spread i1=-9999 i2=-9999 do i=-ia,ia From 7bd12494525dbc4bddfab91245325cdd64c7ed80 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 30 Jan 2021 14:12:02 -0500 Subject: [PATCH 403/426] Code cleanup involving ccf1, ccf2, and plotting thereof. --- lib/qra/q65/q65.f90 | 58 +++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 8f2413297..ada41298d 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -148,39 +148,23 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & call timer('list_dec',0) call q65_dec_q3(s1,iz,jz,s3,LL,ipk,jpk,snr2,dat4,idec,decoded) call timer('list_dec',1) - if(idec.eq.3) then - ic=ia2/4; - base=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/(2.0+2.0*ic); - ccf1=ccf1-base - smax=maxval(ccf1) - if(smax.gt.10.0) ccf1=10.0*ccf1/smax - base=(sum(ccf2(-ia2:-ia2+ic)) + sum(ccf2(ia2-ic:ia2)))/(2.0+2.0*ic); - ccf2=ccf2-base - smax=maxval(ccf2) - if(smax.gt.10.0) ccf2=10.0*ccf2/smax - endif endif ! Get 2d CCF and ccf2 using sync symbols only - call q65_ccf_22(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0a,xdta,ccf,ccf2) + call q65_ccf_22(s1,iz,jz,nfqso,ia2,ipk,jpk,f0a,xdta,ccf2) if(idec.lt.0) then f0=f0a xdt=xdta endif -! Estimate rms on ccf baseline - call pctile(ccf2(-ia2:ia2),2*ia2+1,40,base) - rms=0.6*base !Empirical constant 0.6 +! Estimate rms on ccf2 baseline + call q65_sync_curve(ccf2,ia2,rms2) smax=maxval(ccf2) - snr1=smax/rms - ccf2=ccf2/rms - if(snr1.gt.10.0) ccf2=(10.0/snr1)*ccf2 + snr1=smax/rms2 if(idec.le.0) then -! The q3 decode attempt failed. Copy synchronied symbol energies from s1 +! The q3 decode attempt failed. Copy synchronized symbol energies from s1 ! into s3 and prepare to try a more general decode. - ccf1=ccf(:,jpk)/rms - if(snr1.gt.10.0) ccf1=(10.0/snr1)*ccf1 call q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) endif @@ -399,16 +383,14 @@ subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) return end subroutine q65_ccf_85 -subroutine q65_ccf_22(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,ccf,ccf2) +subroutine q65_ccf_22(s1,iz,jz,nfqso,ia2,ipk,jpk,f0,xdt,ccf2) ! Attempt synchronization using only the 22 sync symbols. Return ccf2 ! for the "orange sync curve". real s1(iz,jz) - real ccf(-ia2:ia2,-53:214) real ccf2(-ia2:ia2) - ccf=0. ccfbest=0. ibest=0 lagpk=0 @@ -429,7 +411,6 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,ccf,ccf2) ccfmax=ccft lagpk=lag endif - ccf(i,lag)=ccft enddo ccf2(i)=ccfmax if(ccfmax.gt.ccfbest) then @@ -527,10 +508,14 @@ end subroutine q65_s1_to_s3 subroutine q65_write_red(ia2,nfqso,xdt,ccf1,ccf2) +! Write data for the red and orange sync curves to LU 17. + real ccf1(-ia2:ia2) real ccf2(-ia2:ia2) -! Write data for the red and orange sync curves to LU 17. + call q65_sync_curve(ccf1,ia2,rms1) + call q65_sync_curve(ccf2,ia2,rms2) + rewind 17 do i=-ia2,ia2 freq=nfqso + i*df @@ -543,4 +528,25 @@ subroutine q65_write_red(ia2,nfqso,xdt,ccf1,ccf2) return end subroutine q65_write_red +subroutine q65_sync_curve(ccf1,ia2,rms1) + +! Condition the red or orange sync curve for plotting. + + real ccf1(-ia2:ia2) + + ic=ia2/4; + nsum=2*(ic+1) + + base1=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/nsum + ccf1(-ia2:ia2)=ccf1(-ia2:ia2)-base1 + sq=dot_product(ccf1(-ia2:-ia2+ic),ccf1(-ia2:-ia2+ic)) + & + dot_product(ccf1(ia2-ic:ia2),ccf1(ia2-ic:ia2)) + rms1=sqrt(sq/nsum) + ccf1=2.0*ccf1/rms1 + smax1=maxval(ccf1) + if(smax1.gt.10.0) ccf1=10.0*ccf1/smax1 + + return +end subroutine q65_sync_curve + end module q65 From 14c4164651cd927a88a254ff97c65321d85b55c1 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 30 Jan 2021 14:52:53 -0500 Subject: [PATCH 404/426] Remove the if(snr1.lt.2.8) test, for now. --- lib/q65_decode.f90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index d76e12087..7f9b516ec 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -140,11 +140,11 @@ contains go to 100 endif - if(snr1.lt.2.8) then - dtdec=0. !No reliable sync, abandon decoding attempt - f0dec=0. - go to 100 - endif +! if(snr1.lt.2.8) then +! dtdec=0. !No reliable sync, abandon decoding attempt +! f0dec=0. +! go to 100 +! endif ! Prepare for a single-period decode woth iaptype = 0, 1, or 2 (also 4?) jpk0=(xdt+1.0)*6000 !Index of nominal start of signal From 2f5c368aadbe7d1d07e9a2f678eaf5d7d4da58b4 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 30 Jan 2021 14:59:55 -0500 Subject: [PATCH 405/426] Remove a diagnostic write. --- lib/spec64.f90 | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/spec64.f90 b/lib/spec64.f90 index dd752c692..b3b7d54d6 100644 --- a/lib/spec64.f90 +++ b/lib/spec64.f90 @@ -21,7 +21,6 @@ subroutine spec64(c0,nsps,mode_q65,jpk,s3,LL,NN) j=j+1 ja=(k-1)*nsps + jpk jb=ja+nsps-1 - write(71,*) k,ja,jb ; flush(71) cs(0:nfft-1)=c0(ja:jb) call four2a(cs,nsps,1,-1,1) !c2c FFT to frequency do ii=1,LL From 9e822d1806f6b25fe0b376e20f8d7eb91b48eec9 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 30 Jan 2021 15:10:05 -0500 Subject: [PATCH 406/426] Clean up possible cases where rms evaluates to 0.0. --- lib/qra/q65/q65.f90 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index ada41298d..7db539e7b 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -160,7 +160,8 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & ! Estimate rms on ccf2 baseline call q65_sync_curve(ccf2,ia2,rms2) smax=maxval(ccf2) - snr1=smax/rms2 + snr1=0. + if(rms2.gt.0) snr1=smax/rms2 if(idec.le.0) then ! The q3 decode attempt failed. Copy synchronized symbol energies from s1 @@ -541,8 +542,9 @@ subroutine q65_sync_curve(ccf1,ia2,rms1) ccf1(-ia2:ia2)=ccf1(-ia2:ia2)-base1 sq=dot_product(ccf1(-ia2:-ia2+ic),ccf1(-ia2:-ia2+ic)) + & dot_product(ccf1(ia2-ic:ia2),ccf1(ia2-ic:ia2)) - rms1=sqrt(sq/nsum) - ccf1=2.0*ccf1/rms1 + rms1=0. + if(nsum.gt.0) rms1=sqrt(sq/nsum) + if(rms1.gt.0.0) ccf1=2.0*ccf1/rms1 smax1=maxval(ccf1) if(smax1.gt.10.0) ccf1=10.0*ccf1/smax1 From b8ea4c1c2ccca6418cca30922312b73b95979ba2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 30 Jan 2021 15:23:30 -0500 Subject: [PATCH 407/426] Zero the red curve in we're not trying for q3 decodes. --- lib/qra/q65/q65.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 7db539e7b..0162d66d3 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -180,6 +180,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & enddo width=df*(i2-i1) + if(ncw.eq.0) ccf1=0. call q65_write_red(ia2,nfqso,xdt,ccf1,ccf2) if(iavg.eq.2) then From ec6fe749f9749fd89468b217c40c2642ad0e29e9 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 31 Jan 2021 09:06:20 -0500 Subject: [PATCH 408/426] Update the Q65 test program. --- lib/test_q65.f90 | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/test_q65.f90 b/lib/test_q65.f90 index dc54f0ad0..29d3ebef8 100644 --- a/lib/test_q65.f90 +++ b/lib/test_q65.f90 @@ -73,7 +73,7 @@ program test_q65 ! 1 2 3 4 5 6 7 ! 123456789012345678901234567890123456789012345678901234567890123456789012345' cmd1='q65sim "K1ABC W9XYZ EN37 " A 1500 5.0 0.0 0.0 1 60 100 -10.0 > junk0' - cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A -Q 3 -f 1500 *.wav > junk' + cmd2='jt9 -3 -p 15 -L 300 -H 3000 -d 3 -b A -Q 3 -f 1500 *.wav > junk' write(cmd1(10:33),'(a)') '"'//msg//'"' cmd1(35:35)=csubmode @@ -86,10 +86,10 @@ program test_q65 write(cmd1(62:66),'(i5)') nfiles write(cmd2(11:13),'(i3)') ntrperiod - write(cmd2(33:34),'(i2)') ndepth - write(cmd2(44:44),'(i1)') nQSOprogress - write(cmd2(49:52),'(i4)') nf0 - cmd2(39:39)=csubmode + write(cmd2(33:35),'(i3)') ndepth + write(cmd2(45:45),'(i1)') nQSOprogress + write(cmd2(50:53),'(i4)') nf0 + cmd2(40:40)=csubmode call system('rm -f *.wav') @@ -142,13 +142,14 @@ program test_q65 if(line(i0:i0).ne.' ') then i1=index(line,'q') idec=-1 - read(line(i1+1:),*) idec - endif - if(idec.lt.0) cycle - if(idec.ge.12) then - iavg=idec-10 - idec=1 + read(line(i1+1:i1+1),*) idec + if(line(i1+2:i1+2).eq.'*') then + iavg=10 + else + read(line(i1+2:i1+2),*,end=100) iavg + endif endif +100 if(idec.lt.0) cycle if(decok) then ndecn=ndecn + 1 if(iavg.le.1) ndec1=ndec1 + 1 From 449fab0687317af97c4c67843d0f6cede2051e6d Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 31 Jan 2021 10:49:25 -0500 Subject: [PATCH 409/426] Save an updated and expanded UnitTests.txt. --- UnitTests.txt | 78 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/UnitTests.txt b/UnitTests.txt index cac20ff92..f199243bd 100644 --- a/UnitTests.txt +++ b/UnitTests.txt @@ -1,50 +1,78 @@ Mode: Q65-30A -Data: 30A_K1JT_6m_Ionoscatter (62 files, DT 0.1 to 0.4) +Data: 30A_K1JT_6m_Ionoscatter (62 files, 6m ionoscatter DT 0.1 to 0.4) Message: "K1JT K9AN R-16" RxFreq: 1000/10 -Commit No_AP MyCall BothCalls ------------------------------------ -ef4787: 3 10 6 14 30 33 -ada5a6: 3 6 6 10 29 36 +Commit No_AP MyCall BothCalls +----------------------------------------------- +ef4787: 3 10 6 14 30 33 +ada5a6: 3 6 6 10 29 36 +10f574: 2 7 97.6 6 10 95.4 21 33 80.7 +b8ea4c: 2 7 100.5 6 10 96.9 21 33 81.0 Mode: Q65-30A -Data: 30A_N0AN_6m_Ionoscatter (69 files, DT=0 to 0.1) +Data: 30A_N0AN_6m_Ionoscatter (69 files, 6m ionoscatter) Message: "N0AN K1JT -19" RxFreq: 1500/10 -Commit No_AP MyCall BothCalls ------------------------------------ -ef4787: 7 14 16 22 38 40 -70a348: 38 43 -ada5a6: 10 17 11 23 40 46 +Commit No_AP MyCall BothCalls +------------------------------------------------ +ef4787: 7 14 16 22 38 40 +70a348: 38 43 +ada5a6: 10 17 11 23 40 46 +10f574: 8 16 99.4 15 24 91.8 34 44 68.8 +b8ea4c: 8 16 96.0 15 23 92.4 34 44 68.8 Mode: Q65-60B Data: 60B_1296_Troposcatter (75 files) Message: "VK7MO VK7PD QE38" RxFreq: 1000/10 -Commit No_AP MyCall BothCalls ------------------------------------ -ef4787: 1 2 11 23 64 67 -ada5a6: 1 5 14 28 64 67 +Commit No_AP MyCall BothCalls +-------------------------------------------------- +ef4787: 1 2 11 23 64 67 +ada5a6: 1 5 14 28 64 67 +10f574: 1 5 142.7 11 27 129.8 65 67 36.8 +b8ea4c: 1 5 144.3 11 27 132.2 65 67 39.3 Mode: Q65-60D Data: MsgAvg (22 files, simulated fDop = 50 Hz) Message: "K1ABC W9XYZ EN37" RxFreq: 1000/10 -Commit No_AP MyCall BothCalls ------------------------------------ -ef4787: 0 10 21 21 22 22 -ada5a6 0 11 21 21 22 22 +Commit No_AP MyCall BothCalls +------------------------------------------------ +ef4787: 0 10 21 21 22 22 +ada5a6 0 11 21 21 22 22 +10f574: 0 11 47.7 21 21 33.9 22 22 11.6 +b8ea4c: 0 11 46.4 21 21 33.8 22 22 11.9 Mode: Q65-60D -Data: Nil Average (40 files, simulated fDop = 0? Hz ) +Data: 60D_2 (21 files, 1296 troposcatter) Message: "VK7MO VK3WE QF32" -RxFreq: 1200/20 -Requires nsmo=1 ### +RxFreq: 1000/20 -Commit No_AP MyCall BothCalls ------------------------------------ -ef4787: 1 16 24 28 39 39 +Commit No_AP MyCall BothCalls +---------------------------------------------- +10f574: 5 5 33.6 7 8 31.8 12 14 25.1 +b8ea4v: 5 5 39.1 7 8 38.0 13 14 30.8 + +Mode: Q65-120D +Data: 120D (14 files, 10 GHz troposcatter) +Message: "VK7MO VK3WE QF32" +RxFreq: 1000/20 + +Commit No_AP MyCall BothCalls +---------------------------------------------- +10f574: 0 0 24.9 0 0 25.0 1 4 25.0 +b8ea4v: 0 0 39.1 0 0 25.4 1 5 40.1 + +Mode: Q65-60D +Data: 60D_10_GHz_EME (14 files) +Message: "VK7MO K6QPV DM12", "VK7MO K6QPV -15" +RxFreq: 1000/50 + +Commit No_AP MyCall BothCalls +---------------------------------------------- +10f574: 9 10 13.6 10 11 12.7 14 14 7.1 +b8ea4v: 9 10 13.7 10 11 12.6 14 14 7.5 From cae3095174578e9d10427d47f86adc939d4e4d6f Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 31 Jan 2021 11:39:04 -0500 Subject: [PATCH 410/426] Allocate and use 2d CCF only within q65_ccf_85(). --- lib/qra/q65/q65.f90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 0162d66d3..658e37ed6 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -61,7 +61,6 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & logical first,lclearave real, allocatable :: s1(:,:) !Symbol spectra, 1/8-symbol steps real, allocatable :: s3(:,:) !Data-symbol energies s3(LL,63) - real, allocatable :: ccf(:,:) !CCF(freq,lag) real, allocatable :: ccf1(:) !CCF(freq) at fixed lag (red) real, allocatable :: ccf2(:) !Max CCF(freq) at any lag (orange) data first/.true./ @@ -95,7 +94,6 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & allocate(s1(iz,jz)) allocate(s3(-64:LL-65,63)) - allocate(ccf(-ia2:ia2,-53:214)) allocate(ccf1(-ia2:ia2)) allocate(ccf2(-ia2:ia2)) if(LL.ne.LL0 .or. iz.ne.iz0 .or. jz.ne.jz0 .or. lclearave) then @@ -142,7 +140,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & ! Try list decoding via "Deep Likelihood". call timer('ccf_85 ',0) ! Try to synchronize using all 85 symbols - call q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) + call q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf1) call timer('ccf_85 ',1) call timer('list_dec',0) @@ -325,17 +323,18 @@ subroutine q65_dec_q012(s3,LL,snr2,dat4,idec,decoded) 100 return end subroutine q65_dec_q012 -subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) +subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf1) ! Attempt synchronization using all 85 symbols, in advance of an ! attempt at q3 decoding. Return ccf1 for the "red sync curve". real s1(iz,jz) - real ccf(-ia2:ia2,-53:214) + real, allocatable :: ccf(:,:) !CCF(freq,lag) real ccf1(-ia2:ia2) integer ijpk(2) integer itone(85) - + + allocate(ccf(-ia2:ia2,-53:214)) ipk=0 jpk=0 ccf_best=0. @@ -381,6 +380,7 @@ subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf,ccf1) ccf1=ccf(:,jpk) endif enddo ! imsg + deallocate(ccf) return end subroutine q65_ccf_85 From f7cde117fe0391d541d9ba705402a5ecd7b21fda Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 31 Jan 2021 13:41:32 -0500 Subject: [PATCH 411/426] Extens orange sync surve to the full displayed frequency range. --- lib/qra/q65/q65.f90 | 57 ++++++++++++++++++++++++--------------------- widgets/plotter.cpp | 16 ++++++++----- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 658e37ed6..d5e9b6a97 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -18,7 +18,7 @@ module q65 logical lnewdat real,allocatable,save :: s1a(:,:,:) !Cumulative symbol spectra real sync(85) !sync vector - real df,dtstep,dtdec,f0dec + real df,dtstep,dtdec,f0dec,ftol contains @@ -81,6 +81,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & txt=85.0*nsps/12000.0 jz=(txt+1.0)*12000.0/istep !Number of symbol/NSTEP bins if(nsps.ge.6912) jz=(txt+2.0)*12000.0/istep !For TR 60 s and higher + ftol=ntol ia=ntol/df ia2=max(ia,10*mode_q65,nint(100.0/df)) nsmo=int(0.7*mode_q65*mode_q65) @@ -95,7 +96,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & allocate(s1(iz,jz)) allocate(s3(-64:LL-65,63)) allocate(ccf1(-ia2:ia2)) - allocate(ccf2(-ia2:ia2)) + allocate(ccf2(iz)) if(LL.ne.LL0 .or. iz.ne.iz0 .or. jz.ne.jz0 .or. lclearave) then if(allocated(s1a)) deallocate(s1a) allocate(s1a(iz,jz,0:1)) @@ -149,14 +150,14 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & endif ! Get 2d CCF and ccf2 using sync symbols only - call q65_ccf_22(s1,iz,jz,nfqso,ia2,ipk,jpk,f0a,xdta,ccf2) + call q65_ccf_22(s1,iz,jz,nfqso,ipk,jpk,f0a,xdta,ccf2) if(idec.lt.0) then f0=f0a xdt=xdta endif ! Estimate rms on ccf2 baseline - call q65_sync_curve(ccf2,ia2,rms2) + call q65_sync_curve(ccf2,1,iz,rms2) smax=maxval(ccf2) snr1=0. if(rms2.gt.0) snr1=smax/rms2 @@ -179,7 +180,7 @@ subroutine q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, & width=df*(i2-i1) if(ncw.eq.0) ccf1=0. - call q65_write_red(ia2,nfqso,xdt,ccf1,ccf2) + call q65_write_red(iz,ia2,xdt,ccf1,ccf2) if(iavg.eq.2) then call q65_dec_q012(s3,LL,snr2,dat4,idec,decoded) @@ -385,20 +386,19 @@ subroutine q65_ccf_85(s1,iz,jz,nfqso,ia,ia2,ipk,jpk,f0,xdt,imsg_best,ccf1) return end subroutine q65_ccf_85 -subroutine q65_ccf_22(s1,iz,jz,nfqso,ia2,ipk,jpk,f0,xdt,ccf2) +subroutine q65_ccf_22(s1,iz,jz,nfqso,ipk,jpk,f0,xdt,ccf2) ! Attempt synchronization using only the 22 sync symbols. Return ccf2 ! for the "orange sync curve". - + real s1(iz,jz) - real ccf2(-ia2:ia2) + real ccf2(iz) !Orange sync curve ccfbest=0. ibest=0 lagpk=0 lagbest=0 - do i=-ia2,ia2 - if(i0+i.lt.1 .or. i0+i.gt.iz) cycle + do i=1,iz ccfmax=0. do lag=lag1,lag2 ccft=0. @@ -406,7 +406,7 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ia2,ipk,jpk,f0,xdt,ccf2) n=NSTEP*(k-1) + 1 j=n+lag+j0 if(j.ge.1 .and. j.le.jz) then - ccft=ccft + sync(k)*s1(i0+i,j) + ccft=ccft + sync(k)*s1(i,j) endif enddo if(ccft.gt.ccfmax) then @@ -415,14 +415,14 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ia2,ipk,jpk,f0,xdt,ccf2) endif enddo ccf2(i)=ccfmax - if(ccfmax.gt.ccfbest) then + if(ccfmax.gt.ccfbest .and. abs(i*df-nfqso).le.ftol) then ccfbest=ccfmax ibest=i lagbest=lagpk endif enddo - ipk=ibest + ipk=ibest - i0 jpk=lagbest f0=nfqso + ipk*df xdt=jpk*dtstep @@ -508,21 +508,24 @@ subroutine q65_s1_to_s3(s1,iz,jz,ipk,jpk,LL,mode_q65,sync,s3) return end subroutine q65_s1_to_s3 -subroutine q65_write_red(ia2,nfqso,xdt,ccf1,ccf2) +subroutine q65_write_red(iz,ia2,xdt,ccf1,ccf2) ! Write data for the red and orange sync curves to LU 17. real ccf1(-ia2:ia2) - real ccf2(-ia2:ia2) + real ccf2(iz) - call q65_sync_curve(ccf1,ia2,rms1) - call q65_sync_curve(ccf2,ia2,rms2) + call q65_sync_curve(ccf1,-ia2,ia2,rms1) + call q65_sync_curve(ccf2,1,iz,rms2) rewind 17 - do i=-ia2,ia2 - freq=nfqso + i*df + do i=1,iz + freq=i*df + ii=i-i0 if(freq.ge.float(nfa) .and. freq.le.float(nfb)) then - write(17,1100) freq,ccf1(i),xdt,ccf2(i) + ccf1a=-99.0 + if(ii.ge.-ia2 .and. ii.le.ia2) ccf1a=ccf1(ii) + write(17,1100) freq,ccf1a,xdt,ccf2(i) 1100 format(4f10.3) endif enddo @@ -530,19 +533,19 @@ subroutine q65_write_red(ia2,nfqso,xdt,ccf1,ccf2) return end subroutine q65_write_red -subroutine q65_sync_curve(ccf1,ia2,rms1) +subroutine q65_sync_curve(ccf1,ia,ib,rms1) ! Condition the red or orange sync curve for plotting. - real ccf1(-ia2:ia2) + real ccf1(ia:ib) - ic=ia2/4; + ic=(ib-ia)/8; nsum=2*(ic+1) - base1=(sum(ccf1(-ia2:-ia2+ic)) + sum(ccf1(ia2-ic:ia2)))/nsum - ccf1(-ia2:ia2)=ccf1(-ia2:ia2)-base1 - sq=dot_product(ccf1(-ia2:-ia2+ic),ccf1(-ia2:-ia2+ic)) + & - dot_product(ccf1(ia2-ic:ia2),ccf1(ia2-ic:ia2)) + base1=(sum(ccf1(ia:ia+ic)) + sum(ccf1(ib-ic:ib)))/nsum + ccf1=ccf1-base1 + sq=dot_product(ccf1(ia:ia+ic),ccf1(ia:ia+ic)) + & + dot_product(ccf1(ib-ic:ib),ccf1(ib-ic:ib)) rms1=0. if(nsum.gt.0) rms1=sqrt(sq/nsum) if(rms1.gt.0.0) ccf1=2.0*ccf1/rms1 diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 65d82c336..ca197f109 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -276,6 +276,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) if(bRed and (m_bQ65_Sync or m_bQ65_MultiSync)) { //Plot the Q65 red or orange sync curve int k=0; + int k2=0; std::ifstream f; f.open(m_redFile.toLatin1()); if(f) { @@ -286,11 +287,14 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) if(f.eof()) break; x=XfromFreq(freq); // if(m_bQ65_MultiSync) sync=sync2; - y=m_h2*(0.9 - 0.09*gain2d*sync) - m_plot2dZero; - LineBuf2[k].setX(x); - LineBuf2[k].setY(y); - y=m_h2*(0.9 - 0.09*gain2d*sync2) - m_plot2dZero - 10; - LineBuf3[k].setX(x); + if(sync>-99.0) { + y=m_h2*(0.9 - 0.09*gain2d*sync) - m_plot2dZero - 10; + LineBuf2[k2].setX(x); //Red sync curve + LineBuf2[k2].setY(y); + k2++; + } + y=m_h2*(0.9 - 0.09*gain2d*sync2) - m_plot2dZero; + LineBuf3[k].setX(x); //Orange sync curve LineBuf3[k].setY(y); k++; } @@ -298,7 +302,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) QPen pen0(Qt::red,2); // if(m_bQ65_MultiSync) pen0.setColor("orange"); painter2D.setPen(pen0); - painter2D.drawPolyline(LineBuf2,k); + painter2D.drawPolyline(LineBuf2,k2); pen0.setColor("orange"); painter2D.setPen(pen0); painter2D.drawPolyline(LineBuf3,k); From e711aa23da41f8b62c8b3dae1fd0906cbb1e41c5 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 31 Jan 2021 14:44:06 -0500 Subject: [PATCH 412/426] Don't display red sync curve where it's equal to 0.0. --- widgets/plotter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index ca197f109..12ee7885d 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -287,7 +287,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) if(f.eof()) break; x=XfromFreq(freq); // if(m_bQ65_MultiSync) sync=sync2; - if(sync>-99.0) { + if(sync > -99.0 and sync != 0.0) { y=m_h2*(0.9 - 0.09*gain2d*sync) - m_plot2dZero - 10; LineBuf2[k2].setX(x); //Red sync curve LineBuf2[k2].setY(y); From 6519c07a71fbef5df3e07a6c3b742757d453f8ab Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sun, 31 Jan 2021 15:10:50 -0500 Subject: [PATCH 413/426] Working toward a good multi-decode solution ... --- lib/qra/q65/q65.f90 | 8 ++++++-- widgets/plotter.cpp | 5 +++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index d5e9b6a97..ff62be1b5 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -393,7 +393,9 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ipk,jpk,f0,xdt,ccf2) real s1(iz,jz) real ccf2(iz) !Orange sync curve + real, allocatable :: xdt2(:) + allocate(xdt2(iz)) ccfbest=0. ibest=0 lagpk=0 @@ -415,6 +417,7 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ipk,jpk,f0,xdt,ccf2) endif enddo ccf2(i)=ccfmax + xdt2(i)=lagpk*dtstep if(ccfmax.gt.ccfbest .and. abs(i*df-nfqso).le.ftol) then ccfbest=ccfmax ibest=i @@ -519,14 +522,15 @@ subroutine q65_write_red(iz,ia2,xdt,ccf1,ccf2) call q65_sync_curve(ccf2,1,iz,rms2) rewind 17 + write(17,1000) xdt do i=1,iz freq=i*df ii=i-i0 if(freq.ge.float(nfa) .and. freq.le.float(nfb)) then ccf1a=-99.0 if(ii.ge.-ia2 .and. ii.le.ia2) ccf1a=ccf1(ii) - write(17,1100) freq,ccf1a,xdt,ccf2(i) -1100 format(4f10.3) + write(17,1000) freq,ccf1a,ccf2(i) +1000 format(3f10.3) endif enddo diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index 12ee7885d..ed1ed9d86 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -281,9 +281,10 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) f.open(m_redFile.toLatin1()); if(f) { int x,y; - float freq,sync,xdt,sync2; + float freq,xdt,sync,sync2; + f >> xdt; for(int i=0; i<99999; i++) { - f >> freq >> sync >> xdt >> sync2; + f >> freq >> sync >> sync2; if(f.eof()) break; x=XfromFreq(freq); // if(m_bQ65_MultiSync) sync=sync2; From 86ada8c9d4b038dd27b7be5cdecb780d209fd48b Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 1 Feb 2021 10:54:04 -0500 Subject: [PATCH 414/426] Working toward multi-decode capability. --- lib/q65_decode.f90 | 17 ++++++++--------- lib/qra/q65/q65.f90 | 24 +++++++++++++++++++++++- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 7f9b516ec..9df5c3822 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -135,18 +135,12 @@ contains call timer('q65_dec0',1) if(idec.ge.0) then - dtdec=xdt !We have a list-decode result + dtdec=xdt !We have a list-decode result at nfqso f0dec=f0 go to 100 endif -! if(snr1.lt.2.8) then -! dtdec=0. !No reliable sync, abandon decoding attempt -! f0dec=0. -! go to 100 -! endif - -! Prepare for a single-period decode woth iaptype = 0, 1, or 2 (also 4?) +! 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 @@ -203,7 +197,7 @@ contains go to 100 endif -! There was no 'q3n' decode. Try for a 'q[012]n' decode. +! 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. @@ -268,6 +262,11 @@ contains endif navg0=1000*navg(0) + navg(1) +! do icand=1,ncand +! write(72,3072) icand,candidates(icand,1:3) +!3072 format(i2,3f10.3) +! enddo + return end subroutine decode diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index ff62be1b5..197e9de11 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -12,10 +12,11 @@ module q65 integer codewords(63,206) integer ibwa,ibwb,ncw,nsps,mode_q65,nfa,nfb integer idf,idt,ibw - integer istep,nsmo,lag1,lag2,npasses,nused,iseq + integer istep,nsmo,lag1,lag2,npasses,nused,iseq,ncand integer i0,j0 integer navg(0:1) logical lnewdat + real candidates(20,3) !snr, xdt, and f0 of top candidates real,allocatable,save :: s1a(:,:,:) !Cumulative symbol spectra real sync(85) !sync vector real df,dtstep,dtdec,f0dec,ftol @@ -394,8 +395,11 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ipk,jpk,f0,xdt,ccf2) real s1(iz,jz) real ccf2(iz) !Orange sync curve real, allocatable :: xdt2(:) + integer, allocatable :: indx(:) allocate(xdt2(iz)) + allocate(indx(iz)) + ccfbest=0. ibest=0 lagpk=0 @@ -425,11 +429,29 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ipk,jpk,f0,xdt,ccf2) endif enddo +! Parameters for the top candidate: ipk=ibest - i0 jpk=lagbest f0=nfqso + ipk*df xdt=jpk*dtstep +! Save parameters for best candidates + i1=nfa/df + i2=nfb/df + jzz=i2-i1+1 + call pctile(ccf2(i1:i2),jzz,40,base) + ccf2=ccf2/base + call indexx(ccf2(i1:i2),jzz,indx) + ncand=0 + do j=1,20 + i=indx(jzz-j+1)+i1-1 + if(ccf2(i).lt.3.0) exit + ncand=ncand+1 + candidates(ncand,1)=ccf2(i) + candidates(ncand,2)=xdt2(i) + candidates(ncand,3)=i*df + enddo + return end subroutine q65_ccf_22 From 72a415889882356ec411885ba750dbc84e3c816f Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 1 Feb 2021 13:28:59 -0500 Subject: [PATCH 415/426] First working multi-decode procedure for Q65. --- lib/ana64.f90 | 8 +-- lib/q65_decode.f90 | 80 ++++++++++++++++++++++++--- lib/qra/q65/q65.f90 | 4 +- lib/qra64a.f90 | 128 -------------------------------------------- 4 files changed, 80 insertions(+), 140 deletions(-) delete mode 100644 lib/qra64a.f90 diff --git a/lib/ana64.f90 b/lib/ana64.f90 index 1f3b54820..f7586756e 100644 --- a/lib/ana64.f90 +++ b/lib/ana64.f90 @@ -1,16 +1,16 @@ -subroutine ana64(dd,npts,c0) +subroutine ana64(iwave,npts,c0) use timer_module, only: timer - real dd(npts) !Raw data at 12000 Hz + integer*2 iwave(npts) !Raw data at 12000 Hz complex c0(0:npts-1) !Complex data at 6000 Hz save nfft1=npts nfft2=nfft1/2 df1=12000.0/nfft1 - fac=2.0/nfft1 - c0(0:npts-1)=fac*dd(1:npts) + fac=2.0/(32767.0*nfft1) + c0(0:npts-1)=fac*iwave(1:npts) call four2a(c0,nfft1,1,-1,1) !Forward c2c FFT c0(nfft2/2+1:nfft2-1)=0. c0(0)=0.5*c0(0) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 9df5c3822..9d5f0dfb5 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -144,9 +144,7 @@ contains 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 - fac=1.0/32767.0 - dd=fac*iwave(1:npts) - call ana64(dd,npts,c00) !Convert to complex c00() at 6000 Sa/s + 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 @@ -262,10 +260,78 @@ contains endif navg0=1000*navg(0) + navg(1) -! do icand=1,ncand -! write(72,3072) icand,candidates(icand,1:3) -!3072 format(i2,3f10.3) -! enddo + 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) +! idec=-1 !### TEMPORARY ### + 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 + nsnr=nint(snr2) + call this%callback(nutc,snr1,nsnr,dtdec,f0dec,decoded, & + idec,nused,ntrperiod) + if(iand(ndepth,128).ne.0) call q65_clravg !AutoClrAvg after decode + 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. + c6=hiscall(1:6) + if(c6.eq.' ') c6=' ' + c4=hisgrid(1:4) + if(c4.eq.' ') c4=' ' + if(ntrperiod.ge.60) then + write(22,1022) nutc,ntrperiod,nsubmode,nQSOprogress,idec, & + nused,iaptype,irc,idf,idt,ibw,xdt,f0,snr1,snr2, & + tdecode,mycall(1:6),c6,c4,trim(decoded) + else + write(22,1023) nutc,ntrperiod,nsubmode,nQSOprogress,idec, & + nused,iaptype,irc,idf,idt,ibw,xdt,f0,snr1,snr2, & + tdecode,mycall(1:6),c6,c4,trim(decoded) + endif + close(22) + endif + endif + enddo return end subroutine decode diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 197e9de11..7b3053e4c 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -446,10 +446,12 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ipk,jpk,f0,xdt,ccf2) do j=1,20 i=indx(jzz-j+1)+i1-1 if(ccf2(i).lt.3.0) exit + f=i*df + if(f.ge.(nfqso-ftol) .and. f.le.(nfqso+ftol)) cycle ncand=ncand+1 candidates(ncand,1)=ccf2(i) candidates(ncand,2)=xdt2(i) - candidates(ncand,3)=i*df + candidates(ncand,3)=f enddo return diff --git a/lib/qra64a.f90 b/lib/qra64a.f90 deleted file mode 100644 index d4189827d..000000000 --- a/lib/qra64a.f90 +++ /dev/null @@ -1,128 +0,0 @@ -subroutine qra64a(dd,npts,nf1,nf2,nfqso,ntol,mode64,minsync,ndepth, & - emedelay,mycall_12,hiscall_12,hisgrid_6,sync,nsnr,dtx,nfreq,decoded,nft) - - use packjt - use timer_module, only: timer - - parameter (NMAX=60*12000,LN=1152*63) - character decoded*22 - character*12 mycall_12,hiscall_12 - character*6 mycall,hiscall,hisgrid_6 - character*4 hisgrid - logical ltext - complex c00(0:720000) !Analytic signal for dd() - real dd(NMAX) !Raw data sampled at 12000 Hz - integer dat4(12) !Decoded message (as 12 integers) - data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/ - save - - call timer('qra64a ',0) - irc=-1 - decoded=' ' - nft=99 - if(nfqso.lt.nf1 .or. nfqso.gt.nf2) go to 900 - - mycall=mycall_12(1:6) !### May need fixing? ### - hiscall=hiscall_12(1:6) - hisgrid=hisgrid_6(1:4) - call packcall(mycall,nc1,ltext) - call packcall(hiscall,nc2,ltext) - call packgrid(hisgrid,ng2,ltext) - nSubmode=0 - if(mode64.eq.2) nSubmode=1 - if(mode64.eq.4) nSubmode=2 - if(mode64.eq.8) nSubmode=3 - if(mode64.eq.16) nSubmode=4 - b90=1.0 - nFadingModel=1 - maxaptype=4 - if(iand(ndepth,64).ne.0) maxaptype=5 - call qra_params(ndepth,maxaptype,idfmax,idtmax,ibwmin,ibwmax,maxdist) - 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(s3dummy,nc1,nc2,ng2,naptype,1,nSubmode,b90, & - nFadingModel,dat4,snr2,irc) - enddo - nc1z=nc1 - nc2z=nc2 - ng2z=ng2 - maxaptypez=maxaptype - endif - naptype=maxaptype - - call ana64(dd,npts,c00) - - call timer('sync64 ',0) - call sync64(c00,nf1,nf2,nfqso,ntol,minsync,mode64,emedelay,dtx,f0, & - jpk0,sync,sync2,width) - call timer('sync64 ',1) - nfreq=nint(f0) - if(mode64.eq.1 .and. minsync.ne.-1 .and. (sync-7.0).lt.minsync) go to 900 - - nsps=6912 - call timer('qraloops',0) - call qra_loops(c00,npts/2,nsps,64,mode64,nsubmode,nFadingModel, & - ndepth,nc1,nc2,ng2,naptype,jpk0,dtx,f0,width,snr2,irc,dat4) - call timer('qraloops',1) - - 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. - endif - nfreq=nint(f0) - -900 if(irc.lt.0) then - sy=max(1.0,sync) - if(nSubmode.eq.0) nsnr=nint(10.0*log10(sy)-35.0) !A - if(nSubmode.eq.1) nsnr=nint(10.0*log10(sy)-34.0) !B - if(nSubmode.eq.2) nsnr=nint(10.0*log10(sy)-29.0) !C - if(nSubmode.eq.3) nsnr=nint(10.0*log10(sy)-29.0) !D - if(nSubmode.eq.4) nsnr=nint(10.0*log10(sy)-24.0) !E - endif - call timer('qra64a ',1) - - return -end subroutine qra64a - -subroutine qra_params(ndepth,maxaptype,idf0max,idt0max,ibwmin,ibwmax,maxdist) - -! If file qra_params is present in CWD, read decoding params from it. - - integer iparam(7) - logical first,ex -! data iparam/3,5,11,11,0,11,60/ !Maximum effort - data iparam/3,5,7,7,0,4,15/ !Default values - data first/.true./ - save first,iparam - - if(first) then - inquire(file='qra_params',exist=ex) - if(ex) then - open(29,file='qra_params',status='old') - read(29,*) iparam - close(29) - endif - first=.false. - endif - ndepth=iparam(1) - maxaptype=iparam(2) - idf0max=iparam(3) - idt0max=iparam(4) - ibwmin=iparam(5) - ibwmax=iparam(6) - maxdist=iparam(7) - - return -end subroutine qra_params From 34cb4c502d4be6ee7b5c188a81155d69ecb1ecd2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 1 Feb 2021 14:23:49 -0500 Subject: [PATCH 416/426] Eliminate some redundant candidates. --- lib/qra/q65/q65.f90 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 7b3053e4c..94e4d3ba1 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -447,7 +447,9 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ipk,jpk,f0,xdt,ccf2) i=indx(jzz-j+1)+i1-1 if(ccf2(i).lt.3.0) exit f=i*df - if(f.ge.(nfqso-ftol) .and. f.le.(nfqso+ftol)) cycle + if(f.ge.(nfqso-ftol) .and. f.le.(nfqso+ftol)) cycle + biggest=maxval(ccf2(i-3:i+3)) + if(ccf2(i).ne.biggest) cycle ncand=ncand+1 candidates(ncand,1)=ccf2(i) candidates(ncand,2)=xdt2(i) From 558b42694491bd05483b18ad28ee5f58cc71d656 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 1 Feb 2021 15:28:22 -0500 Subject: [PATCH 417/426] Remove Q65_MultiSync as a separate option. We always show both red and orange sync curves. --- widgets/plotter.cpp | 8 +++----- widgets/plotter.h | 4 +--- widgets/widegraph.cpp | 7 ------- widgets/widegraph.ui | 5 ----- 4 files changed, 4 insertions(+), 20 deletions(-) diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index ed1ed9d86..14c13a50d 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -141,7 +141,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) //move current data down one line (must do this before attaching a QPainter object) if(bScroll and !m_bReplot) m_WaterfallPixmap.scroll(0,1,0,0,m_w,m_h1); QPainter painter1(&m_WaterfallPixmap); - if(m_bFirst or bRed or (!m_bQ65_Sync and !m_bQ65_MultiSync) or m_mode!=m_mode0 + if(m_bFirst or bRed or !m_bQ65_Sync or m_mode!=m_mode0 or m_bResized or m_rxFreq!=m_rxFreq0) { m_2DPixmap = m_OverlayPixmap.copy(0,0,m_w,m_h2); m_bFirst=false; @@ -230,7 +230,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) } - if(i==iz-1 and !m_bQ65_Sync and !m_bQ65_MultiSync) { + if(i==iz-1 and !m_bQ65_Sync) { painter2D.drawPolyline(LineBuf,j); } LineBuf[j].setX(i); @@ -274,7 +274,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) painter2D.drawText(x1-4,y,"73"); } - if(bRed and (m_bQ65_Sync or m_bQ65_MultiSync)) { //Plot the Q65 red or orange sync curve + if(bRed and m_bQ65_Sync) { //Plot the Q65 red or orange sync curve int k=0; int k2=0; std::ifstream f; @@ -287,7 +287,6 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) f >> freq >> sync >> sync2; if(f.eof()) break; x=XfromFreq(freq); -// if(m_bQ65_MultiSync) sync=sync2; if(sync > -99.0 and sync != 0.0) { y=m_h2*(0.9 - 0.09*gain2d*sync) - m_plot2dZero - 10; LineBuf2[k2].setX(x); //Red sync curve @@ -301,7 +300,6 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) } f.close(); QPen pen0(Qt::red,2); -// if(m_bQ65_MultiSync) pen0.setColor("orange"); painter2D.setPen(pen0); painter2D.drawPolyline(LineBuf2,k2); pen0.setColor("orange"); diff --git a/widgets/plotter.h b/widgets/plotter.h index a4a8d6e04..582d36451 100644 --- a/widgets/plotter.h +++ b/widgets/plotter.h @@ -82,8 +82,7 @@ public: bool Reference() const {return m_bReference;} void setQ65_Sync(bool b) {m_bQ65_Sync = b;} bool Q65_Sync() const {return m_bQ65_Sync;} - void setQ65_MultiSync(bool b) {m_bQ65_MultiSync = b;} - bool Q65_MultiSync() const {return m_bQ65_MultiSync;} void drawRed(int ia, int ib, float swide[]); + void drawRed(int ia, int ib, float swide[]); void setVHF(bool bVHF); void setRedFile(QString fRed); void setFST4_FreqRange(int fLow,int fHigh); @@ -117,7 +116,6 @@ private: bool m_bReference; bool m_bReference0; bool m_bQ65_Sync; - bool m_bQ65_MultiSync; bool m_bVHF; bool m_bSingleDecode; bool m_bFirst=true; diff --git a/widgets/widegraph.cpp b/widgets/widegraph.cpp index 6f6736029..bb53279cd 100644 --- a/widgets/widegraph.cpp +++ b/widgets/widegraph.cpp @@ -71,13 +71,11 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) : ui->widePlot->setLinearAvg(m_settings->value("LinearAvg",false).toBool()); ui->widePlot->setReference(m_settings->value("Reference",false).toBool()); ui->widePlot->setQ65_Sync(m_settings->value("Q65_Sync",false).toBool()); - ui->widePlot->setQ65_MultiSync(m_settings->value("Q65_MultiSync",false).toBool()); if(ui->widePlot->current()) ui->spec2dComboBox->setCurrentIndex(0); if(ui->widePlot->cumulative()) ui->spec2dComboBox->setCurrentIndex(1); if(ui->widePlot->linearAvg()) ui->spec2dComboBox->setCurrentIndex(2); if(ui->widePlot->Reference()) ui->spec2dComboBox->setCurrentIndex(3); if(ui->widePlot->Q65_Sync()) ui->spec2dComboBox->setCurrentIndex(4); - if(ui->widePlot->Q65_MultiSync()) ui->spec2dComboBox->setCurrentIndex(5); int nbpp=m_settings->value("BinsPerPixel",2).toInt(); ui->widePlot->setBinsPerPixel(nbpp); ui->sbPercent2dPlot->setValue(m_Percent2DScreen); @@ -135,7 +133,6 @@ void WideGraph::saveSettings() //saveS m_settings->setValue ("LinearAvg", ui->widePlot->linearAvg()); m_settings->setValue ("Reference", ui->widePlot->Reference()); m_settings->setValue ("Q65_Sync", ui->widePlot->Q65_Sync()); - m_settings->setValue ("Q65_MultiSync", ui->widePlot->Q65_MultiSync()); m_settings->setValue ("BinsPerPixel", ui->widePlot->binsPerPixel ()); m_settings->setValue ("StartFreq", ui->widePlot->startFreq ()); m_settings->setValue ("WaterfallPalette", m_waterfallPalette); @@ -323,7 +320,6 @@ void WideGraph::on_spec2dComboBox_currentIndexChanged(int index) ui->widePlot->setLinearAvg(false); ui->widePlot->setReference(false); ui->widePlot->setQ65_Sync(false); - ui->widePlot->setQ65_MultiSync(false); ui->smoSpinBox->setEnabled(false); switch (index) { @@ -343,9 +339,6 @@ void WideGraph::on_spec2dComboBox_currentIndexChanged(int index) case 4: ui->widePlot->setQ65_Sync(true); break; - case 5: - ui->widePlot->setQ65_MultiSync(true); - break; } replot(); } diff --git a/widgets/widegraph.ui b/widgets/widegraph.ui index 202482f54..758cad8c4 100644 --- a/widgets/widegraph.ui +++ b/widgets/widegraph.ui @@ -340,11 +340,6 @@ Q65_Sync - - - Q65_MultiSync - - From 7d069c5617dab7239f59dc4a6a9f0660d9ca2f0f Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 1 Feb 2021 15:52:25 -0500 Subject: [PATCH 418/426] Don't get too close to the Wide Graps frequency limits. --- lib/qra/q65/q65.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 94e4d3ba1..9735d2d7b 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -436,8 +436,8 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ipk,jpk,f0,xdt,ccf2) xdt=jpk*dtstep ! Save parameters for best candidates - i1=nfa/df - i2=nfb/df + i1=max(nfa,100)/df + i2=min(nfb,4900)/df jzz=i2-i1+1 call pctile(ccf2(i1:i2),jzz,40,base) ccf2=ccf2/base From 2ed499c779e6a62952d8c0dbd080d83e96c2d675 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Mon, 1 Feb 2021 23:29:41 +0000 Subject: [PATCH 419/426] Ensure loopback is used for outgoing UDP if no others selected --- Configuration.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Configuration.cpp b/Configuration.cpp index 2b668f902..197018ad4 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -2992,13 +2992,18 @@ void Configuration::impl::load_network_interfaces (CheckableItemComboBox * combo auto flags = QNetworkInterface::IsUp | QNetworkInterface::CanMulticast; if ((net_if.flags () & flags) == flags) { + bool check_it = current.contains (net_if.name ()); if (net_if.flags () & QNetworkInterface::IsLoopBack) { loopback_interface_name_ = net_if.name (); + if (!current.size ()) + { + check_it = true; + } } auto item = combo_box->addCheckItem (net_if.humanReadableName () , net_if.name () - , current.contains (net_if.name ()) ? Qt::Checked : Qt::Unchecked); + , check_it ? Qt::Checked : Qt::Unchecked); auto tip = QString {"name(index): %1(%2) - %3"}.arg (net_if.name ()).arg (net_if.index ()) .arg (net_if.flags () & QNetworkInterface::IsUp ? "Up" : "Down"); auto hw_addr = net_if.hardwareAddress (); From 5850317de0b08b0c284d690bd97e180f96a29d7e Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 2 Feb 2021 09:27:40 -0500 Subject: [PATCH 420/426] Speed improvements for Q65 multi-decode. --- lib/q65_decode.f90 | 48 ++++++++++++++++++++++----------------------- lib/qra/q65/q65.f90 | 8 ++++++-- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 9d5f0dfb5..2d83d0728 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -60,7 +60,8 @@ contains character*77 c77 character*78 c78 character*6 cutc - character c6*6,c4*4 + character c6*6,c4*4,cmode*4 + character*80 fmt integer*2 iwave(NMAX) !Raw data real, allocatable :: dd(:) !Raw data integer dat4(13) !Decoded message as 12 6-bit integers @@ -113,9 +114,12 @@ contains baud=12000.0/nsps this%callback => callback nFadingModel=1 - ibwa=max(1,int(1.8*log(baud*mode_q65)) + 2) - ibwb=min(10,ibwa+5) - if(iand(ndepth,3).eq.3) then + ibwa=max(1,int(1.8*log(baud*mode_q65)) + 1) + ibwb=min(10,ibwa+3) + if(iand(ndepth,3).ge.2) then + ibwa=max(1,int(1.8*log(baud*mode_q65)) + 2) + ibwb=min(10,ibwa+5) + else if(iand(ndepth,3).eq.3) then ibwa=max(1,ibwa-1) ibwb=min(10,ibwb+1) endif @@ -233,21 +237,18 @@ contains 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=' ' c4=hisgrid(1:4) if(c4.eq.' ') c4=' ' - if(ntrperiod.ge.60) then - write(22,1022) nutc,ntrperiod,nsubmode,nQSOprogress,idec, & - nused,iaptype,irc,idf,idt,ibw,xdt,f0,snr1,snr2, & - tdecode,mycall(1:6),c6,c4,trim(decoded) -1022 format(i6.4,10i3,f6.2,f7.1,f7.2,f6.1,f6.2,1x,a6,1x,a6,1x,a4,1x,a) - else - write(22,1023) nutc,ntrperiod,nsubmode,nQSOprogress,idec, & - nused,iaptype,irc,idf,idt,ibw,xdt,f0,snr1,snr2, & - tdecode,mycall(1:6),c6,c4,trim(decoded) -1023 format(i6.6,10i3,f6.2,f7.1,f7.2,f6.1,f6.2,1x,a6,1x,a6,1x,a4,1x,a) - endif + fmt='(i6.4,1x,a4,5i2,3i3,f6.2,f7.1,f7.2,f6.1,f6.2,'// & + '1x,a6,1x,a6,1x,a4,1x,a)' + if(ntrperiod.le.30) fmt(5:5)='6' + write(22,fmt) nutc,cmode,nQSOprogress,idec,idf,idt,ibw,nused, & + icand,ncand,xdt,f0,snr1,snr2,tdecode,mycall(1:6),c6,c4, & + trim(decoded) close(22) endif else @@ -315,19 +316,18 @@ contains 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=' ' c4=hisgrid(1:4) if(c4.eq.' ') c4=' ' - if(ntrperiod.ge.60) then - write(22,1022) nutc,ntrperiod,nsubmode,nQSOprogress,idec, & - nused,iaptype,irc,idf,idt,ibw,xdt,f0,snr1,snr2, & - tdecode,mycall(1:6),c6,c4,trim(decoded) - else - write(22,1023) nutc,ntrperiod,nsubmode,nQSOprogress,idec, & - nused,iaptype,irc,idf,idt,ibw,xdt,f0,snr1,snr2, & - tdecode,mycall(1:6),c6,c4,trim(decoded) - endif + fmt='(i6.4,1x,a4,5i2,3i3,f6.2,f7.1,f7.2,f6.1,f6.2,'// & + '1x,a6,1x,a6,1x,a4,1x,a)' + if(ntrperiod.le.30) fmt(5:5)='6' + write(22,fmt) nutc,cmode,nQSOprogress,idec,idf,idt,ibw,nused, & + icand,ncand,xdt,f0,snr1,snr2,tdecode,mycall(1:6),c6,c4, & + trim(decoded) close(22) endif endif diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 9735d2d7b..151bda712 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -443,17 +443,21 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ipk,jpk,f0,xdt,ccf2) ccf2=ccf2/base call indexx(ccf2(i1:i2),jzz,indx) ncand=0 + maxcand=20 do j=1,20 i=indx(jzz-j+1)+i1-1 - if(ccf2(i).lt.3.0) exit + if(ccf2(i).lt.3.4) exit !Candidate limit f=i*df if(f.ge.(nfqso-ftol) .and. f.le.(nfqso+ftol)) cycle - biggest=maxval(ccf2(i-3:i+3)) + i3=i-67*mode_q65 + i4=i+3*mode_q65 + biggest=maxval(ccf2(i3:i4)) if(ccf2(i).ne.biggest) cycle ncand=ncand+1 candidates(ncand,1)=ccf2(i) candidates(ncand,2)=xdt2(i) candidates(ncand,3)=f + if(ncand.ge.maxcand) exit enddo return From ba2f2366077b256b85ed2ffdb546aae00d7a35a1 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 2 Feb 2021 14:36:07 +0000 Subject: [PATCH 421/426] Updated User Guide Linux install instructions --- doc/common/links.adoc | 1 + doc/user_guide/en/install-linux.adoc | 28 ++++++++++++---------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/doc/common/links.adoc b/doc/common/links.adoc index c9473c392..fd13cd5a5 100644 --- a/doc/common/links.adoc +++ b/doc/common/links.adoc @@ -52,6 +52,7 @@ d). Edit lines as needed. Keeping them in alphabetic order help see dupes. :debian32: https://physics.princeton.edu/pulsar/K1JT/wsjtx_{VERSION}_i386.deb[wsjtx_{VERSION}_i386.deb] :debian64: https://physics.princeton.edu/pulsar/K1JT/wsjtx_{VERSION}_amd64.deb[wsjtx_{VERSION}_amd64.deb] :raspbian: https://physics.princeton.edu/pulsar/K1JT/wsjtx_{VERSION}_armhf.deb[wsjtx_{VERSION}_armhf.deb] +:raspbian64: https://physics.princeton.edu/pulsar/K1JT/wsjtx_{VERSION}_arm64.deb[wsjtx_{VERSION}_arm64.deb] :debian: https://www.debian.org/[Debian] :dev_guide: https://www.physics.princeton.edu/pulsar/K1JT/wsjtx-doc/wsjt-dev-guide.html[Dev-Guide] :devsvn: https://sourceforge.net/p/wsjt/wsjt/HEAD/tree/[Devel-SVN] diff --git a/doc/user_guide/en/install-linux.adoc b/doc/user_guide/en/install-linux.adoc index ff108ef34..c34baaaa1 100644 --- a/doc/user_guide/en/install-linux.adoc +++ b/doc/user_guide/en/install-linux.adoc @@ -1,6 +1,6 @@ // Status=edited -Debian, Ubuntu, and other Debian-based systems including Raspbian: +Debian, Ubuntu, and other Debian-based systems including Raspberry Pi OS: NOTE: The project team release binary installer packages targeted for one contemporary version of a Linux distribution. Although these may @@ -10,30 +10,31 @@ with the release for details of the targeted Linux distributions and versions. If the binary package is not compatible with your Linux distribution or version, you must build the application from sources. -* 32-bit: {debian32} +* 32-bit Intel/AMD: {debian32} - To install: + [example] sudo dpkg -i wsjtx_{VERSION}_i386.deb -- Uninstall: -+ -[example] -sudo dpkg -P wsjtx - -* 64-bit: {debian64} +* 64-bit Intel/AMD: {debian64} - To install: + [example] sudo dpkg -i wsjtx_{VERSION}_amd64.deb -* 32-bit: {raspbian} +* 32-bit ARM hardware FP: {raspbian} - To install: + [example] sudo dpkg -i wsjtx_{VERSION}_armhf.deb -- Uninstall: +* 64-bit ARM: {raspbian64} +- To install: ++ +[example] +sudo dpkg -i wsjtx_{VERSION}_arm64.deb + +* Uninstall for any of the above platforms: + [example] sudo dpkg -P wsjtx @@ -55,18 +56,13 @@ Fedora, CentOS, Red Hat, and other rpm-based systems: [example] sudo rpm -i wsjtx-{VERSION}-i686.rpm -- Uninstall: -+ -[example] -sudo rpm -e wsjtx - * 64-bit: {fedora64} - To install: + [example] sudo rpm -i wsjtx-{VERSION}-x86_64.rpm -- Uninstall: +* Uninstall for either of the above platforms: + [example] sudo rpm -e wsjtx From 14d18306e6754c215419839b197d38812139d733 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 2 Feb 2021 09:58:04 -0500 Subject: [PATCH 422/426] Use single_decode and lagain as intended in Q65. --- lib/decoder.f90 | 1 + lib/q65_decode.f90 | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index fb4e80c01..5a254a56b 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -208,6 +208,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) call my_q65%decode(q65_decoded,id2,params%nutc,params%ntr, & params%nsubmode,params%nfqso,params%ntol,params%ndepth, & params%nfa,params%nfb,logical(params%nclearave), & + single_decode,logical(params%nagain), & logical(params%newdat),params%emedelay,mycall,hiscall,hisgrid, & params%nQSOProgress,ncontest,logical(params%lapcqonly),navg0) call timer('dec_q65 ',1) diff --git a/lib/q65_decode.f90 b/lib/q65_decode.f90 index 2d83d0728..0bea7d8ee 100644 --- a/lib/q65_decode.f90 +++ b/lib/q65_decode.f90 @@ -27,8 +27,8 @@ module q65_decode contains subroutine decode(this,callback,iwave,nutc,ntrperiod,nsubmode,nfqso, & - ntol,ndepth,nfa0,nfb0,lclearave,lnewdat0,emedelay,mycall, & - hiscall,hisgrid,nQSOprogress,ncontest,lapcqonly,navg0) + ntol,ndepth,nfa0,nfb0,lclearave,single_decode,lagain,lnewdat0, & + emedelay,mycall,hiscall,hisgrid,nQSOprogress,ncontest,lapcqonly,navg0) ! Top-level routine that organizes the decoding of Q65 signals ! Input: iwave Raw data, i*2 @@ -67,6 +67,7 @@ contains integer dat4(13) !Decoded message as 12 6-bit integers integer dgen(13) logical lclearave,lnewdat0,lapcqonly,unpk77_success + logical single_decode,lagain complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s @@ -260,6 +261,7 @@ contains idec,0,ntrperiod) 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 @@ -333,7 +335,7 @@ contains endif enddo - return +900 return end subroutine decode end module q65_decode From 9ca57f9a5cd3302366520641f91f05241d245717 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Tue, 2 Feb 2021 11:39:29 -0500 Subject: [PATCH 423/426] Protect against a possible bounds error. --- lib/qra/q65/q65.f90 | 4 ++-- samples/QRA64/QRA64C/161113_0111.wav | Bin 1440228 -> 0 bytes 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 samples/QRA64/QRA64C/161113_0111.wav diff --git a/lib/qra/q65/q65.f90 b/lib/qra/q65/q65.f90 index 151bda712..106df8a3c 100644 --- a/lib/qra/q65/q65.f90 +++ b/lib/qra/q65/q65.f90 @@ -449,8 +449,8 @@ subroutine q65_ccf_22(s1,iz,jz,nfqso,ipk,jpk,f0,xdt,ccf2) if(ccf2(i).lt.3.4) exit !Candidate limit f=i*df if(f.ge.(nfqso-ftol) .and. f.le.(nfqso+ftol)) cycle - i3=i-67*mode_q65 - i4=i+3*mode_q65 + i3=max(1,i-67*mode_q65) + i4=min(iz,i+3*mode_q65) biggest=maxval(ccf2(i3:i4)) if(ccf2(i).ne.biggest) cycle ncand=ncand+1 diff --git a/samples/QRA64/QRA64C/161113_0111.wav b/samples/QRA64/QRA64C/161113_0111.wav deleted file mode 100644 index 03dec21bb3346972bc6bb27f296c50b36a8aa267..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1440228 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 +1_3Q) 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

A4u@~hBAygXS9PN0A`fL5CUe>*^}FGG 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 Hwk{MAFTNAjm|QOehe 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 z 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∓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
J?f+jY|K>8@^W*dWYnUy+mW6%^Im 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%0AoLV5 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 z zVN452?|-^+uq@0Y@-xHyPEI2A?k8@!ZaAdNXy!rQZuhhp%*+xew+vvEeH9j>(mrRl z1`EPkGLJXItth{;3#bxq+wDQOc(LGX5@=F-pLprqUd)I~K<>$;3)^$#N;G!Y!7wNZ zL-Jp?&okf%ysK)8#`;+BuKrBSWu}@ z|7HSlR(xeEm?fm;l{Pii8TTir5U1aAvzZ(56*QAckazzMlalJiPm5>Kv(e9`7v2pA zqjn(QBitFB4+Pyrk^(K0sJ5NLooJjw} zMZ3}TG9z_!^Gwe1HhFJD5}c-ay6r*wg_opgi0G%@T5@D{e1WNhIw-42iT

H$Y9% z9>&*Jh*Rb9&sBqo+S0Lh4qH9kcW%DOPN%rofrH^4db}#+c0Xk2T}y^xqJKU36+cH& zIBW&vPx$T&%WUMSR!K3r#F|4QJA+4W2Xv5k@JKqGf>Tgwc=ojZNIx{S)pPfV>&u0Z z8hmtNb(n<|rx($%5*vt@+*&74l`fYx(etg;M@*oWLVUh!uf)5?ibByW=e_N%RHN*e za0cmAw{aq!5>XxXCx@%ul_}b$@FEq(KcNV(1iMIu-+^PUyDDZ6vH6~$C&8I$r&2)G zohbiDUDZgg6~CH~!*juCQ%`;EEEJ{m9_qz(kvi^d;{;1^Y<>X^t|~QsTGU`iL==vhtgN4agw~fPH)uFZ<+=6d-h#9RYX-1?Wt&{P}{UXjk`*BaZ*QDcq8Rz^DR!K z@~AmZgi&-F>6p4^f=T?o913TC92qMo{Vz=;XRmuxRyL(cMv8EDoQCc_Qlzt;No*eL zY;%V~19(67Dn822a=l1`tI_&(#5Z(~y7nE_!gTUe#}ob1rl`1WkMY_5Plet`(LwGK zJAuhrH%;v$i`D&Bu-jyD{(+Y~N$%3~$#mVPH`}t}3-~Qx;z%o_mN1JH zPAbm(f#DHo7H=>mSS}46Y8G#|d_%Y6Nz5Y3IQ!5f{m$>TLhh6+ZF4;)tfYsbimnEy zsUy?Pk#0Tcee1&o!8l!A6hR3!27PfU__v+pNUEbv=r*?LKXg0xKzmgy{tP84c?D7fD%0ls~W*54MRLYp!a8Y}U{7?dx+8*(?v1w*UL?t`wOw%{RQgV;m z!WnJO`3L>#`X*V9z40g&MXz$tQa`Xo{*f6#F8tovn9_8o3VoNZvYXwFy1|O|ZoSBH zcQ3ofUCixU+T@U#82I9rymL@PY$u)hpp(|Qipu95@(wbF4jlDyj)e5RSr-XsnzqiRNYTh#*(faNw+j!8 zCeAFGL+6FE=-LN(fj)yJkwR^fp6 z8W|SptODIJEUs_qD|ifqTH@Apf8(eBm&~j;f@|^p;qNL-v}xoUp1!Zcx6zK2pjYaz zYlh{iz{jf#&NbvL(0>`zQXDeqU~K9=PAQFJukVG8~J-yhp*PL>Kt|XN+|oVG5ccA=^+}QBmW_Oi7w#(lhIVQxDjQrUHqkr_ z`p3KYtxaZ<9~27HKlN2pQGP*k)kC`Lirl7mb#`4Cc36J*A^MmD#zMVGulsTuHx^c1 zWg7FI%oMosonbCj&-t2tdPUt&UGm~i89Rh|b0N0R1!Z599@m)H_Rxt;G^|sTM7B*N z6@0*+wQR5y-Uw4vm5u%2KdKAw^Gx}R?&p6%o$^B0r^;L{cDZMx|3yAiugHxmEGDBC z*{gpKw(1?Sp|e9I!>Kk9Qs@r1r1-^86Kfnl0ePo`+ag*xp^Mkh-i~ET91#1&>>$0a zA-kr_c%16Uhs?VQu-!VK--X^fj9Y(hSTH!E6V+a?ms8yS4?gNtc;a(V9rV(#^ktO$ zi%B?Hf_D5O`|-itgzuQ@B8Ril=_lu#1pP&LGLUq>SJjEgd6?su!Ylq7-GU8bMSk-9 z!PMX@vz!fNj2h#6T=+F`Th&)7)kk(Z%8HTdNMx_qjdbqk|(XPz2texZs9+F3;IH+pFn`>?G1}m z^ZpR!{h6=MysG2hw42ypjCb=onI$Qs%-&xouC8U~a|&ljL*^<2p_5I9W|k->xUIeK z@j9muuc6>LCbqkCBE8%-wn|toe5vo@V$EQFGjZDr0>vcf9^B2ldIu>xi(msE2=>I+ zg!9!n^l*vd7cwiGi5luZG++-%_<75uvWsj@u~XJ&5_!iAiie50{6cb4v{|wSOwu1P zLoXzTKq}~~7X`ogvjbylJ2$*D-USG$J=8({VQfs|P=6j;h4W}OijZ+y#U8W#6FKVp z?LKFG!mxxP>P+yq{}JB2Hg!E2Vn0Oxo09$B|v>Yq(-+}TZ> zdd_4y#AJq6P(?`h54R}lyccXlR|kER%pt#Fj@5(y~ZYnsPuWhBPdp#?Lc zRc!0l{eS->oKE`FSpURMu^_ys;t97>luee+IUKf%dww(2$AjRgWe~1PrzVO@WVmFe zO0cLHGuno1iMp8v>L#4aJkA@;@VnEwys~c^LFVjXaYimAnf6=rJ$agM!LcOsjI7#s zZB_f3XolbGkSJ%nvFZ7q9b7rJT@A*?)Sju3w3+Z1&%zC$MO#^2KEuIZkGC(6*+yP3 ziheOIboriEi0R(rNHvn!uA_~)uCLjlay2(va>t0xx*cyaOwFV`xswrf<|f{r+o+&+bGzn{J!C|+;f&U{xGoqRLZS2)byyK**k!m| z%HURN$c*QRb@d8AJ?^t7VhB2~5&UF1Q7&DTdr24csDJ9(`=SXcyACsv^R~6#8+@Wm z%N#gjo}y!{sxHfFs6htkTWI<(+D*J4AHc`!A)lkhSS^x?-eR82pf=+zOc{p30)3UI z{V7iIROXnjCYwbH0&ZLtz7hL4KHH>oM@EWyO=U~H4^l=tTy-_i<9c};nXQ=xUd?=*HQ!!sWlUJY;IVx7d?-~z6_d3eE}>s{eF)6V%eQqa36 z9G(KYQ`v_3({OvuOwpxDojFKvH4j(!W9CV-xe;qPQMaX&LtcgKcavSgZ`5E%aaDg} zr|1%-3H@Zon|9o>>v;FpGY@1w&7Uw5?oKi~+M_nfwy-yFGQ3MKRn48~41pxw$ByP* zP0g%uoGHjT2Zx@jzYv?69L(s)^3M<0qzpstYN7vjByYHce<&Dd$GukQl2^OKro@q7 zlR4tYPG$8e)X;yJ$bGLz>f7AHy~wWYibr|8o6E^4A``5ox+_XE?{!v`_=e2PZBvC{(wN)1xum!jObfJ0}cpT#V34n=hB9kE-d#crrxC0BA;-)E@gy*;$ zkB7~IxAi#L!28e}FAtN`yxab!vN_WQX|q8dy~S35E;kYiQ!Cu~AM>r`h2t?*MjYlZ zvMo89J;StOC+TPhMZXY&E@xdeRS*SEYyFGaL>gQg@rp!m*DOC9jBpN5+5^JPBU<=b+PSk}152bv5ysev5P4_0#$L;hn)gTv-72V+WpC*yx zSI3IKbWHby%9%rE4)geh$a=a6U$~1bNhMrD{>tqBSWwp-L;u^E>DfbPsm%6zE)3zY z0(Jc%h>$mNqra=OQPVtSJ3Ewg#Cdv~In4R7(`k=}^9G9Vx%Njst*<$mBIvC$^Ila) z>$$=%aI+eb;MP62uA94w0^oKYLJ4{dFqs=#~sEt~aSu#_7@oiD)z ztcY4q{WjlzM(6YhQpK&%=QOG17I1&2M$AnA+ls!tFTF=4C=_j#MyqyB7ob|o1@SMv zO$sBp4~{T(=_s>6pj||7+BR&aSJNBLpyoP{!lRW)p=LQTXOy~TH*k(LCo%S}%&uzC zO@GF`%om&7_3mAg@IDVd*9Ao#xtpC_FJ?1q-N9m;pDT9EuO(8mAxGbjRwY69WnMp! z*=Yqcn7pJXVz0LT3x5l#BN@FQ@@Dj!TUK24i^P=Q(v*;w#R-1X((W+tpgN$N`+fa8 zVJn!cDInIY<<#6n z@1ya0!Bg~`*&x4W>#!Wne3WbBwrL$CLHF5Y=2N|V?L3eb>Fxdrhnpg}r?;RK>#r(# z$zQ8<>_cd?6VUA52wUSD>R_(OVNPk(=Y8R;FG(2eEfRCXlK$-2H%SFze+TL0cK0v* z9Lwk|+j9mjH1|mS_!s_0BXwPTY66sfThU<+#Yg!c8{Ti_#js%fl0Q&3jb@9kmW%MR z|IQ|0o32Dk)(dZjdQYAtW_QKKHPZ@Dtz zV8X{&+nRXl7(P zJA*@}xe0k++M~5kBd)p+yr?`B-icO97;Ig?hqsFP5X}h8-s{hcb2({4UF z8dcz6=Fcx6QNGf5{Q7X=BWSN0s8{r?Ux~l$5O@>0$%z;oo(cw%SH271ZC~XuVax1K z4w|CREarXeeuBcOx_)K`@tlmaABXjWFSI9qW~ZIWHk51Jf4D6QQYPtkpsqVnp8UVNDIMq~i|$)n6^sFa!2%on5BYAKTsdi6|u z%RS7LF^#FI-?nezmZ=4KG^3d&K39(Wi&vGJd#nE_X-`nsIUSwpWrWU=L)>;wz~)|? zRO8jB@!O(tG?sA4YbI*q)LI_v9L|=Ry!X5}NZ$NXT%{gNq~<$|nq`N%&V*_=xu9+3 zerP!Vakp$_3$z2}YHhTMTCJs{?yC=n_kz0Nb8XEl62S_nXKq8UI@H=z_IHzB%Wxre zX_Ae|VX7CJre+Ea9=&%}aZF_ri-LW@8F?o?sn^Dw8J&~8( z%s=4kv=7_*1H!tpuG>r9)eF)1Z9&I_mPhA9N8Sa+N?W?{T<*fiJJGw&Bs0!`9zO&H z@E)$5eaxzII6rzhBimIS5|%Q@w(4&j?Y2;PVPI@9*HB9KlrN>j=*opWy zJ3P8Bc^LUYBoB)Oqr(57|D3gFsp8k*u$#tB$7$zPanp+hYidmnXso%wOym7rUJyVm#`g zmbRr=(aS1YhvZR1IDW%+U^exS)7;4@@qv*RkHw9|zou1CD)v^aXV4NY&l0=A9#Rh@-z7|mbYWBfuKhFI?Pp*o zom2YkOooxzc8-1De`+*ojg!fqdw?f&j$N)!sBB^sq`SYxQdFdyR1P@`a+S7S@gO#` z56D67%{Q3_X8PyyPo^7B^kutVjDyztwq2sKL|R8mv8CCZDNc8T`-D zb}4S@&gx62g=ieEpkGaJ%8`b1AKmVCU6OaEJE!{hc)O-UTTAQik7kV?mou5rJqkw4 z=3ZX+oOwo4=4UVy2e>ocznyF(qyFTtiC@$A@jJmV4Sx(%sR@znc>Xg5+x<`A*R0V8 zZN&M;EvcUCvBAG#NAVe3bmdle{(+HJT3-(*nV#aPcucNa=~ZmD>1vb|2c0* zR(l_+H<=CW3D2VY9S5U-hFs|;I@QH-HaZ>EXs4?@MOIcb(N8X*#u$P}xgf6TlKQ53 zEPqzt*dxK_pt-%G-a@}U272u4-E1x#gLByKmZ#s|sb2EkE)BEk|M3=2aayv0yrnnl zS!^5nLEF15j2!IjN7GA+7XFB0s7Tw z5qaiz6kkG{{+F%rY9`rP+)GYx@m)ACSfjhxqmXKw=|o8UeK>~-dbQE{7qT75EJ}~( zuqHokQ7IwkW(cd9GwLw!?*e-dFVY{R$KEi%;8mN!bpNI8fLbmY6W606FD|><%7KwF zEx5~b&MOCL!gthE z@3hz2d5HHbC9bAX;X?FRIdu!UfL!gf_E(cuj`i+F8+du_;&|cM4ZnmN2vc>LLriCn?9T6g7pMCI-7CDwRJE(>&OW{~s;mR-Mem75=#tODrAo%+P1z>m zscmIG#AlF=DrgK;g;z{i4)TpTY`43ps%%4+`uF4MgL7ns_lc}@KVpsvF`QXmqCUx8 zd{^c`9q^|!&D-HcoOz~1FdI^HlGgfI*oyqC8ls;1Pu0X7a01fPX74XAv&s_sNp)iz zgO_HrsIHp0aW_4lvpcYiQ(1=!^;>$z2Dmi;Q7OcN;M3Tr@fW7LS3Y{nxdLh86P|)^ zsCe3y9&HQPHplkZQqk23kJWHLL!uKuAQnWYCCqRJ>(oI~ zSXHEg@Y@d_z!m0e^TqCPb^K(!g6=Q-J3pvzVAU+v--n~oP3}y8R352t85VJ?xx-0iIcsL2 z!1@Dq;Yi3owfX0b^xr?&x$=gS$6W=>>A0R^hsZH97aof>?2am_S-3#DgtvnACbzQ~ zmU&Zg3NL*!@fpwa=co(D>AsM|I`Ca(L!b0g4)eN2x;uTDySI?n@#PLNp9hPA%S>^ z8H?GRb6=^Pw89$uT0c8ZINSTlV;Wz1CZ)%}!3IBPy;UZ0h6ooX#26 z@k_@`1P8=7kw4&nA12p(94z1#@LbX}xy&YZ>q#i;l8Hp85fhlT_!fU*N_$d&N&Z41 zXj%{dfB$omPr58;d&$rVKQ-Tz7#MM0(<+YPwYfyn#!|Yi9BjxJqA*f;8fVXReFRRVd(^*2b(e5kn1|{&MYKZn8|QcX#6DLYoc*u?zm1OrPI(^F1MEFqq7~WXa2w5?w0JzoAd-O;jf%}XG|uw%w5h2 z?}@tpfyAA$OLjuET=I*NZ(+_Y33GcCavjX#&d7(q?3Be=A;B3yO!!xCN$%5L46>5iOXgJ+WLLe{Ht&u zny?m;=gm)d+WOx+i~!Uge_!93i8uhy0Ly5-o z7}gFKnUpGx!&W}r8s?R)NknKN(u9Q}w6?RK;*n{f3?AeZdMxDM24rHKr&etkX%e{r zrL2R$At-52sjne!t+SQbx4nmVra33ocwBnRp*|F`%DzMi)Wwzy-i%N3PjUZUWODX2 zGBP^Hc@Z{`Wr*L>D^xOgp2cl$XplE>HooNqPDi({(^FgzKcTbuG+1XEt3RCQVhp$6 zCO?Bb;f?T4u$kG+{qX~JU}ilHKga7`+C%Zwlw%H#!w;InP?42c-QQ{^N`bv*P|z}d zCiait-&RI%Hr{yy2YyX^n5k(kPU4AV%$Jg1kXARqmNoTB1(_|g@Ckp1OLv3*m^9|+ zXgV534ypb4ztVV}+-^28_V3FIuNKDJidm5#6KZ;eIg@U4wq3;;c+5PP({bCqAI=Tl z5M|v`&SiA^JDAtDv$w;HaPx1d&Q79A#*MYq)(R412mCs=g!6&BMQvlt*-6fGMliJ~ zWLo%Lx+|R%+_}4T zLnfp{P^`4TZxIR7$11}^8t>$R{Z&m@H|@Dcva0fAqh_aPsKp#`J|w&WFq+cQ%SR)Q zcUpd+H;~L&&^TylYO<-=%qdxhRL6C;Hn$w4KmU5TR;*P;)DX5Lqwskyz_%TWqHYfN zE!eA9QHZ`xWpxC1Xd3E~AJkB1yVF#yV4~k0EoDG|))00^$)Ir994_h<^*Y0)k`tV8jey5QBIDW)GWl0NRhSUJ2_DX8aWs%g8qv|KzcO(50 zaWw0RE&Tdop1TgeFTW|th+Sl8aD3S7qP5HQ210Y#C`E$>$V- zNBRh5#$5fQxn;8RUn~>aZ5sWbH%P1u7Y7ByC2)wzXtHCW$QFvEjZSypw1?vjV-*5! z3KYvr*kSh5S5e2OvVWO7BDL2sA)2t)sbFUL>7iu&%AV}^U{}~nRP^G}4Uw5}_FsfQ zaVzH`W#c2aqw~Tp3Wmii2HWjb{OmhaNj6j;!|7YaM5(4}&(x|qTsC4EpfnaHk7d0s5au#Jsst~&a|WDB$e_LAh`G9TrajKcY z=4<;U8nEb+yGLHqXha z8B6I*?0;J6>G}k!`sIwEnR$i>eXn9oeHQnGmOKIi@WG;qFh@Iq)*&5;G$A zl5L89s=m?DV1mCnn1!nRQ@5@2A8O2!@GL5suh?>ZKz?;cHq1p)E&U{_Fl~#4!%_HL zh&=T&IrnW*-6SX%OwsM72irFw&HUf^CVj&>6YUr2A&=|HCLKh|4q-a~AMJVN6AHLL z>dpR}VNd-y^z<4#OMRn?ijgpodO4L;Z+1B;(SMvL(|wCu+3hCoh1IF3dOB}7ulGLv z%oP+i(cpwXEG%KuGKqQH_BEqKMtDN4-L80Id!l;1FZ`fae0CtXZ|||knFt4Zg2)at zaiQHCRtR3{yUwX-4{wL5NQTWVXmM|oi;zef(J1^JzlGiGLa6$CV1wQ@$?(y1h2QqM zE4)!^jQK85!JY6dZqT*326s>;XSUzki6Ug)xrL-EO!Aal@TP?3$e!?>W!66$(B7vj^{ge zBOUV%+%1`e4QG-((G;fpa=X**oRB-3Tz#nRnA`B%3N>1RJvLFYC~wT~S2s@RLM8L_4MAUvgB-pAf()jC|AxI6LlaFnuc z)cq6YZaY;;E~6GlsfLrf0;I72k$^V$_3VQkX1DMI=EZBBr(R7DZMB(7S2bC;VjFNB z&g=^I97E;z;q}0R0?0O~Dds_5E_4p}U1>?i3dIXiqKD)||MT0RbdZDk6x!E@YPUON?r z6JyO|^}EgG?$dW?F7CgwX`jCblgJnsuJUgU7DA-;Z8TwlUIMO!pfk zbxS(#|0@*z^xuX~4c{m+8w`cHEWvS$q=WH07YmEmNaqQe*t$^H*J z8bb3AQ2R66**HP+I%`nA_9r*sg?@y}ZZVG9!)P$G%RY88&z?dxy3GE8XCbvpO%m(Y zaELajo2PRwdGN~8$RE81(dAx!HQ1Im-x)zp1evznndf8o{PZFR8k)nh9ggk1Y8A?t zWH3la*lOl22++mZ=zV7fz@FKsNaf`B&SetmiWZ3DFn^w#ygZ3@@Z9!cVgUn$?N=sr zrH9FWoF|vL*T_ol&6|2#Uko0^EBPyRO_jmZ?hklP-juMu@nvVjH{Fxmqp~;-;~^e9wyHZLTHaj}1TVY2>KH7NwcJt83+d9$6((u7D3w** zTL1M0&P5x1mHO z!xf#58T>hOnY?H;f$Y^{>It88GgBpuhF|L+ZAEq;N#Z1`q6U&`lnkEnoB`*lUxu-t z+{Mi~RyC1%*iURiA(9=wSZdg-o9V|l<9x{tp)SAq)t;7>RekY@Il(g2kCiOhlwu6O z=TB6Z5h~5?q=tV-)>daS8SkLRn#sb;ttEHrn%Og zqaL84KM_pR6-7jCkoWC=`nzIsfgB8PV}`wot~3_TfNWkBeb6J(7%gWzs3tY#AUPbG z#WyG_C*lA~$4s>slWqZHxw(zY!_GXl8M^;JIM%0;%5zQ+t~X+Z@cv8=;7PY0jfnpUo`kB)Zd5>LgWp26GMF>Iin!DbbW} z)n8LH_rxvumX)ZVp4zEst8tIYH__%KaW6#W1r$nu@);gOKk+W}ue)lolTP;2hj~Z( zqE>xRI8e|Vz@p!ZV&iuhT6Lh}e9H8!1UiX!c$Mzi(>MTzx=m#rz0;S$TzC%|Vd|{J zwb31q=})}DO-Zy%1v!7C_#MSX6}0YW^+G)yfBt^_J7iCy%Adq7o(t;q8tB=I=VqkN z!uc~F_3w7RxyL9kyUBjYQGcTkzQ@jJm%IcOYauBT1;Q~9E6b=gq@ulKfAptKAXTCr zbgWr;Q>!s=+E0)2b2uW%uKS4|PENOo%47@j6D-2>{*h|0x+zJ*&nfd0pIMsM?*eM6 zSIpN>+NpX9$%+s4ZdAOP=_Y3gA9hJbborzZiQ4>K`E`CMmp#QuRm;idyr(LQqGlq` zcX#=_Y6$stGp9yDJ(IrqUrrdNh$fwVgI&>j6X-n?qseW>8?#%OdPp&)G&MkNHS$^&Um0s^dYv*p+xw^M@Rvp!}k2%RZ0JZFYmShZz3oSok5F-+#-wq5uI6qjo5pXl+&g)jYF;T}GLMtG5D@&B39_;Bt+K5OTO=6nh-=%sMsI zv7A6JsB%2!*x&1K?R)Zw7*7^MAvA$S#D?&uzbX6;|3MkH`kTV^dN|~+hHiH^t;%fb z<6n}hdGv1dFI6ergRVG}`U@gh5mO3&+)wXc@?0m=2kFs+wov5a|0`*#EzF zRUVY*#B%*!kT%#DRPY^CSzapn33t&Mb6@`jDSi-YjV0viB-aCUL3E(w>`75d z)pVYc>e2$YNOJa#Wl`d}YBWi47j+)!BuNp zjxwMis?lR+k|-^@h0kNn{2bzy`-9s_v}JQWiln(!c%Z%@`L-?lf+!Q;M)t0Ffmf+L z+ns%Ek_s`6X&B@P8iYTQ$-T$vH4sM7AUOR6-Gsk9Z7Vn@^KMd3E60@_)q>J56dX<*eMzWE)WdLLXgi@3w3^F*~5Z|Re$6q?G_Py*|5cKpvCL*+k*+A}fq zAiDQ+BW&$=k)JomypJAi`2TTq7SL8zYa5<(uC?|)bX-6>rMtU3Bn2q}Ns$gINm05} zMY=;mNSj&bi8_j=*%eO7$){oeO^O)j3dElA}pC6jhq zbY;V|obT`qxz3d|BgVter-3u_jWbLC7#k5Jl~ui$BpAB0FZ~(MRYOw=-ZF?^D+$4@42dn??IHfdPeWOjSRZH;_qw9GaY=|F|>M=n{6uX*{X=-km)n6BG7kkwXHBv28c~Q!G z+8pd-rjkV&WpB)2?qdqu1@eT^3(3NFuL4TPGKq8u-*-y+9A$h?^k!5 zOhvb3K3#`DqdD2b3!F{fm%l;_xh{sNUsQdSQ#FyVb?MlE_`bnbybnX&kIXUuJE)W? zy$RkRr?t3%%EZZCWmg&O7eOt*Cfks1&J_2Onut2GiyeAvRS6b%d0G$}+0V&=W=DrU zD_3&=WHBAXMS3*8$F6Dy@4!j6yiOgopcl25Q;6>75&RQh;^gR}Adt&4wgUPYZ`c&i(Br}xWGp(v zeCr6&KC7(DZ|*n!0|~Ddq>!gW27TdX@~Wt)`8+VBCF-MvuY!+R@BdwxAuZu2pj+Gu zrr`ri=cJ1K&ubyh`yVB=^F#BDcD!Nk8aP@rb!NYXKi4#HhPXZ?f?0SGO3Y_d=BT5VxgXeRQ@`Rc67CT<4LVgte+@}&E*8MGkK z=pW=FA99yz7`4%v=%#k+a?TnE)vocs$v|TiRGCCmh;y76nDxY+mjx-g5m;%Rt}@Tlg||IWXJ-Tg5R> zvT>?B?+;6>ZvpvIe~Z4724N*#i;|TAr=g0$o zu7sQZE(nQJqbuUC8WoqwITLIgZ#6^zrlP$MUZCCeq8D1i7i2-#UE5zOf6`aT)^I z+#TUNdSdd)FL-N*H00C`A18eOZbbZpAWr=h*&QA3)l`8^!6bc?Wc?cRy}ir-^RQc8 zEiik-Wsv9|a$mLPExw5iXk$(cCn0kVqm%i*t>=`%_q&4o$+6kF7w^FZVAAB>ksOlq ze&>MOl}5q;oTn3D1^u9>LR7fpjwfw9%`}IXcogS;cL=5(P1C@}GV_e74jZ{JNxm}f zg18rP0XlzK{YBWBH&oGZ16h@OX1(}_XKHfJRKL*w(N#^xk$%ATl0N6k4dL6^RDYN0 z2PO5Oh$APoSfz=ya3|XoxXEARN6KT`>MHhYW|*Eb+y4`!Qqrn&PZxvz*u+`I$<(tg zIkz9+ob#!T;FW0ar-edU$?4}Vb$@kly4uME!!>nGCj1%`x}`X%esjKZ=Bn#tfD=Q( zZ5>vP6-_whpBDY2^AlHslk)>yw(;Riw)5*COck+Jc+&r>Lic^|lG<fcvhMyIHW`qg67f`y!i|K;0n zV?UVCDsXr*e1Q%6?xQB7tbJYsyQ4wbP z0eU&9RLuJUz zLEA(H5s|sXJ~JLSsO-Zp7-#$9d_B(-w-3JS zv1*u1O|x)5QBRyQ&B6!J-1einUQlD;SEN8^S&Wtx5hc{u&^D^_{Jn2FKs{|8#{1p$ zRvC3eHB(f_|J#qJTxztPI&v^Hl{C(K>I^OMjY#8eHi_jtxm~=orP+&AlNs<^wi0FR zAf1x-r=$9TT_s$i>&(h6QM}I0ois(FQ&TgI;J#{~_!!P9%Ig>a1X7YO+ zL~#g?7c-pE&QURvd|@0JpfsjR_$d5Rr1hpp|Mp&s5~M-?Fh$ueeyvvGEi0$;h_`;h z__YZ+m_|cYU3|=5bUYigSZ6SE^|L9-TQLmDBR~112oC#qp2kJmpy(YLDq^7TO4%>(8 zsBE`vUpax_eRokgoXBS2S{Rd=A(|NZCwa0YP8*wqZ9AQbvIT7+V<4|Ifq1LTNHa%X zb?PZPl){L4z$t2*ctyX!SaRdN#J9E~Pvt$f7C-k~()ItMkaXpGZV|?U<@6xl!aF{V z>);pCdQZYMrYYTaiP;;hAV-oHhR$gw%lkM*Dp?8j{sx-uM|Lc($>g>S+o@JO8NQPv zoI1`O94wv0d5Dhrb^e4Rbko1GJ#2N8S0;~sm#D7S$n*{_>D{7)NMYJTvp>Y!WNGlu zKVaR+t!O`ILHK~P(=sz%ZF5U|sa@y4l(oN|B;{l#%KL6cH^%d)G8~%2;+gCv&kD!~ zb{}k%&AJbp$Sd+@a6jQn@Lc}r&Ed@4(+auGNlX5t5zTuY(dBbPPU@zrh&r~3J!2B< z9x!e9+5friBD=kVP6c&FywQ7OZ{AHxxX(_euG0)gLmaH|zL9}W8QTvxM|rkjJ`?nP z^O%;D_3lERGPy)6Q6G573eFi0Mlq_PKTO;-;ILY+@ta}J?N4IuAUMO1(!lmq?C zGNYftC36HefObYiggaMvPS}y~PM>owdplu_EKgV+>u$SyKSp*bpKVoVvD|6z?6-de z_59Vr8z=;|@Z6u|9%xM#O4%pjec$@Eb!JhTDYuC;k?erwR8iS!NU9*aUK$$<$*%+L z8>`&rH0AsO`DuBuJ)CU6at=qnh|H58(|we|yn%aqN+r`Vc3;DtS1M{p2Rr;;;aWS{ z=@&8H9Ch7`o zgF*hdu%Gyh%tl0B0ut&v&B8Mi*t0Ge#V^tTxT?& znfW%G>gr~7XYsWA5)JmcxD%{+_vhQ$esWbeZbzaaUP<#iOsQ+Ss*{Sn_Ih?c4Vb{P zI$PZFcwEN>`(x>XyR>c=QDxn@$QI9+C&N5`S~C0(-~`6|JD}TMhn4kCyjIh^G+s5i z2p-l==CC_zmve_a{~NebE!l0=*7N)+{#0F+?D|)Z$MsW^R`tvfAm-uUdl?#t!q>&G zqL#QNAA4=$X1TY5({CTdpR%(fC88%(B1l~0!igrcT)|9J6Q%PQ=avKRO!W*3+5097 ztho{@1FHCgU`LRM`KcAx@hx~jH=XtFJf`;gH0D;f{K59t!>dC^H{9*}aW!6_wUF$7`J9K`rl}-G86T4-IaaV2wJm? zI}e@OPC>cYBx5i9K3nl67qR@+6K zoGIIgbNVmR+f{HC4wjG9b2W)X_i-3x!&Iu?w!BvKmOR54tB$!Da4cFSA5I=*jDNX zctfEaN;he!|Jy6gmw!WzyDKh*d3`7N%yg823`9lV6{k`CADLTbgy^9%JLA;~C=)x- zfK!Ka!mRdZ*^4vlYwilp&m@IUGAlgN57|?^l?ByQyE)|hfV^|r4&hXp)!FMj;P2_@ z(Z%W12>%z~IDSsh$>!nBT}Y%PgVl7dYopiua)%%!2du4z5!i8=6a;5Gkc^S$UkKm7$#OPM_t7)>+{?)g z*2R5y6<^xtwwxViH{uQbiuR=&x&-an4a0w6Hy0pDkii`u$>MDjpXl}`6-s|aQ8GyE zr!tRV&DR%uNtAuYCUHHy+jwP}VYBI!Xv%ZhjZBBl^E{F}>UdK{hd?Jx3Iq*2*PMy; zP9-wObq5r;IzcUHFcsAb{+7j&MebbL*nZ89Oqh*_Ju!5=jPjJE-&I; z>MRC1%b}Az(~p7~yyLdQy`h`H{g)}S1nT20Cc;hpG~c=}MSob6f07r^uAY#MTWD4& z<>n>rhv3DIeJ5SGzL|vYE}QqOvsfN5M}wvQiBQm+`;oehinGv8bRM~rh~CJZ)x zaa&}>E1ivN{5F)J_u0tTHmQQ|{FJ7>D(3{YQy2tgnFvSXf$A&TIjh~LFap1WoO97> zBJfhg?uJ`Q;iczl9Zte}wLTd96LywY+^xQlmdN2p#9!u?f>^^3C zs^YguI2c=|Ypcy(=E!Hx2A(k)`1$MWFM_|q1}a^28WYX@gn#|<&V@vWqvN>BuLtYw zAXS3o)Sp55SbXffIqBYw3~-vF-nM2J@=Vr2xsKqFs3mgSd~5*D=(T1M)Vu5`(w$8& zo1eVYWB43@y6MFEAbC)U{?yB+7jLFyq!Ta0G-~3X)3u!Ek%jaE?9k`QUZyrF#YTHg zXTZ~y7Q*&g?~Y8)&zXW#Z$nc)?4;+=(sWX-FpucVKBvx+Ho%X{{jg5^0lhXbMQ^BA z==Up4Kh*j*q-Q_Y!)$f;Wu%qc)8zI?`dYuCN4k!ku1D$j@I6*mS^x7QI%nM1?p>Lg zME9Z~9r??Wc1G|EJL74hloyLcyu#!x;_&1rfge^d%#MQHN-c#<|49GLopwo_4gd6O zX#8)HQ&IjObSs?*wuL|9yQ*%p@{Md4XPsr<3wIAZy$cCT;v4$eVe(|A7b~UhD66~u z+`TBrKikf1jenJ=b+zD7kQ)y|L6YE=(b?NWR87e=-UeU!XggnK#lQcdQ;+MWa_3sC;}R$CW+yH zk5+Ymu%pBGpeYT{ISK*UbRW$tObmH#g3aHFm@{u!4{0@n6 zLC_9T!As7ivqdK-tD8s_voG~-@_5(O7$_C@L@M;hto|E4f!y*N-U(Awe)}r87*w11K^dswv9jqr-`RdPl;xsw9yiPA0uORwleGs;E7-ubxQH zKrv@CowSq05oesYN}UZqicJnal7Dy=A|>55>W18J#{~=G-;W>YPeLz8JtBE<(tYBk zafgc45N4MP<@R)+GV@IdikKfnH`^}A6`RNQb-8=ey+<1=I+=yphm``UkVwITBqH zJx`NCUYXM_4n9nn8~;4!n&iwJ`I_W- zZFs;}{Cu%HvBbKNnh?#OcnZ9oQznQ1=$(GIH&D)?=q5bMGf)O#pvc=K#%r;=TfqC| z0Mxh4axwd0?$22HSj@b1s;X|`U$OoAIL_^uGn1=7O7lybu()kXA6-wk42o_%^RWs4 z(O2cr$m_^znK5UiqeD@MP-GB&~$aP^lFm%ObI!_H|%Uhoum?pB^5AERi}?-9mv zntAJ5SpvsTR?e3i^`@cij{~U-iOQ2CLaK&q6B@=I+l_IjleCF`DN2X+(LpDfhww;u zg*1Mjsr#xWzPyHf7hgHwt1s=P;9Tr0zkp6aPtSy7=O zmXP^T)x{U|L$o$-&Y@{YKJ zP)uC&R!76xG-#c*Kl0DdCJe_OhNulM&S~u3 zQbpKKmDR_@U#f_TLO>aBp4jO!)&DzilQ8|}W$Tm~wowa64VLHgkzfyA&`GK|r}AS27t1w3OncNBf|wGg-;w zYAcMe4P0OEf{tM~{EACSttUX&8c9#pSoHtLoN}))r9QD5r(bCpG)03|;ZX5}PVo!$ z41SEtxE3b(K!_sQVCX{q3sU+Q!%eu>7drRkeOboI;`9^~nJYK46RQvojPDuWPZ#r4 zqP)?AYLcylTG9e;Y%lJnH0l)l$$y|oH^

PmFMCxVhCRlObFgR_CX3Af8QSe%^1s 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%0DiW%m<5%RdoBU9U|^bz-ft1(Q>2>Zde%fJnz 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 From 57bdb53aefb15b2cbc25850b172bca933f87a9cb Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 2 Feb 2021 18:09:46 +0000 Subject: [PATCH 424/426] First v2.4.0 release candidate preparation --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e41500fc3..b1f964f3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ message (STATUS "******************************************************") include (set_build_type) # RC 0 or omitted is a development build, GA is a General Availability release build -set_build_type (RC 0) +set_build_type (RC 1) set (wsjtx_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${BUILD_TYPE_REVISION}") # From 1592a728800bac0b865ffbfafbed8e4d139282ee Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 2 Feb 2021 18:25:18 +0000 Subject: [PATCH 425/426] First cut at a Q65 release note --- Release_Notes.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Release_Notes.txt b/Release_Notes.txt index 40ffdbe2c..a06d781da 100644 --- a/Release_Notes.txt +++ b/Release_Notes.txt @@ -13,6 +13,28 @@ Copyright 2001 - 2021 by Joe Taylor, K1JT. + Release: WSJT-X 2.4.0-rc1 + Feb 3, 2021 + ------------------------- + +WSJT-X 2.4.0 is a program upgrade offering a new mode called Q65. It +builds on the capabilities of the QRA64 mode to offer a flexible large +symbol alphabet, low code rate, mode with excellent characteristics +when used for EME on VHF through to microwave bands, and with +challenging weak signal propagation via iono-scatter and similar on +VHF bands. The Q65 mode carries the same 77-bit message payload as the +popular FT8, MSK144, FT4, and FST4 QSO modes. Q65 also offers a range +of T/R periods (15 second through to 5 mins) and tone spacings (up to +32 times the base spacing) suitable for adapting to required +sensitivity and various channel frequency spreading characteristics. +The Wide Graph plotter has a super-sensitive synchronization spectrum +capable of detecting signals below visibility on the waterfall plot. +The new synchronization plot combined with comprehensive a priori (AP) +decoding techniques and message summing and averaging over multiple Rx +periods should make use near the limits of extreme weak signal +operating better than prior modes in all appropriate situations. + + Release: WSJT-X 2.3.0 Feb 2, 2021 --------------------- From e2b068eae7bc7c13735905c050001f9ec082040e Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 2 Feb 2021 19:02:53 +0000 Subject: [PATCH 426/426] Sync NEWS file with Release_Notes.txt --- NEWS | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/NEWS b/NEWS index f80ef3344..c9acdcef8 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,28 @@ Copyright 2001 - 2021 by Joe Taylor, K1JT. + Release: WSJT-X 2.4.0-rc1 + Feb 3, 2021 + ------------------------- + +WSJT-X 2.4.0 is a program upgrade offering a new mode called Q65. It +builds on the capabilities of the QRA64 mode to offer a flexible large +symbol alphabet, low code rate, mode with excellent characteristics +when used for EME on VHF through to microwave bands, and with +challenging weak signal propagation via iono-scatter and similar on +VHF bands. The Q65 mode carries the same 77-bit message payload as the +popular FT8, MSK144, FT4, and FST4 QSO modes. Q65 also offers a range +of T/R periods (15 second through to 5 mins) and tone spacings (up to +32 times the base spacing) suitable for adapting to required +sensitivity and various channel frequency spreading characteristics. +The Wide Graph plotter has a super-sensitive synchronization spectrum +capable of detecting signals below visibility on the waterfall plot. +The new synchronization plot combined with comprehensive a priori (AP) +decoding techniques and message summing and averaging over multiple Rx +periods should make use near the limits of extreme weak signal +operating better than prior modes in all appropriate situations. + + Release: WSJT-X 2.3.0 Feb 2, 2021 ---------------------