kopia lustrzana https://github.com/kgoba/ft8_lib
Cleanup of code
rodzic
7d57d9e855
commit
89c1cd90ec
|
@ -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
|
|
@ -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 "/
|
|
@ -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 "<message>"'
|
||||
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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 <callsign>'
|
||||
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
|
|
@ -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 <callsign>'
|
||||
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
|
|
@ -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
|
|
@ -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 "/
|
|
@ -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 <call_std>'
|
||||
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
|
|
@ -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},
|
||||
|
|
|
@ -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)
|
||||
|
|
16
ft8/encode.c
16
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)
|
||||
|
|
56
ft8/ldpc.c
56
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;
|
||||
}
|
||||
|
|
27
ft8/pack.c
27
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
|
||||
|
|
21
ft8/unpack.c
21
ft8/unpack.c
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
//#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
|
||||
|
|
308
ft8/v1/arrays.h
308
ft8/v1/arrays.h
|
@ -1,308 +0,0 @@
|
|||
#include <stdint.h>
|
||||
|
||||
// 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
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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);
|
367
ft8/v1/pack.cpp
367
ft8/v1/pack.cpp
|
@ -1,367 +0,0 @@
|
|||
#include <string.h>
|
||||
#include <cstdio>
|
||||
|
||||
#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!
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// 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);
|
|
@ -1,216 +0,0 @@
|
|||
#include "unpack.h"
|
||||
#include "../text.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
int unpack(const uint8_t *a72, char *message);
|
Ładowanie…
Reference in New Issue