Merged master 8748
This commit is contained in:
@@ -1,358 +0,0 @@
|
||||
subroutine ft8b(dd0,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,napwid, &
|
||||
lsubtract,nagain,iaptype,mygrid6,bcontest,sync0,f1,xdt,apsym,nharderrors,&
|
||||
dmin,nbadcrc,ipass,iera,message,xsnr)
|
||||
|
||||
use timer_module, only: timer
|
||||
include 'ft8_params.f90'
|
||||
parameter(NRECENT=10,NP2=2812)
|
||||
character message*22,msgsent*22
|
||||
character*12 recent_calls(NRECENT)
|
||||
character*6 mygrid6
|
||||
logical bcontest
|
||||
real a(5)
|
||||
real s1(0:7,ND),s2(0:7,NN)
|
||||
real ps(0:7)
|
||||
real rxdata(3*ND),rxdatap(3*ND)
|
||||
real llr(3*ND),llra(3*ND),llr0(3*ND),llrap(3*ND) !Soft symbols
|
||||
real dd0(15*12000)
|
||||
integer*1 decoded(KK),apmask(3*ND),cw(3*ND)
|
||||
integer*1 msgbits(KK)
|
||||
integer apsym(KK)
|
||||
integer mcq(28),mde(28),mrrr(16),m73(16),mrr73(16)
|
||||
integer itone(NN)
|
||||
integer icos7(0:6),ip(1)
|
||||
integer nappasses(0:5) ! the number of decoding passes to use for each QSO state
|
||||
integer naptypes(0:5,4) ! (nQSOProgress, decoding pass) maximum of 4 passes for now
|
||||
complex cd0(3200)
|
||||
complex ctwk(32)
|
||||
complex csymb(32)
|
||||
logical first,newdat,lsubtract,lapon,nagain
|
||||
data icos7/2,5,6,0,4,1,3/
|
||||
data mcq/1,1,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,1,0,0,0,1,1,0,0,1/
|
||||
data mrrr/0,1,1,1,1,1,1,0,1,1,0,0,1,1,1,1/
|
||||
data m73/0,1,1,1,1,1,1,0,1,1,0,1,0,0,0,0/
|
||||
data mde/1,1,1,1,1,1,1,1,0,1,1,0,0,1,0,0,0,0,0,1,1,1,0,1,0,0,0,1/
|
||||
data mrr73/0,0,0,0,0,0,1,0,0,0,0,1,0,1,0,1/
|
||||
data first/.true./
|
||||
save nappasses,naptypes
|
||||
|
||||
if(first) then
|
||||
mcq=2*mcq-1
|
||||
mde=2*mde-1
|
||||
mrrr=2*mrrr-1
|
||||
m73=2*m73-1
|
||||
mrr73=2*mrr73-1
|
||||
nappasses(0)=2
|
||||
nappasses(1)=2
|
||||
nappasses(2)=2
|
||||
nappasses(3)=4
|
||||
nappasses(4)=4
|
||||
nappasses(5)=3
|
||||
|
||||
! iaptype
|
||||
!------------------------
|
||||
! 1 CQ ??? ???
|
||||
! 2 MyCall ??? ???
|
||||
! 3 MyCall DxCall ???
|
||||
! 4 MyCall DxCall RRR
|
||||
! 5 MyCall DxCall 73
|
||||
! 6 MyCall DxCall RR73
|
||||
! 7 ??? DxCall ???
|
||||
|
||||
naptypes(0,1:4)=(/1,2,0,0/)
|
||||
naptypes(1,1:4)=(/2,3,0,0/)
|
||||
naptypes(2,1:4)=(/2,3,0,0/)
|
||||
naptypes(3,1:4)=(/3,4,5,6/)
|
||||
naptypes(4,1:4)=(/3,4,5,6/)
|
||||
naptypes(5,1:4)=(/3,1,2,0/) !?
|
||||
first=.false.
|
||||
endif
|
||||
|
||||
max_iterations=30
|
||||
nharderrors=-1
|
||||
fs2=12000.0/NDOWN
|
||||
dt2=1.0/fs2
|
||||
twopi=8.0*atan(1.0)
|
||||
delfbest=0.
|
||||
ibest=0
|
||||
|
||||
call timer('ft8_down',0)
|
||||
call ft8_downsample(dd0,newdat,f1,cd0) !Mix f1 to baseband and downsample
|
||||
call timer('ft8_down',1)
|
||||
|
||||
i0=nint((xdt+0.5)*fs2) !Initial guess for start of signal
|
||||
smax=0.0
|
||||
do idt=i0-8,i0+8 !Search over +/- one quarter symbol
|
||||
call sync8d(cd0,idt,ctwk,0,sync)
|
||||
if(sync.gt.smax) then
|
||||
smax=sync
|
||||
ibest=idt
|
||||
endif
|
||||
enddo
|
||||
xdt2=ibest*dt2 !Improved estimate for DT
|
||||
|
||||
! Now peak up in frequency
|
||||
i0=nint(xdt2*fs2)
|
||||
smax=0.0
|
||||
do ifr=-5,5 !Search over +/- 2.5 Hz
|
||||
delf=ifr*0.5
|
||||
dphi=twopi*delf*dt2
|
||||
phi=0.0
|
||||
do i=1,32
|
||||
ctwk(i)=cmplx(cos(phi),sin(phi))
|
||||
phi=mod(phi+dphi,twopi)
|
||||
enddo
|
||||
call sync8d(cd0,i0,ctwk,1,sync)
|
||||
if( sync .gt. smax ) then
|
||||
smax=sync
|
||||
delfbest=delf
|
||||
endif
|
||||
enddo
|
||||
a=0.0
|
||||
a(1)=-delfbest
|
||||
call twkfreq1(cd0,NP2,fs2,a,cd0)
|
||||
xdt=xdt2
|
||||
f1=f1+delfbest !Improved estimate of DF
|
||||
|
||||
call sync8d(cd0,i0,ctwk,2,sync)
|
||||
|
||||
j=0
|
||||
do k=1,NN
|
||||
i1=ibest+(k-1)*32
|
||||
csymb=cmplx(0.0,0.0)
|
||||
if( i1.ge.1 .and. i1+31 .le. NP2 ) csymb=cd0(i1:i1+31)
|
||||
call four2a(csymb,32,1,-1,1)
|
||||
s2(0:7,k)=abs(csymb(1:8))
|
||||
enddo
|
||||
|
||||
! sync quality check
|
||||
is1=0
|
||||
is2=0
|
||||
is3=0
|
||||
do k=1,7
|
||||
ip=maxloc(s2(:,k))
|
||||
if(icos7(k-1).eq.(ip(1)-1)) is1=is1+1
|
||||
ip=maxloc(s2(:,k+36))
|
||||
if(icos7(k-1).eq.(ip(1)-1)) is2=is2+1
|
||||
ip=maxloc(s2(:,k+72))
|
||||
if(icos7(k-1).eq.(ip(1)-1)) is3=is3+1
|
||||
enddo
|
||||
! hard sync sum - max is 21
|
||||
nsync=is1+is2+is3
|
||||
if(nsync .le. 6) then ! bail out
|
||||
nbadcrc=1
|
||||
return
|
||||
endif
|
||||
|
||||
j=0
|
||||
do k=1,NN
|
||||
if(k.le.7) cycle
|
||||
if(k.ge.37 .and. k.le.43) cycle
|
||||
if(k.gt.72) cycle
|
||||
j=j+1
|
||||
s1(0:7,j)=s2(0:7,k)
|
||||
enddo
|
||||
|
||||
do j=1,ND
|
||||
ps=s1(0:7,j)
|
||||
where (ps.gt.0.0) ps=log(ps)
|
||||
r1=max(ps(1),ps(3),ps(5),ps(7))-max(ps(0),ps(2),ps(4),ps(6))
|
||||
r2=max(ps(2),ps(3),ps(6),ps(7))-max(ps(0),ps(1),ps(4),ps(5))
|
||||
r4=max(ps(4),ps(5),ps(6),ps(7))-max(ps(0),ps(1),ps(2),ps(3))
|
||||
i4=3*j-2
|
||||
i2=3*j-1
|
||||
i1=3*j
|
||||
rxdata(i4)=r4
|
||||
rxdata(i2)=r2
|
||||
rxdata(i1)=r1
|
||||
rxdatap(i4)=r4
|
||||
rxdatap(i2)=r2
|
||||
rxdatap(i1)=r1
|
||||
|
||||
if(nQSOProgress .eq. 0 .or. nQSOProgress .eq. 5) then
|
||||
! When bits 88:115 are set as ap bits, bit 115 lives in symbol 39 along
|
||||
! with no-ap bits 116 and 117. Take care of metrics for bits 116 and 117.
|
||||
if(j.eq.39) then ! take care of bits that live in symbol 39
|
||||
if(apsym(28).lt.0) then
|
||||
rxdatap(i2)=max(ps(2),ps(3))-max(ps(0),ps(1))
|
||||
rxdatap(i1)=max(ps(1),ps(3))-max(ps(0),ps(2))
|
||||
else
|
||||
rxdatap(i2)=max(ps(6),ps(7))-max(ps(4),ps(5))
|
||||
rxdatap(i1)=max(ps(5),ps(7))-max(ps(4),ps(6))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
! When bits 116:143 are set as ap bits, bit 115 lives in symbol 39 along
|
||||
! with ap bits 116 and 117. Take care of metric for bit 115.
|
||||
! if(j.eq.39) then ! take care of bit 115
|
||||
! iii=2*(apsym(29)+1)/2 + (apsym(30)+1)/2 ! known values of bits 116 & 117
|
||||
! if(iii.eq.0) rxdatap(i4)=ps(4)-ps(0)
|
||||
! if(iii.eq.1) rxdatap(i4)=ps(5)-ps(1)
|
||||
! if(iii.eq.2) rxdatap(i4)=ps(6)-ps(2)
|
||||
! if(iii.eq.3) rxdatap(i4)=ps(7)-ps(3)
|
||||
! endif
|
||||
|
||||
! bit 144 lives in symbol 48 and will be 1 if it is set as an ap bit.
|
||||
! take care of metrics for bits 142 and 143
|
||||
if(j.eq.48) then ! bit 144 is always 1
|
||||
rxdatap(i4)=max(ps(5),ps(7))-max(ps(1),ps(3))
|
||||
rxdatap(i2)=max(ps(3),ps(7))-max(ps(1),ps(5))
|
||||
endif
|
||||
|
||||
! bit 154 lives in symbol 52 and will be 0 if it is set as an ap bit
|
||||
! take care of metrics for bits 155 and 156
|
||||
if(j.eq.52) then ! bit 154 will be 0 if it is set as an ap bit.
|
||||
rxdatap(i2)=max(ps(2),ps(3))-max(ps(0),ps(1))
|
||||
rxdatap(i1)=max(ps(1),ps(3))-max(ps(0),ps(2))
|
||||
endif
|
||||
|
||||
enddo
|
||||
|
||||
rxav=sum(rxdata)/(3.0*ND)
|
||||
rx2av=sum(rxdata*rxdata)/(3.0*ND)
|
||||
var=rx2av-rxav*rxav
|
||||
if( var .gt. 0.0 ) then
|
||||
rxsig=sqrt(var)
|
||||
else
|
||||
rxsig=sqrt(rx2av)
|
||||
endif
|
||||
rxdata=rxdata/rxsig
|
||||
! Let's just assume that rxsig is OK for rxdatap too...
|
||||
rxdatap=rxdatap/rxsig
|
||||
|
||||
ss=0.84
|
||||
llr0=2.0*rxdata/(ss*ss)
|
||||
llra=2.0*rxdatap/(ss*ss) ! llr's for use with ap
|
||||
apmag=4.0
|
||||
|
||||
! pass #
|
||||
!------------------------------
|
||||
! 1 regular decoding
|
||||
! 2 erase 24
|
||||
! 3 erase 48
|
||||
! 4 ap pass 1
|
||||
! 5 ap pass 2
|
||||
! 6 ap pass 3
|
||||
! 7 ap pass 4, etc.
|
||||
|
||||
if(lapon) then
|
||||
npasses=3+nappasses(nQSOProgress)
|
||||
else
|
||||
npasses=3
|
||||
endif
|
||||
|
||||
do ipass=1,npasses
|
||||
|
||||
llr=llr0
|
||||
if(ipass.ne.2 .and. ipass.ne.3) nblank=0
|
||||
if(ipass.eq.2) nblank=24
|
||||
if(ipass.eq.3) nblank=48
|
||||
if(nblank.gt.0) llr(1:nblank)=0.
|
||||
|
||||
if(ipass.le.3) then
|
||||
apmask=0
|
||||
llrap=llr
|
||||
iaptype=0
|
||||
endif
|
||||
|
||||
if(ipass .gt. 3) then
|
||||
iaptype=naptypes(nQSOProgress,ipass-3)
|
||||
if(iaptype.ge.3 .and. (abs(f1-nfqso).gt.napwid .and. abs(f1-nftx).gt.napwid) ) cycle
|
||||
if(iaptype.eq.1 .or. iaptype.eq.2 ) then ! AP,???,???
|
||||
apmask=0
|
||||
apmask(88:115)=1 ! first 28 bits are AP
|
||||
apmask(144)=1 ! not free text
|
||||
llrap=llr
|
||||
if(iaptype.eq.1) llrap(88:115)=apmag*mcq/ss
|
||||
if(iaptype.eq.2) llrap(88:115)=apmag*apsym(1:28)/ss
|
||||
llrap(116:117)=llra(116:117)
|
||||
llrap(142:143)=llra(142:143)
|
||||
llrap(144)=-apmag/ss
|
||||
endif
|
||||
if(iaptype.eq.3) then ! mycall, dxcall, ???
|
||||
apmask=0
|
||||
apmask(88:115)=1 ! mycall
|
||||
apmask(116:143)=1 ! hiscall
|
||||
apmask(144)=1 ! not free text
|
||||
llrap=llr
|
||||
llrap(88:143)=apmag*apsym(1:56)/ss
|
||||
llrap(144)=-apmag/ss
|
||||
endif
|
||||
if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype.eq.6) then
|
||||
apmask=0
|
||||
apmask(88:115)=1 ! mycall
|
||||
apmask(116:143)=1 ! hiscall
|
||||
apmask(144:159)=1 ! RRR or 73 or RR73
|
||||
llrap=llr
|
||||
llrap(88:143)=apmag*apsym(1:56)/ss
|
||||
if(iaptype.eq.4) llrap(144:159)=apmag*mrrr/ss
|
||||
if(iaptype.eq.5) llrap(144:159)=apmag*m73/ss
|
||||
if(iaptype.eq.6) llrap(144:159)=apmag*mrr73/ss
|
||||
endif
|
||||
if(iaptype.eq.7) then ! ???, dxcall, ???
|
||||
apmask=0
|
||||
apmask(116:143)=1 ! hiscall
|
||||
apmask(144)=1 ! not free text
|
||||
llrap=llr
|
||||
llrap(115)=llra(115)
|
||||
llrap(116:143)=apmag*apsym(29:56)/ss
|
||||
llrap(144)=-apmag/ss
|
||||
endif
|
||||
endif
|
||||
|
||||
cw=0
|
||||
call timer('bpd174 ',0)
|
||||
call bpdecode174(llrap,apmask,max_iterations,decoded,cw,nharderrors, &
|
||||
niterations)
|
||||
call timer('bpd174 ',1)
|
||||
dmin=0.0
|
||||
if(ndepth.eq.3 .and. nharderrors.lt.0) then
|
||||
norder=2
|
||||
if(abs(nfqso-f1).le.napwid .or. abs(nftx-f1).le.napwid) then
|
||||
if((ipass.eq.2 .or. ipass.eq.3) .and. .not.nagain) then
|
||||
norder=2
|
||||
else
|
||||
norder=3 ! for nagain, use norder=3 for all passes
|
||||
endif
|
||||
endif
|
||||
call timer('osd174 ',0)
|
||||
call osd174(llrap,apmask,norder,decoded,cw,nharderrors,dmin)
|
||||
call timer('osd174 ',1)
|
||||
endif
|
||||
nbadcrc=1
|
||||
message=' '
|
||||
xsnr=-99.0
|
||||
if(count(cw.eq.0).eq.174) cycle !Reject the all-zero codeword
|
||||
if(any(decoded(73:75).ne.0)) cycle !Reject if any of the 3 extra bits are nonzero
|
||||
if(nharderrors.ge.0 .and. nharderrors+dmin.lt.60.0 .and. &
|
||||
.not.(sync.lt.2.0 .and. nharderrors.gt.35) .and. &
|
||||
.not.(ipass.gt.1 .and. nharderrors.gt.39) .and. &
|
||||
.not.(ipass.eq.3 .and. nharderrors.gt.30) &
|
||||
) then
|
||||
call chkcrc12a(decoded,nbadcrc)
|
||||
else
|
||||
nharderrors=-1
|
||||
cycle
|
||||
endif
|
||||
if(nbadcrc.eq.0) then
|
||||
call extractmessage174(decoded,message,ncrcflag,recent_calls,nrecent)
|
||||
call genft8(message,mygrid6,bcontest,msgsent,msgbits,itone)
|
||||
if(lsubtract) call subtractft8(dd0,itone,f1,xdt2)
|
||||
xsig=0.0
|
||||
xnoi=0.0
|
||||
do i=1,79
|
||||
xsig=xsig+s2(itone(i),i)**2
|
||||
ios=mod(itone(i)+4,7)
|
||||
xnoi=xnoi+s2(ios,i)**2
|
||||
enddo
|
||||
xsnr=0.001
|
||||
if(xnoi.gt.0 .and. xnoi.lt.xsig) xsnr=xsig/xnoi-1.0
|
||||
xsnr=10.0*log10(xsnr)-27.0
|
||||
if(xsnr .lt. -24.0) xsnr=-24.0
|
||||
return
|
||||
endif
|
||||
enddo
|
||||
|
||||
return
|
||||
end subroutine ft8b
|
||||
@@ -1,69 +0,0 @@
|
||||
subroutine extract4(sym0,ncount,decoded)
|
||||
|
||||
use packjt
|
||||
real sym0(207)
|
||||
real sym(207)
|
||||
character decoded*22
|
||||
character*72 c72
|
||||
integer*1 symbol(207)
|
||||
integer*1 data1(13) !Decoded data (8-bit bytes)
|
||||
integer data4a(9) !Decoded data (8-bit bytes)
|
||||
integer data4(12) !Decoded data (6-bit bytes)
|
||||
integer mettab(-128:127,0:1) !Metric table
|
||||
logical first
|
||||
data first/.true./
|
||||
save first,mettab,ndelta
|
||||
|
||||
if(first) then
|
||||
call getmet4(mettab,ndelta)
|
||||
first=.false.
|
||||
endif
|
||||
|
||||
!### Optimize these params: ...
|
||||
amp=30.0
|
||||
limit=10000
|
||||
|
||||
ave0=sum(sym0)/207.0
|
||||
sym=sym0-ave0
|
||||
sq=dot_product(sym,sym)
|
||||
rms0=sqrt(sq/206.0)
|
||||
sym=sym/rms0
|
||||
|
||||
do j=1,207
|
||||
n=nint(amp*sym(j))
|
||||
if(n.lt.-127) n=-127
|
||||
if(n.gt.127) n=127
|
||||
symbol(j)=n
|
||||
enddo
|
||||
|
||||
nbits=72
|
||||
ncycles=0
|
||||
ncount=-1
|
||||
decoded=' '
|
||||
call interleave4(symbol(2),-1) !Remove the interleaving
|
||||
call fano232(symbol(2),nbits+31,mettab,ndelta,limit,data1, &
|
||||
ncycles,metric,ncount)
|
||||
nlim=ncycles/(nbits+31)
|
||||
|
||||
!### Make usage here like that in jt9fano...
|
||||
if(ncount.ge.0) then
|
||||
do i=1,9
|
||||
i4=data1(i)
|
||||
if(i4.lt.0) i4=i4+256
|
||||
data4a(i)=i4
|
||||
enddo
|
||||
write(c72,1100) (data4a(i),i=1,9)
|
||||
1100 format(9b8.8)
|
||||
read(c72,1102) data4
|
||||
1102 format(12b6)
|
||||
|
||||
call unpackmsg(data4,decoded)
|
||||
if(decoded(1:6).eq.'000AAA') then
|
||||
! decoded='***WRONG MODE?***'
|
||||
decoded=' '
|
||||
ncount=-1
|
||||
endif
|
||||
endif
|
||||
|
||||
return
|
||||
end subroutine extract4
|
||||
@@ -1,61 +0,0 @@
|
||||
subroutine subtractft8(dd,itone,f0,dt)
|
||||
|
||||
! Subtract an ft8 signal
|
||||
!
|
||||
! Measured signal : dd(t) = a(t)cos(2*pi*f0*t+theta(t))
|
||||
! Reference signal : cref(t) = exp( j*(2*pi*f0*t+phi(t)) )
|
||||
! Complex amp : cfilt(t) = LPF[ dd(t)*CONJG(cref(t)) ]
|
||||
! Subtract : dd(t) = dd(t) - 2*REAL{cref*cfilt}
|
||||
|
||||
use timer_module, only: timer
|
||||
|
||||
parameter (NMAX=15*12000,NFRAME=1920*79)
|
||||
parameter (NFFT=NMAX,NFILT=400)
|
||||
real*4 dd(NMAX), window(-NFILT/2:NFILT/2)
|
||||
complex cref,camp,cfilt,cw
|
||||
integer itone(79)
|
||||
logical first
|
||||
data first/.true./
|
||||
common/heap8/cref(NFRAME),camp(NMAX),cfilt(NMAX),cw(NMAX)
|
||||
save first
|
||||
|
||||
nstart=dt*12000+1
|
||||
call genft8refsig(itone,cref,f0)
|
||||
camp=0.
|
||||
do i=1,nframe
|
||||
id=nstart-1+i
|
||||
if(id.ge.1.and.id.le.NMAX) camp(i)=dd(id)*conjg(cref(i))
|
||||
enddo
|
||||
|
||||
if(first) then
|
||||
! Create and normalize the filter
|
||||
pi=4.0*atan(1.0)
|
||||
fac=1.0/float(nfft)
|
||||
sum=0.0
|
||||
do j=-NFILT/2,NFILT/2
|
||||
window(j)=cos(pi*j/NFILT)**2
|
||||
sum=sum+window(j)
|
||||
enddo
|
||||
cw=0.
|
||||
cw(1:NFILT+1)=window/sum
|
||||
cw=cshift(cw,NFILT/2+1)
|
||||
call four2a(cw,nfft,1,-1,1)
|
||||
cw=cw*fac
|
||||
first=.false.
|
||||
endif
|
||||
|
||||
cfilt=0.0
|
||||
cfilt(1:nframe)=camp(1:nframe)
|
||||
call four2a(cfilt,nfft,1,-1,1)
|
||||
cfilt(1:nfft)=cfilt(1:nfft)*cw(1:nfft)
|
||||
call four2a(cfilt,nfft,1,1,1)
|
||||
|
||||
! Subtract the reconstructed signal
|
||||
do i=1,nframe
|
||||
j=nstart+i-1
|
||||
if(j.ge.1 .and. j.le.NMAX) dd(j)=dd(j)-2*REAL(cfilt(i)*cref(i))
|
||||
enddo
|
||||
|
||||
return
|
||||
end subroutine subtractft8
|
||||
|
||||
@@ -1,670 +0,0 @@
|
||||
#include "FrequencyList.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
#include <limits>
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QAbstractTableModel>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QListIterator>
|
||||
#include <QVector>
|
||||
#include <QStringList>
|
||||
#include <QMimeData>
|
||||
#include <QDataStream>
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QDebugStateSaver>
|
||||
|
||||
#include "Radio.hpp"
|
||||
#include "Bands.hpp"
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
#include "moc_FrequencyList.cpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
FrequencyList::FrequencyItems const default_frequency_list =
|
||||
{
|
||||
{136000, Modes::WSPR},
|
||||
{136130, Modes::JT65},
|
||||
{474200, Modes::JT65},
|
||||
{474200, Modes::JT9},
|
||||
{474200, Modes::WSPR},
|
||||
{660000, Modes::FreqCal},
|
||||
{880000, Modes::FreqCal},
|
||||
{1210000, Modes::FreqCal},
|
||||
{1836600, Modes::WSPR},
|
||||
{1838000, Modes::JT65},
|
||||
{1840000, Modes::JT9},
|
||||
{1841000, Modes::FT8},
|
||||
{2500000, Modes::FreqCal},
|
||||
{3330000, Modes::FreqCal},
|
||||
{3576000, Modes::JT65},
|
||||
{3578000, Modes::JT9},
|
||||
{3579000, Modes::FT8},
|
||||
{3592600, Modes::WSPR},
|
||||
{5357000, Modes::JT65},
|
||||
{5000000, Modes::FreqCal},
|
||||
{7038600, Modes::WSPR},
|
||||
{7076000, Modes::JT65},
|
||||
{7078000, Modes::JT9},
|
||||
{7079000, Modes::FT8},
|
||||
{7850000, Modes::FreqCal},
|
||||
{10000000, Modes::FreqCal},
|
||||
{10138000, Modes::JT65},
|
||||
{10138700, Modes::WSPR},
|
||||
{10140000, Modes::JT9},
|
||||
{10141000, Modes::FT8},
|
||||
{14095600, Modes::WSPR},
|
||||
{14076000, Modes::JT65},
|
||||
{14078000, Modes::JT9},
|
||||
{14079000, Modes::FT8},
|
||||
{14670000, Modes::FreqCal},
|
||||
{15000000, Modes::FreqCal},
|
||||
{18102000, Modes::JT65},
|
||||
{18104000, Modes::JT9},
|
||||
{18105000, Modes::FT8},
|
||||
{18104600, Modes::WSPR},
|
||||
{20000000, Modes::FreqCal},
|
||||
{21076000, Modes::JT65},
|
||||
{21078000, Modes::JT9},
|
||||
{21079000, Modes::FT8},
|
||||
{21094600, Modes::WSPR},
|
||||
{24917000, Modes::JT65},
|
||||
{24919000, Modes::JT9},
|
||||
{24920000, Modes::FT8},
|
||||
{24924600, Modes::WSPR},
|
||||
{28076000, Modes::JT65},
|
||||
{28078000, Modes::JT9},
|
||||
{28079000, Modes::FT8},
|
||||
{28124600, Modes::WSPR},
|
||||
{50000000, Modes::Echo},
|
||||
{50276000, Modes::JT65},
|
||||
{50280000, Modes::MSK144},
|
||||
{50293000, Modes::WSPR},
|
||||
{50310000, Modes::JT65},
|
||||
{50313000, Modes::FT8},
|
||||
{70091000, Modes::JT65},
|
||||
{70091000, Modes::WSPR},
|
||||
{70094000, Modes::FT8},
|
||||
{144000000, Modes::Echo},
|
||||
{144120000, Modes::JT65},
|
||||
{144120000, Modes::Echo},
|
||||
{144140000, Modes::MSK144},
|
||||
{144489000, Modes::WSPR},
|
||||
{222065000, Modes::JT65},
|
||||
{222065000, Modes::Echo},
|
||||
{432065000, Modes::Echo},
|
||||
{432065000, Modes::JT65},
|
||||
{432300000, Modes::WSPR},
|
||||
{902065000, Modes::JT65},
|
||||
{1296065000, Modes::Echo},
|
||||
{1296065000, Modes::JT65},
|
||||
{1296500000, Modes::WSPR},
|
||||
{2301000000, Modes::Echo},
|
||||
{2301065000, Modes::JT4},
|
||||
{2301065000, Modes::JT65},
|
||||
{2304065000, Modes::Echo},
|
||||
{2304065000, Modes::JT4},
|
||||
{2304065000, Modes::JT65},
|
||||
{2320065000, Modes::Echo},
|
||||
{2320065000, Modes::JT4},
|
||||
{2320065000, Modes::JT65},
|
||||
{3400065000, Modes::Echo},
|
||||
{3400065000, Modes::JT4},
|
||||
{3400065000, Modes::JT65},
|
||||
{3456065000, Modes::JT4},
|
||||
{3456065000, Modes::JT65},
|
||||
{5760065000, Modes::Echo},
|
||||
{5760065000, Modes::JT4},
|
||||
{5760065000, Modes::JT65},
|
||||
{10368100000, Modes::Echo},
|
||||
{10368100000, Modes::JT4},
|
||||
{10368100000, Modes::JT65},
|
||||
{24048100000, Modes::Echo},
|
||||
{24048100000, Modes::JT4},
|
||||
{24048100000, Modes::JT65},
|
||||
};
|
||||
}
|
||||
|
||||
#if !defined (QT_NO_DEBUG_STREAM)
|
||||
QDebug operator << (QDebug debug, FrequencyList::Item const& item)
|
||||
{
|
||||
QDebugStateSaver saver {debug};
|
||||
debug.nospace () << "FrequencyItem("
|
||||
<< item.frequency_ << ", "
|
||||
<< item.mode_ << ')';
|
||||
return debug;
|
||||
}
|
||||
#endif
|
||||
|
||||
QDataStream& operator << (QDataStream& os, FrequencyList::Item const& item)
|
||||
{
|
||||
return os << item.frequency_
|
||||
<< item.mode_;
|
||||
}
|
||||
|
||||
QDataStream& operator >> (QDataStream& is, FrequencyList::Item& item)
|
||||
{
|
||||
return is >> item.frequency_
|
||||
>> item.mode_;
|
||||
}
|
||||
|
||||
class FrequencyList::impl final
|
||||
: public QAbstractTableModel
|
||||
{
|
||||
public:
|
||||
impl (Bands const * bands, QObject * parent)
|
||||
: QAbstractTableModel {parent}
|
||||
, bands_ {bands}
|
||||
, mode_filter_ {Modes::NULL_MODE}
|
||||
{
|
||||
}
|
||||
|
||||
FrequencyItems frequency_list (FrequencyItems);
|
||||
QModelIndex add (Item);
|
||||
|
||||
// Implement the QAbstractTableModel interface
|
||||
int rowCount (QModelIndex const& parent = QModelIndex {}) const override;
|
||||
int columnCount (QModelIndex const& parent = QModelIndex {}) const override;
|
||||
Qt::ItemFlags flags (QModelIndex const& = QModelIndex {}) const override;
|
||||
QVariant data (QModelIndex const&, int role = Qt::DisplayRole) const override;
|
||||
bool setData (QModelIndex const&, QVariant const& value, int role = Qt::EditRole) override;
|
||||
QVariant headerData (int section, Qt::Orientation, int = Qt::DisplayRole) const override;
|
||||
bool removeRows (int row, int count, QModelIndex const& parent = QModelIndex {}) override;
|
||||
bool insertRows (int row, int count, QModelIndex const& parent = QModelIndex {}) override;
|
||||
QStringList mimeTypes () const override;
|
||||
QMimeData * mimeData (QModelIndexList const&) const override;
|
||||
|
||||
static int constexpr num_cols {3};
|
||||
static auto constexpr mime_type = "application/wsjt.Frequencies";
|
||||
|
||||
Bands const * bands_;
|
||||
FrequencyItems frequency_list_;
|
||||
Mode mode_filter_;
|
||||
};
|
||||
|
||||
FrequencyList::FrequencyList (Bands const * bands, QObject * parent)
|
||||
: QSortFilterProxyModel {parent}
|
||||
, m_ {bands, parent}
|
||||
{
|
||||
setSourceModel (&*m_);
|
||||
setSortRole (SortRole);
|
||||
}
|
||||
|
||||
FrequencyList::~FrequencyList ()
|
||||
{
|
||||
}
|
||||
|
||||
auto FrequencyList::frequency_list (FrequencyItems frequency_list) -> FrequencyItems
|
||||
{
|
||||
return m_->frequency_list (frequency_list);
|
||||
}
|
||||
|
||||
auto FrequencyList::frequency_list () const -> FrequencyItems const&
|
||||
{
|
||||
return m_->frequency_list_;
|
||||
}
|
||||
|
||||
int FrequencyList::best_working_frequency (Frequency f) const
|
||||
{
|
||||
int result {-1};
|
||||
auto const& target_band = m_->bands_->find (f);
|
||||
if (!target_band.isEmpty ())
|
||||
{
|
||||
Radio::FrequencyDelta delta {std::numeric_limits<Radio::FrequencyDelta>::max ()};
|
||||
// find a frequency in the same band that is allowed
|
||||
for (int row = 0; row < rowCount (); ++row)
|
||||
{
|
||||
auto const& source_row = mapToSource (index (row, 0)).row ();
|
||||
auto const& candidate_frequency = m_->frequency_list_[source_row].frequency_;
|
||||
auto const& band = m_->bands_->find (candidate_frequency);
|
||||
if (band == target_band)
|
||||
{
|
||||
// take closest band match
|
||||
Radio::FrequencyDelta new_delta = f - candidate_frequency;
|
||||
if (std::abs (new_delta) < std::abs (delta))
|
||||
{
|
||||
delta = new_delta;
|
||||
result = row;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int FrequencyList::best_working_frequency (QString const& target_band) const
|
||||
{
|
||||
int result {-1};
|
||||
if (!target_band.isEmpty ())
|
||||
{
|
||||
// find a frequency in the same band that is allowed
|
||||
for (int row = 0; row < rowCount (); ++row)
|
||||
{
|
||||
auto const& source_row = mapToSource (index (row, 0)).row ();
|
||||
auto const& band = m_->bands_->find (m_->frequency_list_[source_row].frequency_);
|
||||
if (band == target_band)
|
||||
{
|
||||
return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void FrequencyList::reset_to_defaults ()
|
||||
{
|
||||
m_->frequency_list (default_frequency_list);
|
||||
}
|
||||
|
||||
QModelIndex FrequencyList::add (Item f)
|
||||
{
|
||||
return mapFromSource (m_->add (f));
|
||||
}
|
||||
|
||||
bool FrequencyList::remove (Item f)
|
||||
{
|
||||
auto row = m_->frequency_list_.indexOf (f);
|
||||
|
||||
if (0 > row)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_->removeRow (row);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool row_is_higher (QModelIndex const& lhs, QModelIndex const& rhs)
|
||||
{
|
||||
return lhs.row () > rhs.row ();
|
||||
}
|
||||
}
|
||||
|
||||
bool FrequencyList::removeDisjointRows (QModelIndexList rows)
|
||||
{
|
||||
bool result {true};
|
||||
|
||||
// We must work with source model indexes because we don't want row
|
||||
// removes to invalidate model indexes we haven't yet processed. We
|
||||
// achieve that by processing them in decending row order.
|
||||
for (int r = 0; r < rows.size (); ++r)
|
||||
{
|
||||
rows[r] = mapToSource (rows[r]);
|
||||
}
|
||||
|
||||
// reverse sort by row
|
||||
qSort (rows.begin (), rows.end (), row_is_higher);
|
||||
Q_FOREACH (auto index, rows)
|
||||
{
|
||||
if (result && !m_->removeRow (index.row ()))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void FrequencyList::filter (Mode mode)
|
||||
{
|
||||
m_->mode_filter_ = mode;
|
||||
invalidateFilter ();
|
||||
}
|
||||
|
||||
bool FrequencyList::filterAcceptsRow (int source_row, QModelIndex const& /* parent */) const
|
||||
{
|
||||
bool result {true};
|
||||
if (m_->mode_filter_ != Modes::NULL_MODE)
|
||||
{
|
||||
auto const& item = m_->frequency_list_[source_row];
|
||||
// we pass NULL_MODE mode rows unless filtering for FreqCal mode
|
||||
result = (Modes::NULL_MODE == item.mode_ && m_->mode_filter_ != Modes::FreqCal)
|
||||
|| m_->mode_filter_ == item.mode_;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
auto FrequencyList::impl::frequency_list (FrequencyItems frequency_list) -> FrequencyItems
|
||||
{
|
||||
beginResetModel ();
|
||||
std::swap (frequency_list_, frequency_list);
|
||||
endResetModel ();
|
||||
return frequency_list;
|
||||
}
|
||||
|
||||
QModelIndex FrequencyList::impl::add (Item f)
|
||||
{
|
||||
// Any Frequency that isn't in the list may be added
|
||||
if (!frequency_list_.contains (f))
|
||||
{
|
||||
auto row = frequency_list_.size ();
|
||||
|
||||
beginInsertRows (QModelIndex {}, row, row);
|
||||
frequency_list_.append (f);
|
||||
endInsertRows ();
|
||||
|
||||
return index (row, 0);
|
||||
}
|
||||
return QModelIndex {};
|
||||
}
|
||||
|
||||
int FrequencyList::impl::rowCount (QModelIndex const& parent) const
|
||||
{
|
||||
return parent.isValid () ? 0 : frequency_list_.size ();
|
||||
}
|
||||
|
||||
int FrequencyList::impl::columnCount (QModelIndex const& parent) const
|
||||
{
|
||||
return parent.isValid () ? 0 : num_cols;
|
||||
}
|
||||
|
||||
Qt::ItemFlags FrequencyList::impl::flags (QModelIndex const& index) const
|
||||
{
|
||||
auto result = QAbstractTableModel::flags (index) | Qt::ItemIsDropEnabled;
|
||||
auto row = index.row ();
|
||||
auto column = index.column ();
|
||||
if (index.isValid ()
|
||||
&& row < frequency_list_.size ()
|
||||
&& column < num_cols)
|
||||
{
|
||||
if (frequency_mhz_column != column)
|
||||
{
|
||||
result |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariant FrequencyList::impl::data (QModelIndex const& index, int role) const
|
||||
{
|
||||
QVariant item;
|
||||
|
||||
auto const& row = index.row ();
|
||||
auto const& column = index.column ();
|
||||
|
||||
if (index.isValid ()
|
||||
&& row < frequency_list_.size ()
|
||||
&& column < num_cols)
|
||||
{
|
||||
auto const& frequency_item = frequency_list_.at (row);
|
||||
switch (column)
|
||||
{
|
||||
case mode_column:
|
||||
switch (role)
|
||||
{
|
||||
case SortRole:
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
case Qt::AccessibleTextRole:
|
||||
item = Modes::name (frequency_item.mode_);
|
||||
break;
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
case Qt::AccessibleDescriptionRole:
|
||||
item = tr ("Mode");
|
||||
break;
|
||||
|
||||
case Qt::TextAlignmentRole:
|
||||
item = Qt::AlignHCenter + Qt::AlignVCenter;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case frequency_column:
|
||||
switch (role)
|
||||
{
|
||||
case SortRole:
|
||||
case Qt::EditRole:
|
||||
case Qt::AccessibleTextRole:
|
||||
item = frequency_item.frequency_;
|
||||
break;
|
||||
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
auto const& band = bands_->find (frequency_item.frequency_);
|
||||
item = Radio::pretty_frequency_MHz_string (frequency_item.frequency_)
|
||||
+ " MHz (" + (band.isEmpty () ? "OOB" : band) + ')';
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
case Qt::AccessibleDescriptionRole:
|
||||
item = tr ("Frequency");
|
||||
break;
|
||||
|
||||
case Qt::TextAlignmentRole:
|
||||
item = Qt::AlignRight + Qt::AlignVCenter;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case frequency_mhz_column:
|
||||
switch (role)
|
||||
{
|
||||
case Qt::EditRole:
|
||||
case Qt::AccessibleTextRole:
|
||||
item = Radio::frequency_MHz_string (frequency_item.frequency_);
|
||||
break;
|
||||
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
auto const& band = bands_->find (frequency_item.frequency_);
|
||||
item = Radio::pretty_frequency_MHz_string (frequency_item.frequency_)
|
||||
+ " MHz (" + (band.isEmpty () ? "OOB" : band) + ')';
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
case Qt::AccessibleDescriptionRole:
|
||||
item = tr ("Frequency (MHz)");
|
||||
break;
|
||||
|
||||
case Qt::TextAlignmentRole:
|
||||
item = Qt::AlignRight + Qt::AlignVCenter;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
bool FrequencyList::impl::setData (QModelIndex const& model_index, QVariant const& value, int role)
|
||||
{
|
||||
bool changed {false};
|
||||
|
||||
auto const& row = model_index.row ();
|
||||
if (model_index.isValid ()
|
||||
&& Qt::EditRole == role
|
||||
&& row < frequency_list_.size ())
|
||||
{
|
||||
QVector<int> roles;
|
||||
roles << role;
|
||||
|
||||
auto& item = frequency_list_[row];
|
||||
switch (model_index.column ())
|
||||
{
|
||||
case mode_column:
|
||||
if (value.canConvert<Mode> ())
|
||||
{
|
||||
auto mode = Modes::value (value.toString ());
|
||||
if (mode != item.mode_)
|
||||
{
|
||||
item.mode_ = mode;
|
||||
Q_EMIT dataChanged (model_index, model_index, roles);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case frequency_column:
|
||||
if (value.canConvert<Frequency> ())
|
||||
{
|
||||
Radio::Frequency frequency {qvariant_cast <Radio::Frequency> (value)};
|
||||
if (frequency != item.frequency_)
|
||||
{
|
||||
item.frequency_ = frequency;
|
||||
// mark derived column (1) changed as well
|
||||
Q_EMIT dataChanged (index (model_index.row (), 1), model_index, roles);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
QVariant FrequencyList::impl::headerData (int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
QVariant header;
|
||||
if (Qt::DisplayRole == role
|
||||
&& Qt::Horizontal == orientation
|
||||
&& section < num_cols)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case mode_column: header = tr ("Mode"); break;
|
||||
case frequency_column: header = tr ("Frequency"); break;
|
||||
case frequency_mhz_column: header = tr ("Frequency (MHz)"); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
header = QAbstractTableModel::headerData (section, orientation, role);
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
bool FrequencyList::impl::removeRows (int row, int count, QModelIndex const& parent)
|
||||
{
|
||||
if (0 < count && (row + count) <= rowCount (parent))
|
||||
{
|
||||
beginRemoveRows (parent, row, row + count - 1);
|
||||
for (auto r = 0; r < count; ++r)
|
||||
{
|
||||
frequency_list_.removeAt (row);
|
||||
}
|
||||
endRemoveRows ();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FrequencyList::impl::insertRows (int row, int count, QModelIndex const& parent)
|
||||
{
|
||||
if (0 < count)
|
||||
{
|
||||
beginInsertRows (parent, row, row + count - 1);
|
||||
for (auto r = 0; r < count; ++r)
|
||||
{
|
||||
frequency_list_.insert (row, Item {0, Mode::NULL_MODE});
|
||||
}
|
||||
endInsertRows ();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList FrequencyList::impl::mimeTypes () const
|
||||
{
|
||||
QStringList types;
|
||||
types << mime_type;
|
||||
return types;
|
||||
}
|
||||
|
||||
QMimeData * FrequencyList::impl::mimeData (QModelIndexList const& items) const
|
||||
{
|
||||
QMimeData * mime_data = new QMimeData {};
|
||||
QByteArray encoded_data;
|
||||
QDataStream stream {&encoded_data, QIODevice::WriteOnly};
|
||||
|
||||
Q_FOREACH (auto const& item, items)
|
||||
{
|
||||
if (item.isValid () && frequency_column == item.column ())
|
||||
{
|
||||
stream << frequency_list_.at (item.row ());
|
||||
}
|
||||
}
|
||||
|
||||
mime_data->setData (mime_type, encoded_data);
|
||||
return mime_data;
|
||||
}
|
||||
|
||||
auto FrequencyList::const_iterator::operator * () const -> Item const&
|
||||
{
|
||||
return parent_->frequency_list ().at(parent_->mapToSource (parent_->index (row_, 0)).row ());
|
||||
}
|
||||
|
||||
auto FrequencyList::const_iterator::operator -> () const -> Item const *
|
||||
{
|
||||
return &parent_->frequency_list ().at(parent_->mapToSource (parent_->index (row_, 0)).row ());
|
||||
}
|
||||
|
||||
bool FrequencyList::const_iterator::operator != (const_iterator const& rhs) const
|
||||
{
|
||||
return parent_ != rhs.parent_ || row_ != rhs.row_;
|
||||
}
|
||||
|
||||
bool FrequencyList::const_iterator::operator == (const_iterator const& rhs) const
|
||||
{
|
||||
return parent_ == rhs.parent_ && row_ == rhs.row_;
|
||||
}
|
||||
|
||||
auto FrequencyList::const_iterator::operator ++ () -> const_iterator&
|
||||
{
|
||||
++row_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto FrequencyList::begin () const -> const_iterator
|
||||
{
|
||||
return const_iterator (this, 0);
|
||||
}
|
||||
|
||||
auto FrequencyList::end () const -> const_iterator
|
||||
{
|
||||
return const_iterator (this, rowCount ());
|
||||
}
|
||||
|
||||
auto FrequencyList::find (Frequency f) const -> const_iterator
|
||||
{
|
||||
int row {0};
|
||||
for (; row < rowCount (); ++row)
|
||||
{
|
||||
if (m_->frequency_list_[mapToSource (index (row, 0)).row ()].frequency_ == f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return const_iterator (this, row);
|
||||
}
|
||||
|
||||
auto FrequencyList::filtered_bands () const -> BandSet
|
||||
{
|
||||
BandSet result;
|
||||
for (auto const& item : *this)
|
||||
{
|
||||
result << m_->bands_->find (item.frequency_);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto FrequencyList::all_bands (Mode mode) const -> BandSet
|
||||
{
|
||||
BandSet result;
|
||||
for (auto const& item : m_->frequency_list_)
|
||||
{
|
||||
if (mode == Modes::NULL_MODE || item.mode_ == mode)
|
||||
{
|
||||
result << m_->bands_->find (item.frequency_);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
#include "decodedtext.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QRegularExpression>
|
||||
|
||||
|
||||
extern "C" {
|
||||
bool stdmsg_(const char* msg, int len);
|
||||
}
|
||||
|
||||
QString DecodedText::CQersCall()
|
||||
{
|
||||
// extract the CQer's call TODO: does this work with all call formats?
|
||||
int s1 {0};
|
||||
int position;
|
||||
QString t=_string;
|
||||
if ((position = _string.indexOf (" CQ DX ")) >= 0)
|
||||
{
|
||||
s1 = 7 + position;
|
||||
}
|
||||
else if ((position = _string.indexOf (" CQDX ")) >= 0)
|
||||
{
|
||||
s1 = 6 + position;
|
||||
}
|
||||
else if ((position = _string.indexOf (" CQ ")) >= 0)
|
||||
{
|
||||
s1 = 4 + position;
|
||||
if(_string.mid(s1,3).toInt() > 0 and _string.mid(s1,3).toInt() <= 999) s1 += 4;
|
||||
}
|
||||
else if ((position = _string.indexOf (" DE ")) >= 0)
|
||||
{
|
||||
s1 = 4 + position;
|
||||
}
|
||||
else if ((position = _string.indexOf (" QRZ ")) >= 0)
|
||||
{
|
||||
s1 = 5 + position;
|
||||
}
|
||||
auto s2 = _string.indexOf (" ", s1);
|
||||
return _string.mid (s1, s2 - s1);
|
||||
}
|
||||
|
||||
|
||||
bool DecodedText::isJT65()
|
||||
{
|
||||
return _string.indexOf("#") == column_mode + padding_;
|
||||
}
|
||||
|
||||
bool DecodedText::isJT9()
|
||||
{
|
||||
return _string.indexOf("@") == column_mode + padding_;
|
||||
}
|
||||
|
||||
bool DecodedText::isTX()
|
||||
{
|
||||
int i = _string.indexOf("Tx");
|
||||
return (i >= 0 && i < 15); // TODO guessing those numbers. Does Tx ever move?
|
||||
}
|
||||
|
||||
bool DecodedText::isLowConfidence ()
|
||||
{
|
||||
return QChar {'?'} == _string.mid (padding_ + column_qsoText + 21, 1);
|
||||
}
|
||||
|
||||
int DecodedText::frequencyOffset()
|
||||
{
|
||||
return _string.mid(column_freq + padding_,4).toInt();
|
||||
}
|
||||
|
||||
int DecodedText::snr()
|
||||
{
|
||||
int i1=_string.indexOf(" ")+1;
|
||||
return _string.mid(i1,3).toInt();
|
||||
}
|
||||
|
||||
float DecodedText::dt()
|
||||
{
|
||||
return _string.mid(column_dt + padding_,5).toFloat();
|
||||
}
|
||||
|
||||
/*
|
||||
2343 -11 0.8 1259 # YV6BFE F6GUU R-08
|
||||
2343 -19 0.3 718 # VE6WQ SQ2NIJ -14
|
||||
2343 -7 0.3 815 # KK4DSD W7VP -16
|
||||
2343 -13 0.1 3627 @ CT1FBK IK5YZT R+02
|
||||
|
||||
0605 Tx 1259 # CQ VK3ACF QF22
|
||||
*/
|
||||
|
||||
// find and extract any report. Returns true if this is a standard message
|
||||
bool DecodedText::report(QString const& myBaseCall, QString const& dxBaseCall, /*mod*/QString& report)
|
||||
{
|
||||
QString msg=_string.mid(column_qsoText + padding_).trimmed();
|
||||
if(msg.length() < 1) return false;
|
||||
msg = msg.left (22).remove (QRegularExpression {"[<>]"});
|
||||
int i1=msg.indexOf('\r');
|
||||
if (i1>0)
|
||||
msg=msg.left (i1-1);
|
||||
bool b = stdmsg_ ((msg + " ").toLatin1().constData(),22); // stdmsg is a fortran routine that packs the text, unpacks it and compares the result
|
||||
|
||||
QStringList w=msg.split(" ",QString::SkipEmptyParts);
|
||||
if(w.size ()
|
||||
&& b && (w[0] == myBaseCall
|
||||
|| w[0].endsWith ("/" + myBaseCall)
|
||||
|| w[0].startsWith (myBaseCall + "/")
|
||||
|| (w.size () > 1 && !dxBaseCall.isEmpty ()
|
||||
&& (w[1] == dxBaseCall
|
||||
|| w[1].endsWith ("/" + dxBaseCall)
|
||||
|| w[1].startsWith (dxBaseCall + "/")))))
|
||||
{
|
||||
QString tt="";
|
||||
if(w.size() > 2) tt=w[2];
|
||||
bool ok;
|
||||
i1=tt.toInt(&ok);
|
||||
if (ok and i1>=-50 and i1<50)
|
||||
{
|
||||
report = tt;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tt.mid(0,1)=="R")
|
||||
{
|
||||
i1=tt.mid(1).toInt(&ok);
|
||||
if(ok and i1>=-50 and i1<50)
|
||||
{
|
||||
report = tt.mid(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// get the first text word, usually the call
|
||||
QString DecodedText::call()
|
||||
{
|
||||
auto call = _string;
|
||||
call = call.replace (QRegularExpression {" CQ ([A-Z]{2,2}|[0-9]{3,3}) "}, " CQ_\\1 ").mid (column_qsoText + padding_);
|
||||
int i = call.indexOf(" ");
|
||||
return call.mid(0,i);
|
||||
}
|
||||
|
||||
// get the second word, most likely the de call and the third word, most likely grid
|
||||
void DecodedText::deCallAndGrid(/*out*/QString& call, QString& grid)
|
||||
{
|
||||
auto msg = _string;
|
||||
if(msg.mid(4,1)!=" ") msg=msg.mid(0,4)+msg.mid(6,-1); //Remove seconds from UTC
|
||||
msg = msg.replace (QRegularExpression {" CQ ([A-Z]{2,2}|[0-9]{3,3}) "}, " CQ_\\1 ").mid (column_qsoText + padding_);
|
||||
int i1 = msg.indexOf (" ");
|
||||
call = msg.mid (i1 + 1);
|
||||
int i2 = call.indexOf (" ");
|
||||
if (" R " == call.mid (i2, 3)) // MSK144 contest mode report
|
||||
{
|
||||
grid = call.mid (i2 + 3, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
grid = call.mid (i2 + 1, 4);
|
||||
}
|
||||
call = call.left (i2).replace (">", "");
|
||||
}
|
||||
|
||||
int DecodedText::timeInSeconds()
|
||||
{
|
||||
return 60*_string.mid(column_time,2).toInt() + _string.mid(2,2).toInt();
|
||||
}
|
||||
|
||||
/*
|
||||
2343 -11 0.8 1259 # YV6BFE F6GUU R-08
|
||||
2343 -19 0.3 718 # VE6WQ SQ2NIJ -14
|
||||
2343 -7 0.3 815 # KK4DSD W7VP -16
|
||||
2343 -13 0.1 3627 @ CT1FBK IK5YZT R+02
|
||||
|
||||
0605 Tx 1259 # CQ VK3ACF QF22
|
||||
*/
|
||||
|
||||
QString DecodedText::report() // returns a string of the SNR field with a leading + or - followed by two digits
|
||||
{
|
||||
int sr = snr();
|
||||
if (sr<-50)
|
||||
sr = -50;
|
||||
else
|
||||
if (sr > 49)
|
||||
sr = 49;
|
||||
|
||||
QString rpt;
|
||||
rpt.sprintf("%d",abs(sr));
|
||||
if (sr > 9)
|
||||
rpt = "+" + rpt;
|
||||
else
|
||||
if (sr >= 0)
|
||||
rpt = "+0" + rpt;
|
||||
else
|
||||
if (sr >= -9)
|
||||
rpt = "-0" + rpt;
|
||||
else
|
||||
rpt = "-" + rpt;
|
||||
return rpt;
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
subroutine wqenc(msg,ntype,data0)
|
||||
|
||||
! Parse and encode a WSPR message.
|
||||
|
||||
use packjt
|
||||
parameter (MASK15=32767)
|
||||
character*22 msg
|
||||
character*12 call1,call2
|
||||
character*4 grid
|
||||
character*9 name
|
||||
character ccur*4,cxp*2
|
||||
logical lbad1,lbad2
|
||||
integer*1 data0(11)
|
||||
integer nu(0:9)
|
||||
data nu/0,-1,1,0,-1,2,1,0,-1,1/
|
||||
|
||||
read(msg,1001,end=1,err=1) ng,n1
|
||||
1001 format(z4,z7)
|
||||
ntype=62
|
||||
n2=128*ng + (ntype+64)
|
||||
call pack50(n1,n2,data0) !Pack 8 bits per byte, add tail
|
||||
go to 900
|
||||
|
||||
1 if(msg(1:6).eq.'73 DE ') go to 80
|
||||
if(index(msg,' W ').gt.0 .and. index(msg,' DBD ').gt.0) go to 90
|
||||
if(msg(1:4).eq.'QRZ ') go to 100
|
||||
if(msg(1:8).eq.'PSE QSY ') go to 110
|
||||
if(msg(1:3).eq.'WX ') go to 120
|
||||
|
||||
! Standard WSPR message (types 0 3 7 10 13 17 ... 60)
|
||||
i1=index(msg,' ')
|
||||
if(i1.lt.4 .or. i1.gt.7) go to 10
|
||||
call1=msg(:i1-1)
|
||||
grid=msg(i1+1:i1+4)
|
||||
call packcall(call1,n1,lbad1)
|
||||
call packgrid(grid,ng,lbad2)
|
||||
if(lbad1 .or. lbad2) go to 10
|
||||
ndbm=0
|
||||
read(msg(i1+5:),*,err=10,end=800) ndbm
|
||||
if(ndbm.lt.0 .or. ndbm.gt.60) go to 800
|
||||
ndbm=ndbm+nu(mod(ndbm,10))
|
||||
n2=128*ng + (ndbm+64)
|
||||
call pack50(n1,n2,data0)
|
||||
ntype=ndbm
|
||||
go to 900
|
||||
|
||||
! "BestDX" automated WSPR reply (type 1)
|
||||
10 if(i1.ne.5 .or. msg(5:8).ne.' DE ') go to 20
|
||||
grid=msg(1:4)
|
||||
call packgrid(grid,ng,lbad2)
|
||||
if(lbad2) go to 800
|
||||
call1=msg(9:)
|
||||
call packcall(call1,n1,lbad1)
|
||||
if(lbad1) go to 800
|
||||
ntype=1
|
||||
n2=128*ng + (ntype+64)
|
||||
call pack50(n1,n2,data0) !Pack 8 bits per byte, add tail
|
||||
go to 900
|
||||
|
||||
! CQ (msg #1; types 2, 4, 5)
|
||||
20 if(msg(1:3).ne.'CQ ') go to 30
|
||||
if(index(msg,'/').le.0) then
|
||||
i2=index(msg(4:),' ')
|
||||
call1=msg(4:i2+3)
|
||||
grid=msg(i2+4:)
|
||||
call packcall(call1,n1,lbad1)
|
||||
if(lbad1) go to 30
|
||||
call packgrid(grid,ng,lbad2)
|
||||
if(lbad2) go to 30
|
||||
ntype=2
|
||||
n2=128*ng + (ntype+64)
|
||||
call pack50(n1,n2,data0)
|
||||
else
|
||||
ntype=4 ! or 5
|
||||
call1=msg(4:)
|
||||
call packpfx(call1,n1,ng,nadd)
|
||||
ntype=ntype+nadd
|
||||
n2=128*ng + ntype + 64
|
||||
call pack50(n1,n2,data0)
|
||||
endif
|
||||
go to 900
|
||||
|
||||
! Reply to CQ (msg #2; types 6,8,9,11)
|
||||
30 if(msg(1:1).ne.'<' .and. msg(1:3).ne.'DE ') go to 40
|
||||
if(index(msg,' RRR ').gt.0) go to 50
|
||||
if(msg(1:1).eq.'<') then
|
||||
ntype=6
|
||||
i1=index(msg,'>')
|
||||
call1=msg(2:i1-1)
|
||||
read(msg(i1+1:),*,err=31,end=31) k,muf,ccur,cxp
|
||||
go to 130
|
||||
31 call2=msg(i1+2:)
|
||||
call hash(call1,i1-2,ih)
|
||||
call packcall(call2,n1,lbad1)
|
||||
n2=128*ih + (ntype+64)
|
||||
call pack50(n1,n2,data0)
|
||||
else
|
||||
i1=index(msg(4:),' ')
|
||||
call1=msg(4:i1+2)
|
||||
if(index(msg,'/').le.0) then
|
||||
ntype=8
|
||||
ih=0
|
||||
call packcall(call1,n1,lbad1)
|
||||
grid=msg(i1+4:i1+7)
|
||||
call packgrid(grid,ng,lbad2)
|
||||
n2=128*ng + (ntype+64)
|
||||
call pack50(n1,n2,data0)
|
||||
else
|
||||
ntype=9 ! or 11
|
||||
call1=msg(4:)
|
||||
call packpfx(call1,n1,ng,nadd)
|
||||
ntype=ntype + 2*nadd
|
||||
n2=128*ng + ntype + 64
|
||||
call pack50(n1,n2,data0)
|
||||
endif
|
||||
endif
|
||||
go to 900
|
||||
|
||||
! Call(s) + report (msg #3; types -1 to -27)
|
||||
! Call(s) + R + report (msg #4; types -28 to -54)
|
||||
40 if(index(msg,' RRR').gt.0) go to 50
|
||||
i1=index(msg,'<')
|
||||
if(i1.gt.0 .and. (i1.lt.5 .or. i1.gt.8)) go to 50
|
||||
i2=index(msg,'/')
|
||||
if(i2.gt.0 .and.i2.le.4) then
|
||||
ntype=-10 ! -10 to -27
|
||||
i0=index(msg,' ')
|
||||
call1=msg(:i0-1)
|
||||
call packpfx(call1,n1,ng,nadd)
|
||||
ntype=ntype - 9*nadd
|
||||
i2=index(msg,' ')
|
||||
i3=index(msg,' R ')
|
||||
if(i3.gt.0) i2=i2+2 !-28 to -36
|
||||
read(msg(i2+2:i2+2),*,end=800,err=800) nrpt
|
||||
ntype=ntype - (nrpt-1)
|
||||
if(i3.gt.0) ntype=ntype-27
|
||||
n2=128*ng + ntype + 64
|
||||
call pack50(n1,n2,data0)
|
||||
go to 900
|
||||
else if(i1.eq.0) then
|
||||
go to 50
|
||||
endif
|
||||
call1=msg(:i1-2) !-1 to -9
|
||||
i2=index(msg,'>')
|
||||
call2=msg(i1+1:i2-1)
|
||||
call hash(call2,i2-i1-1,ih)
|
||||
i3=index(msg,' R ')
|
||||
if(i3.gt.0) i2=i2+2 !-28 to -36
|
||||
read(msg(i2+3:i2+3),*,end=42,err=42) nrpt
|
||||
go to 43
|
||||
42 nrpt=1
|
||||
43 ntype=-nrpt
|
||||
if(i3.gt.0) ntype=-(nrpt+27)
|
||||
call packcall(call1,n1,lbad1)
|
||||
n2=128*ih + (ntype+64)
|
||||
call pack50(n1,n2,data0)
|
||||
go to 900
|
||||
|
||||
50 i0=index(msg,'<')
|
||||
if(i0.le.0 .and. msg(1:3).ne.'DE ') go to 60
|
||||
i3=index(msg,' RRR')
|
||||
if(i3.le.0) go to 60
|
||||
! Call or calls and RRR (msg#5; type2 12,14,15,16)
|
||||
i0=index(msg,'<')
|
||||
if(i0.eq.1) then
|
||||
if(index(msg,'/').le.0) then
|
||||
ntype=14
|
||||
i1=index(msg,'>')
|
||||
call1=msg(2:i1-1)
|
||||
call2=msg(i1+2:)
|
||||
i2=index(call2,' ')
|
||||
call2=call2(:i2-1)
|
||||
call packcall(call2,n1,lbad1)
|
||||
call hash(call1,i1-2,ih)
|
||||
n2=128*ih + (ntype+64)
|
||||
call pack50(n1,n2,data0)
|
||||
else
|
||||
stop '0002'
|
||||
endif
|
||||
else if(i0.ge.5 .and. i0.le.8) then
|
||||
if(index(msg,'/').le.0) then
|
||||
ntype=12
|
||||
i1=index(msg,'>')
|
||||
call1=msg(:i0-2)
|
||||
call2=msg(i0+1:i1-1)
|
||||
call packcall(call1,n1,lbad1)
|
||||
call hash(call2,i1-i0-1,ih)
|
||||
n2=128*ih + (ntype+64)
|
||||
call pack50(n1,n2,data0)
|
||||
else
|
||||
stop '0002'
|
||||
endif
|
||||
else
|
||||
i1=index(msg(4:),' ')
|
||||
call1=msg(4:i1+2)
|
||||
if(index(msg,'/').le.0) then
|
||||
ntype=9
|
||||
grid=msg(i1+4:i1+7)
|
||||
else
|
||||
ntype=15 ! or 16
|
||||
call1=msg(4:)
|
||||
i0=index(call1,' ')
|
||||
call1=call1(:i0-1)
|
||||
call packpfx(call1,n1,ng,nadd)
|
||||
ntype=ntype+nadd
|
||||
n2=128*ng + ntype + 64
|
||||
call pack50(n1,n2,data0)
|
||||
endif
|
||||
endif
|
||||
go to 900
|
||||
|
||||
! TNX <name> 73 GL (msg #6; type 18 ...)
|
||||
60 if(msg(1:4).ne.'TNX ') go to 70
|
||||
ntype=18
|
||||
n1=0
|
||||
i2=index(msg(5:),' ')
|
||||
name=msg(5:i2+4)
|
||||
call packname(name,i2-1,n1,ng)
|
||||
n2=128*ng + (ntype+64)
|
||||
call pack50(n1,n2,data0)
|
||||
go to 900
|
||||
|
||||
! TNX name 73 GL (msg #6; type -56 ...)
|
||||
70 if(msg(1:3).ne.'OP ') go to 80
|
||||
ntype=-56
|
||||
n1=0
|
||||
i2=index(msg(4:),' ')
|
||||
name=msg(4:i2+3)
|
||||
call packname(name,i2-1,n1,ng)
|
||||
n2=128*ng + (ntype+64)
|
||||
call pack50(n1,n2,data0)
|
||||
go to 900
|
||||
|
||||
! 73 DE call grid (msg #6; type 19)
|
||||
80 if(msg(1:6).ne.'73 DE ') go to 90
|
||||
ntype=19
|
||||
i1=index(msg(7:),' ')
|
||||
call1=msg(7:)
|
||||
if(index(call1,'/').le.0) then
|
||||
i1=index(call1,' ')
|
||||
grid=call1(i1+1:)
|
||||
call1=call1(:i1-1)
|
||||
call packcall(call1,n1,lbad1)
|
||||
call packgrid(grid,ng,lbad2)
|
||||
if(lbad1 .or. lbad2) go to 800
|
||||
n2=128*ng + (ntype+64)
|
||||
call pack50(n1,n2,data0)
|
||||
go to 900
|
||||
else
|
||||
ntype=21 ! or 22
|
||||
call packpfx(call1,n1,ng,nadd)
|
||||
ntype=ntype + nadd
|
||||
n2=128*ng + ntype + 64
|
||||
call pack50(n1,n2,data0)
|
||||
go to 900
|
||||
endif
|
||||
|
||||
! [pwr] W [gain] DBD [73 GL] (msg #6; types 24, 25)
|
||||
90 if(index(msg,' W ').le.0) go to 140
|
||||
ntype=25
|
||||
if(index(msg,' DBD 73 GL').gt.0) ntype=24
|
||||
i1=index(msg,' ')
|
||||
read(msg(:i1-1),*,end=800,err=800) watts
|
||||
if(watts.ge.1.0) nwatts=watts
|
||||
if(watts.lt.1.0) nwatts=3000 + nint(1000.*watts)
|
||||
if(index(msg,'DIPOLE').gt.0) then
|
||||
ndbd=30000
|
||||
else if(index(msg,'VERTICAL').gt.0) then
|
||||
ndbd=30001
|
||||
else
|
||||
i2=index(msg(i1+3:),' ')
|
||||
read(msg(i1+3:i1+i2+1),*,end=800,err=800) ndbd
|
||||
endif
|
||||
n1=nwatts
|
||||
ng=ndbd + 32
|
||||
n2=128*ng + (ntype+64)
|
||||
call pack50(n1,n2,data0)
|
||||
go to 900
|
||||
|
||||
! QRZ call (msg #3; type 26)
|
||||
100 call1=msg(5:)
|
||||
call packcall(call1,n1,lbad1)
|
||||
if(lbad1) go to 800
|
||||
ntype=26
|
||||
n2=ntype+64
|
||||
call pack50(n1,n2,data0)
|
||||
go to 900
|
||||
|
||||
! PSE QSY [nnn] KHZ (msg #6; type 28)
|
||||
110 ntype=28
|
||||
read(msg(9:),*,end=800,err=800) n1
|
||||
n2=ntype+64
|
||||
call pack50(n1,n2,data0)
|
||||
go to 900
|
||||
|
||||
! WX wx temp C|F wind (msg #6; type 29)
|
||||
120 ntype=29
|
||||
if(index(msg,' CLEAR ').gt.0) then
|
||||
i1=10
|
||||
n1=10000
|
||||
else if(index(msg,' CLOUDY ').gt.0) then
|
||||
i1=11
|
||||
n1=20000
|
||||
else if(index(msg,' RAIN ').gt.0) then
|
||||
i1=9
|
||||
n1=30000
|
||||
else if(index(msg,' SNOW ').gt.0) then
|
||||
i1=9
|
||||
n1=40000
|
||||
endif
|
||||
read(msg(i1:),*,err=800,end=800) ntemp
|
||||
ntemp=ntemp+100
|
||||
i1=index(msg,' C ')
|
||||
if(i1.gt.0) ntemp=ntemp+1000
|
||||
n1=n1+ntemp
|
||||
if(index(msg,' CALM').gt.0) ng=1
|
||||
if(index(msg,' BREEZES').gt.0) ng=2
|
||||
if(index(msg,' WINDY').gt.0) ng=3
|
||||
if(index(msg,' DRY').gt.0) ng=4
|
||||
if(index(msg,' HUMID').gt.0) ng=5
|
||||
|
||||
n2=128*ng + (ntype+64)
|
||||
call pack50(n1,n2,data0)
|
||||
|
||||
go to 900
|
||||
|
||||
! Solar/geomagnetic/ionospheric data
|
||||
130 ntype=63
|
||||
call packprop(k,muf,ccur,cxp,n1)
|
||||
call hash(call1,i1-2,ih)
|
||||
n2=128*ih + ntype + 64
|
||||
call pack50(n1,n2,data0)
|
||||
go to 900
|
||||
|
||||
140 continue
|
||||
|
||||
! Plain text
|
||||
800 ntype=-57
|
||||
call packtext2(msg(:8),n1,ng)
|
||||
n2=128*ng + ntype + 64
|
||||
call pack50(n1,n2,data0)
|
||||
go to 900
|
||||
|
||||
900 continue
|
||||
return
|
||||
end subroutine wqenc
|
||||
@@ -1,63 +0,0 @@
|
||||
#ifndef RADIO_HPP__
|
||||
#define RADIO_HPP__
|
||||
|
||||
#include <QObject>
|
||||
#include <QLocale>
|
||||
#include <QList>
|
||||
|
||||
#include "udp_export.h"
|
||||
|
||||
class QVariant;
|
||||
class QString;
|
||||
|
||||
//
|
||||
// Declarations common to radio software.
|
||||
//
|
||||
|
||||
namespace Radio
|
||||
{
|
||||
//
|
||||
// Frequency types
|
||||
//
|
||||
using Frequency = quint64;
|
||||
using Frequencies = QList<Frequency>;
|
||||
using FrequencyDelta = qint64;
|
||||
|
||||
//
|
||||
// Qt type registration
|
||||
//
|
||||
void UDP_NO_EXPORT register_types ();
|
||||
|
||||
//
|
||||
// Frequency type conversion.
|
||||
//
|
||||
// QVariant argument is convertible to double and is assumed to
|
||||
// be scaled by (10 ** -scale).
|
||||
//
|
||||
Frequency UDP_EXPORT frequency (QVariant const&, int scale,
|
||||
bool * ok = nullptr, QLocale const& = QLocale ());
|
||||
FrequencyDelta UDP_EXPORT frequency_delta (QVariant const&, int scale,
|
||||
bool * ok = nullptr, QLocale const& = QLocale ());
|
||||
|
||||
//
|
||||
// Frequency type formatting
|
||||
//
|
||||
QString UDP_EXPORT frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
||||
QString UDP_EXPORT frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
||||
QString UDP_EXPORT pretty_frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
||||
QString UDP_EXPORT pretty_frequency_MHz_string (double, int scale, QLocale const& = QLocale ());
|
||||
QString UDP_EXPORT pretty_frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
||||
|
||||
//
|
||||
// Callsigns
|
||||
//
|
||||
bool UDP_EXPORT is_callsign (QString const&);
|
||||
bool UDP_EXPORT is_compound_callsign (QString const&);
|
||||
QString UDP_EXPORT base_callsign (QString);
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE (Radio::Frequency);
|
||||
Q_DECLARE_METATYPE (Radio::Frequencies);
|
||||
Q_DECLARE_METATYPE (Radio::FrequencyDelta);
|
||||
|
||||
#endif
|
||||
@@ -1,358 +0,0 @@
|
||||
subroutine ft8b(dd0,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,napwid, &
|
||||
lsubtract,nagain,iaptype,mygrid6,bcontest,sync0,f1,xdt,apsym,nharderrors,&
|
||||
dmin,nbadcrc,ipass,iera,message,xsnr)
|
||||
|
||||
use timer_module, only: timer
|
||||
include 'ft8_params.f90'
|
||||
parameter(NRECENT=10,NP2=2812)
|
||||
character message*22,msgsent*22
|
||||
character*12 recent_calls(NRECENT)
|
||||
character*6 mygrid6
|
||||
logical bcontest
|
||||
real a(5)
|
||||
real s1(0:7,ND),s2(0:7,NN)
|
||||
real ps(0:7)
|
||||
real rxdata(3*ND),rxdatap(3*ND)
|
||||
real llr(3*ND),llra(3*ND),llr0(3*ND),llrap(3*ND) !Soft symbols
|
||||
real dd0(15*12000)
|
||||
integer*1 decoded(KK),apmask(3*ND),cw(3*ND)
|
||||
integer*1 msgbits(KK)
|
||||
integer apsym(KK)
|
||||
integer mcq(28),mde(28),mrrr(16),m73(16),mrr73(16)
|
||||
integer itone(NN)
|
||||
integer icos7(0:6),ip(1)
|
||||
integer nappasses(0:5) ! the number of decoding passes to use for each QSO state
|
||||
integer naptypes(0:5,4) ! (nQSOProgress, decoding pass) maximum of 4 passes for now
|
||||
complex cd0(3200)
|
||||
complex ctwk(32)
|
||||
complex csymb(32)
|
||||
logical first,newdat,lsubtract,lapon,nagain
|
||||
data icos7/2,5,6,0,4,1,3/
|
||||
data mcq/1,1,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,1,0,0,0,1,1,0,0,1/
|
||||
data mrrr/0,1,1,1,1,1,1,0,1,1,0,0,1,1,1,1/
|
||||
data m73/0,1,1,1,1,1,1,0,1,1,0,1,0,0,0,0/
|
||||
data mde/1,1,1,1,1,1,1,1,0,1,1,0,0,1,0,0,0,0,0,1,1,1,0,1,0,0,0,1/
|
||||
data mrr73/0,0,0,0,0,0,1,0,0,0,0,1,0,1,0,1/
|
||||
data first/.true./
|
||||
save nappasses,naptypes
|
||||
|
||||
if(first) then
|
||||
mcq=2*mcq-1
|
||||
mde=2*mde-1
|
||||
mrrr=2*mrrr-1
|
||||
m73=2*m73-1
|
||||
mrr73=2*mrr73-1
|
||||
nappasses(0)=2
|
||||
nappasses(1)=2
|
||||
nappasses(2)=2
|
||||
nappasses(3)=4
|
||||
nappasses(4)=4
|
||||
nappasses(5)=3
|
||||
|
||||
! iaptype
|
||||
!------------------------
|
||||
! 1 CQ ??? ???
|
||||
! 2 MyCall ??? ???
|
||||
! 3 MyCall DxCall ???
|
||||
! 4 MyCall DxCall RRR
|
||||
! 5 MyCall DxCall 73
|
||||
! 6 MyCall DxCall RR73
|
||||
! 7 ??? DxCall ???
|
||||
|
||||
naptypes(0,1:4)=(/1,2,0,0/)
|
||||
naptypes(1,1:4)=(/2,3,0,0/)
|
||||
naptypes(2,1:4)=(/2,3,0,0/)
|
||||
naptypes(3,1:4)=(/3,4,5,6/)
|
||||
naptypes(4,1:4)=(/3,4,5,6/)
|
||||
naptypes(5,1:4)=(/3,1,2,0/) !?
|
||||
first=.false.
|
||||
endif
|
||||
|
||||
max_iterations=30
|
||||
nharderrors=-1
|
||||
fs2=12000.0/NDOWN
|
||||
dt2=1.0/fs2
|
||||
twopi=8.0*atan(1.0)
|
||||
delfbest=0.
|
||||
ibest=0
|
||||
|
||||
call timer('ft8_down',0)
|
||||
call ft8_downsample(dd0,newdat,f1,cd0) !Mix f1 to baseband and downsample
|
||||
call timer('ft8_down',1)
|
||||
|
||||
i0=nint((xdt+0.5)*fs2) !Initial guess for start of signal
|
||||
smax=0.0
|
||||
do idt=i0-8,i0+8 !Search over +/- one quarter symbol
|
||||
call sync8d(cd0,idt,ctwk,0,sync)
|
||||
if(sync.gt.smax) then
|
||||
smax=sync
|
||||
ibest=idt
|
||||
endif
|
||||
enddo
|
||||
xdt2=ibest*dt2 !Improved estimate for DT
|
||||
|
||||
! Now peak up in frequency
|
||||
i0=nint(xdt2*fs2)
|
||||
smax=0.0
|
||||
do ifr=-5,5 !Search over +/- 2.5 Hz
|
||||
delf=ifr*0.5
|
||||
dphi=twopi*delf*dt2
|
||||
phi=0.0
|
||||
do i=1,32
|
||||
ctwk(i)=cmplx(cos(phi),sin(phi))
|
||||
phi=mod(phi+dphi,twopi)
|
||||
enddo
|
||||
call sync8d(cd0,i0,ctwk,1,sync)
|
||||
if( sync .gt. smax ) then
|
||||
smax=sync
|
||||
delfbest=delf
|
||||
endif
|
||||
enddo
|
||||
a=0.0
|
||||
a(1)=-delfbest
|
||||
call twkfreq1(cd0,NP2,fs2,a,cd0)
|
||||
xdt=xdt2
|
||||
f1=f1+delfbest !Improved estimate of DF
|
||||
|
||||
call sync8d(cd0,i0,ctwk,2,sync)
|
||||
|
||||
j=0
|
||||
do k=1,NN
|
||||
i1=ibest+(k-1)*32
|
||||
csymb=cmplx(0.0,0.0)
|
||||
if( i1.ge.1 .and. i1+31 .le. NP2 ) csymb=cd0(i1:i1+31)
|
||||
call four2a(csymb,32,1,-1,1)
|
||||
s2(0:7,k)=abs(csymb(1:8))
|
||||
enddo
|
||||
|
||||
! sync quality check
|
||||
is1=0
|
||||
is2=0
|
||||
is3=0
|
||||
do k=1,7
|
||||
ip=maxloc(s2(:,k))
|
||||
if(icos7(k-1).eq.(ip(1)-1)) is1=is1+1
|
||||
ip=maxloc(s2(:,k+36))
|
||||
if(icos7(k-1).eq.(ip(1)-1)) is2=is2+1
|
||||
ip=maxloc(s2(:,k+72))
|
||||
if(icos7(k-1).eq.(ip(1)-1)) is3=is3+1
|
||||
enddo
|
||||
! hard sync sum - max is 21
|
||||
nsync=is1+is2+is3
|
||||
if(nsync .le. 6) then ! bail out
|
||||
nbadcrc=1
|
||||
return
|
||||
endif
|
||||
|
||||
j=0
|
||||
do k=1,NN
|
||||
if(k.le.7) cycle
|
||||
if(k.ge.37 .and. k.le.43) cycle
|
||||
if(k.gt.72) cycle
|
||||
j=j+1
|
||||
s1(0:7,j)=s2(0:7,k)
|
||||
enddo
|
||||
|
||||
do j=1,ND
|
||||
ps=s1(0:7,j)
|
||||
where (ps.gt.0.0) ps=log(ps)
|
||||
r1=max(ps(1),ps(3),ps(5),ps(7))-max(ps(0),ps(2),ps(4),ps(6))
|
||||
r2=max(ps(2),ps(3),ps(6),ps(7))-max(ps(0),ps(1),ps(4),ps(5))
|
||||
r4=max(ps(4),ps(5),ps(6),ps(7))-max(ps(0),ps(1),ps(2),ps(3))
|
||||
i4=3*j-2
|
||||
i2=3*j-1
|
||||
i1=3*j
|
||||
rxdata(i4)=r4
|
||||
rxdata(i2)=r2
|
||||
rxdata(i1)=r1
|
||||
rxdatap(i4)=r4
|
||||
rxdatap(i2)=r2
|
||||
rxdatap(i1)=r1
|
||||
|
||||
if(nQSOProgress .eq. 0 .or. nQSOProgress .eq. 5) then
|
||||
! When bits 88:115 are set as ap bits, bit 115 lives in symbol 39 along
|
||||
! with no-ap bits 116 and 117. Take care of metrics for bits 116 and 117.
|
||||
if(j.eq.39) then ! take care of bits that live in symbol 39
|
||||
if(apsym(28).lt.0) then
|
||||
rxdatap(i2)=max(ps(2),ps(3))-max(ps(0),ps(1))
|
||||
rxdatap(i1)=max(ps(1),ps(3))-max(ps(0),ps(2))
|
||||
else
|
||||
rxdatap(i2)=max(ps(6),ps(7))-max(ps(4),ps(5))
|
||||
rxdatap(i1)=max(ps(5),ps(7))-max(ps(4),ps(6))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
! When bits 116:143 are set as ap bits, bit 115 lives in symbol 39 along
|
||||
! with ap bits 116 and 117. Take care of metric for bit 115.
|
||||
! if(j.eq.39) then ! take care of bit 115
|
||||
! iii=2*(apsym(29)+1)/2 + (apsym(30)+1)/2 ! known values of bits 116 & 117
|
||||
! if(iii.eq.0) rxdatap(i4)=ps(4)-ps(0)
|
||||
! if(iii.eq.1) rxdatap(i4)=ps(5)-ps(1)
|
||||
! if(iii.eq.2) rxdatap(i4)=ps(6)-ps(2)
|
||||
! if(iii.eq.3) rxdatap(i4)=ps(7)-ps(3)
|
||||
! endif
|
||||
|
||||
! bit 144 lives in symbol 48 and will be 1 if it is set as an ap bit.
|
||||
! take care of metrics for bits 142 and 143
|
||||
if(j.eq.48) then ! bit 144 is always 1
|
||||
rxdatap(i4)=max(ps(5),ps(7))-max(ps(1),ps(3))
|
||||
rxdatap(i2)=max(ps(3),ps(7))-max(ps(1),ps(5))
|
||||
endif
|
||||
|
||||
! bit 154 lives in symbol 52 and will be 0 if it is set as an ap bit
|
||||
! take care of metrics for bits 155 and 156
|
||||
if(j.eq.52) then ! bit 154 will be 0 if it is set as an ap bit.
|
||||
rxdatap(i2)=max(ps(2),ps(3))-max(ps(0),ps(1))
|
||||
rxdatap(i1)=max(ps(1),ps(3))-max(ps(0),ps(2))
|
||||
endif
|
||||
|
||||
enddo
|
||||
|
||||
rxav=sum(rxdata)/(3.0*ND)
|
||||
rx2av=sum(rxdata*rxdata)/(3.0*ND)
|
||||
var=rx2av-rxav*rxav
|
||||
if( var .gt. 0.0 ) then
|
||||
rxsig=sqrt(var)
|
||||
else
|
||||
rxsig=sqrt(rx2av)
|
||||
endif
|
||||
rxdata=rxdata/rxsig
|
||||
! Let's just assume that rxsig is OK for rxdatap too...
|
||||
rxdatap=rxdatap/rxsig
|
||||
|
||||
ss=0.84
|
||||
llr0=2.0*rxdata/(ss*ss)
|
||||
llra=2.0*rxdatap/(ss*ss) ! llr's for use with ap
|
||||
apmag=4.0
|
||||
|
||||
! pass #
|
||||
!------------------------------
|
||||
! 1 regular decoding
|
||||
! 2 erase 24
|
||||
! 3 erase 48
|
||||
! 4 ap pass 1
|
||||
! 5 ap pass 2
|
||||
! 6 ap pass 3
|
||||
! 7 ap pass 4, etc.
|
||||
|
||||
if(lapon) then
|
||||
npasses=3+nappasses(nQSOProgress)
|
||||
else
|
||||
npasses=3
|
||||
endif
|
||||
|
||||
do ipass=1,npasses
|
||||
|
||||
llr=llr0
|
||||
if(ipass.ne.2 .and. ipass.ne.3) nblank=0
|
||||
if(ipass.eq.2) nblank=24
|
||||
if(ipass.eq.3) nblank=48
|
||||
if(nblank.gt.0) llr(1:nblank)=0.
|
||||
|
||||
if(ipass.le.3) then
|
||||
apmask=0
|
||||
llrap=llr
|
||||
iaptype=0
|
||||
endif
|
||||
|
||||
if(ipass .gt. 3) then
|
||||
iaptype=naptypes(nQSOProgress,ipass-3)
|
||||
if(iaptype.ge.3 .and. (abs(f1-nfqso).gt.napwid .and. abs(f1-nftx).gt.napwid) ) cycle
|
||||
if(iaptype.eq.1 .or. iaptype.eq.2 ) then ! AP,???,???
|
||||
apmask=0
|
||||
apmask(88:115)=1 ! first 28 bits are AP
|
||||
apmask(144)=1 ! not free text
|
||||
llrap=llr
|
||||
if(iaptype.eq.1) llrap(88:115)=apmag*mcq/ss
|
||||
if(iaptype.eq.2) llrap(88:115)=apmag*apsym(1:28)/ss
|
||||
llrap(116:117)=llra(116:117)
|
||||
llrap(142:143)=llra(142:143)
|
||||
llrap(144)=-apmag/ss
|
||||
endif
|
||||
if(iaptype.eq.3) then ! mycall, dxcall, ???
|
||||
apmask=0
|
||||
apmask(88:115)=1 ! mycall
|
||||
apmask(116:143)=1 ! hiscall
|
||||
apmask(144)=1 ! not free text
|
||||
llrap=llr
|
||||
llrap(88:143)=apmag*apsym(1:56)/ss
|
||||
llrap(144)=-apmag/ss
|
||||
endif
|
||||
if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype.eq.6) then
|
||||
apmask=0
|
||||
apmask(88:115)=1 ! mycall
|
||||
apmask(116:143)=1 ! hiscall
|
||||
apmask(144:159)=1 ! RRR or 73 or RR73
|
||||
llrap=llr
|
||||
llrap(88:143)=apmag*apsym(1:56)/ss
|
||||
if(iaptype.eq.4) llrap(144:159)=apmag*mrrr/ss
|
||||
if(iaptype.eq.5) llrap(144:159)=apmag*m73/ss
|
||||
if(iaptype.eq.6) llrap(144:159)=apmag*mrr73/ss
|
||||
endif
|
||||
if(iaptype.eq.7) then ! ???, dxcall, ???
|
||||
apmask=0
|
||||
apmask(116:143)=1 ! hiscall
|
||||
apmask(144)=1 ! not free text
|
||||
llrap=llr
|
||||
llrap(115)=llra(115)
|
||||
llrap(116:143)=apmag*apsym(29:56)/ss
|
||||
llrap(144)=-apmag/ss
|
||||
endif
|
||||
endif
|
||||
|
||||
cw=0
|
||||
call timer('bpd174 ',0)
|
||||
call bpdecode174(llrap,apmask,max_iterations,decoded,cw,nharderrors, &
|
||||
niterations)
|
||||
call timer('bpd174 ',1)
|
||||
dmin=0.0
|
||||
if(ndepth.eq.3 .and. nharderrors.lt.0) then
|
||||
norder=1
|
||||
if(abs(nfqso-f1).le.napwid .or. abs(nftx-f1).le.napwid) then
|
||||
if(ipass.le.3 .and. .not.nagain) then
|
||||
norder=2
|
||||
else ! norder=3 for nagain and AP decodes
|
||||
norder=3
|
||||
endif
|
||||
endif
|
||||
call timer('osd174 ',0)
|
||||
call osd174(llrap,apmask,norder,decoded,cw,nharderrors,dmin)
|
||||
call timer('osd174 ',1)
|
||||
endif
|
||||
nbadcrc=1
|
||||
message=' '
|
||||
xsnr=-99.0
|
||||
if(count(cw.eq.0).eq.174) cycle !Reject the all-zero codeword
|
||||
if(any(decoded(75:75).ne.0)) cycle !Reject if any of the 3 extra bits are nonzero
|
||||
if(nharderrors.ge.0 .and. nharderrors+dmin.lt.60.0 .and. &
|
||||
.not.(sync.lt.2.0 .and. nharderrors.gt.35) .and. &
|
||||
.not.(ipass.gt.1 .and. nharderrors.gt.39) .and. &
|
||||
.not.(ipass.eq.3 .and. nharderrors.gt.30) &
|
||||
) then
|
||||
call chkcrc12a(decoded,nbadcrc)
|
||||
else
|
||||
nharderrors=-1
|
||||
cycle
|
||||
endif
|
||||
if(nbadcrc.eq.0) then
|
||||
call extractmessage174(decoded,message,ncrcflag,recent_calls,nrecent)
|
||||
call genft8(message,mygrid6,bcontest,msgsent,msgbits,itone)
|
||||
if(lsubtract) call subtractft8(dd0,itone,f1,xdt2)
|
||||
xsig=0.0
|
||||
xnoi=0.0
|
||||
do i=1,79
|
||||
xsig=xsig+s2(itone(i),i)**2
|
||||
ios=mod(itone(i)+4,7)
|
||||
xnoi=xnoi+s2(ios,i)**2
|
||||
enddo
|
||||
xsnr=0.001
|
||||
if(xnoi.gt.0 .and. xnoi.lt.xsig) xsnr=xsig/xnoi-1.0
|
||||
xsnr=10.0*log10(xsnr)-27.0
|
||||
if(xsnr .lt. -24.0) xsnr=-24.0
|
||||
return
|
||||
endif
|
||||
enddo
|
||||
|
||||
return
|
||||
end subroutine ft8b
|
||||
@@ -1,112 +0,0 @@
|
||||
/* EXTRACT.C - Extract message bits from coded blocks. */
|
||||
|
||||
/* Copyright (c) 1995-2012 by Radford M. Neal.
|
||||
*
|
||||
* Permission is granted for anyone to copy, use, modify, and distribute
|
||||
* these programs and accompanying documents for any purpose, provided
|
||||
* this copyright notice is retained and prominently displayed, and note
|
||||
* is made of any changes made to these programs. These programs and
|
||||
* documents are distributed without any warranty, express or implied.
|
||||
* As the programs were written for research purposes only, they have not
|
||||
* been tested to the degree that would be advisable in any important
|
||||
* application. All use of these programs is entirely at the user's own
|
||||
* risk.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "alloc.h"
|
||||
#include "blockio.h"
|
||||
#include "open.h"
|
||||
#include "mod2sparse.h"
|
||||
#include "mod2dense.h"
|
||||
#include "mod2convert.h"
|
||||
#include "rcode.h"
|
||||
|
||||
void usage(void);
|
||||
|
||||
|
||||
/* MAIN PROGRAM. */
|
||||
|
||||
int main
|
||||
( int argc,
|
||||
char **argv
|
||||
)
|
||||
{
|
||||
char *gen_file, *coded_file, *ext_file;
|
||||
FILE *codef, *extf;
|
||||
char *cblk;
|
||||
int i;
|
||||
|
||||
/* Look at arguments. */
|
||||
|
||||
if (!(gen_file = argv[1])
|
||||
|| !(coded_file = argv[2])
|
||||
|| !(ext_file = argv[3])
|
||||
|| argv[4])
|
||||
{ usage();
|
||||
}
|
||||
|
||||
if ((strcmp(gen_file,"-")==0) + (strcmp(coded_file,"-")==0) > 1)
|
||||
{ fprintf(stderr,"Can't read more than one stream from standard input\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Read generator matrix file, up to the point of finding out which
|
||||
are the message bits. */
|
||||
|
||||
read_gen(gen_file,1,1);
|
||||
|
||||
/* Open decoded file. */
|
||||
|
||||
codef = open_file_std(coded_file,"r");
|
||||
if (codef==NULL)
|
||||
{ fprintf(stderr,"Can't open coded file: %s\n",coded_file);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Open file to write extracted message bits to. */
|
||||
|
||||
extf = open_file_std(ext_file,"w");
|
||||
if (extf==NULL)
|
||||
{ fprintf(stderr,"Can't create file for extracted bits: %s\n",ext_file);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
cblk = chk_alloc (N, sizeof *cblk);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/* Read block from coded file. */
|
||||
|
||||
if (blockio_read(codef,cblk,N)==EOF) break;
|
||||
|
||||
/* Extract message bits and write to file, followed by newline to mark
|
||||
block boundary. */
|
||||
|
||||
for (i = M; i<N; i++)
|
||||
{ putc("01"[cblk[cols[i]]],extf);
|
||||
}
|
||||
|
||||
putc('\n',extf);
|
||||
}
|
||||
|
||||
if (ferror(extf) || fclose(extf)!=0)
|
||||
{ fprintf(stderr,"Error writing extracted data to %s\n",ext_file);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* PRINT USAGE MESSAGE AND EXIT. */
|
||||
|
||||
void usage(void)
|
||||
{ fprintf(stderr,
|
||||
"Usage: extract gen-file decoded-file extracted-file\n");
|
||||
exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user