diff --git a/ft4_ft8_public/Makefile b/ft4_ft8_public/Makefile new file mode 100644 index 0000000..3831acb --- /dev/null +++ b/ft4_ft8_public/Makefile @@ -0,0 +1,54 @@ +# +# On MS Windows using Msys/MinGW gfortran invoke like this: +# +# FC=gfortran make +# +# On macOS using MacPorts gfortran invoke like this: +# +# FC=gfortran make +# +# or if the gfortran compiler is named gfortran-mp-8 or similar +# +# FC=gfortran-mp-8 make +# +# otherwise invoke like this: +# +# make +# + +ifeq ($(OS),Windows_NT) + EXE = .exe +endif + +EXES = hashcodes$(EXE) std_call_to_c28$(EXE) nonstd_to_c58$(EXE) \ + free_text_to_f71$(EXE) grid4_to_g15$(EXE) grid6_to_g25$(EXE) \ + gen_crc14$(EXE) + +%.o: %.f90 + $(FC) -c $(FFLAGS) -o $@ $< + +all: $(EXES) + +hashcodes$(EXE): hashcodes.o + ${FC} -o $@ $^ + +std_call_to_c28$(EXE): std_call_to_c28.o + ${FC} -o $@ $^ + +nonstd_to_c58$(EXE): nonstd_to_c58.o + ${FC} -o $@ $^ + +free_text_to_f71$(EXE): free_text_to_f71.o + ${FC} -o $@ $^ + +grid4_to_g15$(EXE): grid4_to_g15.o + ${FC} -o $@ $^ + +grid6_to_g25$(EXE): grid6_to_g25.o + ${FC} -o $@ $^ + +gen_crc14$(EXE): gen_crc14.o + ${FC} -o $@ $^ + +clean: + -rm $(EXES) *.o diff --git a/ft4_ft8_public/arrl_rac_sections.txt b/ft4_ft8_public/arrl_rac_sections.txt new file mode 100644 index 0000000..948a24a --- /dev/null +++ b/ft4_ft8_public/arrl_rac_sections.txt @@ -0,0 +1,13 @@ +! Abbreviations for ARRL/RAC Sections as a Fortran 90 data statement: + + +data csec/ & + "AB ","AK ","AL ","AR ","AZ ","BC ","CO ","CT ","DE ","EB ", & + "EMA","ENY","EPA","EWA","GA ","GTA","IA ","ID ","IL ","IN ", & + "KS ","KY ","LA ","LAX","MAR","MB ","MDC","ME ","MI ","MN ", & + "MO ","MS ","MT ","NC ","ND ","NE ","NFL","NH ","NL ","NLI", & + "NM ","NNJ","NNY","NT ","NTX","NV ","OH ","OK ","ONE","ONN", & + "ONS","OR ","ORG","PAC","PR ","QC ","RI ","SB ","SC ","SCV", & + "SD ","SDG","SF ","SFL","SJV","SK ","SNJ","STX","SV ","TN ", & + "UT ","VA ","VI ","VT ","WCF","WI ","WMA","WNY","WPA","WTX", & + "WV ","WWA","WY ","DX "/ diff --git a/ft4_ft8_public/free_text_to_f71.f90 b/ft4_ft8_public/free_text_to_f71.f90 new file mode 100644 index 0000000..eb38ffe --- /dev/null +++ b/ft4_ft8_public/free_text_to_f71.f90 @@ -0,0 +1,67 @@ +program free_text_to_f71 + + character*13 c13,w + character*71 f71 + character*42 c + character*1 qa(10),qb(10) + data c/' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?'/ + + nargs=iargc() + if(nargs.ne.1) then + print*,'Usage: free_text_to_f71 ""' + print*,'Example: free_text_to_f71 "TNX BOB 73 GL"' + go to 999 + endif + call getarg(1,c13) + call mp_short_init + qa=char(0) + w=adjustr(c13) + do i=1,13 + j=index(c,w(i:i))-1 + if(j.lt.0) j=0 + call mp_short_mult(qb,qa(2:10),9,42) !qb(1:9)=42*qa(2:9) + call mp_short_add(qa,qb(2:10),9,j) !qa(1:9)=qb(2:9)+j + enddo + write(f71,1000) qa(2:10) +1000 format(b7.7,8b8.8) + write(*,1010) c13,f71 +1010 format('Free text: ',a13/'f71: ',a71) + +999 end program free_text_to_f71 + +subroutine mp_short_ops(w,u) +! Multi-precision arithmetic with storage in character arrays. + character*1 w(*),u(*) + integer i,ireg,j,n,ir,iv,ii1,ii2 + character*1 creg(4) + save ii1,ii2 + equivalence (ireg,creg) + + entry mp_short_init + ireg=256*ichar('2')+ichar('1') + do j=1,4 + if (creg(j).eq.'1') ii1=j + if (creg(j).eq.'2') ii2=j + enddo + return + + entry mp_short_add(w,u,n,iv) + ireg=256*iv + do j=n,1,-1 + ireg=ichar(u(j))+ichar(creg(ii2)) + w(j+1)=creg(ii1) + enddo + w(1)=creg(ii2) + return + + entry mp_short_mult(w,u,n,iv) + ireg=0 + do j=n,1,-1 + ireg=ichar(u(j))*iv+ichar(creg(ii2)) + w(j+1)=creg(ii1) + enddo + w(1)=creg(ii2) + return + + return +end subroutine mp_short_ops diff --git a/ft4_ft8_public/gen_crc14.f90 b/ft4_ft8_public/gen_crc14.f90 new file mode 100644 index 0000000..e4ec122 --- /dev/null +++ b/ft4_ft8_public/gen_crc14.f90 @@ -0,0 +1,36 @@ +program gen_crc14 + + character m77*77,c14*14 + + integer mc(96),r(15),p(15),ncrc +! polynomial for 14-bit CRC 0x6757 + data p/1,1,0,0,1,1,1,0,1,0,1,0,1,1,1/ + + nargs=iargc() + if(nargs.ne.1) then + print*,'Usage: gen_crc14 <77-bit message>' + print*,'Example: gen_crc14 "00000000000000000000000000100000010011011111110011011100100010100001010000001"' + go to 999 + endif + +! pad the 77bit message out to 96 bits + call getarg(1,m77) + read(m77,'(77i1)') mc(1:77) + mc(78:96)=0 + +! divide by polynomial + r=mc(1:15) + do i=0,81 + r(15)=mc(i+15) + r=mod(r+r(1)*p,2) + r=cshift(r,1) + enddo + +! the crc is in r(1:14) - print it in various ways: + write(c14,'(14b1)') r(1:14) + write(*,'(a40,1x,a14)') 'crc14 as a string: ',c14 + read(c14,'(b14.14)') ncrc + write(*,'(a40,i6)') 'crc14 as an integer: ',ncrc + write(*,'(a40,1x,b14.14)') 'binary representation of the integer: ',ncrc + +999 end program gen_crc14 diff --git a/ft4_ft8_public/generator.dat b/ft4_ft8_public/generator.dat new file mode 100644 index 0000000..235d350 --- /dev/null +++ b/ft4_ft8_public/generator.dat @@ -0,0 +1,86 @@ +This file contains the generator matrix for the FT8/FT4 (174,91) LDPC code. +The matrix has 91 columns and 83 rows. + +1000001100101001110011100001000110111111001100011110101011110101000010011111001001111111110 +0111011000011100001001100100111000100101110000100101100100110011010101001001001100010011001 +1101110000100110010110010000001011111011001001110111110001100100000100001010000110111101110 +0001101100111111010000010111100001011000110011010010110111010011001111101100011111110110001 +0000100111111101101001001111111011100000010000011001010111111101000000110100011110000011101 +0000011101111100110011001100000100011011100010000111001111101101010111000011110101001000101 +0010100110110110001010101111111000111100101000000011011011110100111111100001101010011101101 +0110000001010100111110101111010111110011010111011001011011010011101100001100100011000011111 +1110001000000111100110001110010000110001000011101110110100100111100010000100101011101001000 +0111011101011100100111000000100011101000000011100010011011011101101011100101011000110001100 +1011000010111000000100010000001010001100001010111111100110010111001000010011010010000111110 +0001100010100000110010010010001100011111110001100000101011011111010111000101111010100011001 +0111011001000111000111101000001100000010101000000111001000011110000000011011000100101011100 +1111111110111100110010111000000011001010100000110100000111111010111110110100011110110010111 +0110011010100111001010100001010110001111100100110010010110100010101111110110011100010111000 +1100010000100100001101101000100111111110100001011011000111000101000100110110001110100001100 +0000110111111111011100111001010000010100110100011010000110110011010010110001110000100111000 +0001010110110100100010000011000001100011011011001000101110011001100010010100100101110010111 +0010100110101000100111000000110100111101111010000001110101100110010101001000100110110000111 +0100111100010010011011110011011111111010010100011100101111100110000110111101011010111001010 +1001100111000100011100100011100111010000110110010111110100111100100001001110000010010100000 +0001100100011001101101110101000100011001011101100101011000100001101110110100111100011110100 +0000100111011011000100101101011100110001111110101110111000001011100001101101111101101011100 +0100100010001111110000110011110111110100001111111011110111101110101001001110101011111011010 +1000001001110100001000111110111001000000101101100111010111110111010101101110101101011111111 +1010101111100001100101111100010010000100110010110111010001110101011100010100010010101001101 +0010101101010000000011100100101111000000111011000101101001101101001010111101101111011101000 +1100010001110100101010100101001111010111000000100001100001110110000101100110100100110110000 +1000111010111010000110100001001111011011001100111001000010111101011001110001100011001110110 +0111010100111000010001000110011100111010001001110111100000101100110001000010000000010010111 +0000011011111111100000111010000101000101110000110111000000110101101001011100000100100110100 +0011101100110111010000010111100001011000110011000010110111010011001111101100001111110110001 +1001101001001010010110100010100011101110000101111100101010011100001100100100100001000010110 +1011110000101001111101000110010100110000100111001001011101111110100010010110000100001010010 +0010011001100011101011100110110111011111100010110101110011100010101110110010100101001000100 +0100011011110010001100011110111111100100010101110000001101001100000110000001010001000001100 +0011111110110010110011101000010110101011111010011011000011000111001011100000011011111011111 +1101111010000111010010000001111100101000001011000001010100111001011100011010000010100010111 +1111110011010111110011001111001000111100011010011111101010011001101110111010000101000001001 +1111000000100110000101000100011111101001010010010000110010101000111001000111010011001110110 +0100010000010000000100010101100000011000000110010110111110010101110011011101011100000001001 +0000100010001111110000110001110111110100101111111011110111100010101001001110101011111011010 +1011100011111110111100011011011000110000011101110010100111111011000010100000011110001100000 +0101101011111110101001111010110011001100101101110111101110111100100111011001100110101001000 +0100100110100111000000010110101011000110010100111111011001011110110011011100100100000111011 +0001100101000100110100001000010110111110010011100111110110101000110101101100110001111101000 +0010010100011111011000101010110111000100000000110010111100001110111001110001010000000000001 +0101011001000111000111111000011100000010101000000111001000011110000000001011000100101011100 +0010101110001110010010010010001111110010110111010101000111100010110101010011011111111010000 +0110101101010101000010100100000010100110011011110100011101010101110111101001010111000010011 +1010000110001010110100101000110101001110001001111111111010010010101001001111011011001000010 +0001000011000010111001011000011000111000100011001011100000101010001111011000000001110101100 +1110111100110100101001000001100000010111111011100000001000010011001111011011001011101011000 +0111111010011100000011000101010000110010010110101001110000010101100000110110111000000000000 +0011011010010011111001010111001011010001111111011110010011001101111100000111100111101000011 +1011111110110010110011101100010110101011111000011011000011000111001011100000011111111011111 +0111111011100001100000100011000011000101100000111100110011001100010101111101010010110000100 +1010000001100110110010110010111111101101101011111100100111110101001001100110010000010010011 +1011101100100011011100100101101010111100010001111100110001011111010011001100010011001101001 +1101111011011001110110111010001110111110111001000000110001011001101101010110000010011011010 +1101100110100111000000010110101011000110010100111110011011011110110011011100100100000011011 +1001101011010100011010101110110101011111011100000111111100101000000010101011010111111100010 +1110010110010010000111000111011110000010001001011000011100110001011011010111110100111100001 +0100111100010100110110101000001001000010101010001011100001101101110010100111001100110101001 +1000101110001011010100000111101011010100011001111101010001000100000111011111011101110000111 +0010001010000011000111001001110011110001000101101001010001100111101011010000010010110110100 +0010000100111011100000111000111111100010101011100101010011000011100011101110011100011000000 +0101110110010010011010110110110111010111000111110000100001010001100000011010010011100001001 +0110011010101011011110011101010010110010100111101110011011100110100101010000100111100101011 +1001010110000001010010000110100000101101011101001000101000111000110111010110100010111010101 +1011100011001110000000100000110011110000011010011100001100101010011100100011101010110001010 +1111010000110011000111010110110101000110000101100000011111101001010101110101001001110100011 +0110110110100010001110111010010000100100101110010101100101100001001100111100111110011100100 +1010011000110110101111001011110001111011001100001100010111111011111010101110011001111111111 +0101110010110000110110000110101000000111110111110110010101001010100100001000100110100010000 +1111000100011111000100000110100001001000011110000000111111001001111011001101110110000000101 +0001111110111011010100110110010011111011100011010010110010011101011100110000110101011011101 +1111110010111000011010111100011100001010010100001100100111010000001010100101110100000011010 +1010010100110100010000110011000000101001111010101100000101011111001100100010111000110100110 +1100100110001001110110011100011111000011110100111011100011000101010111010111010100010011000 +0111101110110011100010110010111100000001100001101101010001100110010000111010111010010110001 +0010011001000100111010111010110111101011010001001011100101000110011111010001111101000010110 +0110000010001100110010000101011101011001010010111111101110110101010111010110100101100000000 diff --git a/ft4_ft8_public/grid4_to_g15.f90 b/ft4_ft8_public/grid4_to_g15.f90 new file mode 100644 index 0000000..e88bbc9 --- /dev/null +++ b/ft4_ft8_public/grid4_to_g15.f90 @@ -0,0 +1,55 @@ +program grid4_to_g15 + + parameter (MAXGRID4=32400) + character*4 w,grid4 + character c1*1,c2*2 + logical is_grid4 + is_grid4(grid4)=len(trim(grid4)).eq.4 .and. & + grid4(1:1).ge.'A' .and. grid4(1:1).le.'R' .and. & + grid4(2:2).ge.'A' .and. grid4(2:2).le.'R' .and. & + grid4(3:3).ge.'0' .and. grid4(3:3).le.'9' .and. & + grid4(4:4).ge.'0' .and. grid4(4:4).le.'9' + + nargs=iargc() + if(nargs.ne.1) then + print*,'Convert a 4-character grid, signal report, etc., to a g15 value.' + print*,'Usage examples:' + print*,'grid4_to_g15 FN20' + print*,'grid4_to_g15 -11' + print*,'grid4_to_g15 +02' + print*,'grid4_to_g15 RRR' + print*,'grid4_to_g15 RR73' + print*,'grid4_to_g15 73' + print*,'grid4_to_g15 ""' + go to 999 + endif + call getarg(1,w) + if(is_grid4(w) .and. w.ne.'RR73') then + j1=(ichar(w(1:1))-ichar('A'))*18*10*10 + j2=(ichar(w(2:2))-ichar('A'))*10*10 + j3=(ichar(w(3:3))-ichar('0'))*10 + j4=(ichar(w(4:4))-ichar('0')) + igrid4=j1+j2+j3+j4 + else + c1=w(1:1) + if(c1.ne.'+' .and. c1.ne.'-'.and. trim(w).ne.'RRR' .and. w.ne.'RR73' & + .and. trim(w).ne.'73' .and. len(trim(w)).ne.0) go to 900 + if(c1.eq.'+' .or. c1.eq.'-') then + read(w,*,err=900) irpt + irpt=irpt+35 + endif + if(len(trim(w)).eq.0) irpt=1 + if(trim(w).eq.'RRR') irpt=2 + if(w.eq.'RR73') irpt=3 + if(trim(w).eq.'73') irpt=4 + igrid4=MAXGRID4 + irpt + endif + + write(*,1000) w,igrid4,igrid4 +1000 format('Encoded word: ',a4,' g15 in binary: ',b15.15,' decimal:',i6) + go to 999 + +900 write(*,1900) +1900 format('Invalid input') + +999 end program grid4_to_g15 diff --git a/ft4_ft8_public/grid6_to_g25.f90 b/ft4_ft8_public/grid6_to_g25.f90 new file mode 100644 index 0000000..47a7b63 --- /dev/null +++ b/ft4_ft8_public/grid6_to_g25.f90 @@ -0,0 +1,41 @@ +program grid6_to_g25 + + parameter (MAXGRID4=32400) + character*6 w,grid6 + character c1*1,c2*2 + logical is_grid6 + + is_grid6(grid6)=len(trim(grid6)).eq.6 .and. & + grid6(1:1).ge.'A' .and. grid6(1:1).le.'R' .and. & + grid6(2:2).ge.'A' .and. grid6(2:2).le.'R' .and. & + grid6(3:3).ge.'0' .and. grid6(3:3).le.'9' .and. & + grid6(4:4).ge.'0' .and. grid6(4:4).le.'9' .and. & + grid6(5:5).ge.'A' .and. grid6(5:5).le.'X' .and. & + grid6(6:6).ge.'A' .and. grid6(6:6).le.'X' + + nargs=iargc() + if(nargs.ne.1) then + print*,'Convert a 6-character grid to a g25 value.' + print*,'Usage: grid6_to_g25 IO91NP' + go to 999 + endif + call getarg(1,w) + if(.not. is_grid6(w)) go to 900 + + j1=(ichar(w(1:1))-ichar('A'))*18*10*10*24*24 + j2=(ichar(w(2:2))-ichar('A'))*10*10*24*24 + j3=(ichar(w(3:3))-ichar('0'))*10*24*24 + j4=(ichar(w(4:4))-ichar('0'))*24*24 + j5=(ichar(w(5:5))-ichar('A'))*24 + j6=(ichar(w(6:6))-ichar('A')) + igrid6=j1+j2+j3+j4+j5+j6 + + write(*,1000) w,igrid6,igrid6 +1000 format('Encoded word: ',a6,' g25 in binary: ',b25.25/ & + 30x,'decimal:',i9) + go to 999 + +900 write(*,1900) +1900 format('Invalid input') + +999 end program grid6_to_g25 diff --git a/ft4_ft8_public/hashcodes.f90 b/ft4_ft8_public/hashcodes.f90 new file mode 100644 index 0000000..0c0628e --- /dev/null +++ b/ft4_ft8_public/hashcodes.f90 @@ -0,0 +1,34 @@ +program hashcodes + + parameter (NTOKENS=2063592) + integer*8 nprime,n8(3) + integer nbits(3),ihash(3) + character*11 callsign + character*38 c + data c/' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/'/ + data nprime/47055833459_8/,nbits/10,12,22/ + + nargs=iargc() + if(nargs.ne.1) then + print*,'Usage: hashcodes ' + print*,'Examples: hashcodes PJ4/K1ABC' + print*,' hashcodes YW18FIFA' + go to 999 + endif + call getarg(1,callsign) + callsign=adjustl(callsign) + + do k=1,3 + n8(k)=0 + do i=1,11 + j=index(c,callsign(i:i)) - 1 + n8(k)=38*n8(k) + j + enddo + ihash(k)=ishft(nprime*n8(k),nbits(k)-64) + enddo + ih22_biased=ihash(3) + NTOKENS + write(*,1000) callsign,ihash,ih22_biased +1000 format('Callsign',9x,'h10',7x,'h12',7x,'h22'/41('-')/ & + a11,i9,2i10,/'Biased for storage in c28:',i14) + +999 end program hashcodes diff --git a/ft4_ft8_public/nonstd_to_c58.f90 b/ft4_ft8_public/nonstd_to_c58.f90 new file mode 100644 index 0000000..ffec7f0 --- /dev/null +++ b/ft4_ft8_public/nonstd_to_c58.f90 @@ -0,0 +1,24 @@ +program nonstd_to_c58 + + integer*8 n58 + character*11 callsign + character*38 c + data c/' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/'/ + + nargs=iargc() + if(nargs.ne.1) then + print*,'Usage: nonstd_to_c58 ' + print*,'Examples: nonstd_to_c58 PJ4/K1ABC' + print*,' nonstd_to_c58 YW18FIFA' + go to 999 + endif + call getarg(1,callsign) + + n58=0 + do i=1,11 + n58=n58*38 + index(c,callsign(i:i)) - 1 + enddo + write(*,1000) callsign,n58,n58 +1000 format('Callsign: ',a11/'c58 (binary): ' b58.58/'c58 (decimal):',i20) + +999 end program nonstd_to_c58 diff --git a/ft4_ft8_public/parity.dat b/ft4_ft8_public/parity.dat new file mode 100644 index 0000000..ca6f76a --- /dev/null +++ b/ft4_ft8_public/parity.dat @@ -0,0 +1,183 @@ +This file specifies the sparse 83x174 parity-check matrix for the +FT8/FT4 (174,91) LDPC code. Each of the 174 columns contains +exactly 3 ones. The rows contain either 6 or 7 ones. +The matrix is specified by the following list consisting of +174 lines, each of which includes 3 numbers. +Each line corresponds to a column of the parity check matrix. +The three numbers are indices of the rows that contain a one in +the corresponding column. The indices range from 1 through 83. + + 16 45 73 + 25 51 62 + 33 58 78 + 1 44 45 + 2 7 61 + 3 6 54 + 4 35 48 + 5 13 21 + 8 56 79 + 9 64 69 + 10 19 66 + 11 36 60 + 12 37 58 + 14 32 43 + 15 63 80 + 17 28 77 + 18 74 83 + 22 53 81 + 23 30 34 + 24 31 40 + 26 41 76 + 27 57 70 + 29 49 65 + 3 38 78 + 5 39 82 + 46 50 73 + 51 52 74 + 55 71 72 + 44 67 72 + 43 68 78 + 1 32 59 + 2 6 71 + 4 16 54 + 7 65 67 + 8 30 42 + 9 22 31 + 10 18 76 + 11 23 82 + 12 28 61 + 13 52 79 + 14 50 51 + 15 81 83 + 17 29 60 + 19 33 64 + 20 26 73 + 21 34 40 + 24 27 77 + 25 55 58 + 35 53 66 + 36 48 68 + 37 46 75 + 38 45 47 + 39 57 69 + 41 56 62 + 20 49 53 + 46 52 63 + 45 70 75 + 27 35 80 + 1 15 30 + 2 68 80 + 3 36 51 + 4 28 51 + 5 31 56 + 6 20 37 + 7 40 82 + 8 60 69 + 9 10 49 + 11 44 57 + 12 39 59 + 13 24 55 + 14 21 65 + 16 71 78 + 17 30 76 + 18 25 80 + 19 61 83 + 22 38 77 + 23 41 50 + 7 26 58 + 29 32 81 + 33 40 73 + 18 34 48 + 13 42 64 + 5 26 43 + 47 69 72 + 54 55 70 + 45 62 68 + 10 63 67 + 14 66 72 + 22 60 74 + 35 39 79 + 1 46 64 + 1 24 66 + 2 5 70 + 3 31 65 + 4 49 58 + 1 4 5 + 6 60 67 + 7 32 75 + 8 48 82 + 9 35 41 + 10 39 62 + 11 14 61 + 12 71 74 + 13 23 78 + 11 35 55 + 15 16 79 + 7 9 16 + 17 54 63 + 18 50 57 + 19 30 47 + 20 64 80 + 21 28 69 + 22 25 43 + 13 22 37 + 2 47 51 + 23 54 74 + 26 34 72 + 27 36 37 + 21 36 63 + 29 40 44 + 19 26 57 + 3 46 82 + 14 15 58 + 33 52 53 + 30 43 52 + 6 9 52 + 27 33 65 + 25 69 73 + 38 55 83 + 20 39 77 + 18 29 56 + 32 48 71 + 42 51 59 + 28 44 79 + 34 60 62 + 31 45 61 + 46 68 77 + 6 24 76 + 8 10 78 + 40 41 70 + 17 50 53 + 42 66 68 + 4 22 72 + 36 64 81 + 13 29 47 + 2 8 81 + 56 67 73 + 5 38 50 + 12 38 64 + 59 72 80 + 3 26 79 + 45 76 81 + 1 65 74 + 7 18 77 + 11 56 59 + 14 39 54 + 16 37 66 + 10 28 55 + 15 60 70 + 17 25 82 + 20 30 31 + 12 67 68 + 23 75 80 + 27 32 62 + 24 69 75 + 19 21 71 + 34 53 61 + 35 46 47 + 33 59 76 + 40 43 83 + 41 42 63 + 49 75 83 + 20 44 48 + 42 49 57 diff --git a/ft4_ft8_public/states_provinces.txt b/ft4_ft8_public/states_provinces.txt new file mode 100644 index 0000000..abc68d2 --- /dev/null +++ b/ft4_ft8_public/states_provinces.txt @@ -0,0 +1,11 @@ +! Abbreviations for US States and Canadian Provinces as a Fortran 90 +! data statement: + + data cmult/ & + "AL ","AK ","AZ ","AR ","CA ","CO ","CT ","DE ","FL ","GA ", & + "HI ","ID ","IL ","IN ","IA ","KS ","KY ","LA ","ME ","MD ", & + "MA ","MI ","MN ","MS ","MO ","MT ","NE ","NV ","NH ","NJ ", & + "NM ","NY ","NC ","ND ","OH ","OK ","OR ","PA ","RI ","SC ", & + "SD ","TN ","TX ","UT ","VT ","VA ","WA ","WV ","WI ","WY ", & + "NB ","NS ","QC ","ON ","MB ","SK ","AB ","BC ","NWT","NF ", & + "LB ","NU ","YT ","PEI","DC "/ diff --git a/ft4_ft8_public/std_call_to_c28.f90 b/ft4_ft8_public/std_call_to_c28.f90 new file mode 100644 index 0000000..952134f --- /dev/null +++ b/ft4_ft8_public/std_call_to_c28.f90 @@ -0,0 +1,31 @@ +program std_call_to_c28 + + parameter (NTOKENS=2063592,MAX22=4194304) + character*6 call_std + character a1*37,a2*36,a3*10,a4*27 + data a1/' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'/ + data a2/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'/ + data a3/'0123456789'/ + data a4/' ABCDEFGHIJKLMNOPQRSTUVWXYZ'/ + + nargs=iargc() + if(nargs.ne.1) then + print*,'Usage: std_call_to_c28 ' + print*,'Example: std_call_to_c28 K1ABC' + go to 999 + endif + call getarg(1,call_std) + call_std=adjustr(call_std) + i1=index(a1,call_std(1:1))-1 + i2=index(a2,call_std(2:2))-1 + i3=index(a3,call_std(3:3))-1 + i4=index(a4,call_std(4:4))-1 + i5=index(a4,call_std(5:5))-1 + i6=index(a4,call_std(6:6))-1 + n28=NTOKENS + MAX22 + 36*10*27*27*27*i1 + 10*27*27*27*i2 + & + 27*27*27*i3 + 27*27*i4 + 27*i5 + i6 + + write(*,1000) call_std,n28 +1000 format('Callsign: ',a6,2x,'c28 as decimal integer:',i10) + +999 end program std_call_to_c28 diff --git a/ft8/constants.c b/ft8/constants.c index 8bdba6e..3fa5444 100644 --- a/ft8/constants.c +++ b/ft8/constants.c @@ -92,25 +92,9 @@ const uint8_t kFT8_LDPC_generator[FT8_M][FT8_K_BYTES] = { {0x26, 0x44, 0xeb, 0xad, 0xeb, 0x44, 0xb9, 0x46, 0x7d, 0x1f, 0x42, 0xc0}, {0x60, 0x8c, 0xc8, 0x57, 0x59, 0x4b, 0xfb, 0xb5, 0x5d, 0x69, 0x60, 0x00}}; -// Column order (permutation) in which the bits in codeword are stored -// (Not really used in FT8 v2 - instead the Nm, Mn and generator matrices are already permuted) -const uint8_t kFT8_LDPC_column_order[FT8_N] = { - 0, 1, 2, 3, 28, 4, 5, 6, 7, 8, 9, 10, 11, 34, 12, 32, 13, 14, 15, 16, - 17, 18, 36, 29, 43, 19, 20, 42, 21, 40, 30, 37, 22, 47, 61, 45, 44, 23, 41, 39, - 49, 24, 46, 50, 48, 26, 31, 33, 51, 38, 52, 59, 55, 66, 57, 27, 60, 35, 54, 58, - 25, 56, 62, 64, 67, 69, 63, 68, 70, 72, 65, 73, 75, 74, 71, 77, 78, 76, 79, 80, - 53, 81, 83, 82, 84, 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}; - -// this is the LDPC(174,91) parity check matrix. -// 83 rows. -// each row describes one parity check. -// each number is an index into the codeword (1-origin). -// the codeword bits mentioned in each row must xor to zero. -// From WSJT-X's ldpc_174_91_c_reordered_parity.f90. +// Each row describes one LDPC parity check. +// Each number is an index into the codeword (1-origin). +// The codeword bits mentioned in each row must XOR to zero. const uint8_t kFT8_LDPC_Nm[FT8_M][7] = { {4, 31, 59, 91, 92, 96, 153}, {5, 32, 60, 93, 115, 146, 0}, @@ -196,10 +180,8 @@ const uint8_t kFT8_LDPC_Nm[FT8_M][7] = { {25, 38, 65, 99, 122, 160, 0}, {17, 42, 75, 129, 170, 172, 0}}; -// Mn from WSJT-X's bpdecode174.f90. -// each row corresponds to a codeword bit. -// the numbers indicate which three parity -// checks (rows in Nm) refer to the codeword bit. +// Each row corresponds to a codeword bit. +// The numbers indicate which three LDPC parity checks (rows in Nm) refer to the codeword bit. // 1-origin. const uint8_t kFT8_LDPC_Mn[FT8_N][3] = { {16, 45, 73}, diff --git a/ft8/decode.c b/ft8/decode.c index bde3f40..4e1e81d 100644 --- a/ft8/decode.c +++ b/ft8/decode.c @@ -158,8 +158,7 @@ void extract_likelihood(const waterfall_t *power, const candidate_t *cand, float float inv_n = 1.0f / FT8_N; float variance = (sum2 - (sum * sum * inv_n)) * inv_n; - // Normalize log174 such that sigma = 2.83 (Why? It's in WSJT-X, ft8b.f90) - // Seems to be 2.83 = sqrt(8). Experimentally sqrt(32) works better. + // Normalize log174 distribution and scale it with experimentally found coefficient float norm_factor = sqrtf(32.0f / variance); for (int i = 0; i < FT8_N; ++i) { @@ -187,10 +186,10 @@ bool decode(const waterfall_t *power, const candidate_t *cand, message_t *messag // Extract CRC and check it status->crc_extracted = extract_crc(a91); + // [1]: 'The CRC is calculated on the source-encoded message, zero-extended from 77 to 82 bits.' // TODO: not sure why the zeroing of message is needed and also why CRC over 96-14 bits? a91[9] &= 0xF8; - a91[10] = 0; - a91[11] = 0; + a91[10] &= 0x00; status->crc_calculated = ft8_crc(a91, 96 - 14); if (status->crc_extracted != status->crc_calculated) diff --git a/ft8/encode.c b/ft8/encode.c index ea7fd51..9ddf9d5 100644 --- a/ft8/encode.c +++ b/ft8/encode.c @@ -15,25 +15,13 @@ uint8_t parity8(uint8_t x) // Encode a 91-bit message and return a 174-bit codeword. // The generator matrix has dimensions (87,87). -// The code is a (174,91) regular ldpc code with column weight 3. -// The code was generated using the PEG algorithm. +// The code is a (174,91) regular LDPC code with column weight 3. // Arguments: // [IN] message - array of 91 bits stored as 12 bytes (MSB first) // [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first) void encode174(const uint8_t *message, uint8_t *codeword) { - // Here we don't generate the generator bit matrix as in WSJT-X implementation - // Instead we access the generator bits straight from the binary representation in kFT8_LDPC_generator - - // For reference: - // codeword(1:K)=message - // codeword(K+1:N)=pchecks - - // printf("Encode "); - // for (int i = 0; i < FT8_K_BYTES; ++i) { - // printf("%02x ", message[i]); - // } - // printf("\n"); + // This implementation accesses the generator bits straight from the packed binary representation in kFT8_LDPC_generator // Fill the codeword with message and zeros, as we will only update binary ones later for (int j = 0; j < FT8_N_BYTES; ++j) diff --git a/ft8/ldpc.c b/ft8/ldpc.c index 69fa715..1a091c4 100644 --- a/ft8/ldpc.c +++ b/ft8/ldpc.c @@ -20,8 +20,6 @@ static int ldpc_check(uint8_t codeword[]); static float fast_tanh(float x); static float fast_atanh(float x); -static float pltanh(float x); -static float platanh(float x); // Packs a string of bits each represented as a zero/non-zero byte in plain[], // as a string of packed bits starting from the MSB of the first byte of packed[] @@ -287,57 +285,3 @@ static float fast_atanh(float x) float b = (945.0f + x2 * (-1050.0f + x2 * 225.0f)); return a / b; } - -static float pltanh(float x) -{ - float isign = +1; - if (x < 0) - { - isign = -1; - x = -x; - } - if (x < 0.8f) - { - return isign * 0.83 * x; - } - if (x < 1.6f) - { - return isign * (0.322f * x + 0.4064f); - } - if (x < 3.0f) - { - return isign * (0.0524f * x + 0.8378f); - } - if (x < 7.0f) - { - return isign * (0.0012f * x + 0.9914f); - } - return isign * 0.9998f; -} - -static float platanh(float x) -{ - float isign = +1; - if (x < 0) - { - isign = -1; - x = -x; - } - if (x < 0.664f) - { - return isign * x / 0.83f; - } - if (x < 0.9217f) - { - return isign * (x - 0.4064f) / 0.322f; - } - if (x < 0.9951f) - { - return isign * (x - 0.8378f) / 0.0524f; - } - if (x < 0.9998f) - { - return isign * (x - 0.9914f) / 0.0012f; - } - return isign * 7.0f; -} diff --git a/ft8/pack.c b/ft8/pack.c index 42a95cc..fc113e1 100644 --- a/ft8/pack.c +++ b/ft8/pack.c @@ -34,14 +34,9 @@ int32_t pack28(const char *callsign) int nnum = 0, nlet = 0; // TODO: - // if(nnum.eq.3 .and. nlet.eq.0) then n28=3+nqsy - // if(nlet.ge.1 .and. nlet.le.4 .and. nnum.eq.0) then n28=3+1000+m } // TODO: Check for <...> callsign - // if(text(1:1).eq.'<')then - // call save_hash_call(text,n10,n12,n22) !Save callsign in hash table - // n28=NTOKENS + n22 char c6[6] = {' ', ' ', ' ', ' ', ' ', ' '}; @@ -84,7 +79,6 @@ int32_t pack28(const char *callsign) (i2 = char_index(A3, c6[2])) >= 0 && (i3 = char_index(A4, c6[3])) >= 0 && (i4 = char_index(A4, c6[4])) >= 0 && (i5 = char_index(A4, c6[5])) >= 0) { - //printf("Pack28: idx=[%d, %d, %d, %d, %d, %d]\n", i0, i1, i2, i3, i4, i5); // This is a standard callsign int32_t n28 = i0; n28 = n28 * 36 + i1; @@ -92,20 +86,14 @@ int32_t pack28(const char *callsign) n28 = n28 * 27 + i3; n28 = n28 * 27 + i4; n28 = n28 * 27 + i5; - //printf("Pack28: n28=%d (%04xh)\n", n28, n28); return NTOKENS + MAX22 + n28; } //char text[13]; - //if (length > 13) return -1; // TODO: // Treat this as a nonstandard callsign: compute its 22-bit hash - // call save_hash_call(text,n10,n12,n22) !Save callsign in hash table - // n28=NTOKENS + n22 - - // n28=iand(n28,ishft(1,28)-1) return -1; } @@ -129,12 +117,6 @@ bool chkcall(const char *call, char *bc) return false; // TODO: implement suffix parsing (or rework?) - //bc=w(1:6) - //i0=char_index(w,'/') - //if(max(i0-1,n1-i0).gt.6) go to 100 !Base call must be < 7 characters - //if(i0.ge.2 .and. i0.le.n1-1) then !Extract base call from compound call - // if(i0-1.le.n1-i0) bc=w(i0+1:n1)//' ' - // if(i0-1.gt.n1-i0) bc=w(1:i0-1)//' ' return true; } @@ -160,7 +142,6 @@ uint16_t packgrid(const char *grid4) in_range(grid4[1], 'A', 'R') && is_digit(grid4[2]) && is_digit(grid4[3])) { - //if (w(3).eq.'R ') ir=1 uint16_t igrid4 = (grid4[0] - 'A'); igrid4 = igrid4 * 18 + (grid4[1] - 'A'); igrid4 = igrid4 * 10 + (grid4[2] - '0'); @@ -220,18 +201,12 @@ int pack77_1(const char *msg, uint8_t *b77) uint8_t i3 = 1; // No suffix or /R // TODO: check for suffixes - // if(char_index(w(1),'/P').ge.4 .or. char_index(w(2),'/P').ge.4) i3=2 !Type 2, with "/P" - // if(char_index(w(1),'/P').ge.4 .or. char_index(w(1),'/R').ge.4) ipa=1 - // if(char_index(w(2),'/P').ge.4 .or. char_index(w(2),'/R').ge.4) ipb=1 // Shift in ipa and ipb bits into n28a and n28b n28a <<= 1; // ipa = 0 n28b <<= 1; // ipb = 0 // Pack into (28 + 1) + (28 + 1) + (1 + 15) + 3 bits - // write(c77,1000) n28a,ipa,n28b,ipb,ir,igrid4,i3 - // 1000 format(2(b28.28,b1),b1,b15.15,b3.3) - b77[0] = (n28a >> 21); b77[1] = (n28a >> 13); b77[2] = (n28a >> 5); @@ -320,10 +295,8 @@ int pack77(const char *msg, uint8_t *c77) // TODO: // Check 0.5 (telemetry) - // i3=0 n3=5 write(c77,1006) ntel,n3,i3 1006 format(b23.23,2b24.24,2b3.3) // Check Type 4 (One nonstandard call and one hashed call) - // pack77_4(nwords,w,i3,n3,c77) // Default to free text // i3=0 n3=0 diff --git a/ft8/unpack.c b/ft8/unpack.c index 412b098..22c7a9e 100644 --- a/ft8/unpack.c +++ b/ft8/unpack.c @@ -3,7 +3,6 @@ #include -//#define NBASE (uint32_t)(37L*36L*10L*27L*27L*27L) #define MAX22 ((uint32_t)4194304L) #define NTOKENS ((uint32_t)2063592L) #define MAXGRID4 ((uint16_t)32400L) @@ -59,13 +58,12 @@ int unpack28(uint32_t n28, uint8_t ip, uint8_t i3, char *result) if (n28 < MAX22) { // This is a 22-bit hash of a result - //call hash22(n22,c13) !Retrieve result from hash table // TODO: implement - // strcpy(result, "<...>"); - result[0] = '<'; - int_to_dd(result + 1, n28, 7, false); - result[8] = '>'; - result[9] = '\0'; + strcpy(result, "<...>"); + // result[0] = '<'; + // int_to_dd(result + 1, n28, 7, false); + // result[8] = '>'; + // result[9] = '\0'; return 0; } @@ -114,8 +112,6 @@ int unpack_type1(const uint8_t *a77, uint8_t i3, char *field1, char *field2, cha uint8_t ir; // Extract packed fields - // read(c77,1000) n28a,ipa,n28b,ipb,ir,igrid4,i3 - // 1000 format(2(b28,b1),b1,b15,b3) n28a = (a77[0] << 21); n28a |= (a77[1] << 13); n28a |= (a77[2] << 5); @@ -168,7 +164,6 @@ int unpack_type1(const uint8_t *a77, uint8_t i3, char *field1, char *field2, cha dst[1] = 'A' + (n % 18); n /= 18; dst[0] = 'A' + (n % 18); - // if(msg(1:3).eq.'CQ ' .and. ir.eq.1) unpk77_success=.false. // if (ir > 0 && strncmp(field1, "CQ", 2) == 0) return -1; } else @@ -195,7 +190,6 @@ int unpack_type1(const uint8_t *a77, uint8_t i3, char *field1, char *field2, cha } int_to_dd(dst, irpt - 35, 2, true); } - // if(msg(1:3).eq.'CQ ' .and. irpt.ge.2) unpk77_success=.false. // if (irpt >= 2 && strncmp(field1, "CQ", 2) == 0) return -1; } @@ -264,11 +258,6 @@ int unpack_telemetry(const uint8_t *a71, char *telemetry) //by KD8CEC int unpack_nonstandard(const uint8_t *a77, char *field1, char *field2, char *field3) { - /* - wsjt-x 2.1.0 rc5 - read(c77,1050) n12,n58,iflip,nrpt,icq - 1050 format(b12,b58,b1,b2,b1) -*/ uint32_t n12, iflip, nrpt, icq; uint64_t n58; n12 = (a77[0] << 4); //11 ~4 : 8 diff --git a/ft8/v1/arrays.h b/ft8/v1/arrays.h deleted file mode 100644 index fe7ebb6..0000000 --- a/ft8/v1/arrays.h +++ /dev/null @@ -1,308 +0,0 @@ -#include - -// LDPC(174,87) parameters from WSJT-X. -// this is an indirection table that moves a -// codeword's 87 systematic (message) bits to the end. -uint8_t 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 -}; - -// this is the LDPC(174,87) parity check matrix. -// 87 rows. -// each row describes one parity check. -// each number is an index into the codeword (1-origin). -// the codeword bits mentioned in each row must xor to zero. -// From WSJT-X's bpdecode174.f90. -uint8_t Nm[][7] = { - {1, 30, 60, 89, 118, 147, 0}, - {2, 31, 61, 90, 119, 147, 0}, - {3, 32, 62, 91, 120, 148, 0}, - {4, 33, 63, 92, 121, 149, 0}, - {2, 34, 64, 93, 122, 150, 0}, - {5, 33, 65, 94, 123, 148, 0}, - {6, 34, 66, 95, 124, 151, 0}, - {7, 35, 67, 96, 120, 152, 0}, - {8, 36, 68, 97, 125, 153, 0}, - {9, 37, 69, 98, 126, 152, 0}, - {10, 38, 70, 99, 127, 154, 0}, - {11, 39, 71, 100, 126, 155, 0}, - {12, 40, 61, 101, 128, 145, 0}, - {10, 33, 60, 95, 128, 156, 0}, - {13, 41, 72, 97, 126, 157, 0}, - {13, 42, 73, 90, 129, 156, 0}, - {14, 39, 74, 99, 130, 158, 0}, - {15, 43, 75, 102, 131, 159, 0}, - {16, 43, 71, 103, 118, 160, 0}, - {17, 44, 76, 98, 130, 156, 0}, - {18, 45, 60, 96, 132, 161, 0}, - {19, 46, 73, 83, 133, 162, 0}, - {12, 38, 77, 102, 134, 163, 0}, - {19, 47, 78, 104, 135, 147, 0}, - {1, 32, 77, 105, 136, 164, 0}, - {20, 48, 73, 106, 123, 163, 0}, - {21, 41, 79, 107, 137, 165, 0}, - {22, 42, 66, 108, 138, 152, 0}, - {18, 42, 80, 109, 139, 154, 0}, - {23, 49, 81, 110, 135, 166, 0}, - {16, 50, 82, 91, 129, 158, 0}, - {3, 48, 63, 107, 124, 167, 0}, - {6, 51, 67, 111, 134, 155, 0}, - {24, 35, 77, 100, 122, 162, 0}, - {20, 45, 76, 112, 140, 157, 0}, - {21, 36, 64, 92, 130, 159, 0}, - {8, 52, 83, 111, 118, 166, 0}, - {21, 53, 84, 113, 138, 168, 0}, - {25, 51, 79, 89, 122, 158, 0}, - {22, 44, 75, 107, 133, 155, 172}, - {9, 54, 84, 90, 141, 169, 0}, - {22, 54, 85, 110, 136, 161, 0}, - {8, 37, 65, 102, 129, 170, 0}, - {19, 39, 85, 114, 139, 150, 0}, - {26, 55, 71, 93, 142, 167, 0}, - {27, 56, 65, 96, 133, 160, 174}, - {28, 31, 86, 100, 117, 171, 0}, - {28, 52, 70, 104, 132, 144, 0}, - {24, 57, 68, 95, 137, 142, 0}, - {7, 30, 72, 110, 143, 151, 0}, - {4, 51, 76, 115, 127, 168, 0}, - {16, 45, 87, 114, 125, 172, 0}, - {15, 30, 86, 115, 123, 150, 0}, - {23, 46, 64, 91, 144, 173, 0}, - {23, 35, 75, 113, 145, 153, 0}, - {14, 41, 87, 108, 117, 149, 170}, - {25, 40, 85, 94, 124, 159, 0}, - {25, 58, 69, 116, 143, 174, 0}, - {29, 43, 61, 116, 132, 162, 0}, - {15, 58, 88, 112, 121, 164, 0}, - {4, 59, 72, 114, 119, 163, 173}, - {27, 47, 86, 98, 134, 153, 0}, - {5, 44, 78, 109, 141, 0, 0}, - {10, 46, 69, 103, 136, 165, 0}, - {9, 50, 59, 93, 128, 164, 0}, - {14, 57, 58, 109, 120, 166, 0}, - {17, 55, 62, 116, 125, 154, 0}, - {3, 54, 70, 101, 140, 170, 0}, - {1, 36, 82, 108, 127, 174, 0}, - {5, 53, 81, 105, 140, 0, 0}, - {29, 53, 67, 99, 142, 173, 0}, - {18, 49, 74, 97, 115, 167, 0}, - {2, 57, 63, 103, 138, 157, 0}, - {26, 38, 79, 112, 135, 171, 0}, - {11, 52, 66, 88, 119, 148, 0}, - {20, 40, 68, 117, 141, 160, 0}, - {11, 48, 81, 89, 146, 169, 0}, - {29, 47, 80, 92, 146, 172, 0}, - {6, 32, 87, 104, 145, 169, 0}, - {27, 34, 74, 106, 131, 165, 0}, - {12, 56, 84, 88, 139, 0, 0}, - {13, 56, 62, 111, 146, 171, 0}, - {26, 37, 80, 105, 144, 151, 0}, - {17, 31, 82, 113, 121, 161, 0}, - {28, 49, 59, 94, 137, 0, 0}, - {7, 55, 83, 101, 131, 168, 0}, - {24, 50, 78, 106, 143, 149, 0}, -}; - -// Mn from WSJT-X's bpdecode174.f90. -// each row corresponds to a codeword bit. -// the numbers indicate which three parity -// checks (rows in Nm) refer to the codeword bit. -// 1-origin. -uint8_t Mn[][3] = { - {1, 25, 69}, - {2, 5, 73}, - {3, 32, 68}, - {4, 51, 61}, - {6, 63, 70}, - {7, 33, 79}, - {8, 50, 86}, - {9, 37, 43}, - {10, 41, 65}, - {11, 14, 64}, - {12, 75, 77}, - {13, 23, 81}, - {15, 16, 82}, - {17, 56, 66}, - {18, 53, 60}, - {19, 31, 52}, - {20, 67, 84}, - {21, 29, 72}, - {22, 24, 44}, - {26, 35, 76}, - {27, 36, 38}, - {28, 40, 42}, - {30, 54, 55}, - {34, 49, 87}, - {39, 57, 58}, - {45, 74, 83}, - {46, 62, 80}, - {47, 48, 85}, - {59, 71, 78}, - {1, 50, 53}, - {2, 47, 84}, - {3, 25, 79}, - {4, 6, 14}, - {5, 7, 80}, - {8, 34, 55}, - {9, 36, 69}, - {10, 43, 83}, - {11, 23, 74}, - {12, 17, 44}, - {13, 57, 76}, - {15, 27, 56}, - {16, 28, 29}, - {18, 19, 59}, - {20, 40, 63}, - {21, 35, 52}, - {22, 54, 64}, - {24, 62, 78}, - {26, 32, 77}, - {30, 72, 85}, - {31, 65, 87}, - {33, 39, 51}, - {37, 48, 75}, - {38, 70, 71}, - {41, 42, 68}, - {45, 67, 86}, - {46, 81, 82}, - {49, 66, 73}, - {58, 60, 66}, - {61, 65, 85}, - {1, 14, 21}, - {2, 13, 59}, - {3, 67, 82}, - {4, 32, 73}, - {5, 36, 54}, - {6, 43, 46}, - {7, 28, 75}, - {8, 33, 71}, - {9, 49, 76}, - {10, 58, 64}, - {11, 48, 68}, - {12, 19, 45}, - {15, 50, 61}, - {16, 22, 26}, - {17, 72, 80}, - {18, 40, 55}, - {20, 35, 51}, - {23, 25, 34}, - {24, 63, 87}, - {27, 39, 74}, - {29, 78, 83}, - {30, 70, 77}, - {31, 69, 84}, - {22, 37, 86}, - {38, 41, 81}, - {42, 44, 57}, - {47, 53, 62}, - {52, 56, 79}, - {60, 75, 81}, - {1, 39, 77}, - {2, 16, 41}, - {3, 31, 54}, - {4, 36, 78}, - {5, 45, 65}, - {6, 57, 85}, - {7, 14, 49}, - {8, 21, 46}, - {9, 15, 72}, - {10, 20, 62}, - {11, 17, 71}, - {12, 34, 47}, - {13, 68, 86}, - {18, 23, 43}, - {19, 64, 73}, - {24, 48, 79}, - {25, 70, 83}, - {26, 80, 87}, - {27, 32, 40}, - {28, 56, 69}, - {29, 63, 66}, - {30, 42, 50}, - {33, 37, 82}, - {35, 60, 74}, - {38, 55, 84}, - {44, 52, 61}, - {51, 53, 72}, - {58, 59, 67}, - {47, 56, 76}, - {1, 19, 37}, - {2, 61, 75}, - {3, 8, 66}, - {4, 60, 84}, - {5, 34, 39}, - {6, 26, 53}, - {7, 32, 57}, - {9, 52, 67}, - {10, 12, 15}, - {11, 51, 69}, - {13, 14, 65}, - {16, 31, 43}, - {17, 20, 36}, - {18, 80, 86}, - {21, 48, 59}, - {22, 40, 46}, - {23, 33, 62}, - {24, 30, 74}, - {25, 42, 64}, - {27, 49, 85}, - {28, 38, 73}, - {29, 44, 81}, - {35, 68, 70}, - {41, 63, 76}, - {45, 49, 71}, - {50, 58, 87}, - {48, 54, 83}, - {13, 55, 79}, - {77, 78, 82}, - {1, 2, 24}, - {3, 6, 75}, - {4, 56, 87}, - {5, 44, 53}, - {7, 50, 83}, - {8, 10, 28}, - {9, 55, 62}, - {11, 29, 67}, - {12, 33, 40}, - {14, 16, 20}, - {15, 35, 73}, - {17, 31, 39}, - {18, 36, 57}, - {19, 46, 76}, - {21, 42, 84}, - {22, 34, 59}, - {23, 26, 61}, - {25, 60, 65}, - {27, 64, 80}, - {30, 37, 66}, - {32, 45, 72}, - {38, 51, 86}, - {41, 77, 79}, - {43, 56, 68}, - {47, 74, 82}, - {40, 52, 78}, - {54, 61, 71}, - {46, 58, 69}, -}; - -uint8_t nrw[] = { - 6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,7, - 6,6,6,6,6,7,6,6,6,6, - 6,6,6,6,6,7,6,6,6,6, - 7,6,5,6,6,6,6,6,6,5, - 6,6,6,6,6,6,6,6,6,6, - 5,6,6,6,5,6,6 -}; diff --git a/ft8/v1/encode.cpp b/ft8/v1/encode.cpp deleted file mode 100644 index e74237b..0000000 --- a/ft8/v1/encode.cpp +++ /dev/null @@ -1,270 +0,0 @@ -#include "encode.h" - -// Define the LDPC sizes -constexpr int N = 174; // Number of bits in the encoded message -constexpr int K = 87; // Number of payload bits -constexpr int M = N - K; // Number of checksum bits - -constexpr uint16_t POLYNOMIAL = 0xC06; // CRC-12 polynomial without the leading (MSB) 1 - -constexpr int K_BYTES = (K + 7) / 8; // Number of whole bytes needed to store K bits - -// Parity generator matrix for (174,87) LDPC code, stored in bitpacked format (MSB first) -const uint8_t kGenerator[M][K_BYTES] = { - { 0x23, 0xbb, 0xa8, 0x30, 0xe2, 0x3b, 0x6b, 0x6f, 0x50, 0x98, 0x2e }, - { 0x1f, 0x8e, 0x55, 0xda, 0x21, 0x8c, 0x5d, 0xf3, 0x30, 0x90, 0x52 }, - { 0xca, 0x7b, 0x32, 0x17, 0xcd, 0x92, 0xbd, 0x59, 0xa5, 0xae, 0x20 }, - { 0x56, 0xf7, 0x83, 0x13, 0x53, 0x7d, 0x0f, 0x43, 0x82, 0x96, 0x4e }, - { 0x29, 0xc2, 0x9d, 0xba, 0x9c, 0x54, 0x5e, 0x26, 0x77, 0x62, 0xfe }, - { 0x6b, 0xe3, 0x96, 0xb5, 0xe2, 0xe8, 0x19, 0xe3, 0x73, 0x34, 0x0c }, - { 0x29, 0x35, 0x48, 0xa1, 0x38, 0x85, 0x83, 0x28, 0xaf, 0x42, 0x10 }, - { 0xcb, 0x6c, 0x6a, 0xfc, 0xdc, 0x28, 0xbb, 0x3f, 0x7c, 0x6e, 0x86 }, - { 0x3f, 0x2a, 0x86, 0xf5, 0xc5, 0xbd, 0x22, 0x5c, 0x96, 0x11, 0x50 }, - { 0x84, 0x9d, 0xd2, 0xd6, 0x36, 0x73, 0x48, 0x18, 0x60, 0xf6, 0x2c }, - { 0x56, 0xcd, 0xae, 0xc6, 0xe7, 0xae, 0x14, 0xb4, 0x3f, 0xee, 0xee }, - { 0x04, 0xef, 0x5c, 0xfa, 0x37, 0x66, 0xba, 0x77, 0x8f, 0x45, 0xa4 }, - { 0xc5, 0x25, 0xae, 0x4b, 0xd4, 0xf6, 0x27, 0x32, 0x0a, 0x39, 0x74 }, - { 0xfe, 0x37, 0x80, 0x29, 0x41, 0xd6, 0x6d, 0xde, 0x02, 0xb9, 0x9c }, - { 0x41, 0xfd, 0x95, 0x20, 0xb2, 0xe4, 0xab, 0xeb, 0x2f, 0x98, 0x9c }, - { 0x40, 0x90, 0x7b, 0x01, 0x28, 0x0f, 0x03, 0xc0, 0x32, 0x39, 0x46 }, - { 0x7f, 0xb3, 0x6c, 0x24, 0x08, 0x5a, 0x34, 0xd8, 0xc1, 0xdb, 0xc4 }, - { 0x40, 0xfc, 0x3e, 0x44, 0xbb, 0x7d, 0x2b, 0xb2, 0x75, 0x6e, 0x44 }, - { 0xd3, 0x8a, 0xb0, 0xa1, 0xd2, 0xe5, 0x2a, 0x8e, 0xc3, 0xbc, 0x76 }, - { 0x3d, 0x0f, 0x92, 0x9e, 0xf3, 0x94, 0x9b, 0xd8, 0x4d, 0x47, 0x34 }, - { 0x45, 0xd3, 0x81, 0x4f, 0x50, 0x40, 0x64, 0xf8, 0x05, 0x49, 0xae }, - { 0xf1, 0x4d, 0xbf, 0x26, 0x38, 0x25, 0xd0, 0xbd, 0x04, 0xb0, 0x5e }, - { 0xf0, 0x8a, 0x91, 0xfb, 0x2e, 0x1f, 0x78, 0x29, 0x06, 0x19, 0xa8 }, - { 0x7a, 0x8d, 0xec, 0x79, 0xa5, 0x1e, 0x8a, 0xc5, 0x38, 0x80, 0x22 }, - { 0xca, 0x41, 0x86, 0xdd, 0x44, 0xc3, 0x12, 0x15, 0x65, 0xcf, 0x5c }, - { 0xdb, 0x71, 0x4f, 0x8f, 0x64, 0xe8, 0xac, 0x7a, 0xf1, 0xa7, 0x6e }, - { 0x8d, 0x02, 0x74, 0xde, 0x71, 0xe7, 0xc1, 0xa8, 0x05, 0x5e, 0xb0 }, - { 0x51, 0xf8, 0x15, 0x73, 0xdd, 0x40, 0x49, 0xb0, 0x82, 0xde, 0x14 }, - { 0xd0, 0x37, 0xdb, 0x82, 0x51, 0x75, 0xd8, 0x51, 0xf3, 0xaf, 0x00 }, - { 0xd8, 0xf9, 0x37, 0xf3, 0x18, 0x22, 0xe5, 0x7c, 0x56, 0x23, 0x70 }, - { 0x1b, 0xf1, 0x49, 0x06, 0x07, 0xc5, 0x40, 0x32, 0x66, 0x0e, 0xde }, - { 0x16, 0x16, 0xd7, 0x80, 0x18, 0xd0, 0xb4, 0x74, 0x5c, 0xa0, 0xf2 }, - { 0xa9, 0xfa, 0x8e, 0x50, 0xbc, 0xb0, 0x32, 0xc8, 0x5e, 0x33, 0x04 }, - { 0x83, 0xf6, 0x40, 0xf1, 0xa4, 0x8a, 0x8e, 0xbc, 0x04, 0x43, 0xea }, - { 0xec, 0xa9, 0xaf, 0xa0, 0xf6, 0xb0, 0x1d, 0x92, 0x30, 0x5e, 0xdc }, - { 0x37, 0x76, 0xaf, 0x54, 0xcc, 0xfb, 0xae, 0x91, 0x6a, 0xfd, 0xe6 }, - { 0x6a, 0xbb, 0x21, 0x2d, 0x97, 0x39, 0xdf, 0xc0, 0x25, 0x80, 0xf2 }, - { 0x05, 0x20, 0x9a, 0x0a, 0xbb, 0x53, 0x0b, 0x9e, 0x7e, 0x34, 0xb0 }, - { 0x61, 0x2f, 0x63, 0xac, 0xc0, 0x25, 0xb6, 0xab, 0x47, 0x6f, 0x7c }, - { 0x0a, 0xf7, 0x72, 0x31, 0x61, 0xec, 0x22, 0x30, 0x80, 0xbe, 0x86 }, - { 0xa8, 0xfc, 0x90, 0x69, 0x76, 0xc3, 0x56, 0x69, 0xe7, 0x9c, 0xe0 }, - { 0x45, 0xb7, 0xab, 0x62, 0x42, 0xb7, 0x74, 0x74, 0xd9, 0xf1, 0x1a }, - { 0xb2, 0x74, 0xdb, 0x8a, 0xbd, 0x3c, 0x6f, 0x39, 0x6e, 0xa3, 0x56 }, - { 0x90, 0x59, 0xdf, 0xa2, 0xbb, 0x20, 0xef, 0x7e, 0xf7, 0x3a, 0xd4 }, - { 0x3d, 0x18, 0x8e, 0xa4, 0x77, 0xf6, 0xfa, 0x41, 0x31, 0x7a, 0x4e }, - { 0x8d, 0x90, 0x71, 0xb7, 0xe7, 0xa6, 0xa2, 0xee, 0xd6, 0x96, 0x5e }, - { 0xa3, 0x77, 0x25, 0x37, 0x73, 0xea, 0x67, 0x83, 0x67, 0xc3, 0xf6 }, - { 0xec, 0xbd, 0x7c, 0x73, 0xb9, 0xcd, 0x34, 0xc3, 0x72, 0x0c, 0x8a }, - { 0xb6, 0x53, 0x7f, 0x41, 0x7e, 0x61, 0xd1, 0xa7, 0x08, 0x53, 0x36 }, - { 0x6c, 0x28, 0x0d, 0x2a, 0x05, 0x23, 0xd9, 0xc4, 0xbc, 0x59, 0x46 }, - { 0xd3, 0x6d, 0x66, 0x2a, 0x69, 0xae, 0x24, 0xb7, 0x4d, 0xcb, 0xd8 }, - { 0xd7, 0x47, 0xbf, 0xc5, 0xfd, 0x65, 0xef, 0x70, 0xfb, 0xd9, 0xbc }, - { 0xa9, 0xfa, 0x2e, 0xef, 0xa6, 0xf8, 0x79, 0x6a, 0x35, 0x57, 0x72 }, - { 0xcc, 0x9d, 0xa5, 0x5f, 0xe0, 0x46, 0xd0, 0xcb, 0x3a, 0x77, 0x0c }, - { 0xf6, 0xad, 0x48, 0x24, 0xb8, 0x7c, 0x80, 0xeb, 0xfc, 0xe4, 0x66 }, - { 0xcc, 0x6d, 0xe5, 0x97, 0x55, 0x42, 0x09, 0x25, 0xf9, 0x0e, 0xd2 }, - { 0x16, 0x4c, 0xc8, 0x61, 0xbd, 0xd8, 0x03, 0xc5, 0x47, 0xf2, 0xac }, - { 0xc0, 0xfc, 0x3e, 0xc4, 0xfb, 0x7d, 0x2b, 0xb2, 0x75, 0x66, 0x44 }, - { 0x0d, 0xbd, 0x81, 0x6f, 0xba, 0x15, 0x43, 0xf7, 0x21, 0xdc, 0x72 }, - { 0xa0, 0xc0, 0x03, 0x3a, 0x52, 0xab, 0x62, 0x99, 0x80, 0x2f, 0xd2 }, - { 0xbf, 0x4f, 0x56, 0xe0, 0x73, 0x27, 0x1f, 0x6a, 0xb4, 0xbf, 0x80 }, - { 0x57, 0xda, 0x6d, 0x13, 0xcb, 0x96, 0xa7, 0x68, 0x9b, 0x27, 0x90 }, - { 0x81, 0xcf, 0xc6, 0xf1, 0x8c, 0x35, 0xb1, 0xe1, 0xf1, 0x71, 0x14 }, - { 0x48, 0x1a, 0x2a, 0x0d, 0xf8, 0xa2, 0x35, 0x83, 0xf8, 0x2d, 0x6c }, - { 0x1a, 0xc4, 0x67, 0x2b, 0x54, 0x9c, 0xd6, 0xdb, 0xa7, 0x9b, 0xcc }, - { 0xc8, 0x7a, 0xf9, 0xa5, 0xd5, 0x20, 0x6a, 0xbc, 0xa5, 0x32, 0xa8 }, - { 0x97, 0xd4, 0x16, 0x9c, 0xb3, 0x3e, 0x74, 0x35, 0x71, 0x8d, 0x90 }, - { 0xa6, 0x57, 0x3f, 0x3d, 0xc8, 0xb1, 0x6c, 0x9d, 0x19, 0xf7, 0x46 }, - { 0x2c, 0x41, 0x42, 0xbf, 0x42, 0xb0, 0x1e, 0x71, 0x07, 0x6a, 0xcc }, - { 0x08, 0x1c, 0x29, 0xa1, 0x0d, 0x46, 0x8c, 0xcd, 0xbc, 0xec, 0xb6 }, - { 0x5b, 0x0f, 0x77, 0x42, 0xbc, 0xa8, 0x6b, 0x80, 0x12, 0x60, 0x9a }, - { 0x01, 0x2d, 0xee, 0x21, 0x98, 0xeb, 0xa8, 0x2b, 0x19, 0xa1, 0xda }, - { 0xf1, 0x62, 0x77, 0x01, 0xa2, 0xd6, 0x92, 0xfd, 0x94, 0x49, 0xe6 }, - { 0x35, 0xad, 0x3f, 0xb0, 0xfa, 0xeb, 0x5f, 0x1b, 0x0c, 0x30, 0xdc }, - { 0xb1, 0xca, 0x4e, 0xa2, 0xe3, 0xd1, 0x73, 0xba, 0xd4, 0x37, 0x9c }, - { 0x37, 0xd8, 0xe0, 0xaf, 0x92, 0x58, 0xb9, 0xe8, 0xc5, 0xf9, 0xb2 }, - { 0xcd, 0x92, 0x1f, 0xdf, 0x59, 0xe8, 0x82, 0x68, 0x37, 0x63, 0xf6 }, - { 0x61, 0x14, 0xe0, 0x84, 0x83, 0x04, 0x3f, 0xd3, 0xf3, 0x8a, 0x8a }, - { 0x2e, 0x54, 0x7d, 0xd7, 0xa0, 0x5f, 0x65, 0x97, 0xaa, 0xc5, 0x16 }, - { 0x95, 0xe4, 0x5e, 0xcd, 0x01, 0x35, 0xac, 0xa9, 0xd6, 0xe6, 0xae }, - { 0xb3, 0x3e, 0xc9, 0x7b, 0xe8, 0x3c, 0xe4, 0x13, 0xf9, 0xac, 0xc8 }, - { 0xc8, 0xb5, 0xdf, 0xfc, 0x33, 0x50, 0x95, 0xdc, 0xdc, 0xaf, 0x2a }, - { 0x3d, 0xd0, 0x1a, 0x59, 0xd8, 0x63, 0x10, 0x74, 0x3e, 0xc7, 0x52 }, - { 0x14, 0xcd, 0x0f, 0x64, 0x2f, 0xc0, 0xc5, 0xfe, 0x3a, 0x65, 0xca }, - { 0x3a, 0x0a, 0x1d, 0xfd, 0x7e, 0xee, 0x29, 0xc2, 0xe8, 0x27, 0xe0 }, - { 0x8a, 0xbd, 0xb8, 0x89, 0xef, 0xbe, 0x39, 0xa5, 0x10, 0xa1, 0x18 }, - { 0x3f, 0x23, 0x1f, 0x21, 0x20, 0x55, 0x37, 0x1c, 0xf3, 0xe2, 0xa2 } -}; - -// Column order (permutation) in which the bits in codeword are stored -const uint8_t kColumn_order[174] = { - 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 -}; - -// Costas 7x7 tone pattern -const uint8_t kCostas_map[] = { 2,5,6,0,4,1,3 }; - - -// Returns 1 if an odd number of bits are set in x, zero otherwise -uint8_t parity8(uint8_t x) { - x ^= x >> 4; // a b c d ae bf cg dh - x ^= x >> 2; // a b ac bd cae dbf aecg bfdh - x ^= x >> 1; // a ab bac acbd bdcae caedbf aecgbfdh - return (x) & 1; -} - - -// Encode an 87-bit message and return a 174-bit codeword. -// The generator matrix has dimensions (87,87). -// The code is a (174,87) regular ldpc code with column weight 3. -// The code was generated using the PEG algorithm. -// After creating the codeword, the columns are re-ordered according to -// "kColumn_order" to make the codeword compatible with the parity-check matrix -// Arguments: -// [IN] message - array of 87 bits stored as 11 bytes (MSB first) -// [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first) -void encode174(const uint8_t *message, uint8_t *codeword) { - // Here we don't generate the generator bit matrix as in WSJT-X implementation - // Instead we access the generator bits straight from the binary representation in kGenerator - - // Also we don't use the itmp temporary buffer, instead filling codeword bit by bit - // in the reordered order as we compute the result. - - // For reference: - // itmp(1:M)=pchecks - // itmp(M+1:N)=message(1:K) - // codeword(kColumn_order+1)=itmp(1:N) - - int colidx = 0; // track the current column in codeword - - // Fill the codeword with zeroes, as we will only update binary ones later - for (int i = 0; i < (N + 7) / 8; i++) { - codeword[i] = 0; - } - - // Compute the first part of itmp (1:M) and store the result in codeword - for (int i = 0; i < M; ++i) { // do i=1,M - // Fast implementation of bitwise multiplication and parity checking - // Normally nsum would contain the result of dot product between message and kGenerator[i], - // but we only compute the sum modulo 2. - uint8_t nsum = 0; - for (int j = 0; j < K_BYTES; ++j) { - uint8_t bits = message[j] & kGenerator[i][j]; // bitwise AND (bitwise multiplication) - nsum ^= parity8(bits); // bitwise XOR (addition modulo 2) - } - // Check if we need to set a bit in codeword - if (nsum % 2) { // pchecks(i)=mod(nsum,2) - uint8_t col = kColumn_order[colidx]; // Index of the bit to set - codeword[col/8] |= (1 << (7 - col%8)); - } - ++colidx; - } - - // Compute the second part of itmp (M+1:N) and store the result in codeword - uint8_t mask = 0x80; // Rolling mask starting with the MSB - for (int j = 0; j < K; ++j) { - // Copy the j-th bit from message to codeword - if (message[j/8] & mask) { - uint8_t col = kColumn_order[colidx]; // Index of the bit to set - codeword[col/8] |= (1 << (7 - col%8)); - } - ++colidx; - - // Roll the bitmask to the right - mask >>= 1; - if (mask == 0) mask = 0x80; - } -} - - -// Compute 12-bit CRC for a sequence of given number of bits -// [IN] message - byte sequence (MSB first) -// [IN] num_bits - number of bits in the sequence -uint16_t ft8_crc(uint8_t *message, int num_bits) { - // Adapted from https://barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code - constexpr int WIDTH = 12; - constexpr uint16_t TOPBIT = (1 << (WIDTH - 1)); - - uint16_t remainder = 0; - int idx_byte = 0; - - // Perform modulo-2 division, a bit at a time. - for (int idx_bit = 0; idx_bit < num_bits; ++idx_bit) { - if (idx_bit % 8 == 0) { - // Bring the next byte into the remainder. - remainder ^= (message[idx_byte] << (WIDTH - 8)); - ++idx_byte; - } - - // Try to divide the current data bit. - if (remainder & TOPBIT) { - remainder = (remainder << 1) ^ POLYNOMIAL; - } - else { - remainder = (remainder << 1); - } - } - return remainder & ((1 << WIDTH) - 1); -} - - -// Generate FT8 tone sequence from payload data -// [IN] payload - 9 byte array consisting of 72 bit payload (MSB first) -// [IN] i3 - 3 bits containing message type (zero?) -// [OUT] itone - array of NN (79) bytes to store the generated tones (encoded as 0..7) -void genft8(const uint8_t *payload, uint8_t i3, uint8_t *itone) { - uint8_t a87[11]; // Store 72 bits of payload + 3 bits i3 + 12 bits CRC - - for (int i = 0; i < 9; i++) - a87[i] = payload[i]; - - // Append 3 bits of i3 at the end of 72 bit payload - a87[9] = ((i3 & 0x07) << 5); - - // Calculate CRC of 11 bytes = 88 bits, see WSJT-X code - uint16_t checksum = ft8_crc(a87, 88 - 12); - - // Store the CRC at the end of 75 bit message (yes, 72 + 3) - uint16_t tmp = (checksum << 1); - a87[9] |= (uint8_t)(tmp >> 8); - a87[10] = (uint8_t)tmp; - - // a87 contains 72 bits of payload + 3 bits of i3 + 12 bits of CRC - uint8_t codeword[22]; - encode174(a87, codeword); - - // Message structure: S7 D29 S7 D29 S7 - for (int i = 0; i < 7; ++i) { - itone[i] = kCostas_map[i]; - itone[36 + i] = kCostas_map[i]; - itone[72 + i] = kCostas_map[i]; - } - - int k = 7; // Skip over the first set of Costas symbols - for (int j = 0; j < ND; ++j) { // do j=1,ND - if (j == 29) { - k += 7; // Skip over the second set of Costas symbols - } - // Extract 3 bits from codeword at i-th position - itone[k] = 0; - int i = 3*j; - if (codeword[i/8] & (1 << (7 - i%8))) itone[k] |= 4; - ++i; - if (codeword[i/8] & (1 << (7 - i%8))) itone[k] |= 2; - ++i; - if (codeword[i/8] & (1 << (7 - i%8))) itone[k] |= 1; - ++k; - } -} diff --git a/ft8/v1/encode.h b/ft8/v1/encode.h deleted file mode 100644 index 3e6db5f..0000000 --- a/ft8/v1/encode.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -constexpr int ND = 58; // Data symbols -constexpr int NS = 21; // Sync symbols (3 @ Costas 7x7) -constexpr int NN = NS+ND; // Total channel symbols (79) - - -// Generate FT8 tone sequence from payload data -// [IN] payload - 9 byte array consisting of 72 bit payload -// [IN] i3 - 3 bits containing message type (zero?) -// [OUT] itone - array of NN (79) bytes to store the generated tones (encoded as 0..7) -void genft8(const uint8_t *payload, uint8_t i3, uint8_t *itone); - - -// Encode an 87-bit message and return a 174-bit codeword. -// The generator matrix has dimensions (87,87). -// The code is a (174,87) regular ldpc code with column weight 3. -// The code was generated using the PEG algorithm. -// After creating the codeword, the columns are re-ordered according to -// "colorder" to make the codeword compatible with the parity-check matrix -// Arguments: -// * message - array of 87 bits stored as 11 bytes (MSB first) -// * codeword - array of 174 bits stored as 22 bytes (MSB first) -void encode174(const uint8_t *message, uint8_t *codeword); - -// Compute 12-bit CRC for a sequence of given number of bits -// [IN] message - byte sequence (MSB first) -// [IN] num_bits - number of bits in the sequence -uint16_t ft8_crc(uint8_t *message, int num_bits); diff --git a/ft8/v1/pack.cpp b/ft8/v1/pack.cpp deleted file mode 100644 index f4a10f2..0000000 --- a/ft8/v1/pack.cpp +++ /dev/null @@ -1,367 +0,0 @@ -#include -#include - -#include "pack.h" -#include "../text.h" - -constexpr int32_t NBASE = 37*36*10*27*27*27L; -constexpr int32_t NGBASE = 180*180L; - -// Returns integer encoding of a character (number/digit/space). -// Alphabet: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ +-./?" -// - Digits are encoded as 0..9 -// - Letters a..z are encoded as 10..35 (case insensitive) -// - Space is encoded as 36 -uint8_t nchar(char c) { - if (is_digit(c)) - return (c - '0'); - if (is_letter(c)) - return (to_upper(c) - 'A') + 10; - - switch (c) { - case ' ': return 36; - case '+': return 37; - case '-': return 38; - case '.': return 39; - case '/': return 40; - case '?': return 41; - - default: return 36; // Equal to ' ' - } -} - - -// Pack FT8 source/destination and grid data into 72 bits (stored as 9 bytes) -// [IN] nc1 - first callsign data (28 bits) -// [IN] nc2 - second callsign data (28 bits) -// [IN] ng - grid data (16 bits) -// [OUT] payload - 9 byte array to store the 72 bit payload (MSB first) -void pack3_8bit(uint32_t nc1, uint32_t nc2, uint16_t ng, uint8_t *payload) { - payload[0] = (uint8_t)(nc1 >> 20); - payload[1] = (uint8_t)(nc1 >> 12); - payload[2] = (uint8_t)(nc1 >> 4); - payload[3] = (uint8_t)(nc1 << 4) | (uint8_t)(nc2 >> 24); - payload[4] = (uint8_t)(nc2 >> 16); - payload[5] = (uint8_t)(nc2 >> 8); - payload[6] = (uint8_t)(nc2); - payload[7] = (uint8_t)(ng >> 8); - payload[8] = (uint8_t)(ng); -} - - -// Pack FT8 source/destination and grid data into 72 bits (stored as 12 bytes of 6-bit values) -// (Unused here, included for compatibility with WSJT-X and testing) -// [IN] nc1 - first callsign data (28 bits) -// [IN] nc2 - second callsign data (28 bits) -// [IN] ng - grid data (16 bits) -// [OUT] payload - 12 byte array to store the 72 bit payload (MSB first) -void pack3_6bit(uint32_t nc1, uint32_t nc2, uint16_t ng, uint8_t *payload) { - payload[0] = (nc1 >> 22) & 0x3f; // 6 bits - payload[1] = (nc1 >> 16) & 0x3f; // 6 bits - payload[2] = (nc1 >> 10) & 0x3f; // 6 bits - payload[3] = (nc1 >> 4) & 0x3f; // 6 bits - payload[4] = ((nc1 & 0xf) << 2) | ((nc2 >> 26) & 0x3); // 4+2 bits - payload[5] = (nc2 >> 20) & 0x3f; // 6 bits - payload[6] = (nc2 >> 14) & 0x3f; // 6 bits - payload[7] = (nc2 >> 8) & 0x3f; // 6 bits - payload[8] = (nc2 >> 2) & 0x3f; // 6 bits - payload[9] = ((nc2 & 0x3) << 4) | ((ng >> 12) & 0xf); // 2+4 bits - payload[10] = (ng >> 6) & 0x3f; // 6 bits - payload[11] = (ng >> 0) & 0x3f; // 6 bits -} - - -// Pack a valid callsign into a 28-bit integer. -// Note that callsign points to a portion of text and may not be zero-terminated. -int32_t packcall(const char *callsign, int length) { - if (length > 6) { - return -1; - } - - if (starts_with(callsign, "CQ ")) { - // TODO: support 'CQ nnn' frequency specification - //if (callsign(4:4).ge.'0' .and. callsign(4:4).le.'9' .and. & - // callsign(5:5).ge.'0' .and. callsign(5:5).le.'9' .and. & - // callsign(6:6).ge.'0' .and. callsign(6:6).le.'9') then - // read(callsign(4:6),*) nfreq - // ncall=NBASE + 3 + nfreq - //endif - return NBASE + 1; - } - if (starts_with(callsign, "QRZ ")) { - return NBASE + 2; - } - if (starts_with(callsign, "DE ")) { - return 267796945; - } - - char callsign2[7] = {' ', ' ', ' ', ' ', ' ', ' ', 0}; // 6 spaces with zero terminator - - // Work-around for Swaziland prefix (see WSJT-X code): - if (starts_with(callsign, "3DA0")) { - // callsign='3D0'//callsign(5:6) - memcpy(callsign2, "3D0", 3); - if (length > 4) { - memcpy(callsign2 + 3, callsign + 4, length - 4); - } - } - // Work-around for Guinea prefixes (see WSJT-X code): - else if (starts_with(callsign, "3X") && is_letter(callsign[2])) { - //callsign='Q'//callsign(3:6) - memcpy(callsign2, "Q", 1); - if (length > 2) { - memcpy(callsign2 + 1, callsign + 2, length - 2); - } - } - else { - // Just copy, no modifications needed - // Check for callsigns with 1 symbol prefix - if (!is_digit(callsign[2]) && is_digit(callsign[1])) { - if (length > 5) { - return -1; - } - // Leave one space at the beginning as padding - memcpy(callsign2 + 1, callsign, length); - } - else { - memcpy(callsign2, callsign, length); - } - } - - // Check if the callsign consists of valid characters - if (!is_digit(callsign2[0]) && !is_letter(callsign2[0]) && !is_space(callsign2[0])) - return -3; - if (!is_digit(callsign2[1]) && !is_letter(callsign2[1])) - return -3; - if (!is_digit(callsign2[2])) - return -3; - if (!is_letter(callsign2[3]) && !is_space(callsign2[3])) - return -3; - if (!is_letter(callsign2[4]) && !is_space(callsign2[4])) - return -3; - if (!is_letter(callsign2[5]) && !is_space(callsign2[5])) - return -3; - - // Form a 28 bit integer from callsign parts - int32_t ncall = nchar(callsign2[0]); - ncall = 36*ncall + nchar(callsign2[1]); - ncall = 10*ncall + nchar(callsign2[2]); - ncall = 27*ncall + nchar(callsign2[3]) - 10; - ncall = 27*ncall + nchar(callsign2[4]) - 10; - ncall = 27*ncall + nchar(callsign2[5]) - 10; - - return ncall; -} - - -// Pack a valid grid locator into an integer. -int16_t packgrid(const char *grid) { - printf("Grid = [%s]\n", grid); - int len = strlen(grid); - - if (len == 0) { - // Blank grid is OK - return NGBASE + 1; - } - - // Check for RO, RRR, or 73 in the message field normally used for grid - if (equals(grid, "RO")) { - return NGBASE + 62; - } - if (equals(grid, "RRR")) { - return NGBASE + 63; - } - if (equals(grid, "73")) { - return NGBASE + 64; - } - - // Attempt to parse signal reports (e.g. "-07", "R+20") - char c1 = grid[0]; - int n; - if (c1 == 'R') { - n = dd_to_int(grid + 1, 3); // read(grid(2:4),*,err=30,end=30) n - } - else { - n = dd_to_int(grid, 3); // read(grid,*,err=20,end=20) n - } - - // First, handle signal reports in the original range, -01 to -30 dB - if (n >= -30 && n <= -1) { - if (c1 == 'R') { - return NGBASE + 31 + (-n); - } - else { - return NGBASE + 1 + (-n); - } - } - - char grid4[4]; - memcpy(grid4, grid, 4); - - // TODO: Check for extended-range signal reports: -50 to -31, and 0 to +49 - // if (n >= -50 && n <= 49) { - // if (c1 == 'R') { - // // write(grid,1002) n+50 1002 format('LA',i2.2) - // } - // else { - // // write(grid,1003) n+50 1003 format('KA',i2.2) - // } - // // go to 40 - // } - // else { - // // error - // return -1; - // } - - // Check if the grid locator is properly formatted - if (len != 4) return -1; - if (grid4[0] < 'A' || grid4[0] > 'R') return -1; - if (grid4[1] < 'A' || grid4[1] > 'R') return -1; - if (grid4[2] < '0' || grid4[2] > '9') return -1; - if (grid4[3] < '0' || grid4[3] > '9') return -1; - - // Extract latitude and longitude - int lng = (grid4[0] - 'A') * 20; - lng += (grid4[2] - '0') * 2; - lng = 179 - lng; - - int lat = (grid4[1] - 'A') * 10; - lat += (grid4[3] - '0') * 1; - lat -= 90; - - // Convert latitude and longitude into single number - int16_t ng = (lng + 180) / 2; - ng *= 180; - ng += lat + 90; - - return ng; -} - -// Pack a free-text message into 3 integers (28+28+15 bits) -// NOTE: msg MUST contain at least 13 characters! -// No checking is done. Exactly 13 characters will be processed. -void packtext(const char *msg, int32_t &nc1, int32_t &nc2, int16_t &ng) { - int32_t nc3; - nc1 = nc2 = nc3 = 0; - - // Pack 5 characters (42^5) into 27 bits - for (int i = 0; i < 5; ++i) { // First 5 characters in nc1 - uint8_t j = nchar(msg[i]); // Get character code - nc1 = 42*nc1 + j; - } - - // Pack 5 characters (42^5) into 27 bits - for (int i = 5; i < 10; ++i) { // Characters 6-10 in nc2 - uint8_t j = nchar(msg[i]); // Get character code - nc2 = 42*nc2 + j; - } - - // Pack 3 characters (42^3) into 17 bits - for (int i = 10; i < 13; ++i) { // Characters 11-13 in nc3 - uint8_t j = nchar(msg[i]); // Get character code - nc3 = 42*nc3 + j; - } - - // We now have used 17 bits in nc3. Must move one each to nc1 and nc2. - nc1 <<= 1; - if (nc3 & 0x08000) nc1 |= 1; - nc2 <<= 1; - if (nc3 & 0x10000) nc2 |= 1; - ng = nc3 & 0x7FFF; -} - - -int packmsg(const char *msg, uint8_t *dat) { // , itype, bcontest - // TODO: check what is maximum allowed length? - if (strlen(msg) > 22) { - return -1; - } - - char msg2[23]; // Including zero terminator! - - fmtmsg(msg2, msg); - - //LOG("msg2 = [%s]\n", msg2); - - // TODO: Change 'CQ n ' type messages to 'CQ 00n ' - //if(msg(1:3).eq.'CQ ' .and. msg(4:4).ge.'0' .and. msg(4:4).le.'9' & - // .and. msg(5:5).eq.' ') msg='CQ 00'//msg(4:) - - if (starts_with(msg2, "CQ ")) { - if (msg2[3] == 'D' && msg2[4] == 'X' && is_space(msg2[5])) { - // Change 'CQ DX ' to 'CQ9DX ' - msg2[2] = '9'; - } - else if (is_letter(msg2[3]) && is_letter(msg2[4]) && is_space(msg2[5])) { - // Change 'CQ xy ' type messages to 'E9xy ' - msg2[0] = 'E'; - msg2[1] = '9'; - // Delete the extra space - char *ptr = msg2 + 2; - while (*ptr) { - ptr[0] = ptr[1]; - ++ptr; - } - } - } - - int msg2len = strlen(msg2); - int32_t nc1 = -1; - int32_t nc2 = -1; - int16_t ng = -1; - - // Try to split the message into three space-delimited fields - // by locating spaces and changing them to zero terminators - - // Locate the first delimiter in the message - const char *s1 = strchr(msg2, ' '); - if (s1 != 0) { - int s1len = s1 - msg2; - int s2len; - ++s1; // s1 now points to the second field - - // Locate the second delimiter in the message - const char *s2 = strchr(s1 + 1, ' '); - if (s2 == 0) { - // If the second space is not found, point to the end of string - // to allow for blank grid (third field) - s2 = msg2 + msg2len; - s2len = s2 - s1; - } - else { - s2len = s2 - s1; - ++s2; // s2 now points to the third field - } - - // TODO: process callsign prefixes/suffixes - - // Pack message fields into integers - nc1 = packcall(msg2, s1len); - nc2 = packcall(s1, s2len); - ng = packgrid(s2); - } - - // Check for success in all three fields - if (nc1 < 0 || nc2 < 0 || ng < 0) { - // Treat as plain text message - - // Pad with spaces at the end if necessary - for (int i = msg2len; i < 13; ++i) { - msg2[i] = ' '; - } - msg2[13] = 0; - - printf("Treating as free text\n"); - packtext(msg2, nc1, nc2, ng); - ng += 0x8000; // Set bit 15 (we abuse signed int here) - } - - //LOG("nc1 = %d [%04X], nc2 = %d [%04X], ng = %d\n", nc1, nc1, nc2, nc2, ng); - - // Originally the data was packed in bytes of 6 bits. - // This seems to waste memory unnecessary and complicate the code, so we pack it in 8 bit values. - pack3_8bit((uint32_t)nc1, (uint32_t)nc2, (uint16_t)ng, dat); - //pack3_6bit(nc1, nc2, ng, dat); - - return 0; // Success! -} diff --git a/ft8/v1/pack.h b/ft8/v1/pack.h deleted file mode 100644 index 1bbb77f..0000000 --- a/ft8/v1/pack.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - - -// Pack FT8 text message into 72 bits -// [IN] msg - FT8 message (e.g. "CQ TE5T KN01") -// [OUT] packed - 9 byte array to store the 72 bit payload (MSB first) -int packmsg(const char *msg, uint8_t *dat); diff --git a/ft8/v1/unpack.cpp b/ft8/v1/unpack.cpp deleted file mode 100644 index dc8f24e..0000000 --- a/ft8/v1/unpack.cpp +++ /dev/null @@ -1,216 +0,0 @@ -#include "unpack.h" -#include "../text.h" - -#include - -constexpr uint32_t NBASE = 37L*36L*10L*27L*27L*27L; - - -// convert packed character to ASCII character -// 0..9 a..z space +-./? -char charn(uint8_t c) { - if (c >= 0 && c <= 9) - return '0' + c; - if (c >= 10 && c < 36) - return 'A' + c - 10; - - if (c < 42) - return " +-./?" [c - 36]; - - return ' '; -} - - -// nc is a 28-bit integer, e.g. nc1 or nc2, containing all the -// call sign bits from a packed message. -void unpackcall(uint32_t nc, char *callsign) { - callsign[5] = charn((nc % 27) + 10); // + 10 b/c only alpha+space - nc /= 27; - callsign[4] = charn((nc % 27) + 10); - nc /= 27; - callsign[3] = charn((nc % 27) + 10); - nc /= 27; - callsign[2] = charn(nc % 10); // digit only - nc /= 10; - callsign[1] = charn(nc % 36); // letter or digit - nc /= 36; - callsign[0] = charn(nc); - - callsign[6] = 0; -} - - -// extract maidenhead locator -void unpackgrid(uint16_t ng, char *grid) { - // start of special grid locators for sig strength &c. - constexpr uint16_t NGBASE = 180*180; - - if (ng == NGBASE + 1) { - // Empty grid is allowed - grid[0] = 0; - return; - } - - // Check for signal strength reports (-01 to -30 dB) - if (ng >= NGBASE + 1 && ng < NGBASE + 31) { - int_to_dd(grid, ((NGBASE + 1) - ng), 2); - return; - } - if (ng >= NGBASE + 31 && ng < NGBASE + 62) { - grid[0] = 'R'; - int_to_dd(grid + 1, ((NGBASE + 31) - ng), 2); - return; - } - - // Check for special cases - if (ng == NGBASE + 62) { - strcpy(grid, "RO"); - return; - } - if (ng == NGBASE + 63) { - strcpy(grid, "RRR"); - return; - } - if (ng == NGBASE + 64) { - strcpy(grid, "73"); - return; - } - - // Decode 4-symbol grid - int16_t lat = (int16_t)(ng % 180) - 90; - int16_t lng = ((int16_t)(ng / 180) * 2) - 180; - - grid[0] = 'A' + ((179 - lng) / 20); - grid[1] = 'A' + ((90 + lat) / 10); - grid[2] = '0' + (((179 - lng) % 20) / 2); - grid[3] = '0' + ((90 + lat) % 10); - grid[4] = 0; - - // Check for extended range signal reports - if ((grid[0] == 'K') && (grid[1] == 'A')) { - int sig = dd_to_int(grid + 2, 2) - 50; - int_to_dd(grid, sig, 2); - return; - } - if ((grid[0] == 'L') && (grid[1] == 'A')) { - int sig = dd_to_int(grid + 2, 2) - 50; - grid[0] = 'R'; - int_to_dd(grid + 1, sig, 2); - return; - } -} - - -void unpacktext(uint32_t nc1, uint32_t nc2, uint16_t ng, char *text) { - uint32_t nc3 = (ng & 0x7FFF); - - // Check for bit 0 and copy it to nc3 - if ((nc1 & 1) != 0) - nc3 |= 0x08000; - - if ((nc2 & 1) != 0) - nc3 |= 0x10000; - - nc1 >>= 1; - nc2 >>= 1; - - for (int i = 4; i >= 0; --i) { - text[i] = charn(nc1 % 42); - nc1 /= 42; - } - - for (int i = 9; i >= 5; --i) { - text[i] = charn(nc2 % 42); - nc2 /= 42; - } - - for (int i = 12; i >= 10; --i) { - text[i] = charn(nc3 % 42); - nc3 /= 42; - } - - text[13] = 0; -} - - -// message should have at least 19 bytes allocated (18 characters + zero terminator) -int unpack(const uint8_t *a72, char *message) { - uint32_t nc1, nc2; - uint16_t ng; - - nc1 = (a72[0] << 20); - nc1 |= (a72[1] << 12); - nc1 |= (a72[2] << 4); - nc1 |= (a72[3] >> 4); - - nc2 = ((a72[3] & 0x0F) << 24); - nc2 |= (a72[4] << 16); - nc2 |= (a72[5] << 8); - nc2 |= (a72[6]); - - ng = (a72[7] << 8); - ng |= (a72[8]); - - if (ng & 0x8000) { - unpacktext(nc1, nc2, ng, message); - return 0; - } - - char c2[7]; - char grid[5]; - - if (nc1 == NBASE+1) { - // CQ with standard callsign - unpackcall(nc2, c2); - unpackgrid(ng, grid); - strcpy(message, "CQ "); - strcat(message, c2); - strcat(message, " "); - strcat(message, grid); - return 0; - } - - if (nc1 >= 267649090L && nc1 <= 267698374L) { - // CQ with suffix (e.g. /QRP) - uint32_t n = nc1 - 267649090L; - char sf[4]; - sf[0] = charn(n % 37); - n /= 37; - sf[1] = charn(n % 37); - n /= 37; - sf[2] = charn(n % 37); - - unpackcall(nc2, c2); - unpackgrid(ng, grid); - - strcpy(message, "CQ "); - strcat(message, c2); - strcat(message, "/"); - strcat(message, sf); - strcat(message, " "); - strcat(message, grid); - return 0; - } - - // Standard two-call exchange - char c1[7]; - - unpackcall(nc1, c1); - if (equals(c1, "CQ9DX ")) { - strcpy(c1, "CQ DX"); - } - else if (starts_with(c1, " E9") && is_letter(c1[3]) && is_letter(c1[4]) && is_space(c1[5])) { - strcpy(c1, "CQ "); - c1[5] = 0; - } - unpackcall(nc2, c2); - unpackgrid(ng, grid); - - strcpy(message, c1); - strcat(message, " "); - strcat(message, c2); - strcat(message, " "); - strcat(message, grid); - - return 0; -} diff --git a/ft8/v1/unpack.h b/ft8/v1/unpack.h deleted file mode 100644 index eaa1a99..0000000 --- a/ft8/v1/unpack.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - - -int unpack(const uint8_t *a72, char *message);