@e04/ft8ts 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +69 -0
- package/dist/ft8js.cjs +2119 -0
- package/dist/ft8js.cjs.map +1 -0
- package/dist/ft8js.mjs +2116 -0
- package/dist/ft8js.mjs.map +1 -0
- package/dist/ft8ts.cjs +2119 -0
- package/dist/ft8ts.cjs.map +1 -0
- package/dist/ft8ts.d.ts +36 -0
- package/dist/ft8ts.mjs +2116 -0
- package/dist/ft8ts.mjs.map +1 -0
- package/example/browser/index.html +251 -0
- package/example/decode-ft8-wav.ts +78 -0
- package/example/generate-ft8-wav.ts +82 -0
- package/package.json +53 -0
- package/src/__test__/190227_155815.wav +0 -0
- package/src/__test__/decode.test.ts +117 -0
- package/src/__test__/encode.test.ts +52 -0
- package/src/__test__/test_vectors.ts +221 -0
- package/src/__test__/wav.test.ts +45 -0
- package/src/__test__/waveform.test.ts +28 -0
- package/src/ft8/decode.ts +713 -0
- package/src/ft8/encode.ts +85 -0
- package/src/index.ts +2 -0
- package/src/util/constants.ts +118 -0
- package/src/util/crc.ts +39 -0
- package/src/util/decode174_91.ts +375 -0
- package/src/util/fft.ts +108 -0
- package/src/util/ldpc_tables.ts +91 -0
- package/src/util/pack_jt77.ts +531 -0
- package/src/util/unpack_jt77.ts +237 -0
- package/src/util/wav.ts +129 -0
- package/src/util/waveform.ts +120 -0
package/src/util/fft.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Radix-2 Cooley-Tukey FFT for FT8 decoding.
|
|
3
|
+
* Supports real-to-complex, complex-to-complex, and inverse transforms.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export function fftComplex(re: Float64Array, im: Float64Array, inverse: boolean): void {
|
|
7
|
+
const n = re.length;
|
|
8
|
+
if (n <= 1) return;
|
|
9
|
+
|
|
10
|
+
// Bit-reversal permutation
|
|
11
|
+
let j = 0;
|
|
12
|
+
for (let i = 0; i < n; i++) {
|
|
13
|
+
if (j > i) {
|
|
14
|
+
let tmp = re[i]!;
|
|
15
|
+
re[i] = re[j]!;
|
|
16
|
+
re[j] = tmp;
|
|
17
|
+
tmp = im[i]!;
|
|
18
|
+
im[i] = im[j]!;
|
|
19
|
+
im[j] = tmp;
|
|
20
|
+
}
|
|
21
|
+
let m = n >> 1;
|
|
22
|
+
while (m >= 1 && j >= m) {
|
|
23
|
+
j -= m;
|
|
24
|
+
m >>= 1;
|
|
25
|
+
}
|
|
26
|
+
j += m;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const sign = inverse ? 1 : -1;
|
|
30
|
+
|
|
31
|
+
for (let size = 2; size <= n; size <<= 1) {
|
|
32
|
+
const halfsize = size >> 1;
|
|
33
|
+
const step = (sign * Math.PI) / halfsize;
|
|
34
|
+
const wRe = Math.cos(step);
|
|
35
|
+
const wIm = Math.sin(step);
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < n; i += size) {
|
|
38
|
+
let curRe = 1;
|
|
39
|
+
let curIm = 0;
|
|
40
|
+
for (let k = 0; k < halfsize; k++) {
|
|
41
|
+
const evenIdx = i + k;
|
|
42
|
+
const oddIdx = i + k + halfsize;
|
|
43
|
+
const tRe = curRe * re[oddIdx]! - curIm * im[oddIdx]!;
|
|
44
|
+
const tIm = curRe * im[oddIdx]! + curIm * re[oddIdx]!;
|
|
45
|
+
re[oddIdx] = re[evenIdx]! - tRe;
|
|
46
|
+
im[oddIdx] = im[evenIdx]! - tIm;
|
|
47
|
+
re[evenIdx] = re[evenIdx]! + tRe;
|
|
48
|
+
im[evenIdx] = im[evenIdx]! + tIm;
|
|
49
|
+
const newCurRe = curRe * wRe - curIm * wIm;
|
|
50
|
+
curIm = curRe * wIm + curIm * wRe;
|
|
51
|
+
curRe = newCurRe;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (inverse) {
|
|
57
|
+
for (let i = 0; i < n; i++) {
|
|
58
|
+
re[i]! /= n;
|
|
59
|
+
im[i]! /= n;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Real-to-complex FFT. Input: n real values. Output: n/2+1 complex values
|
|
66
|
+
* stored in re[0..n/2] and im[0..n/2].
|
|
67
|
+
*/
|
|
68
|
+
export function fftReal(input: Float64Array, outRe: Float64Array, outIm: Float64Array): void {
|
|
69
|
+
const n = input.length;
|
|
70
|
+
const half = n >> 1;
|
|
71
|
+
|
|
72
|
+
const re = new Float64Array(half);
|
|
73
|
+
const im = new Float64Array(half);
|
|
74
|
+
for (let i = 0; i < half; i++) {
|
|
75
|
+
re[i] = input[i * 2]!;
|
|
76
|
+
im[i] = input[i * 2 + 1]!;
|
|
77
|
+
}
|
|
78
|
+
fftComplex(re, im, false);
|
|
79
|
+
|
|
80
|
+
outRe[0] = re[0]! + im[0]!;
|
|
81
|
+
outIm[0] = 0;
|
|
82
|
+
outRe[half] = re[0]! - im[0]!;
|
|
83
|
+
outIm[half] = 0;
|
|
84
|
+
|
|
85
|
+
for (let k = 1; k < half; k++) {
|
|
86
|
+
const nk = half - k;
|
|
87
|
+
const eRe = 0.5 * (re[k]! + re[nk]!);
|
|
88
|
+
const eIm = 0.5 * (im[k]! - im[nk]!);
|
|
89
|
+
const angle = (-2 * Math.PI * k) / n;
|
|
90
|
+
const twRe = Math.cos(angle);
|
|
91
|
+
const twIm = Math.sin(angle);
|
|
92
|
+
const oRe = 0.5 * (im[k]! + im[nk]!);
|
|
93
|
+
const oIm = -0.5 * (re[k]! - re[nk]!);
|
|
94
|
+
const toRe = twRe * oRe - twIm * oIm;
|
|
95
|
+
const toIm = twRe * oIm + twIm * oRe;
|
|
96
|
+
outRe[k] = eRe + toRe;
|
|
97
|
+
outIm[k] = eIm + toIm;
|
|
98
|
+
outRe[n - k] = eRe - toRe;
|
|
99
|
+
outIm[n - k] = -(eIm - toIm);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Next power of 2 >= n */
|
|
104
|
+
export function nextPow2(n: number): number {
|
|
105
|
+
let v = 1;
|
|
106
|
+
while (v < n) v <<= 1;
|
|
107
|
+
return v;
|
|
108
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LDPC (174,91) parity check matrix data from ldpc_174_91_c_parity.f90
|
|
3
|
+
*
|
|
4
|
+
* Mn[j] = list of 3 check-node indices (1-based) for bit j (j=0..173)
|
|
5
|
+
* Nm[i] = list of variable-node indices (1-based) for check i (i=0..82), padded with 0
|
|
6
|
+
* nrw[i] = row weight for check i
|
|
7
|
+
* ncw = 3 (column weight – every bit participates in exactly 3 checks)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Mn: 174 rows, each with 3 check-node indices (1-based, from Fortran)
|
|
11
|
+
const MnFlat = [
|
|
12
|
+
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,
|
|
13
|
+
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,
|
|
14
|
+
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,
|
|
15
|
+
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,
|
|
16
|
+
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,
|
|
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,
|
|
18
|
+
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,
|
|
19
|
+
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,
|
|
20
|
+
44, 57, 12, 39, 59, 13, 24, 55, 14, 21, 65, 16, 71, 78, 17, 30, 76, 18, 25, 80, 19, 61, 83, 22,
|
|
21
|
+
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,
|
|
22
|
+
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,
|
|
23
|
+
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,
|
|
24
|
+
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,
|
|
25
|
+
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,
|
|
26
|
+
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
|
+
27, 33, 65, 25, 69, 73, 38, 55, 83, 20, 39, 77, 18, 29, 56, 32, 48, 71, 42, 51, 59, 28, 44, 79,
|
|
28
|
+
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,
|
|
29
|
+
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,
|
|
30
|
+
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,
|
|
31
|
+
17, 25, 82, 20, 30, 31, 12, 67, 68, 23, 75, 80, 27, 32, 62, 24, 69, 75, 19, 21, 71, 34, 53, 61,
|
|
32
|
+
35, 46, 47, 33, 59, 76, 40, 43, 83, 41, 42, 63, 49, 75, 83, 20, 44, 48, 42, 49, 57,
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
// Nm: 83 rows, each with up to 7 variable-node indices (1-based, 0-padded)
|
|
36
|
+
const NmFlat = [
|
|
37
|
+
4, 31, 59, 91, 92, 96, 153, 5, 32, 60, 93, 115, 146, 0, 6, 24, 61, 94, 122, 151, 0, 7, 33, 62, 95,
|
|
38
|
+
96, 143, 0, 8, 25, 63, 83, 93, 96, 148, 6, 32, 64, 97, 126, 138, 0, 5, 34, 65, 78, 98, 107, 154,
|
|
39
|
+
9, 35, 66, 99, 139, 146, 0, 10, 36, 67, 100, 107, 126, 0, 11, 37, 67, 87, 101, 139, 158, 12, 38,
|
|
40
|
+
68, 102, 105, 155, 0, 13, 39, 69, 103, 149, 162, 0, 8, 40, 70, 82, 104, 114, 145, 14, 41, 71, 88,
|
|
41
|
+
102, 123, 156, 15, 42, 59, 106, 123, 159, 0, 1, 33, 72, 106, 107, 157, 0, 16, 43, 73, 108, 141,
|
|
42
|
+
160, 0, 17, 37, 74, 81, 109, 131, 154, 11, 44, 75, 110, 121, 166, 0, 45, 55, 64, 111, 130, 161,
|
|
43
|
+
173, 8, 46, 71, 112, 119, 166, 0, 18, 36, 76, 89, 113, 114, 143, 19, 38, 77, 104, 116, 163, 0, 20,
|
|
44
|
+
47, 70, 92, 138, 165, 0, 2, 48, 74, 113, 128, 160, 0, 21, 45, 78, 83, 117, 121, 151, 22, 47, 58,
|
|
45
|
+
118, 127, 164, 0, 16, 39, 62, 112, 134, 158, 0, 23, 43, 79, 120, 131, 145, 0, 19, 35, 59, 73, 110,
|
|
46
|
+
125, 161, 20, 36, 63, 94, 136, 161, 0, 14, 31, 79, 98, 132, 164, 0, 3, 44, 80, 124, 127, 169, 0,
|
|
47
|
+
19, 46, 81, 117, 135, 167, 0, 7, 49, 58, 90, 100, 105, 168, 12, 50, 61, 118, 119, 144, 0, 13, 51,
|
|
48
|
+
64, 114, 118, 157, 0, 24, 52, 76, 129, 148, 149, 0, 25, 53, 69, 90, 101, 130, 156, 20, 46, 65, 80,
|
|
49
|
+
120, 140, 170, 21, 54, 77, 100, 140, 171, 0, 35, 82, 133, 142, 171, 174, 0, 14, 30, 83, 113, 125,
|
|
50
|
+
170, 0, 4, 29, 68, 120, 134, 173, 0, 1, 4, 52, 57, 86, 136, 152, 26, 51, 56, 91, 122, 137, 168,
|
|
51
|
+
52, 84, 110, 115, 145, 168, 0, 7, 50, 81, 99, 132, 173, 0, 23, 55, 67, 95, 172, 174, 0, 26, 41,
|
|
52
|
+
77, 109, 141, 148, 0, 2, 27, 41, 61, 62, 115, 133, 27, 40, 56, 124, 125, 126, 0, 18, 49, 55, 124,
|
|
53
|
+
141, 167, 0, 6, 33, 85, 108, 116, 156, 0, 28, 48, 70, 85, 105, 129, 158, 9, 54, 63, 131, 147, 155,
|
|
54
|
+
0, 22, 53, 68, 109, 121, 174, 0, 3, 13, 48, 78, 95, 123, 0, 31, 69, 133, 150, 155, 169, 0, 12, 43,
|
|
55
|
+
66, 89, 97, 135, 159, 5, 39, 75, 102, 136, 167, 0, 2, 54, 86, 101, 135, 164, 0, 15, 56, 87, 108,
|
|
56
|
+
119, 171, 0, 10, 44, 82, 91, 111, 144, 149, 23, 34, 71, 94, 127, 153, 0, 11, 49, 88, 92, 142, 157,
|
|
57
|
+
0, 29, 34, 87, 97, 147, 162, 0, 30, 50, 60, 86, 137, 142, 162, 10, 53, 66, 84, 112, 128, 165, 22,
|
|
58
|
+
57, 85, 93, 140, 159, 0, 28, 32, 72, 103, 132, 166, 0, 28, 29, 84, 88, 117, 143, 150, 1, 26, 45,
|
|
59
|
+
80, 128, 147, 0, 17, 27, 89, 103, 116, 153, 0, 51, 57, 98, 163, 165, 172, 0, 21, 37, 73, 138, 152,
|
|
60
|
+
169, 0, 16, 47, 76, 130, 137, 154, 0, 3, 24, 30, 72, 104, 139, 0, 9, 40, 90, 106, 134, 151, 0, 15,
|
|
61
|
+
58, 60, 74, 111, 150, 163, 18, 42, 79, 144, 146, 152, 0, 25, 38, 65, 99, 122, 160, 0, 17, 42, 75,
|
|
62
|
+
129, 170, 172, 0,
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const nrwData = [
|
|
66
|
+
7, 6, 6, 6, 7, 6, 7, 6, 6, 7, 6, 6, 7, 7, 6, 6, 6, 7, 6, 7, 6, 7, 6, 6, 6, 7, 6, 6, 6, 7, 6, 6, 6,
|
|
67
|
+
6, 7, 6, 6, 6, 7, 7, 6, 6, 6, 6, 7, 7, 6, 6, 6, 6, 7, 6, 6, 6, 7, 6, 6, 6, 6, 7, 6, 6, 6, 7, 6, 6,
|
|
68
|
+
6, 7, 7, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6,
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
export const ncw = 3;
|
|
72
|
+
|
|
73
|
+
/** Mn[j] = check indices (0-based) for bit j (0..173). Each entry has exactly 3 elements. */
|
|
74
|
+
export const Mn: number[][] = [];
|
|
75
|
+
for (let j = 0; j < 174; j++) {
|
|
76
|
+
Mn.push([MnFlat[j * 3]! - 1, MnFlat[j * 3 + 1]! - 1, MnFlat[j * 3 + 2]! - 1]);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Nm[i] = bit indices (0-based) for check i (0..82). Variable length (nrw[i] elements). */
|
|
80
|
+
export const Nm: number[][] = [];
|
|
81
|
+
/** nrw[i] = row weight for check i */
|
|
82
|
+
export const nrw: number[] = nrwData.slice();
|
|
83
|
+
|
|
84
|
+
for (let i = 0; i < 83; i++) {
|
|
85
|
+
const row: number[] = [];
|
|
86
|
+
for (let k = 0; k < 7; k++) {
|
|
87
|
+
const v = NmFlat[i * 7 + k]!;
|
|
88
|
+
if (v !== 0) row.push(v - 1);
|
|
89
|
+
}
|
|
90
|
+
Nm.push(row);
|
|
91
|
+
}
|