Now have a properly functioning stand-along decoder for JTMSK.

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/jtms3@2529 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Joe Taylor 2012-07-19 18:01:14 +00:00
parent c6544d05c3
commit 357f467ef0
6 changed files with 154 additions and 167 deletions

View File

@ -1,64 +1,72 @@
JTMSK: Possible New Mode for Meteor Scatter
-------------------------------------------
JTMSK is a digital mode designed for amateur meteor-scatter
communication. The "MSK" part of the name could mean "Meteor Scatter
King": the mode works at the same signal levels as the popular mode
FSK441, but at about twice the character transmission rate, so it
makes much better use of short pings. MSK also means "Minimum Shift
Keying", which is the modulation technique used in JTMSK. It is a
form of continuous-phase frequency-shift keying that achieves high
transmission rate with a narrow bandwidth, while maintaining a
constant-envelope waveform.
1. Transmitting
Messages are sent character-by character, 6 bits plus odd parity.
Message length can be one of {5 7 9 11 13 17 19 23 29}; messages are
padded with blanks up to the next available length from this list.
Message length in characters is automatically selected from among the
possibilities 5 7 9 11 13 17 19 23 29. (Note that these lengths share
no common factor.) Messages are padded with blanks when required, up
to the next available length.
Modulation is Minimum Shift Keying (MSK) at 2000 baud. This is a form
of constant-envelope, continuous-phase modulation, equivalent to FSK
with offset equal to half the bit rate. JTMSK sends a "0" bit with
0.5 ms of a sine wave at f0=1000 Hz, and a "1" bit at f1=2000 Hz.
Carrier phase increment over one bit is therefore 0.5 cycles (pi
radians) at f0 and 1.0 cycles (two*pi radians) at f1. With odd parity
there is always an odd number of 1's and even number of 0's in a
character, so the phase increment over a full character is always an
integer number of cycles.
Modulation is Minimum Shift Keying (MSK) at 2000 baud. A "0" bit is
sent as an audio sine wave at f0=1000 Hz, lasting 0.5 ms; a "1" bit is
a sine wave at f1=2000 Hz. The carrier phase increment over one bit
is thus 0.5 cycles (pi radians) at f0, or 1.0 cycles at f1. Odd
parity assures an odd number of 1's and even number of 0's in every
7-bit character, so the phase increment over a full character is
always an integer number of cycles.
2. Receiving
a. Pings are detected (or mouse-picked data is selected) as in
WSJT9, except that it's done in real time rather than at the end
of an Rx interval.
a. Detect pings and update display in real time. (rtping)
b. Compute real-to-complex FFT. Zap birdies, remove frequency
components outside the range 300 - 2700 Hz, and convert to analytic
b. Compute real-to-complex FFT. Zap birdies, remove frequency
components outside the range 300 - 2700 Hz, convert to analytic
time-domain signal. (analytic)
c. Square the complex signal, cx2=cx*cx, and compute FFT. Look for
narrow carriers at frequency 2000 + 2*DF +/- 2*Tol and 2000 Hz
higher. (msdf)
c. Square the complex signal and compute FFT. Look for narrow
spectral lines at frequency 2000 + 2*(DF +/- Tol) Hz and
4000 + 2*(DF +/- Tol) Hz. (msdf)
d. If carrier is found, measure frequency f and phase phi. Multiply
cx by exp(-twopi*i*(f-1500)*t - phi) to recover the
standard-frequency baseband signal. (tweak1)
d. If a signal is found, measure frequency offset DF and phase Dphi.
(DF and Dphi are measured relative to the original frequencies f0
and f1, not their doubled values.) Multiply cx by
exp(-twopi*i*DF*t - Dphi) to recover the standard-frequency
baseband signal. (tweak1)
e. Establish symbol and character sync by cross-correlating with
conjg(cwb), where cwb is the MSK waveform for the <space>
character.
conjg(cwb), MSK waveform for the <space> character.
f. Find message length by computing ACF (of what? cdat? soft
symbol values?)
f. Find message length by computing ACF.
g. Cross-correlate the complex standard-frequency signal against
conjugated templates for each character in the JTMSK alphabet.
Best match yields the decoded character.
h. Decode the message by cross-correlating character-length segments
g. Decode the message by cross-correlating character-length segments
of cdat against complex waveforms for each possible character.
i. If msglen is established and long enough, try folding the data and
determining best-fit characters as above.
h. If ping is long enough, try folding the data and determining the
best-fit characters as above.
3. Comparison of modes:
3. Parameters of fast modes:
| Meteor Scatter | IonoScatter
| FSK441 JTMS JTMSK | JT6M ISCAT
-------------------+-----------------------+-------------------
T/R period (s) | 30 30 30 | 30 30
Modulation type | 4-FSK MSK* MSK* | 44-FSK 41-FSK
Keying rate (baud) | 441 1378 2000 | 21.5 43.1
Characters/s | 147 197 286 | 14.3 32.3
Bandwidth (Hz) | 1764 1378 2000 | 947 1809
+--------------------+-----------------------+-------------------+
| | Meteor Scatter | IonoScatter |
| | FSK441 JTMS JTMSK | JT6M ISCAT |
+--------------------+-----------------------+-------------------|
| T/R period (s) | 30 30 30 | 30 30 |
| Modulation type | 4-FSK MSK MSK | 44-FSK 41-FSK |
| Keying rate (baud) | 441 1378 2000 | 21.5 43.1 |
| Bit rate (bps) | 882 1378 2000 | - - |
| Characters/s | 147 197 286 | 14.3 32.3 |
| Bandwidth (Hz) | 1764 1378 2000 | 947 1809 |
+--------------------+-----------------------+-------------------+

