Cleanup of code

pull/12/merge
Karlis Goba 2021-08-12 14:32:49 +03:00
rodzic 7d57d9e855
commit 89c1cd90ec
25 zmienionych plików z 650 dodań i 1347 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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 "/

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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 "/

Wyświetl plik

@ -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

Wyświetl plik

@ -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}, {0x26, 0x44, 0xeb, 0xad, 0xeb, 0x44, 0xb9, 0x46, 0x7d, 0x1f, 0x42, 0xc0},
{0x60, 0x8c, 0xc8, 0x57, 0x59, 0x4b, 0xfb, 0xb5, 0x5d, 0x69, 0x60, 0x00}}; {0x60, 0x8c, 0xc8, 0x57, 0x59, 0x4b, 0xfb, 0xb5, 0x5d, 0x69, 0x60, 0x00}};
// Column order (permutation) in which the bits in codeword are stored // Each row describes one LDPC parity check.
// (Not really used in FT8 v2 - instead the Nm, Mn and generator matrices are already permuted) // Each number is an index into the codeword (1-origin).
const uint8_t kFT8_LDPC_column_order[FT8_N] = { // The codeword bits mentioned in each row must XOR to zero.
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.
const uint8_t kFT8_LDPC_Nm[FT8_M][7] = { const uint8_t kFT8_LDPC_Nm[FT8_M][7] = {
{4, 31, 59, 91, 92, 96, 153}, {4, 31, 59, 91, 92, 96, 153},
{5, 32, 60, 93, 115, 146, 0}, {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}, {25, 38, 65, 99, 122, 160, 0},
{17, 42, 75, 129, 170, 172, 0}}; {17, 42, 75, 129, 170, 172, 0}};
// Mn from WSJT-X's bpdecode174.f90. // Each row corresponds to a 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.
// the numbers indicate which three parity
// checks (rows in Nm) refer to the codeword bit.
// 1-origin. // 1-origin.
const uint8_t kFT8_LDPC_Mn[FT8_N][3] = { const uint8_t kFT8_LDPC_Mn[FT8_N][3] = {
{16, 45, 73}, {16, 45, 73},

Wyświetl plik

@ -158,8 +158,7 @@ void extract_likelihood(const waterfall_t *power, const candidate_t *cand, float
float inv_n = 1.0f / FT8_N; float inv_n = 1.0f / FT8_N;
float variance = (sum2 - (sum * sum * inv_n)) * inv_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) // Normalize log174 distribution and scale it with experimentally found coefficient
// Seems to be 2.83 = sqrt(8). Experimentally sqrt(32) works better.
float norm_factor = sqrtf(32.0f / variance); float norm_factor = sqrtf(32.0f / variance);
for (int i = 0; i < FT8_N; ++i) 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 // Extract CRC and check it
status->crc_extracted = extract_crc(a91); 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? // TODO: not sure why the zeroing of message is needed and also why CRC over 96-14 bits?
a91[9] &= 0xF8; a91[9] &= 0xF8;
a91[10] = 0; a91[10] &= 0x00;
a91[11] = 0;
status->crc_calculated = ft8_crc(a91, 96 - 14); status->crc_calculated = ft8_crc(a91, 96 - 14);
if (status->crc_extracted != status->crc_calculated) if (status->crc_extracted != status->crc_calculated)

Wyświetl plik

@ -15,25 +15,13 @@ uint8_t parity8(uint8_t x)
// Encode a 91-bit message and return a 174-bit codeword. // Encode a 91-bit message and return a 174-bit codeword.
// The generator matrix has dimensions (87,87). // The generator matrix has dimensions (87,87).
// The code is a (174,91) regular ldpc code with column weight 3. // The code is a (174,91) regular LDPC code with column weight 3.
// The code was generated using the PEG algorithm.
// Arguments: // Arguments:
// [IN] message - array of 91 bits stored as 12 bytes (MSB first) // [IN] message - array of 91 bits stored as 12 bytes (MSB first)
// [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first) // [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first)
void encode174(const uint8_t *message, uint8_t *codeword) void encode174(const uint8_t *message, uint8_t *codeword)
{ {
// Here we don't generate the generator bit matrix as in WSJT-X implementation // This implementation accesses the generator bits straight from the packed binary representation in kFT8_LDPC_generator
// 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");
// Fill the codeword with message and zeros, as we will only update binary ones later // Fill the codeword with message and zeros, as we will only update binary ones later
for (int j = 0; j < FT8_N_BYTES; ++j) for (int j = 0; j < FT8_N_BYTES; ++j)

Wyświetl plik

@ -20,8 +20,6 @@
static int ldpc_check(uint8_t codeword[]); static int ldpc_check(uint8_t codeword[]);
static float fast_tanh(float x); static float fast_tanh(float x);
static float fast_atanh(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[], // 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[] // 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)); float b = (945.0f + x2 * (-1050.0f + x2 * 225.0f));
return a / b; 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;
}

Wyświetl plik

@ -34,14 +34,9 @@ int32_t pack28(const char *callsign)
int nnum = 0, nlet = 0; int nnum = 0, nlet = 0;
// TODO: // 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 // 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] = {' ', ' ', ' ', ' ', ' ', ' '}; 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 && (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) (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 // This is a standard callsign
int32_t n28 = i0; int32_t n28 = i0;
n28 = n28 * 36 + i1; n28 = n28 * 36 + i1;
@ -92,20 +86,14 @@ int32_t pack28(const char *callsign)
n28 = n28 * 27 + i3; n28 = n28 * 27 + i3;
n28 = n28 * 27 + i4; n28 = n28 * 27 + i4;
n28 = n28 * 27 + i5; n28 = n28 * 27 + i5;
//printf("Pack28: n28=%d (%04xh)\n", n28, n28);
return NTOKENS + MAX22 + n28; return NTOKENS + MAX22 + n28;
} }
//char text[13]; //char text[13];
//if (length > 13) return -1; //if (length > 13) return -1;
// TODO: // TODO:
// Treat this as a nonstandard callsign: compute its 22-bit hash // 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; return -1;
} }
@ -129,12 +117,6 @@ bool chkcall(const char *call, char *bc)
return false; return false;
// TODO: implement suffix parsing (or rework?) // 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; return true;
} }
@ -160,7 +142,6 @@ uint16_t packgrid(const char *grid4)
in_range(grid4[1], 'A', 'R') && in_range(grid4[1], 'A', 'R') &&
is_digit(grid4[2]) && is_digit(grid4[3])) is_digit(grid4[2]) && is_digit(grid4[3]))
{ {
//if (w(3).eq.'R ') ir=1
uint16_t igrid4 = (grid4[0] - 'A'); uint16_t igrid4 = (grid4[0] - 'A');
igrid4 = igrid4 * 18 + (grid4[1] - 'A'); igrid4 = igrid4 * 18 + (grid4[1] - 'A');
igrid4 = igrid4 * 10 + (grid4[2] - '0'); 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 uint8_t i3 = 1; // No suffix or /R
// TODO: check for suffixes // 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 // Shift in ipa and ipb bits into n28a and n28b
n28a <<= 1; // ipa = 0 n28a <<= 1; // ipa = 0
n28b <<= 1; // ipb = 0 n28b <<= 1; // ipb = 0
// Pack into (28 + 1) + (28 + 1) + (1 + 15) + 3 bits // 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[0] = (n28a >> 21);
b77[1] = (n28a >> 13); b77[1] = (n28a >> 13);
b77[2] = (n28a >> 5); b77[2] = (n28a >> 5);
@ -320,10 +295,8 @@ int pack77(const char *msg, uint8_t *c77)
// TODO: // TODO:
// Check 0.5 (telemetry) // 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) // Check Type 4 (One nonstandard call and one hashed call)
// pack77_4(nwords,w,i3,n3,c77)
// Default to free text // Default to free text
// i3=0 n3=0 // i3=0 n3=0

Wyświetl plik

@ -3,7 +3,6 @@
#include <string.h> #include <string.h>
//#define NBASE (uint32_t)(37L*36L*10L*27L*27L*27L)
#define MAX22 ((uint32_t)4194304L) #define MAX22 ((uint32_t)4194304L)
#define NTOKENS ((uint32_t)2063592L) #define NTOKENS ((uint32_t)2063592L)
#define MAXGRID4 ((uint16_t)32400L) #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) if (n28 < MAX22)
{ {
// This is a 22-bit hash of a result // This is a 22-bit hash of a result
//call hash22(n22,c13) !Retrieve result from hash table
// TODO: implement // TODO: implement
// strcpy(result, "<...>"); strcpy(result, "<...>");
result[0] = '<'; // result[0] = '<';
int_to_dd(result + 1, n28, 7, false); // int_to_dd(result + 1, n28, 7, false);
result[8] = '>'; // result[8] = '>';
result[9] = '\0'; // result[9] = '\0';
return 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; uint8_t ir;
// Extract packed fields // 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[0] << 21);
n28a |= (a77[1] << 13); n28a |= (a77[1] << 13);
n28a |= (a77[2] << 5); 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); dst[1] = 'A' + (n % 18);
n /= 18; n /= 18;
dst[0] = 'A' + (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; // if (ir > 0 && strncmp(field1, "CQ", 2) == 0) return -1;
} }
else 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); 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; // 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 //by KD8CEC
int unpack_nonstandard(const uint8_t *a77, char *field1, char *field2, char *field3) 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; uint32_t n12, iflip, nrpt, icq;
uint64_t n58; uint64_t n58;
n12 = (a77[0] << 4); //11 ~4 : 8 n12 = (a77[0] << 4); //11 ~4 : 8

Wyświetl plik

@ -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
};

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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);

Wyświetl plik

@ -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!
}

Wyświetl plik

@ -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);

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -1,6 +0,0 @@
#pragma once
#include <stdint.h>
int unpack(const uint8_t *a72, char *message);