Added I decoder and reorganized the E selection code

This commit is contained in:
Jordan Sherer 2019-12-09 14:00:23 -05:00
parent f706685cc9
commit 5215fd324d
15 changed files with 645 additions and 69 deletions

View File

@ -304,6 +304,8 @@ set (wsjt_FSRCS
lib/js8c_decode.f90
lib/js8e_module.f90
lib/js8e_decode.f90
lib/js8i_module.f90
lib/js8i_decode.f90
# remaining non-module sources
lib/addit.f90
@ -420,6 +422,7 @@ set (wsjt_FSRCS
lib/js8/ldpcsim174js8b.f90
lib/js8/ldpcsim174js8c.f90
lib/js8/ldpcsim174js8e.f90
lib/js8/ldpcsim174js8i.f90
lib/ldpcsim40.f90
lib/libration.f90
lib/lorentzian.f90
@ -1006,6 +1009,9 @@ target_link_libraries (ldpcsim174js8c wsjt_fort wsjt_cxx)
add_executable (ldpcsim174js8e lib/js8/ldpcsim174js8e.f90 wsjtx.rc)
target_link_libraries (ldpcsim174js8e wsjt_fort wsjt_cxx)
add_executable (ldpcsim174js8i lib/js8/ldpcsim174js8i.f90 wsjtx.rc)
target_link_libraries (ldpcsim174js8i wsjt_fort wsjt_cxx)
add_executable (js8 ${js8_FSRCS} ${js8_CXXSRCS} wsjtx.rc)
if (${OPENMP_FOUND} OR APPLE)
if (APPLE)

View File

@ -84,6 +84,9 @@ void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
else if(m_TRperiod == JS8E_TX_SECONDS){
delay_ms = JS8E_START_DELAY_MS;
}
else if(m_TRperiod == JS8I_TX_SECONDS){
delay_ms = JS8I_START_DELAY_MS;
}
// noise generator parameters
if (m_addNoise) {

View File

@ -24,6 +24,7 @@
#define JS8_ENABLE_JS8B 1
#define JS8_ENABLE_JS8C 1
#define JS8_ENABLE_JS8E 1
#define JS8_ENABLE_JS8I 0
#define JS8A_SYMBOL_SAMPLES 1920
#define JS8A_TX_SECONDS 15
@ -37,16 +38,13 @@
#define JS8C_TX_SECONDS 6
#define JS8C_START_DELAY_MS 100
#define JS8E_IS_ULTRA 0
#if JS8E_IS_ULTRA
#define JS8E_SYMBOL_SAMPLES 384
#define JS8E_TX_SECONDS 4
#define JS8E_START_DELAY_MS 100
#else
#define JS8E_SYMBOL_SAMPLES 4000
#define JS8E_TX_SECONDS 30
#define JS8E_START_DELAY_MS 500
#endif
#define JS8I_SYMBOL_SAMPLES 384
#define JS8I_TX_SECONDS 4
#define JS8I_START_DELAY_MS 100
#ifndef TEST_FOX_WAVE_GEN
#define TEST_FOX_WAVE_GEN 0
@ -104,10 +102,12 @@ extern struct dec_data {
int kposB; // starting position of decode for submode B
int kposC; // starting position of decode for submode C
int kposE; // starting position of decode for submode E
int kposI; // starting position of decode for submode I
int kszA; // number of frames for decode for submode A
int kszB; // number of frames for decode for submode B
int kszC; // number of frames for decode for submode C
int kszE; // number of frames for decode for submode E
int kszI; // number of frames for decode for submode I
int nzhsym; // half symbol stop index
int nsubmode;
int nsubmodes;

View File

@ -259,7 +259,12 @@ bool DecodedText::tryUnpackFastData(){
return false;
}
if(submode_ != Varicode::JS8CallFast && submode_ != Varicode::JS8CallTurbo && submode_ != Varicode::JS8CallUltraSlow){
if(
submode_ != Varicode::JS8CallFast &&
submode_ != Varicode::JS8CallTurbo &&
submode_ != Varicode::JS8CallSlow &&
submode_ != Varicode::JS8CallUltra
){
return false;
}

View File

@ -7,6 +7,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
use js8b_decode
use js8c_decode
use js8e_decode
use js8i_decode
include 'jt9com.f90'
include 'timer_common.inc'
@ -27,6 +28,10 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
integer :: decoded
end type counting_js8e_decoder
type, extends(js8i_decoder) :: counting_js8i_decoder
integer :: decoded
end type counting_js8i_decoder
real ss(184,NSMAX)
logical baddata,newdat65,newdat9,single_decode,bVHF,bad0,newdat,trydecode
integer*2 id0(NTMAX*12000)
@ -40,6 +45,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
type(counting_js8b_decoder) :: my_js8b
type(counting_js8c_decoder) :: my_js8c
type(counting_js8e_decoder) :: my_js8e
type(counting_js8i_decoder) :: my_js8i
!cast C character arrays to Fortran character strings
datetime=transfer(params%datetime, datetime)
@ -53,6 +59,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
my_js8b%decoded = 0
my_js8c%decoded = 0
my_js8e%decoded = 0
my_js8i%decoded = 0
single_decode=iand(params%nexp_decode,32).ne.0
bVHF=iand(params%nexp_decode,64).ne.0
@ -74,6 +81,29 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
write(*,1012) params%nsubmode, params%nsubmodes
1012 format('<DecodeStarted>',2i4)
if(params%nmode.eq.8 .and. (params%nsubmode.eq.8 .or. iand(params%nsubmodes, 16).eq.16)) then
! We're in JS8 mode I
call timer('decjs8i ',0)
newdat=params%newdat
write(*,*) '<DecodeDebug> mode I decode started'
! copy the relevant frames for decoding
pos = max(0,params%kposI)
sz = max(0,params%kszI)
id0=0
id0(1:sz+1)=id2(pos+1:pos+sz+1)
call my_js8i%decode(js8i_decoded,id0,params%nQSOProgress,params%nfqso, &
params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
params%nexp_decode,params%ndepth,logical(params%nagain), &
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
mycall,mygrid,hiscall,hisgrid)
write(*,*) '<DecodeDebug> mode I decode finished'
call timer('decjs8i ',1)
endif
if(params%nmode.eq.8 .and. (params%nsubmode.eq.4 .or. iand(params%nsubmodes, 8).eq.8)) then
! We're in JS8 mode E
call timer('decjs8e ',0)
@ -229,6 +259,7 @@ contains
if(submode.eq.1) m=' B '
if(submode.eq.2) m=' C '
if(submode.eq.4) m=' E '
if(submode.eq.8) m=' I '
i0=index(decoded0,';')
@ -377,4 +408,30 @@ contains
return
end subroutine js8e_decoded
subroutine js8i_decoded (this,sync,snr,dt,freq,decoded,nap,qual)
use js8i_decode
implicit none
class(js8i_decoder), intent(inout) :: this
real, intent(in) :: sync
integer, intent(in) :: snr
real, intent(in) :: dt
real, intent(in) :: freq
character(len=37), intent(in) :: decoded
integer, intent(in) :: nap
real, intent(in) :: qual
integer :: submode
save
submode=8
call js8_decoded(sync, snr, dt, freq, decoded, nap, qual, submode)
select type(this)
type is (counting_js8i_decoder)
this%decoded = this%decoded + 1
end select
return
end subroutine js8i_decoded
end subroutine multimode_decoder

37
lib/js8/js8i_params.f90 Normal file
View File

@ -0,0 +1,37 @@
!parameter (NSPS=480) !Samples per symbol at 12000 S/s
!parameter (NTXDUR=5) !TX Duration in Seconds
!parameter (NDOWNSPS=16) !Downsampled samples per symbol
!parameter (AZ=6.0) !Near dupe sync spacing
!parameter (NDD=136) !Downconverted FFT Bins - 100 Bins
!parameter (JZ=62) !Sync Search Space over +/- 2.5s relative to 0.5s TX start time. 2.48 = 62/4/(12000/1920) ?
parameter (NSPS=384, NTXDUR=4, NDOWNSPS=12, NDD=125, JZ=250) ! 250 Hz 31.25 baud 60 wpm -18.0dB (1.0Eb/N0) 2.52s
! parameter (NSPS=384, NTXDUR=5, NDOWNSPS=12, NDD=125, JZ=116) ! 250 Hz 31.25 baud 48 wpm -18.0dB (1.0Eb/N0) 2.52s
! parameter (NSPS=480, NTXDUR=5, NDOWNSPS=12, NDD=125, JZ=116) ! 200 Hz 25 baud 48 wpm -19.0dB (1.0Eb/N0) 3.16s
! parameter (NSPS=480, NTXDUR=6, NDOWNSPS=20, NDD=150, JZ=116) ! 200 Hz 25 baud 40 wpm -19.0dB (1.0Eb/N0) 3.16s
! parameter (NSPS=500, NTXDUR=6, NDOWNSPS=20, NDD=144, JZ=116) ! 192 Hz 24 baud 40 wpm -19.4dB (1.0Eb/N0) 3.29s
! parameter (NSPS=600, NTXDUR=6, NDOWNSPS=24, NDD=120, JZ=172) ! 160 Hz 20 baud 40 wpm -20.0dB (1.0Eb/N0) 3.95s
! parameter (NSPS=768, NTXDUR=8, NDOWNSPS=24, NDD=125, JZ=116) ! 125 Hz 15.625 baud 32 wpm -21.0dB (1.0Eb/N0) 5.05s
! parameter (NSPS=800, NTXDUR=8, NDOWNSPS=24, NDD=100, JZ=116) ! 120 Hz 15 baud 32 wpm -21.2dB (1.0Eb/N0) 5.26s
! parameter (NSPS=960, NTXDUR=8, NDOWNSPS=24, NDD=100, JZ=116) ! 100 Hz 12.50 baud 32 wpm -22.0dB (1.0Eb/N0) 5.92s
! parameter (NSPS=1200, NTXDUR=10, NDOWNSPS=20, NDD=100, JZ=116) ! 80 Hz 10 baud 24 wpm -23.0dB (1.0Eb/N0) 7.90s
! parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=116) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s
! parameter (NSPS=3840, NTXDUR=30, NDOWNSPS=32, NDD=94, JZ=116) ! 24 Hz 3.125 baud 8 wpm -28.0dB (1.0Eb/N0) 25.28s
! parameter (NSPS=4000, NTXDUR=28, NDOWNSPS=20, NDD=90, JZ=32) ! 24 Hz 3 baud 8 wpm -28.2dB (1.0Eb/N0) 26.33s
parameter (AZ=12000.0/(1.0*NSPS)*0.8d0) !Dedupe overlap in Hz
parameter (ASTART=0.1) !Start delay in seconds
parameter (ASYNCMIN=1.5) !Minimum Sync
parameter (KK=87) !Information bits (75 + CRC12)
parameter (ND=58) !Data symbols
parameter (NS=21) !Sync symbols (3 @ Costas 7x7)
parameter (NN=NS+ND) !Total channel symbols (79)
parameter (NZ=NSPS*NN) !Samples in full 15 s waveform (151,680)
parameter (NMAX=NTXDUR*12000) !Samples in iwave (180,000)
parameter (NFFT1=2*NSPS, NH1=NFFT1/2) !Length of FFTs for symbol spectra
parameter (NSTEP=NSPS/4) !Rough time-sync step size
parameter (NHSYM=NMAX/NSTEP-3) !Number of symbol spectra (1/4-sym steps)
parameter (NDOWN=NSPS/NDOWNSPS) !Downsample factor to 32 samples per symbol
parameter (NQSYMBOL=NDOWNSPS/4) !Downsample factor of a quarter symbol

238
lib/js8/ldpcsim174js8i.f90 Normal file
View File

@ -0,0 +1,238 @@
program ldpcsim174js8
! End to end test of the (174,75)/crc12 encoder and decoder.
use crc
use packjt
include 'js8_params.f90'
include 'js8i_params.f90'
character*22 msg,msgsent,msgreceived
character*8 arg
character*6 grid
integer*1, allocatable :: codeword(:), decoded(:), message(:)
integer*1, target:: i1Msg8BitBytes(11)
integer*1 msgbits(87)
integer*1 apmask(174), cw(174)
integer*2 checksum
integer*4 i4Msg6BitWords(13)
integer colorder(174)
integer nerrtot(174),nerrdec(174),nmpcbad(87)
logical checksumok,fsk,bpsk
real*8, allocatable :: rxdata(:)
real, allocatable :: llr(:)
data colorder/ &
0, 1, 2, 3, 30, 4, 5, 6, 7, 8, 9, 10, 11, 32, 12, 40, 13, 14, 15, 16,&
17, 18, 37, 45, 29, 19, 20, 21, 41, 22, 42, 31, 33, 34, 44, 35, 47, 51, 50, 43,&
36, 52, 63, 46, 25, 55, 27, 24, 23, 53, 39, 49, 59, 38, 48, 61, 60, 57, 28, 62,&
56, 58, 65, 66, 26, 70, 64, 69, 68, 67, 74, 71, 54, 76, 72, 75, 78, 77, 80, 79,&
73, 83, 84, 81, 82, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,&
100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,&
120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,&
140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,&
160,161,162,163,164,165,166,167,168,169,170,171,172,173/
nerrtot=0
nerrdec=0
nmpcbad=0 ! Used to collect the number of errors in the message+crc part of the codeword
nargs=iargc()
if(nargs.ne.4) then
print*,'Usage: ldpcsim niter ndepth #trials s '
print*,'eg: ldpcsim 10 2 1000 0.84'
print*,'belief propagation iterations: niter, ordered-statistics depth: ndepth'
print*,'If s is negative, then value is ignored and sigma is calculated from SNR.'
return
endif
call getarg(1,arg)
read(arg,*) max_iterations
call getarg(2,arg)
read(arg,*) ndepth
call getarg(3,arg)
read(arg,*) ntrials
call getarg(4,arg)
read(arg,*) s
fsk=.false.
bpsk=.true.
! don't count crc bits as data bits
N=174
K=87
! scale Eb/No for a (174,87) code
rate=real(K)/real(N)
write(*,*) "rate: ",rate
write(*,*) "niter= ",max_iterations," s= ",s
allocate ( codeword(N), decoded(K), message(K) )
allocate ( rxdata(N), llr(N) )
msg="0123456789012"
! msg="G4WJS K9AN EN50"
call packmsg(msg,i4Msg6BitWords,itype,.false.) !Pack into 12 6-bit bytes
call unpackmsg(i4Msg6BitWords,msgsent,.false.,grid) !Unpack to get msgsent
write(*,*) "message sent ",msgsent
i4=0
ik=0
im=0
do i=1,12
nna=i4Msg6BitWords(i)
do j=1, 6
ik=ik+1
i4=i4+i4+iand(1,ishft(nna,j-6))
i4=iand(i4,255)
if(ik.eq.8) then
im=im+1
! if(i4.gt.127) i4=i4-256
i1Msg8BitBytes(im)=i4
ik=0
endif
enddo
enddo
i1Msg8BitBytes(10:11)=0
checksum = crc12 (c_loc (i1Msg8BitBytes), 11)
checksum = xor(checksum, 42) ! TODO: jsherer - could change the crc here
! For reference, the next 3 lines show how to check the CRC
i1Msg8BitBytes(10)=checksum/256
i1Msg8BitBytes(11)=iand(checksum,transfer(255,0_2))
checksumok = crc12_check(c_loc (i1Msg8BitBytes), 11)
if( checksumok ) write(*,*) 'Good checksum'
! K=87, For now:
! msgbits(1:72) JT message bits
! msgbits(73:75) 3 free message bits (set to 0)
! msgbits(76:87) CRC12
mbit=0
do i=1, 9
i1=i1Msg8BitBytes(i)
do ibit=1,8
mbit=mbit+1
msgbits(mbit)=iand(1,ishft(i1,ibit-8))
enddo
enddo
msgbits(73:75)=0 ! the three extra message bits go here
i1=i1Msg8BitBytes(10) ! First 4 bits of crc12 are LSB of this byte
do ibit=1,4
msgbits(75+ibit)=iand(1,ishft(i1,ibit-4))
enddo
i1=i1Msg8BitBytes(11) ! Now shift in last 8 bits of the CRC
do ibit=1,8
msgbits(79+ibit)=iand(1,ishft(i1,ibit-8))
enddo
write(*,*) 'message'
write(*,'(11(8i1,1x))') msgbits
call encode174(msgbits,codeword)
call init_random_seed()
! call sgran()
write(*,*) 'codeword'
write(*,'(22(8i1,1x))') codeword
write(*,*) "Es/N0 SNR2500 ngood nundetected nbadcrc sigma"
do idb = 20,-10,-1
!do idb = -3,-3,-1
db=idb/2.0-1.0
sigma=1/sqrt( 2*(10**(db/10.0)) )
ngood=0
nue=0
nbadcrc=0
nberr=0
do itrial=1, ntrials
! Create a realization of a noisy received word
do i=1,N
if( bpsk ) then
rxdata(i) = 2.0*codeword(i)-1.0 + sigma*gran()
elseif( fsk ) then
if( codeword(i) .eq. 1 ) then
r1=(1.0 + sigma*gran())**2 + (sigma*gran())**2
r2=(sigma*gran())**2 + (sigma*gran())**2
elseif( codeword(i) .eq. 0 ) then
r2=(1.0 + sigma*gran())**2 + (sigma*gran())**2
r1=(sigma*gran())**2 + (sigma*gran())**2
endif
! rxdata(i)=0.35*(sqrt(r1)-sqrt(r2))
! rxdata(i)=0.35*(exp(r1)-exp(r2))
rxdata(i)=0.12*(log(r1)-log(r2))
endif
enddo
nerr=0
do i=1,N
if( rxdata(i)*(2*codeword(i)-1.0) .lt. 0 ) nerr=nerr+1
enddo
if(nerr.ge.1) nerrtot(nerr)=nerrtot(nerr)+1
nberr=nberr+nerr
! Correct signal normalization is important for this decoder.
rxav=sum(rxdata)/N
rx2av=sum(rxdata*rxdata)/N
rxsig=sqrt(rx2av-rxav*rxav)
rxdata=rxdata/rxsig
! To match the metric to the channel, s should be set to the noise standard deviation.
! For now, set s to the value that optimizes decode probability near threshold.
! The s parameter can be tuned to trade a few tenth's dB of threshold for an order of
! magnitude in UER
if( s .lt. 0 ) then
ss=sigma
else
ss=s
endif
llr=2.0*rxdata/(ss*ss)
nap=0 ! number of AP bits
llr(colorder(174-87+1:174-87+nap)+1)=5*(2.0*msgbits(1:nap)-1.0)
apmask=0
apmask(colorder(174-87+1:174-87+nap)+1)=1
! max_iterations is max number of belief propagation iterations
call bpdecode174(llr, apmask, max_iterations, decoded, cw, nharderrors,niterations)
if( ndepth .ge. 0 .and. nharderrors .lt. 0 ) call osd174(llr, apmask, ndepth, decoded, cw, nharderrors, dmin)
! If the decoder finds a valid codeword, nharderrors will be .ge. 0.
if( nharderrors .ge. 0 ) then
call extractmessage174(decoded,msgreceived,ncrcflag)
if( ncrcflag .ne. 1 ) then
nbadcrc=nbadcrc+1
endif
nueflag=0
nerrmpc=0
do i=1,K ! find number of errors in message+crc part of codeword
if( msgbits(i) .ne. decoded(i) ) then
nueflag=1
nerrmpc=nerrmpc+1
endif
enddo
if(nerrmpc.ge.1) nmpcbad(nerrmpc)=nmpcbad(nerrmpc)+1
if( ncrcflag .eq. 1 ) then
if( nueflag .eq. 0 ) then
ngood=ngood+1
if(nerr.ge.1) nerrdec(nerr)=nerrdec(nerr)+1
else if( nueflag .eq. 1 ) then
nue=nue+1;
endif
endif
endif
enddo
baud=12000.0/NSPS
snr2500=db+10.0*log10((baud/2500.0))
pberr=real(nberr)/(real(ntrials*N))
write(*,"(f4.1,4x,f5.1,1x,i8,1x,i8,1x,i8,8x,f5.2,8x,e10.3)") db,snr2500,ngood,nue,nbadcrc,ss,pberr
enddo
open(unit=23,file='nerrhisto.dat',status='unknown')
do i=1,174
write(23,'(i4,2x,i10,i10,f10.2)') i,nerrdec(i),nerrtot(i),real(nerrdec(i))/real(nerrtot(i)+1e-10)
enddo
close(23)
open(unit=25,file='nmpcbad.dat',status='unknown')
do i=1,87
write(25,'(i4,2x,i10)') i,nmpcbad(i)
enddo
close(25)
end program ldpcsim174js8

152
lib/js8i_decode.f90 Normal file
View File

@ -0,0 +1,152 @@
module js8i_decode
type :: js8i_decoder
procedure(js8i_decode_callback), pointer :: callback
contains
procedure :: decode
end type js8i_decoder
abstract interface
subroutine js8i_decode_callback (this,sync,snr,dt,freq,decoded,nap,qual)
import js8i_decoder
implicit none
class(js8i_decoder), intent(inout) :: this
real, intent(in) :: sync
integer, intent(in) :: snr
real, intent(in) :: dt
real, intent(in) :: freq
character(len=37), intent(in) :: decoded
integer, intent(in) :: nap
real, intent(in) :: qual
end subroutine js8i_decode_callback
end interface
contains
subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, &
nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, &
mycall12,mygrid6,hiscall12,hisgrid6)
! use wavhdr
use timer_module, only: timer
! type(hdr) h
use js8i_module
class(js8i_decoder), intent(inout) :: this
procedure(js8i_decode_callback) :: callback
real s(NH1,NHSYM)
real sbase(NH1)
real candidate(3,200)
real dd(NMAX)
logical, intent(in) :: lft8apon,lapcqonly,nagain
logical newdat,lsubtract,ldupe,bcontest
character*12 mycall12, hiscall12
character*6 mygrid6,hisgrid6
integer*2 iwave(NMAX)
integer apsym(KK)
character datetime*13,message*22,msg37*37
character*22 allmessages(100)
integer allsnrs(100)
save s,dd
bcontest=iand(nexp_decode,128).ne.0
this%callback => callback
write(datetime,1001) nutc !### TEMPORARY ###
1001 format("000000_",i6.6)
call ft8apset(mycall12,mygrid6,hiscall12,hisgrid6,bcontest,apsym,iaptype)
dd=iwave
ndecodes=0
allmessages=' '
allsnrs=0
ifa=nfa
ifb=nfb
if(nagain) then
ifa=nfqso-10
ifb=nfqso+10
endif
! For now:
! ndepth=1: no subtraction, 1 pass, belief propagation only
! ndepth=2: subtraction, 3 passes, belief propagation only
! ndepth=3: subtraction, 3 passes, bp+osd
if(ndepth.eq.1) npass=1
if(ndepth.ge.2) npass=3
do ipass=1,npass
newdat=.true. ! Is this a problem? I hijacked newdat.
syncmin=ASYNCMIN
if(ipass.eq.1) then
lsubtract=.true.
if(ndepth.eq.1) lsubtract=.false.
elseif(ipass.eq.2) then
n2=ndecodes
if(ndecodes.eq.0) cycle
lsubtract=.true.
elseif(ipass.eq.3) then
if((ndecodes-n2).eq.0) cycle
lsubtract=.false.
endif
call timer('syncjs8 ',0)
call syncjs8(dd,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase)
call timer('syncjs8 ',1)
if(NWRITELOG.eq.1) then
write(*,*) '<DecodeDebug>', ncand, "candidates"
flush(6)
endif
do icand=1,ncand
sync=candidate(3,icand)
f1=candidate(1,icand)
xdt=candidate(2,icand)
xbase=10.0**(0.1*(sbase(nint(f1/(12000.0/NFFT1)))-40.0)) ! 3.125Hz
nsnr0=min(99,nint(10.0*log10(sync) - 25.5)) !### empirical ###
if(NWRITELOG.eq.1) then
write(*,*) '<DecodeDebug> candidate', icand, 'f1', f1, 'sync', sync, 'xdt', xdt, 'xbase', xbase
flush(6)
endif
call timer('js8dec ',0)
call js8dec(dd,newdat,nQSOProgress,nfqso,nftx,ndepth,lft8apon, &
lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, &
hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, &
nbadcrc,iappass,iera,msg37,xsnr)
message=msg37(1:22) !###
nsnr=nint(xsnr)
xdt=xdt-ASTART
hd=nharderrors+dmin
if(NWRITELOG.eq.1) then
write(*,*) '<DecodeDebug> candidate', icand, 'hard', hd, 'nbadcrc', nbadcrc
flush(6)
endif
call timer('js8dec ',1)
if(nbadcrc.eq.0) then
ldupe=.false.
do id=1,ndecodes
if(message.eq.allmessages(id).and.nsnr.le.allsnrs(id)) ldupe=.true.
enddo
if(.not.ldupe) then
ndecodes=ndecodes+1
allmessages(ndecodes)=message
allsnrs(ndecodes)=nsnr
endif
if(.not.ldupe .and. associated(this%callback)) then
qual=1.0-(nharderrors+dmin)/60.0 ! scale qual to [0.0,1.0]
call this%callback(sync,nsnr,xdt,f1,msg37,iaptype,qual)
endif
endif
if(NWRITELOG.eq.1) then
write(*,*) '<DecodeDebug> ---'
flush(6)
endif
enddo
enddo
return
end subroutine decode
end module js8i_decode

13
lib/js8i_module.f90 Normal file
View File

@ -0,0 +1,13 @@
module js8i_module
include 'js8/js8_params.f90'
include 'js8/js8i_params.f90'
contains
include 'js8/baselinejs8.f90'
include 'js8/syncjs8.f90'
include 'js8/js8_downsample.f90'
include 'js8/syncjs8d.f90'
include 'js8/genjs8refsig.f90'
include 'js8/subtractjs8.f90'
include 'js8/js8dec.f90'
end module js8i_module

View File

@ -23,10 +23,12 @@
integer(c_int) :: kposB
integer(c_int) :: kposC
integer(c_int) :: kposE
integer(c_int) :: kposI
integer(c_int) :: kszA
integer(c_int) :: kszB
integer(c_int) :: kszC
integer(c_int) :: kszE
integer(c_int) :: kszI
integer(c_int) :: nzhsym
integer(c_int) :: nsubmode
integer(c_int) :: nsubmodes

View File

@ -961,7 +961,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
ui->actionModeJS8Normal->setActionGroup(modeActionGroup);
ui->actionModeJS8Fast->setActionGroup(modeActionGroup);
ui->actionModeJS8Turbo->setActionGroup(modeActionGroup);
ui->actionModeJS8UltraSlow->setActionGroup(modeActionGroup);
ui->actionModeJS8Slow->setActionGroup(modeActionGroup);
ui->actionModeJS8Ultra->setActionGroup(modeActionGroup);
auto mbmp = new MousePressEater();
connect(mbmp, &MousePressEater::mousePressed, this, [this](QObject *, QMouseEvent * e, bool *pProcessed){
@ -979,13 +980,10 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
ui->actionModeJS8Turbo->setVisible(false);
}
if(!JS8_ENABLE_JS8E){
ui->actionModeJS8UltraSlow->setVisible(false);
ui->actionModeJS8Slow->setVisible(false);
}
if(JS8_ENABLE_JS8E && !JS8E_IS_ULTRA){
// reorder so slow is at the top
ui->menuModeJS8->removeAction(ui->actionModeJS8UltraSlow);
ui->menuModeJS8->insertAction(ui->actionModeJS8Normal, ui->actionModeJS8UltraSlow);
ui->actionModeJS8UltraSlow->setText("JS8 (&Slow, 30s, 24Hz, ~8WPM)");
if(!JS8_ENABLE_JS8I){
ui->actionModeJS8Ultra->setVisible(false);
}
// prep
@ -2514,10 +2512,11 @@ void MainWindow::setDecodedTextFont (QFont const& font)
int MainWindow::computePeriodForSubmode(int submode){
switch(submode){
case Varicode::JS8CallNormal: return JS8A_TX_SECONDS;
case Varicode::JS8CallFast: return JS8B_TX_SECONDS;
case Varicode::JS8CallTurbo: return JS8C_TX_SECONDS;
case Varicode::JS8CallUltraSlow: return JS8E_TX_SECONDS;
case Varicode::JS8CallNormal: return JS8A_TX_SECONDS;
case Varicode::JS8CallFast: return JS8B_TX_SECONDS;
case Varicode::JS8CallTurbo: return JS8C_TX_SECONDS;
case Varicode::JS8CallSlow: return JS8E_TX_SECONDS;
case Varicode::JS8CallUltra: return JS8I_TX_SECONDS;
}
return 0;
@ -2542,21 +2541,13 @@ int MainWindow::computeStop(int submode, int period){
stop = int(period/0.288);
#else
int symbolSamples = 0;
float threshold = 1.0;
float threshold = 0.0;
switch(submode){
case Varicode::JS8CallNormal: symbolSamples = JS8A_SYMBOL_SAMPLES; /* threshold = 1.00 */ ; break;
case Varicode::JS8CallFast: symbolSamples = JS8B_SYMBOL_SAMPLES; /* threshold = 1.08 */ ; break;
case Varicode::JS8CallTurbo: symbolSamples = JS8C_SYMBOL_SAMPLES; /* threshold = 0.50 */ ; break;
case Varicode::JS8CallUltraSlow:
{
symbolSamples = JS8E_SYMBOL_SAMPLES;
#if JS8E_IS_ULTRA
threshold = 0.0;
#else
threshold = 1.25;
#endif
break;
}
case Varicode::JS8CallNormal: symbolSamples = JS8A_SYMBOL_SAMPLES; threshold = 1.00; break;
case Varicode::JS8CallFast: symbolSamples = JS8B_SYMBOL_SAMPLES; threshold = 1.00; break;
case Varicode::JS8CallTurbo: symbolSamples = JS8C_SYMBOL_SAMPLES; threshold = 1.00; break;
case Varicode::JS8CallSlow: symbolSamples = JS8E_SYMBOL_SAMPLES; threshold = 1.25; break;
case Varicode::JS8CallUltra: symbolSamples = JS8I_SYMBOL_SAMPLES; threshold = 0.50; break;
}
stop = qFloor(float(symbolSamples*JS8_NUM_SYMBOLS + threshold*RX_SAMPLE_RATE)/(float)m_nsps*2.0);
#endif
@ -2591,7 +2582,8 @@ int MainWindow::computeFramesNeededForDecode(int submode){
case Varicode::JS8CallNormal: symbolSamples = JS8A_SYMBOL_SAMPLES; threshold = JS8A_START_DELAY_MS/1000.0 + 0.5; break;
case Varicode::JS8CallFast: symbolSamples = JS8B_SYMBOL_SAMPLES; threshold = JS8B_START_DELAY_MS/1000.0 + 0.5; break;
case Varicode::JS8CallTurbo: symbolSamples = JS8C_SYMBOL_SAMPLES; threshold = JS8C_START_DELAY_MS/1000.0 + 0.5; break;
case Varicode::JS8CallUltraSlow: symbolSamples = JS8E_SYMBOL_SAMPLES; threshold = JS8E_START_DELAY_MS/1000.0 + 0.5; break;
case Varicode::JS8CallSlow: symbolSamples = JS8E_SYMBOL_SAMPLES; threshold = JS8E_START_DELAY_MS/1000.0 + 0.5; break;
case Varicode::JS8CallUltra: symbolSamples = JS8I_SYMBOL_SAMPLES; threshold = JS8I_START_DELAY_MS/1000.0 + 0.5; break;
}
return int(qFloor(float(symbolSamples*JS8_NUM_SYMBOLS + threshold*RX_SAMPLE_RATE)));
}
@ -2774,7 +2766,8 @@ void MainWindow::on_menuModeJS8_aboutToShow(){
ui->actionModeJS8Normal->setEnabled(canChangeMode);
ui->actionModeJS8Fast->setEnabled(canChangeMode);
ui->actionModeJS8Turbo->setEnabled(canChangeMode);
ui->actionModeJS8UltraSlow->setEnabled(canChangeMode);
ui->actionModeJS8Slow->setEnabled(canChangeMode);
ui->actionModeJS8Ultra->setEnabled(canChangeMode);
}
void MainWindow::on_menuControl_aboutToShow(){
@ -3272,7 +3265,8 @@ void MainWindow::setSubmode(int submode){
ui->actionModeJS8Normal->setChecked(submode == Varicode::JS8CallNormal);
ui->actionModeJS8Fast->setChecked(submode == Varicode::JS8CallFast);
ui->actionModeJS8Turbo->setChecked(submode == Varicode::JS8CallTurbo);
ui->actionModeJS8UltraSlow->setChecked(submode == Varicode::JS8CallUltraSlow);
ui->actionModeJS8Slow->setChecked(submode == Varicode::JS8CallSlow);
ui->actionModeJS8Ultra->setChecked(submode == Varicode::JS8CallUltra);
on_actionJS8_triggered();
}
@ -3288,8 +3282,11 @@ int MainWindow::submodeNameToSubmode(QString speedName){
if(speed == submodeName(Varicode::JS8CallTurbo)){
return Varicode::JS8CallTurbo;
}
if(speed == submodeName(Varicode::JS8CallUltraSlow)){
return Varicode::JS8CallUltraSlow;
if(speed == submodeName(Varicode::JS8CallSlow)){
return Varicode::JS8CallSlow;
}
if(speed == submodeName(Varicode::JS8CallUltra)){
return Varicode::JS8CallUltra;
}
// default to the current submode
return m_nSubMode;
@ -3306,12 +3303,11 @@ QString MainWindow::submodeName(int submode){
else if(submode == Varicode::JS8CallTurbo){
return "TURBO";
}
else if(submode == Varicode::JS8CallUltraSlow){
#if JS8E_IS_ULTRA
return "ULTRA";
#else
else if(submode == Varicode::JS8CallSlow){
return "SLOW";
#endif
}
else if(submode == Varicode::JS8CallUltra){
return "ULTRA";
}
return "?";
@ -4161,8 +4157,8 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
qint32 startE = -1;
qint32 szE = -1;
qint32 cycleE = -1;
if(JS8_DEBUG_DECODE) qDebug() << "? ULTRASLOW" << currentDecodeStartE << nextDecodeStartE;
bool couldDecodeE = isDecodeReady(Varicode::JS8CallUltraSlow, k, k0, &currentDecodeStartE, &nextDecodeStartE, &startE, &szE, &cycleE);
if(JS8_DEBUG_DECODE) qDebug() << "? SLOW" << currentDecodeStartE << nextDecodeStartE;
bool couldDecodeE = isDecodeReady(Varicode::JS8CallSlow, k, k0, &currentDecodeStartE, &nextDecodeStartE, &startE, &szE, &cycleE);
if(m_diskData){
startE = 0;
szE = NTMAX*RX_SAMPLE_RATE-1;
@ -4170,6 +4166,21 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
}
#endif
#if JS8_ENABLE_JS8I
static qint32 currentDecodeStartI = -1;
static qint32 nextDecodeStartI = -1;
qint32 startI = -1;
qint32 szI = -1;
qint32 cycleI = -1;
if(JS8_DEBUG_DECODE) qDebug() << "? ULTRA" << currentDecodeStartI << nextDecodeStartI;
bool couldDecodeI = isDecodeReady(Varicode::JS8CallUltra, k, k0, &currentDecodeStartI, &nextDecodeStartI, &startI, &szI, &cycleI);
if(m_diskData){
startI = 0;
szI = NTMAX*RX_SAMPLE_RATE-1;
couldDecodeI = true;
}
#endif
int decodes = 0;
if(couldDecodeA){
@ -4205,7 +4216,7 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
#if JS8_ENABLE_JS8E
if(couldDecodeE){
DecodeParams d;
d.submode = Varicode::JS8CallUltraSlow;
d.submode = Varicode::JS8CallSlow;
d.cycle = cycleE;
d.start = startE;
d.sz = szE;
@ -4214,6 +4225,18 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
}
#endif
#if JS8_ENABLE_JS8E
if(couldDecodeI){
DecodeParams d;
d.submode = Varicode::JS8CallUltra;
d.cycle = cycleI;
d.start = startI;
d.sz = szI;
m_decoderQueue.append(d);
decodes++;
}
#endif
return decodes > 0;
}
@ -4291,11 +4314,18 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){
dec_data.params.nsubmodes |= (params.submode << 1);
break;
#if JS8_ENABLE_JS8E
case Varicode::JS8CallUltraSlow:
case Varicode::JS8CallSlow:
dec_data.params.kposE = params.start;
dec_data.params.kszE = params.sz;
dec_data.params.nsubmodes |= (params.submode << 1);
break;
#endif
#if JS8_ENABLE_JS8I
case Varicode::JS8CallUltra:
dec_data.params.kposI = params.start;
dec_data.params.kszI = params.sz;
dec_data.params.nsubmodes |= (params.submode << 1);
break;
#endif
}
#if JS8_SINGLE_DECODE
@ -5249,8 +5279,10 @@ void MainWindow::guiUpdate()
txDuration=JS8B_START_DELAY_MS/1000.0 + JS8_NUM_SYMBOLS * (double)JS8B_SYMBOL_SAMPLES/(double)RX_SAMPLE_RATE;
} else if(m_nSubMode == Varicode::JS8CallTurbo){
txDuration=JS8C_START_DELAY_MS/1000.0 + JS8_NUM_SYMBOLS * (double)JS8C_SYMBOL_SAMPLES/(double)RX_SAMPLE_RATE;
} else if(m_nSubMode == Varicode::JS8CallUltraSlow){
} else if(m_nSubMode == Varicode::JS8CallSlow){
txDuration=JS8E_START_DELAY_MS/1000.0 + JS8_NUM_SYMBOLS * (double)JS8E_SYMBOL_SAMPLES/(double)RX_SAMPLE_RATE;
} else if(m_nSubMode == Varicode::JS8CallUltra){
txDuration=JS8I_START_DELAY_MS/1000.0 + JS8_NUM_SYMBOLS * (double)JS8I_SYMBOL_SAMPLES/(double)RX_SAMPLE_RATE;
}
}
@ -5336,9 +5368,12 @@ void MainWindow::guiUpdate()
else if(m_nSubMode == Varicode::JS8CallTurbo){
ratio = (((double)m_TRperiod - (JS8_NUM_SYMBOLS*(double)JS8C_SYMBOL_SAMPLES/(double)RX_SAMPLE_RATE))/(double)m_TRperiod);
}
else if(m_nSubMode == Varicode::JS8CallUltraSlow){
else if(m_nSubMode == Varicode::JS8CallSlow){
ratio = (((double)m_TRperiod - (JS8_NUM_SYMBOLS*(double)JS8E_SYMBOL_SAMPLES/(double)RX_SAMPLE_RATE))/(double)m_TRperiod);
}
else if(m_nSubMode == Varicode::JS8CallUltra){
ratio = (((double)m_TRperiod - (JS8_NUM_SYMBOLS*(double)JS8I_SYMBOL_SAMPLES/(double)RX_SAMPLE_RATE))/(double)m_TRperiod);
}
if(fTR > 1.0-ratio && fTR < 1.0){
if(!m_deadAirTone){
@ -5362,6 +5397,10 @@ void MainWindow::guiUpdate()
// for the turbo mode, only allow 1/2 late threshold
lateThreshold *= 0.5;
}
else if(m_nSubMode == Varicode::JS8CallUltra){
// for the ultra mode, only allow 1/2 late threshold
lateThreshold *= 0.5;
}
if(g_iptt==0 and ((m_bTxTime and fTR<lateThreshold and msgLength>0) or m_tune)) {
//### Allow late starts
icw[0]=m_ncw;
@ -5424,7 +5463,7 @@ void MainWindow::guiUpdate()
qDebug() << "gen ft8";
genft8_(message, MyGrid, &bcontest, &m_i3bit, msgsent, const_cast<char *> (ft8msgbits),
const_cast<int *> (itone), 22, 6, 22);
} else if (m_nSubMode == Varicode::JS8CallFast || m_nSubMode == Varicode::JS8CallTurbo || m_nSubMode == Varicode::JS8CallUltraSlow){
} else if (m_nSubMode == Varicode::JS8CallFast || m_nSubMode == Varicode::JS8CallTurbo || m_nSubMode == Varicode::JS8CallSlow || m_nSubMode == Varicode::JS8CallUltra){
qDebug() << "gen js8";
genjs8_(message, MyGrid, &bcontest, &m_i3bit, msgsent, const_cast<char *> (ft8msgbits),
const_cast<int *> (itone), 22, 6, 22);
@ -7399,7 +7438,11 @@ void MainWindow::on_actionModeJS8Turbo_triggered(){
on_actionJS8_triggered();
}
void MainWindow::on_actionModeJS8UltraSlow_triggered(){
void MainWindow::on_actionModeJS8Slow_triggered(){
on_actionJS8_triggered();
}
void MainWindow::on_actionModeJS8Ultra_triggered(){
on_actionJS8_triggered();
}
@ -7418,7 +7461,7 @@ void MainWindow::prepareHeartbeatMode(bool enabled){
ui->hbMacroButton->setChecked(false);
}
ui->actionHeartbeat->setEnabled(enabled);
ui->actionModeJS8HB->setEnabled(m_nSubMode == Varicode::JS8CallNormal || (!JS8E_IS_ULTRA && m_nSubMode == Varicode::JS8CallUltraSlow));
ui->actionModeJS8HB->setEnabled(m_nSubMode == Varicode::JS8CallNormal || m_nSubMode == Varicode::JS8CallSlow);
ui->actionHeartbeatAcknowledgements->setEnabled(ui->actionModeAutoreply->isChecked() && enabled);
#if 0
@ -7461,12 +7504,15 @@ void MainWindow::on_actionJS8_triggered()
else if(ui->actionModeJS8Turbo->isChecked()){
m_nSubMode=Varicode::JS8CallTurbo;
}
else if(ui->actionModeJS8UltraSlow->isChecked()){
m_nSubMode=Varicode::JS8CallUltraSlow;
else if(ui->actionModeJS8Slow->isChecked()){
m_nSubMode=Varicode::JS8CallSlow;
}
else if(ui->actionModeJS8Ultra->isChecked()){
m_nSubMode=Varicode::JS8CallUltra;
}
// Only enable heartbeat for normal mode
ui->actionModeJS8HB->setEnabled(m_nSubMode == Varicode::JS8CallNormal || (!JS8E_IS_ULTRA && m_nSubMode == Varicode::JS8CallUltraSlow));
ui->actionModeJS8HB->setEnabled(m_nSubMode == Varicode::JS8CallNormal || m_nSubMode == Varicode::JS8CallSlow);
prepareHeartbeatMode(ui->actionModeJS8HB->isEnabled() && ui->actionModeJS8HB->isChecked());
//if(m_nSubMode != Varicode::JS8CallNormal){
@ -9559,10 +9605,14 @@ void MainWindow::transmit (double snr)
symbolSamples=(double)JS8C_SYMBOL_SAMPLES;
toneSpacing=(double)RX_SAMPLE_RATE/(double)JS8C_SYMBOL_SAMPLES;
}
else if(m_nSubMode == Varicode::JS8CallUltraSlow){
else if(m_nSubMode == Varicode::JS8CallSlow){
symbolSamples=(double)JS8E_SYMBOL_SAMPLES;
toneSpacing=(double)RX_SAMPLE_RATE/(double)JS8E_SYMBOL_SAMPLES;
}
else if(m_nSubMode == Varicode::JS8CallUltra){
symbolSamples=(double)JS8I_SYMBOL_SAMPLES;
toneSpacing=(double)RX_SAMPLE_RATE/(double)JS8I_SYMBOL_SAMPLES;
}
if(m_config.x2ToneSpacing()) toneSpacing*=2.0;
if(m_config.x4ToneSpacing()) toneSpacing*=4.0;
if(m_config.bFox() and !m_tune) toneSpacing=-1;
@ -9816,9 +9866,7 @@ int MainWindow::rxThreshold(int submode){
int threshold = 10;
if(submode == Varicode::JS8CallFast){ threshold = 16; }
if(submode == Varicode::JS8CallTurbo){ threshold = 32; }
#if JS8E_IS_ULTRA
if(submode == Varicode::JS8CallUltraSlow){ threshold = 50; }
#endif
if(submode == Varicode::JS8CallUltra){ threshold = 50; }
return threshold;
}
@ -9831,10 +9879,8 @@ int MainWindow::rxSnrThreshold(int submode){
return -22;
case Varicode::JS8CallTurbo:
return -20;
#if JS8E_IS_ULTRA
case Varicode::JS8CallUltraSlow:
case Varicode::JS8CallUltra:
return -18;
#endif
}
return -28;
@ -12632,8 +12678,11 @@ void MainWindow::networkMessage(Message const &message)
if(speed == Varicode::JS8CallTurbo){
ui->actionModeJS8Turbo->setChecked(true);
}
if(speed == Varicode::JS8CallUltraSlow){
ui->actionModeJS8UltraSlow->setChecked(true);
if(speed == Varicode::JS8CallSlow){
ui->actionModeJS8Slow->setChecked(true);
}
if(speed == Varicode::JS8CallUltra){
ui->actionModeJS8Ultra->setChecked(true);
}
}
sendNetworkMessage("MODE.SPEED", "", {

View File

@ -275,7 +275,8 @@ private slots:
void on_actionModeJS8Normal_triggered();
void on_actionModeJS8Fast_triggered();
void on_actionModeJS8Turbo_triggered();
void on_actionModeJS8UltraSlow_triggered();
void on_actionModeJS8Slow_triggered();
void on_actionModeJS8Ultra_triggered();
void on_actionHeartbeatAcknowledgements_toggled(bool checked);
void on_actionModeMultiDecoder_toggled(bool checked);
void on_actionModeAutoreply_toggled(bool checked);

View File

@ -4778,10 +4778,11 @@ list. The list can be maintained in Settings (F2).</string>
<addaction name="actionDeepDecode"/>
<addaction name="actionDeepestDecode"/>
</widget>
<addaction name="actionModeJS8Slow"/>
<addaction name="actionModeJS8Normal"/>
<addaction name="actionModeJS8Fast"/>
<addaction name="actionModeJS8Turbo"/>
<addaction name="actionModeJS8UltraSlow"/>
<addaction name="actionModeJS8Ultra"/>
<addaction name="separator"/>
<addaction name="menu_Decode_Passes"/>
<addaction name="actionModeMultiDecoder"/>
@ -5712,7 +5713,7 @@ list. The list can be maintained in Settings (F2).</string>
<string>JS8 (&amp;Turbo, 6s, 160Hz, ~40 WPM)</string>
</property>
</action>
<action name="actionModeJS8UltraSlow">
<action name="actionModeJS8Ultra">
<property name="checkable">
<bool>true</bool>
</property>
@ -5763,6 +5764,14 @@ list. The list can be maintained in Settings (F2).</string>
<string>Enable Autoreply (&amp;AUTO)</string>
</property>
</action>
<action name="actionModeJS8Slow">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>JS8 (&amp;Slow, 30s, 24Hz, ~8 WPM)</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

View File

@ -422,9 +422,12 @@ void CPlotter::DrawOverlay() //DrawOverlay()
else if(m_nSubMode == Varicode::JS8CallTurbo){
bw = 8.0*(double)RX_SAMPLE_RATE/(double)JS8C_SYMBOL_SAMPLES;
}
else if(m_nSubMode == Varicode::JS8CallUltraSlow){
else if(m_nSubMode == Varicode::JS8CallSlow){
bw = 8.0*(double)RX_SAMPLE_RATE/(double)JS8E_SYMBOL_SAMPLES;
}
else if(m_nSubMode == Varicode::JS8CallUltra){
bw = 8.0*(double)RX_SAMPLE_RATE/(double)JS8I_SYMBOL_SAMPLES;
}
painter0.setPen(penGreen);

View File

@ -28,7 +28,8 @@ public:
JS8CallNormal = 0,
JS8CallFast = 1,
JS8CallTurbo = 2,
JS8CallUltraSlow = 4
JS8CallSlow = 4,
JS8CallUltra = 8
};
// frame type transmitted via itype and decoded by the ft8 decoded