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},
{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},

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

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