diff --git a/CMakeLists.txt b/CMakeLists.txt index 634c1e1ab..2b1e06cc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -602,17 +602,21 @@ set (wsjt_FSRCS lib/fst4/fst4_baseline.f90 lib/77bit/hash22calc.f90 + lib/superfox/foxgen2.f90 lib/superfox/qpc_decode2.f90 lib/superfox/qpc_likelihoods2.f90 lib/superfox/qpc_snr.f90 lib/superfox/qpc_sync.f90 lib/superfox/sfox_ana.f90 + lib/superfox/sfox_assemble.f90 lib/superfox/sfox_demod.f90 + lib/superfox/sfox_pack.f90 lib/superfox/sfox_remove_ft8.f90 lib/superfox/sfox_unpack.f90 lib/superfox/sfox_wave.f90 lib/superfox/sfox_wave_gfsk.f90 lib/superfox/sfrx_sub.f90 + lib/superfox/sftx.f90 lib/superfox/twkfreq2.f90 ) @@ -1253,6 +1257,9 @@ target_link_libraries (ft8sim wsjt_fort wsjt_cxx) #add_executable (sfrx lib/superfox/sfrx.f90) #target_link_libraries (sfrx wsjt_fort wsjt_cxx) +add_executable (sftx lib/superfox/sftx.f90) +target_link_libraries (sftx wsjt_fort wsjt_cxx) + add_executable (msk144sim lib/msk144sim.f90) target_link_libraries (msk144sim wsjt_fort wsjt_cxx) diff --git a/lib/superfox/foxgen2.f90 b/lib/superfox/foxgen2.f90 new file mode 100644 index 000000000..9be6ca861 --- /dev/null +++ b/lib/superfox/foxgen2.f90 @@ -0,0 +1,75 @@ +subroutine foxgen2(nslots,cmsg,line,foxcall) + +! Parse old-style Fox messages to extract the necessary pieces for a SuperFox +! transmission. + + use packjt77 + character*120 line + character*40 cmsg(5) !Old-style Fox messages are here + character*37 msg + character*26 sfmsg + character*13 mycall + character*11 foxcall + character*4 mygrid + character*6 hiscall_1,hiscall_2 + character*4 rpt1,rpt2 + character*13 w(19) + integer nw(19) + integer ntype !Message type: 0 Free Text + ! 1 CQ MyCall MyGrid + ! 2 Call_1 MyCall RR73 + ! 3 Call_1 MyCall rpt1 + ! 4 Call_1 RR73; Call_2 rpt2 + + if(nslots.lt.1 .or. nslots.gt.5) return + k=0 + do i=1,nslots + hiscall_1='' + hiscall_2='' + mycall='' + mygrid='' + rpt1='' + rpt2='' + msg=cmsg(i)(1:37) + call split77(msg,nwords,nw,w) + ntype=0 + if(msg(1:3).eq.'CQ ') then + ntype=1 + mycall=w(2)(1:12) + mygrid=w(3)(1:4) + else if(index(msg,';').gt.0) then + ntype=4 + hiscall_1=w(1)(1:6) + hiscall_2=w(3)(1:6) + rpt1='RR73' + rpt2=w(5)(1:4) + mycall=w(4)(2:nw(4)-1) + else if(index(msg,' RR73').gt.0) then + ntype=2 + hiscall_1=w(1)(1:6) + mycall=w(2)(1:12) + rpt1='RR73' + else if(nwords.eq.3 .and. nw(3).eq.3 .and. & + (w(3)(1:1).eq.'-' .or. w(3)(1:1).eq.'+')) then + ntype=3 + hiscall_1=w(1)(1:6) + mycall=w(2)(1:12) + rpt1=w(3)(1:4) + endif + + k=k+1 + if(ntype.le.3) call sfox_assemble(ntype,k,msg(1:26),mycall,mygrid,line) + if(ntype.eq.4) then + sfmsg=w(1)(1:nw(1))//' '//mycall(1:len(trim(mycall))+1)//'RR73' + call sfox_assemble(2,k,sfmsg,mycall,mygrid,line) + sfmsg=w(3)(1:nw(3))//' '//mycall(1:len(trim(mycall))+1)//w(5)(1:3) + k=k+1 + call sfox_assemble(3,k,sfmsg,mycall,mygrid,line) + endif + enddo + + call sfox_assemble(ntype,11,msg(1:26),mycall,mygrid,line) !k=11 to finish up + foxcall=mycall(1:11) + + return +end subroutine foxgen2 diff --git a/lib/superfox/sfox_assemble.f90 b/lib/superfox/sfox_assemble.f90 new file mode 100644 index 000000000..1ea1834f5 --- /dev/null +++ b/lib/superfox/sfox_assemble.f90 @@ -0,0 +1,92 @@ +subroutine sfox_assemble(ntype,k,msg,mycall0,mygrid0,line) + +! In subsequent calls, assemble all necessary information for a SuperFox +! transmission. + + character*120 line + character*26 msg + character*26 msg0,msg1,msg2(5),msg3(5) + character*4 rpt2(5) + character*6 hiscall(10) + character*13 mycall0,mycall + character*4 mygrid0,mygrid + integer ntype !Message type: 0 Free Text + ! 1 CQ MyCall MyGrid + ! 2 Call_1 MyCall RR73 + ! 3 Call_1 MyCall rpt + integer nmsg(0:3) !Number of messages of type ntype + data nmsg/0,0,0,0/,nbits/0/,ntx/0/,nb_mycall/0/ + save + + if(mycall0(1:1).ne.' ') mycall=mycall0 + if(mygrid0(1:1).ne.' ') mygrid=mygrid0 + if(ntype.ge.1) nb_mycall=28 !### Allow for nonstandard MyCall ### + if(sum(nmsg).eq.0) then + hiscall=' ' + rpt2=' ' + endif + + if(k.le.10) then + if(ntype.eq.0) then + if(nbits+nb_mycall.le.191) then !Enough room for a free text message? + nmsg(ntype)=nmsg(ntype)+1 + nbits=nbits+142 + msg0=msg + endif + else if(ntype.eq.1) then + if(nbits+nb_mycall.le.318) then !Enough room for a CQ ? + nmsg(ntype)=nmsg(ntype)+1 + nbits=nbits+15 + msg1=msg + endif + else if(ntype.eq.2) then + if(nbits+nb_mycall.le.305) then !Enough room for a RR73 message? + nmsg(ntype)=nmsg(ntype)+1 + nbits=nbits+28 + j=nmsg(ntype) + msg2(j)=msg + i1=index(msg,' ') + hiscall(j+5)=msg(1:i1-1) + endif + else if(ntype.eq.3) then + if(nbits+nb_mycall.le.300) then !Enough room for a message with report? + nmsg(ntype)=nmsg(ntype)+1 + nbits=nbits+33 + j=nmsg(ntype) + msg3(j)=msg + i1=index(msg,' ') + hiscall(j)=msg(1:i1-1) + i1=max(index(msg,'-'),index(msg,'+')) + rpt2(j)=msg(i1:i1+3) + endif + endif + return + endif + + if(k.ge.11) then +! All pieces are now available. Put them into a command line for external +! program sfox_tx. + ntx=ntx+1 !Transmission number + nbits=nbits+nb_mycall !Add bits for MyCall + + if(nmsg(1).ge.1) then + line=msg1 + else + line=trim(mycall) + do i=1,nmsg(3) + line=trim(line)//' '//trim(hiscall(i))//' '//rpt2(i) + enddo + do i=1,nmsg(2) + line=trim(line)//' '//trim(hiscall(i+5)) + enddo + endif + + nmsg=0 + nbits=0 + nb_mycall=0 + hiscall=' ' + rpt2=' ' + endif + + return +end subroutine sfox_assemble diff --git a/lib/superfox/sfox_pack.f90 b/lib/superfox/sfox_pack.f90 new file mode 100644 index 000000000..19e6ed3fb --- /dev/null +++ b/lib/superfox/sfox_pack.f90 @@ -0,0 +1,158 @@ +subroutine sfox_pack(line,ckey,bMoreCQs,bSendMsg,freeTextMsg,xin) + + use qpc_mod + use packjt + use packjt77 + use julian + integer stat + integer*8 n47,n58,now + integer*1 xin(0:49) !Packed message as 7-bit symbols + logical*1 bMoreCQs,bSendMsg + logical text + character*120 line !SuperFox message pieces + character*10 ckey + character*26 freeTextMsg + character*13 w(16) + character*11 c11 + character*329 msgbits !Packed message as bits + character*38 c + data c/' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/'/ + + i0=index(line,'/') + i3=0 !Default to i3=0, standard message + +! Split the command line into words + w=' ' + i1=1 + do j=1,16 + do i=i1,min(i1+12,120) + if(line(i:i).eq.' ') exit + enddo + i2=i-1 + w(j)=line(i1:i2) + do i=i2+1,120 + if(line(i:i).ne.' ') exit + enddo + i1=i + if(i1.ge.120) exit + enddo + nwords=j + + do i=1,nwords + if(w(i)(1:1).eq.' ') then + nwords=i-1 + exit + endif + enddo + + do i=1,329 !Set all msgbits to '0' + msgbits(i:i)='0' + enddo + + now=itime8()/30 + now=30*now + read(ckey(5:10),*) nsignature + + write(msgbits(307:326),'(b20.20)') nsignature !Insert the digital signature + + if(w(1)(1:3).eq.'CQ ') then + i3=3 + c11=w(2)(1:11) + n58=0 + do i=1,11 + n58=n58*38 + index(c,c11(i:i)) - 1 + enddo + write(msgbits(1:58),'(b58.58)') n58 + call packgrid(w(3)(1:4),n15,text) + write(msgbits(59:73),'(b15.15)') n15 + write(msgbits(327:329),'(b3.3)') i3 !Message type i3=3 + go to 800 + endif + + call pack28(w(1),n28) !Fox call + write(msgbits(1:28),'(b28.28)') n28 + nh1=0 !Number of Hound calls with RR73 + nh2=0 !Number of Hound calls with report + +! Default report is RR73 if we're also sending a free text message. + if(bSendMsg) msgbits(141:160)='11111111111111111111' + + j=29 + +! Process callsigns with RR73 + do i=2,nwords + if(w(i)(1:1).eq.'+' .or. w(i)(1:1).eq.'-') cycle !Skip report words + i1=min(i+1,nwords) + if(w(i1)(1:1) .eq.'+' .or. w(i1)(1:1).eq.'-') cycle !Skip if i+1 is report + call pack28(w(i),n28) + write(msgbits(j:j+27),1002) n28 !Insert this call for RR73 message +1002 format(b28.28) + j=j+28 + nh1=nh1+1 + if( nht.ge.5 ) exit !At most 5 RR73 callsigns + enddo + +! Process callsigns with a report + j=169 + j2=281 + if(bSendMsg) then + i3=2 + j=29 + 28*nh1 + j2=141 + 5*nh1 + endif + + do i=2,nwords + i1=min(i+1,nwords) + if(w(i1)(1:1).eq.'+' .or. w(i1)(1:1).eq.'-') then + call pack28(w(i),n28) + write(msgbits(j:j+27),1002) n28 !Insert this call + read(w(i1),*) n !Valid reports are -18 to +12, plus RR73 + if(n.lt.-18) n=-18 !... Even numbers only ... + if(n.gt.12) n=12 + write(msgbits(j2:j2+4),1000) n+18 +1000 format(b5.5) + w(i1)="" + nh2=nh2+1 +! print*,'C',i3,i,j,n,w(i) + if( nh2.ge.4 .or. (nh1+nh2).ge.9 ) exit ! At most 4 callsigns w/reports + j=j+28 + j2=j2+5 + endif + enddo + +800 if(bSendMsg) then + i1=26 + do i=1,26 + if(freeTextMsg(i:i).ne.' ') i1=i + enddo + do i=i1+1,26 + freeTextMsg(i:i)='.' + enddo + if(i3.eq.3) then + call packtext77(freeTextMsg(1:13),msgbits(74:144)) + call packtext77(freeTextMsg(14:26),msgbits(145:215)) + elseif(i3.eq.2) then + call packtext77(freeTextMsg(1:13),msgbits(161:231)) + call packtext77(freeTextMsg(14:26),msgbits(232:302)) + endif + write(msgbits(327:329),'(b3.3)') i3 !Message type i3=2 + endif + if(bMoreCQs) msgbits(306:306)='1' + + read(msgbits,1004) xin(0:46) +1004 format(47b7) + + mask21=2**21 - 1 + n47=47 + ncrc21=iand(nhash2(xin,n47,571),mask21) !Compute 21-bit CRC + xin(47)=ncrc21/16384 !First 7 of 21 bits + xin(48)=iand(ncrc21/128,127) !Next 7 bits + xin(49)=iand(ncrc21,127) !Last 7 bits + + xin=xin(49:0:-1) !Reverse the symbol order +! NB: CRC is now in first three symbols, fox call in the last four. + + read(msgbits(327:329),'(b3)') i3 + + return +end subroutine sfox_pack diff --git a/lib/superfox/sftx.f90 b/lib/superfox/sftx.f90 new file mode 100644 index 000000000..78b5ca158 --- /dev/null +++ b/lib/superfox/sftx.f90 @@ -0,0 +1,107 @@ +program sftx + +! This program is required in order to create a SuperFox transmission. + +! The present version goes through the following steps: +! 1. Read old-style Fox messages from file 'sfox_1.dat' in the WSJT-X +! writable data directory. +! 2. Parse up to NSlots=5 messages to extract MyCall, up to 9 Hound +! calls, and the report or RR73 to be sent to each Hound. +! 3. Assemble and encode a single SuperFox message to produce itone(1:151), +! the array of channel symbol values. +! 4. Write the contents of array itone to file 'sfox_2.dat'. + + use qpc_mod + use sfox_mod + character*120 fname !Corrected path for sfox_1.dat + character*120 line !List of SuperFox message pieces + character*40 cmsg(5) !Old-style Fox messages + character*26 freeTextMsg + character*2 arg + character*10 ckey +! character*9 foxkey + character*11 foxcall0,foxcall + logical*1 bMoreCQs,bSendMsg + logical crc_ok + real py(0:127,0:127) !Probabilities for received synbol values + integer*8 n47 + integer itone(151) !SuperFox channel-symbol values + integer*1 xin(0:49) !Packed message as 7-bit symbols + integer*1 xdec(0:49) !Decoded message + integer*1 y(0:127) !Encoded symbols as i*1 integers + integer*1 ydec(0:127) !Decoded codeword + integer*1 yy(0:10) + integer chansym0(127) !Transmitted symbols, data only + integer chansym(127) !Received symbols, data only + integer isync(24) !Symbol numbers for sync tones + data isync/1,2,4,7,11,16,22,29,37,39,42,43,45,48,52,57,63,70,78,80, & + 83,84,86,89/ + include 'gtag.f90' + + narg=iargc() + if(narg.ne.3) then + print '(" Git tag: ",z9)',ntag + go to 999 + endif + +! sftx + call getarg(1,fname) + do i=1,len(trim(fname)) + if(fname(i:i).eq.'\\') fname(i:i)='/' + enddo + call getarg(2,foxcall0) + call getarg(3,ckey) + +! if((foxkey(foxcall0).ne.ckey).and.(INDEX(ckey,'OTP:').eq.0)) then ! neither kind +! itone=-99 +! go to 100 +! endif + + fsample=12000.0 + call sfox_init(7,127,50,'no',fspread,delay,fsample,24) + open(25,file=trim(fname),status='unknown') + do i=1,5 + read(25,1000,end=10) cmsg(i) +1000 format(a40) + enddo + i=6 +10 close(25) + nslots=i-1 + freeTextMsg=' ' + bMoreCQs=cmsg(1)(40:40).eq.'1' + bSendMsg=cmsg(nslots)(39:39).eq.'1' + if(bSendMsg) then + freeTextMsg=cmsg(nslots)(1:26) + if(nslots.gt.2) nslots=2 + endif + + call foxgen2(nslots,cmsg,line,foxcall) !Parse old-style Fox messages + +! Pack message information and CRC into xin(0:49) + call sfox_pack(line,ckey,bMoreCQs,bSendMsg,freeTextMsg,xin) + call qpc_encode(y,xin) !Encode the message to 128 symbols + y=cshift(y,1) !Puncture the code by removing y(0) + y(127)=0 + chansym0=y(0:126) + +! Create the full itone sequence containing both data and sync symbols + j=1 + k=0 + do i=1,NDS + if(j.le.NS .and. i.eq.isync(j)) then + if(j.lt.NS) j=j+1 !Index for next sync symbol + itone(i)=0 !Insert sync symbol at tone 0 + else + k=k+1 + itone(i)=chansym0(k) + 1 !Symbol value 0 transmitted as tone 1, etc. + endif + enddo + +100 i1=max(index(fname,'sfox_1'),1) + fname(i1:i1+9)='sfox_2.dat' + open(25,file=trim(fname),status='unknown') + write(25,1100) itone +1100 format(20i4) + close(25) + +999 end program sftx