From 7455ec02c3aabac610062f936c5052ee944201cf Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 25 Apr 2021 11:41:59 +0100 Subject: [PATCH 1/5] Repair User Guide issue, tnx to Sandro, IW3RAB --- doc/user_guide/en/decoder_notes.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user_guide/en/decoder_notes.adoc b/doc/user_guide/en/decoder_notes.adoc index 3da3a8cab..3780828c9 100644 --- a/doc/user_guide/en/decoder_notes.adoc +++ b/doc/user_guide/en/decoder_notes.adoc @@ -112,7 +112,7 @@ summarized in the following Table: |=========================================== |Mode |Mode character|Sync character|End of line information |FST4 | ` | | ?   aP -|FT4 | ~ | | ?   aP +|FT4 | + | | ?   aP |FT8 | ~ | | ?   aP |JT4 | $ | *, # | f, fN, dCN |JT9 | @ | | From 79140b4430df33d79cc8d04c3f82a8663a7ca2f4 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Mon, 3 May 2021 12:54:47 -0500 Subject: [PATCH 2/5] An all-Fortran implementation of Q65 source encoding, CRC calculation, and LDPC coding, for reference. --- lib/qra/q65/q65code.f90 | 235 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 lib/qra/q65/q65code.f90 diff --git a/lib/qra/q65/q65code.f90 b/lib/qra/q65/q65code.f90 new file mode 100644 index 000000000..a4ae00660 --- /dev/null +++ b/lib/qra/q65/q65code.f90 @@ -0,0 +1,235 @@ +module gf64math +! add and subtract in GF(2^6) based on primitive polynomial x^6+x+1 + + implicit none + integer, private :: gf64log(0:63) + integer, private :: gf64antilog(0:62) + +! table of the logarithms of the elements of GF(M) (log(0) never used) + data gf64log/ & + -1, 0, 1, 6, 2, 12, 7, 26, 3, 32, & + 13, 35, 8, 48, 27, 18, 4, 24, 33, 16, & + 14, 52, 36, 54, 9, 45, 49, 38, 28, 41, & + 19, 56, 5, 62, 25, 11, 34, 31, 17, 47, & + 15, 23, 53, 51, 37, 44, 55, 40, 10, 61, & + 46, 30, 50, 22, 39, 43, 29, 60, 42, 21, & + 20, 59, 57, 58/ + +! table of GF(M) elements given their logarithm + data gf64antilog/ & + 1, 2, 4, 8, 16, 32, 3, 6, 12, 24, & + 48, 35, 5, 10, 20, 40, 19, 38, 15, 30, & + 60, 59, 53, 41, 17, 34, 7, 14, 28, 56, & + 51, 37, 9, 18, 36, 11, 22, 44, 27, 54, & + 47, 29, 58, 55, 45, 25, 50, 39, 13, 26, & + 52, 43, 21, 42, 23, 46, 31, 62, 63, 61, & + 57, 49, 33/ + +contains + + integer function gf64_add(i1,i2) + implicit none + integer::i1 + integer::i2 + gf64_add=iand(ieor(i1,i2),63) + end function gf64_add + + integer function gf64_mult(i1,i2) + implicit none + integer::i1 + integer::i2 + integer::j + + if(i1.eq.0 .or. i2.eq.0) then + gf64_mult=0 + elseif(i1.eq.1) then + gf64_mult=i2 + elseif(i2.eq.1) then + gf64_mult=i1 + else + j=mod(gf64log(i1)+gf64log(i2),63) + gf64_mult=gf64antilog(j) + endif + end function gf64_mult + +end module gf64math + +module q65_generator + + integer generator(15,50) + data generator/ & + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, & + 0,20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, & + 0,20, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, & + 0,20, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, & + 0,20, 0, 1, 1, 0, 0, 0,10, 0, 0, 0, 0, 1, 0, & + 0,20, 0, 1, 1, 0, 0, 0,10, 0, 0, 0,44, 1, 0, & + 0,20, 0, 1, 1, 0, 0, 0,10, 1, 0, 0,44, 1, 0, & + 0,20, 0, 1, 1, 0, 0, 0,10, 1, 0, 0,44, 1,14, & + 0,20, 0, 1, 1, 0, 0, 0,10, 1,31, 0,44, 1,14, & + 0,20, 0, 1, 1,33, 0, 0,10, 1,31, 0,44, 1,14, & + 56,20, 0, 1, 1,33, 0, 0,10, 1,31, 0,44, 1,14, & + 56,20, 0, 1, 1,33, 0, 1,10, 1,31, 0,44, 1,14, & + 56, 1, 0, 1, 1,33, 0, 1,10, 1,31, 0,44, 1,14, & + 56, 1, 0, 1, 1,33, 0, 1,10, 1,31,36,44, 1,14, & + 56, 1, 0, 1, 1,33, 0, 1,43, 1,31,36,44, 1,14, & + 56, 1, 0, 1, 1,33, 0, 1,43,17,31,36,44, 1,14, & + 56, 1, 0, 1, 1,33, 0, 1,43,17,31,36,36, 1,14, & + 56, 1, 0, 1, 1,33,53, 1,43,17,31,36,36, 1,14, & + 56, 1, 0,35, 1,33,53, 1,43,17,31,36,36, 1,14, & + 56, 1, 0,35, 1,33,53, 1,43,17,30,36,36, 1,14, & + 56, 1, 0,35, 1,33,53,52,43,17,30,36,36, 1,14, & + 56, 1, 0,35, 1,32,53,52,43,17,30,36,36, 1,14, & + 56, 1,60,35, 1,32,53,52,43,17,30,36,36, 1,14, & + 56, 1,60,35, 1,32,53,52,43,17,30,36,36,49,14, & + 56, 1,60,35, 1,32,53,52,43,17,30,36,37,49,14, & + 56, 1,60,35,54,32,53,52,43,17,30,36,37,49,14, & + 56, 1,60,35,54,32,53,52, 1,17,30,36,37,49,14, & + 1, 1,60,35,54,32,53,52, 1,17,30,36,37,49,14, & + 1, 0,60,35,54,32,53,52, 1,17,30,36,37,49,14, & + 1, 0,60,35,54,32,53,52, 1,17,30,37,37,49,14, & + 1, 0,61,35,54,32,53,52, 1,17,30,37,37,49,14, & + 1, 0,61,35,54,32,53,52, 1,48,30,37,37,49,14, & + 1, 0,61,35,54,32,53,52, 1,48,30,37,37,49,15, & + 1, 0,61,35,54, 0,53,52, 1,48,30,37,37,49,15, & + 1, 0,61,35,54, 0,52,52, 1,48,30,37,37,49,15, & + 1, 0,61,35,54, 0,52,52, 1,48,30,37,37, 0,15, & + 1, 0,61,35,54, 0,52,34, 1,48,30,37,37, 0,15, & + 1, 0,61,35,54, 0,52,34, 1,48,30,37, 0, 0,15, & + 1, 0,61,35,54, 0,52,34, 1,48,30,20, 0, 0,15, & + 1, 0, 0,35,54, 0,52,34, 1,48,30,20, 0, 0,15, & + 1, 0, 0,35,54, 0,52,34, 1, 0,30,20, 0, 0,15, & + 0, 0, 0,35,54, 0,52,34, 1, 0,30,20, 0, 0,15, & + 0, 0, 0,35,54, 0,52,34, 1, 0,38,20, 0, 0,15, & + 0, 0, 0,35, 0, 0,52,34, 1, 0,38,20, 0, 0,15, & + 0, 0, 0,35, 0, 0,52, 0, 1, 0,38,20, 0, 0,15, & + 0, 0, 0,35, 0, 0,52, 0, 1, 0,38,20, 0, 0, 0, & + 0, 0, 0,35, 0, 0,52, 0, 0, 0,38,20, 0, 0, 0, & + 0, 0, 0,35, 0, 0,52, 0, 0, 0,38, 0, 0, 0, 0, & + 0, 0, 0, 0, 0, 0,52, 0, 0, 0,38, 0, 0, 0, 0, & + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,38, 0, 0, 0, 0/ + +end module q65_generator + +subroutine q65_encode(message,codeword) + use gf64math + use q65_generator + integer message(15) + integer codeword(65) + integer i,j + + codeword=0 + codeword(1:15)=message + do i=1,15 + do j=16,65 + codeword(j)=gf64_add(codeword(j),gf64_mult(message(i),generator(i,j-15))) + enddo + enddo + + return +end + +subroutine get_q65crc12(mc2,ncrc1,ncrc2) +! + character c12*12,c6*6 + integer*1 mc(90),mc2(90),tmp(6) + integer*1 r(13),p(13) + integer ncrc,nnn +! polynomial for 12-bit CRC 0xF01 + data p/1,1,0,0,0,0,0,0,0,1,1,1,1/ + +! flip bit order of each 6-bit symbol for consistency with Nico's calculation + do i=0,12 + tmp=mc2(i*6+1:i*6+6) + mc(i*6+1:i*6+6)=tmp(6:1:-1) + enddo + +! divide by polynomial + r=mc(1:13) + do i=0,77 + r(13)=mc(i+13) + r=mod(r+r(1)*p,2) + r=cshift(r,1) + enddo + + write(c6,'(6b1)') r(6:1:-1) + read(c6,'(b6.6)') ncrc1 + read(c6,'(6b1)') mc2(79:84) + write(c6,'(6b1)') r(12:7:-1) + read(c6,'(b6.6)') ncrc2 + read(c6,'(6b1)') mc2(85:90) +end subroutine get_q65crc12 + +program q65code + + use packjt77 + + implicit none + character msg*37,msgsent*27,arg*12,c12*12,c6*6 + integer message(15) + integer codeword(65), shortcodeword(63) + integer isync(22) + integer itone(85) + integer i,j,k + integer nargs,ncrc1,ncrc2,ncrc + integer i3,n3 + integer*1 mbits(90) + integer msymbols(15) + character*77 c77 + data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ + nargs=iargc() + if(nargs .ne. 1) then + print*,'Usage: q65sf "msg"' + goto 999 + endif + call getarg(1,msg) + + i3=-1 + n3=-1 + call pack77(msg,i3,n3,c77) + mbits=0 + read(c77,'(77i1)') mbits(1:77) + +! Message is 77 bits long. Add a 0 bit to create a 78-bit message and pad with +! 12 zeros to create 90-bit mbit array for CRC calculation. + call get_q65crc12(mbits,ncrc1,ncrc2) + +! Now have message in bits 1:78 and CRC in bits 79:90. +! Group message bits into 15 6-bit symbols: + do i=0,14 + write(c6,'(6i1)') mbits( (i*6+1):(i*6+6) ) + read(c6,'(b6.6)') message(i+1) + enddo + +! Encode to create a 65-symbol codeword + call q65_encode(message,codeword) + + write(*,*) 'Generated message plus CRC (90 bits)' + write(*,'(a8,15i4)') '6 bit : ',message + write(*,'(a8,90i1)') 'binary: ',mbits + write(*,*) ' ' + write(*,*) 'Codeword with CRC symbols (65 symbols)' + write(*,'(20i3)') codeword + +!Shorten the codeword by omitting the CRC symbols (symbols 14 and 15) + shortcodeword(1:13)=codeword(1:13) + shortcodeword(14:63)=codeword(16:65) + +!Insert sync symbols to create array of channel symbols + j=1 + k=0 + do i=1,85 + if(i.eq.isync(j)) then + j=j+1 + itone(i)=0 + else + k=k+1 + itone(i)=shortcodeword(k)+1 + endif + enddo + + write(*,*) ' ' + write(*,*) 'Channel symbols (85 total)' + write(*,'(20i3)') itone + +999 end program q65code From 075e0bfd56423f6481c36cca4d5a2e8bde5af777 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Tue, 4 May 2021 07:47:15 -0500 Subject: [PATCH 3/5] Fix uninitialized variables. --- lib/qra/q65/q65code.f90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/qra/q65/q65code.f90 b/lib/qra/q65/q65code.f90 index a4ae00660..4d16ec914 100644 --- a/lib/qra/q65/q65code.f90 +++ b/lib/qra/q65/q65code.f90 @@ -134,12 +134,12 @@ subroutine get_q65crc12(mc2,ncrc1,ncrc2) character c12*12,c6*6 integer*1 mc(90),mc2(90),tmp(6) integer*1 r(13),p(13) - integer ncrc,nnn + integer ncrc ! polynomial for 12-bit CRC 0xF01 data p/1,1,0,0,0,0,0,0,0,1,1,1,1/ ! flip bit order of each 6-bit symbol for consistency with Nico's calculation - do i=0,12 + do i=0,14 tmp=mc2(i*6+1:i*6+6) mc(i*6+1:i*6+6)=tmp(6:1:-1) enddo @@ -158,6 +158,7 @@ subroutine get_q65crc12(mc2,ncrc1,ncrc2) write(c6,'(6b1)') r(12:7:-1) read(c6,'(b6.6)') ncrc2 read(c6,'(6b1)') mc2(85:90) + end subroutine get_q65crc12 program q65code From 4efb2ab28f66505862aa45bdaa69a678977c08a2 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Thu, 6 May 2021 07:28:48 -0500 Subject: [PATCH 4/5] Define 'q65_encoding' module. --- lib/qra/q65/q65code.f90 | 71 +++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/lib/qra/q65/q65code.f90 b/lib/qra/q65/q65code.f90 index 4d16ec914..a6c0c6527 100644 --- a/lib/qra/q65/q65code.f90 +++ b/lib/qra/q65/q65code.f90 @@ -111,6 +111,10 @@ module q65_generator end module q65_generator +module q65_encoding + +contains + subroutine q65_encode(message,codeword) use gf64math use q65_generator @@ -161,33 +165,26 @@ subroutine get_q65crc12(mc2,ncrc1,ncrc2) end subroutine get_q65crc12 -program q65code - +subroutine get_q65_tones(msg37,codeword,itone) use packjt77 - implicit none - character msg*37,msgsent*27,arg*12,c12*12,c6*6 + character*37 msg37 + character*77 c77 + character*12 c12 + character*6 c6 + integer codeword(65) + integer sync(22) integer message(15) - integer codeword(65), shortcodeword(63) - integer isync(22) + integer shortcodeword(63) integer itone(85) integer i,j,k - integer nargs,ncrc1,ncrc2,ncrc - integer i3,n3 integer*1 mbits(90) - integer msymbols(15) - character*77 c77 - data isync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ - nargs=iargc() - if(nargs .ne. 1) then - print*,'Usage: q65sf "msg"' - goto 999 - endif - call getarg(1,msg) + integer i3,n3,ncrc1,ncrc2 + data sync/1,9,12,13,15,22,23,26,27,33,35,38,46,50,55,60,62,66,69,74,76,85/ i3=-1 n3=-1 - call pack77(msg,i3,n3,c77) + call pack77(msg37,i3,n3,c77) mbits=0 read(c77,'(77i1)') mbits(1:77) @@ -205,13 +202,6 @@ program q65code ! Encode to create a 65-symbol codeword call q65_encode(message,codeword) - write(*,*) 'Generated message plus CRC (90 bits)' - write(*,'(a8,15i4)') '6 bit : ',message - write(*,'(a8,90i1)') 'binary: ',mbits - write(*,*) ' ' - write(*,*) 'Codeword with CRC symbols (65 symbols)' - write(*,'(20i3)') codeword - !Shorten the codeword by omitting the CRC symbols (symbols 14 and 15) shortcodeword(1:13)=codeword(1:13) shortcodeword(14:63)=codeword(16:65) @@ -220,7 +210,7 @@ program q65code j=1 k=0 do i=1,85 - if(i.eq.isync(j)) then + if(i.eq.sync(j)) then j=j+1 itone(i)=0 else @@ -228,9 +218,36 @@ program q65code itone(i)=shortcodeword(k)+1 endif enddo +end subroutine get_q65_tones + +end module q65_encoding + +program q65code + use q65_encoding + + implicit none + character*37 msg37 + integer nargs + integer codeword(65),tones(85) + + nargs=iargc() + if(nargs .ne. 1) then + print*,'Usage: q65code "msg"' + goto 999 + endif + call getarg(1,msg37) + + call get_q65_tones(msg37,codeword,tones) + + write(*,*) 'Generated message plus CRC (90 bits)' + write(*,'(a8,15i4)') '6 bit : ',codeword(1:15) + write(*,'(a8,15b6.6)') 'binary: ',codeword(1:15) + write(*,*) ' ' + write(*,*) 'Codeword with CRC symbols (65 symbols)' + write(*,'(20i3)') codeword write(*,*) ' ' write(*,*) 'Channel symbols (85 total)' - write(*,'(20i3)') itone + write(*,'(20i3)') tones 999 end program q65code From 42e881bbc432f1c3b86e98f13e9359f347552095 Mon Sep 17 00:00:00 2001 From: Steven Franke Date: Thu, 6 May 2021 08:03:19 -0500 Subject: [PATCH 5/5] Add q65code.f90 to CMakeLists.txt. --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index db5f8c969..e9a3deca5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1110,6 +1110,9 @@ target_link_libraries (sumsim wsjt_fort wsjt_cxx) add_executable (q65sim lib/qra/q65/q65sim.f90) target_link_libraries (q65sim wsjt_fort wsjt_cxx) +add_executable (q65code lib/qra/q65/q65code.f90) +target_link_libraries (q65code wsjt_fort wsjt_cxx) + add_executable (test_q65 lib/test_q65.f90) target_link_libraries (test_q65 wsjt_fort wsjt_cxx) @@ -1543,7 +1546,8 @@ install (TARGETS jt9 wsprd fmtave fcal fmeasure ) if(WSJT_BUILD_UTILS) -install (TARGETS ft8code jt65code jt9code jt4code msk144code fst4sim q65sim +install (TARGETS ft8code jt65code jt9code jt4code msk144code + q65code fst4sim q65sim RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime )