Merged master 8748

This commit is contained in:
Jordan Sherer
2018-08-05 11:33:30 -04:00
parent 8f8772f1bd
commit 62899069bf
1222 changed files with 70382 additions and 406763 deletions
@@ -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);
}