View File

@ -1,14 +1,14 @@
subroutine jtmsk(dat,npts,cfile6,t2,mswidth,ndb,nrpt,Nfreeze, &
ntol,MouseDF,pick)
subroutine jtmsk(dat,npts,cfile6,tpk,mswidth,ndb,nrpt,Nfreeze, &
ntol,MouseDF,pick,mycall)
! Decode a JTMS ping
! Decode a JTMSK ping
parameter (NZ=30*48000)
parameter (NZ=2*48000)
real dat(npts) !Raw data
complex cdat(NZ) !Analytic form of signal
character*6 cfile6 !FileID
logical pick
character*12 mycall,hiscall
character*12 mycall
real s(NZ) !Power spectrum
real s2(0:63,400)
real r(60000)
@ -16,8 +16,6 @@ subroutine jtmsk(dat,npts,cfile6,t2,mswidth,ndb,nrpt,Nfreeze, &
complex cwb(168) !Complex waveform for <space>
logical first
character msg*400,msg29*29
character*90 line
common/ccom/nline,tping(100),line(100)
data first/.true./
save first,cw,cwb
save cdat !Fix its address, for four2
@ -31,56 +29,44 @@ subroutine jtmsk(dat,npts,cfile6,t2,mswidth,ndb,nrpt,Nfreeze, &
nfft1=2**n !FFT length
call analytic(dat,npts,nfft1,s,cdat) !Convert to analytic signal
call mskdf(cdat,npts,t2,nfft1,f0,nfreeze,mousedf,ntol, &
call mskdf(cdat,npts,tpk,nfft1,f0,nfreeze,mousedf,ntol, &
dfx,snrsq2) !Get DF
print*,'b',dfx,snrsq2
sq2lim=7.0
if(pick) sq2lim=5.0
if(snrsq2.lt.sq2lim) go to 900 !Reject non-JTMS signals
if(snrsq2.lt.sq2lim) go to 900 !Reject non-JTMS signals
call tweak1(cdat,npts,-dfx,cdat) !Mix to standard frequency
! DF is known, now establish character sync.
call syncmsk(cdat,npts,cwb,r,i1) !Get character sync
call syncmsk(cdat,npts,cwb,r,i1) !Get character sync
call lenmsk(r,npts,msglen) !Find message length
call lenmsk(r,npts,msglen) !Find message length
s2=0.
nchar=(npts-168+1-i1)/168
if(nchar.gt.400) nchar=400
call decodemsk(cdat,npts,cw,i1,nchar,s2,msg) !Decode the message
print*,'B',i1,msglen,nchar
print*,msg(1:nchar)
! ia=1
! if(nchar.ge.40) ia=min(nchar/3,nchar-28)
! ib=min(ia+28,nchar) !Can better limits ia, ib be found?
! print*,'A',ia,ib,nchar
! print*,msg(1:nchar)
! msg29=adjustl(msg(ia:ib))
ia=1
if(nchar.ge.40) ia=min(nchar/3,nchar-28)
ib=min(ia+28,nchar) !Can better limits ia, ib be found?
msg29=adjustl(msg(ia:ib))
msg=adjustl(msg)
ib=min(nchar,45)
ndf=nint(dfx)
nchk=max(20,nint(1.5*msglen))
if(msglen.eq.0 .or. nchar.lt.nchk .or. pick) then
if(nline.le.99) nline=nline+1
tping(nline)=t2
write(line(nline),1110) cfile6,t2,mswidth,ndb,nrpt,ndf,msg(1:45)
write(*,1110) cfile6,tpk,mswidth,ndb,nrpt,ndf,msg(1:45)
1110 format(a6,f5.1,i5,i3,1x,i2.2,i5,5x,a45)
endif
if(msglen.gt.0 .and. nchar.ge.nchk) then
call foldmsk(s2,msglen,nchar,mycall,msg,msg29) !Decode folded message
if(nline.le.99) nline=nline+1
tping(nline)=t2
write(line(nline),1120) cfile6,t2,mswidth,ndb,nrpt,ndf,msg29
write(*,1120) cfile6,tpk,mswidth,ndb,nrpt,ndf,msg29
1120 format(a6,f5.1,i5,i3,1x,i2.2,i5,5x,a29,11x,'*')
endif
900 continue
return
end subroutine jtmsk

View File

@ -1,11 +1,12 @@
program msk
! Starting code for a JTMSK decoder.
! Program to test decoding routines for mode JTMSK.
parameter (NSMAX=30*48000)
character*80 infile
character*6 cfile6
character*12 arg
character*12 mycall
real dat(NSMAX)
real x(NSMAX)
complex cx(0:NSMAX/2)
@ -14,46 +15,49 @@ program msk
common/mscom/id(NSMAX),s1(215,703),s2(215,703)
nargs=iargc()
if(nargs.lt.1) then
print*,'Usage: msk <snr>'
if(nargs.lt.2) then
print*,'Usage: msk nslow snr'
go to 999
endif
call getarg(1,arg)
read(arg,*) nslow
call getarg(2,arg)
read(arg,*) snr
! Read simulated pings from a file
open(71,file='dat.71',form='unformatted',status='old')
read(71) id
cfile6='123400'
cfile6='123400'
npts=30*48000
kstep=2048
minsigdb=6
minsigdb=1
mousedf=0
ntol=200
mycall='W8WN'
! Make some band-limited noise.
call random_number(x)
nfft=NSMAX
call four2a(x,nfft,1,-1,0)
df=48000.0/nfft
ia=nint(300.0/df)
ib=nint(2800.0/df)
ib=nint(2700.0/df)
cx(:ia)=0.
cx(ib:)=0.
call four2a(cx,nfft,1,1,-1)
x(1)=0.
sq=0.
do i=1,NSMAX
sq=sq + x(i)**2
enddo
rms=sqrt(sq/NSMAX)
rms=sqrt(dot_product(x,x)/NSMAX)
x=x/rms
sig=(10.0**(0.05*snr))/32768.0
dat=sig*id + x
k=0
sig=(10.0**(0.05*snr))/32768.0 !Scaled signal strength
dat=sig*id + x !Add pings to noise
! This loop simulates being called from "datasink()" in program JTMSK.
do iblk=1,npts/kstep
k=k+kstep
call rtping(dat,k,cfile6,MinSigdB,MouseDF,ntol)
k=iblk*kstep
call rtping(dat,k,cfile6,MinSigdB,MouseDF,ntol,mycall)
if(nslow.ne.0) call usleep(42000)
enddo
999 end program msk

View File

@ -1,103 +1,92 @@
subroutine rtping(dat,k,cfile6,MinSigdB,MouseDF,ntol)
subroutine rtping(dat,k,cfile6,MinSigdB,MouseDF,ntol,mycall)
!subroutine rtping(dat,jz,nz,MinSigdB,MinWidth,NFreeze,DFTolerance, &
! MouseDF,istart,pick,cfile6,mycall,hiscall,mode,ps0)
! Decode Multi-Tone FSK441 mesages.
! Called from datasink() every 2048 sample intervals (approx 43 ms).
! Detects pings (signal level above MinSigdB). When a ping ends, its
! MSK signal is synchronized and decoded.
parameter (NSMAX=30*48000)
parameter (NZMAX=NSMAX/2048)
parameter (NZMAX=703) !703 = NSMAX/2048
real dat(NSMAX) !Raw audio data
logical pick
character*6 cfile6
character*6 cfile6 !Time hhmmss at start of this Rx interval
character*12 mycall
real sig(NZMAX) !Sq-law detected signal, sampled at 43 ms
real sigdb(NZMAX) !Signal in dB, sampled at 43 ms
real work(NZMAX)
real pingdat(3,100)
! character msg*40,msg3*3
character*90 line
common/ccom/nline,tping(100),line(100)
data nping0/0/
real tmp(NZMAX)
logical inside,pingFound
data k0/9999999/
save
if(k.lt.k0) then
inside=.false.
pingFound=.false.
j0=0
t1=0.
width=0.
peak=0.
tpk=0.
dt=1.0/48000.0
kstep=2048
sdt=dt*kstep
wmin=0.043
endif
k0=k
slim=MinSigdB
! nf1=-ntol
! nf2=ntol
dt=1.0/48000.0
kstep=2048
! pick=.false.
istart=1
jz=k
snrlim=10.0**(0.1*slim) - 1.0
sdown=10.0*log10(0.25*snrlim+1.0)
! Find signal power
j=k/kstep
sig(j)=dot_product(dat(k-kstep+1:k),dat(k-kstep+1:k))/kstep
if(j.lt.10) return
if(j.lt.20) return
! Remove baseline, compute signal level in dB
call pctile (sig,work,j,50,base1)
do i=1,j
sigdb(i)=db(sig(i)/base1)
if(j.eq.703) write(13,3001) i,sig(i),sigdb(i)
3001 format(i5,2e12.3)
enddo
! Determine baseline noise level
if(mod(j,20).eq.0) call pctile (sig,tmp,j,50,base)
sigdb(j)=db(sig(j)/base) ! (S+N)/N in dB
dtbuf=kstep*dt
wmin=0.040
call ping(sigdb,j,dtbuf,slim,wmin,pingdat,nping)
! write(72,3001) j*sdt,base,sig(j),sigdb(j)
!3001 format(f10.3,3f12.6)
! If this is a "mouse pick" and no ping was found, force a pseudo-ping
! at center of data.
! if(pick.and.nping.eq.0) then
! if(nping.le.99) nping=nping+1
! pingdat(1,nping)=0.5*jz*dt
! pingdat(2,nping)=0.16
! pingdat(3,nping)=1.0
! endif
if(sigdb(j).ge.slim .and. .not.inside) then
j0=j !Mark the start of a ping
t1=j0*sdt
inside=.true.
peak=0.
endif
do iping=1,nping
! Find starting place and length of data to be analyzed:
tstart=pingdat(1,iping)
width=pingdat(2,iping)
peak=pingdat(3,iping)
! mswidth=10*nint(100.0*width)
jj=(tstart-0.02)/dt
if(jj.lt.1) jj=1
jjz=nint((width+0.02)/dt)+1
jjz=min(jjz,jz+1-jj)
if(inside .and. sigdb(j).gt.peak) then
peak=sigdb(j) !Save peak strength
tpk=j*sdt ! and time of peak
endif
if(inside .and. (sigdb(j).lt.sdown .or. j.eq.NZMAX)) then
width=(j-j0)*sdt !Save ping width
if(width.ge.wmin) pingFound=.true.
endif
if(.not.pingFound) return
! Compute average spectrum of this ping.
! call spec441(dat(jj),jjz,ps,f0)
! A ping was found! Assemble a signal report:
nwidth=0
if(width.ge.0.04) nwidth=1
if(width.ge.0.12) nwidth=2
if(width.gt.1.00) nwidth=3
nstrength=6
if(peak.ge.11.0) nstrength=7
if(peak.ge.17.0) nstrength=8
if(peak.ge.23.0) nstrength=9
nrpt=10*nwidth + nstrength
! Decode the message.
! msg=' '
! call longx(dat(jj),jjz,ps,DFTolerance,noffset,msg,msglen,bauderr)
mswidth=10*nint(100.0*width)
i1=(t1-0.02)/dt
if(i1.lt.1) i1=1
iz=nint((width+0.02)/dt) + 1
iz=min(iz,k+1-i1,2*48000) !Length of ping in samples
! Assemble a signal report:
nwidth=0
if(width.ge.0.04) nwidth=1 !These might depend on NSPD
if(width.ge.0.12) nwidth=2
if(width.gt.1.00) nwidth=3
nstrength=6
if(peak.ge.11.0) nstrength=7
if(peak.ge.17.0) nstrength=8
if(peak.ge.23.0) nstrength=9
! nrpt=10*nwidth + nstrength
t2=tstart + dt*(istart-1)
call jtmsk(dat(i1),iz,cfile6,tpk,mswidth,int(peak),nrpt, &
nfreeze,DFTolerance,MouseDF,pick,mycall)
jjzz=min(jjz,2*48000) !Max data size 2 s
!###
jjzz=14400
jj=jj-200
!###
if(nping.gt.nping0) then
print*,'a',jj,jjzz,jj*dt,jjzz*dt,t2,width
call jtmsk(dat(jj),jjzz,cfile6,t2,mswidth,int(peak),nrpt, &
nfreeze,DFTolerance,MouseDF,pick)
nping0=nping
endif
enddo
pingFound=.false.
inside=.false.
return
end subroutine rtping

View File

@ -1,4 +1,4 @@
//--------------------------------------------------------------- MainWindow
//---------------------------------------------------------------- MainWindow
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "devsetup.h"