@e04/ft8ts 0.0.2 → 0.0.6
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/README.md +33 -24
- package/dist/ft8ts.cjs +130 -11
- package/dist/ft8ts.cjs.map +1 -1
- package/dist/ft8ts.d.ts +57 -1
- package/dist/ft8ts.mjs +130 -12
- package/dist/ft8ts.mjs.map +1 -1
- package/example/browser/index.html +4 -4
- package/package.json +51 -51
- package/src/ft8/decode.ts +15 -4
- package/src/index.ts +1 -0
- package/src/util/hashcall.ts +110 -0
- package/src/util/unpack_jt77.ts +17 -8
package/README.md
CHANGED
|
@@ -6,7 +6,9 @@ FT8 encoder and decoder in TypeScript. A port of the Fortran implementation from
|
|
|
6
6
|
|
|
7
7
|
## Overview
|
|
8
8
|
|
|
9
|
-
FT8 is a digital amateur radio mode designed for weak-signal communication
|
|
9
|
+
FT8 is a digital amateur radio mode designed for weak-signal communication, developed by Joe Taylor (K1JT) and Steve Franke (K9AN).
|
|
10
|
+
|
|
11
|
+
This library provides pure TypeScript implementations of both encoding and decoding, suitable for use in Node.js or the browser.
|
|
10
12
|
|
|
11
13
|
## Demo
|
|
12
14
|
|
|
@@ -32,27 +34,27 @@ npx tsx example/decode-ft8-wav.ts ./src/__test__/190227_155815.wav [--low 200] [
|
|
|
32
34
|
|
|
33
35
|
The benchmark below was compiled with reference to [Comparing PyFT8 with WSJT-x and FT8_lib](https://www.reddit.com/r/amateurradio/comments/1qt27ss/comparing_pyft8_with_wsjtx_and_ft8_lib/).
|
|
34
36
|
|
|
35
|
-
| Call a | Call b | Message | WSJT-x (
|
|
36
|
-
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
37
|
-
| W1FC | F5BZB | -8 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
38
|
-
| WM3PEN | EA6VQ | -9 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
39
|
-
| CQ | F5RXL | IN94 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
40
|
-
| N1JFU | EA6EE | R-07 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
41
|
-
| A92EE | F5PSR | -14 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
42
|
-
| K1BZM | EA3GP | -9 | ☑️ | ☑️ | | ☑️ | ☑️ | ☑️ |
|
|
43
|
-
| W0RSJ | EA3BMU | RR73 | ☑️ | ☑️ | | ☑️ | ☑️ | ☑️ |
|
|
44
|
-
| K1JT | HA0DU | KN07 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
45
|
-
| W1DIG | SV9CVY | -14 | ☑️ | ☑️ | | ☑️ | ☑️ | ☑️ |
|
|
46
|
-
| K1JT | EA3AGB | -15 | ☑️ | ☑️ | | ☑️ | ☑️ | ☑️ |
|
|
47
|
-
| XE2X | HA2NP | RR73 | ☑️ | ☑️ | ☑️ | | | ☑️ |
|
|
48
|
-
| N1PJT | HB9CQK | -10 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
49
|
-
| K1BZM | EA3CJ | JN01 | ☑️ | | | | | |
|
|
50
|
-
| KD2UGC | F6GCP | R-23 | ☑️ | | | | | |
|
|
51
|
-
| WA2FZW | DL5AXX | RR73 | | | | | | |
|
|
52
|
-
| N1API | HA6FQ | -23 | | | | | ☑️ | ☑️ |
|
|
53
|
-
| N1API | F2VX | 73 | | | | | | |
|
|
54
|
-
| K1JT | HA5WA | 73 | | | | | ☑️ | ☑️ |
|
|
55
|
-
| CQ | EA2BFM | IN83 | | | | | | |
|
|
37
|
+
| Call a | Call b | Message | WSJT-x(default) | WSJT-x (fast) | [PyFT8](https://github.com/G1OJS/PyFT8) | [ft8_lib](https://github.com/kgoba/ft8_lib) | ft8ts (depth=1) | ft8ts (depth=2) | ft8ts (depth=3) |
|
|
38
|
+
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
39
|
+
| W1FC | F5BZB | -8 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
40
|
+
| WM3PEN | EA6VQ | -9 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
41
|
+
| CQ | F5RXL | IN94 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
42
|
+
| N1JFU | EA6EE | R-07 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
43
|
+
| A92EE | F5PSR | -14 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
44
|
+
| K1BZM | EA3GP | -9 | ☑️ | ☑️ | ☑️ | | ☑️ | ☑️ | ☑️ |
|
|
45
|
+
| W0RSJ | EA3BMU | RR73 | ☑️ | ☑️ | ☑️ | | ☑️ | ☑️ | ☑️ |
|
|
46
|
+
| K1JT | HA0DU | KN07 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
47
|
+
| W1DIG | SV9CVY | -14 | ☑️ | ☑️ | ☑️ | | ☑️ | ☑️ | ☑️ |
|
|
48
|
+
| K1JT | EA3AGB | -15 | ☑️ | ☑️ | ☑️ | | ☑️ | ☑️ | ☑️ |
|
|
49
|
+
| XE2X | HA2NP | RR73 | ☑️ | ☑️ | ☑️ | ☑️ | | | ☑️ |
|
|
50
|
+
| N1PJT | HB9CQK | -10 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
51
|
+
| K1BZM | EA3CJ | JN01 | ☑️ | ☑️ | | | | | |
|
|
52
|
+
| KD2UGC | F6GCP | R-23 | ☑️ | ☑️ | | | | | |
|
|
53
|
+
| WA2FZW | DL5AXX | RR73 | ☑️ | | | | | | |
|
|
54
|
+
| N1API | HA6FQ | -23 | ☑️ | | | | | ☑️ | ☑️ |
|
|
55
|
+
| N1API | F2VX | 73 | ☑️ | | | | | | |
|
|
56
|
+
| K1JT | HA5WA | 73 | ☑️ | | | | | ☑️ | ☑️ |
|
|
57
|
+
| CQ | EA2BFM | IN83 | ☑️ | | | | | | |
|
|
56
58
|
|
|
57
59
|
At its maximum depth mode (Depth 3), it successfully decodes 14 messages, outperforming both `PyFT8` (12) and `FT8_lib` (8), and matching the total message count of `WSJT-x FAST mode`.
|
|
58
60
|
|
|
@@ -65,7 +67,7 @@ At its maximum depth mode (Depth 3), it successfully decodes 14 messages, outper
|
|
|
65
67
|
### API
|
|
66
68
|
|
|
67
69
|
```typescript
|
|
68
|
-
import { encodeFT8, decodeFT8 } from "@e04/ft8ts";
|
|
70
|
+
import { encodeFT8, decodeFT8, HashCallBook } from "@e04/ft8ts";
|
|
69
71
|
|
|
70
72
|
// Encode a message to audio samples (Float32Array)
|
|
71
73
|
const samples = encodeFT8("CQ JK1IFA PM95", {
|
|
@@ -73,11 +75,17 @@ const samples = encodeFT8("CQ JK1IFA PM95", {
|
|
|
73
75
|
baseFrequency: 1000,
|
|
74
76
|
});
|
|
75
77
|
|
|
78
|
+
// Create a HashCallBook to resolve hashed callsigns.
|
|
79
|
+
// Reuse the same instance across multiple decode calls so that
|
|
80
|
+
// callsigns learned from earlier frames can resolve hashes in later ones.
|
|
81
|
+
const book = new HashCallBook();
|
|
82
|
+
|
|
76
83
|
// Decode audio samples to messages
|
|
77
84
|
const decoded = decodeFT8(samples, 12000, {
|
|
78
85
|
freqLow: 200,
|
|
79
86
|
freqHigh: 3000,
|
|
80
87
|
depth: 2,
|
|
88
|
+
hashCallBook: book,
|
|
81
89
|
});
|
|
82
90
|
|
|
83
91
|
for (const d of decoded) {
|
|
@@ -94,10 +102,11 @@ for (const d of decoded) {
|
|
|
94
102
|
| `syncMin` | 1.2 | Minimum sync threshold |
|
|
95
103
|
| `depth` | 2 | Decoding depth: 1=fast BP only, 2=BP+OSD, 3=deep |
|
|
96
104
|
| `maxCandidates` | 300 | Maximum candidates to process |
|
|
105
|
+
| `hashCallBook` | — | `HashCallBook` instance for resolving hashed callsigns |
|
|
97
106
|
|
|
98
107
|
## ToDo
|
|
99
108
|
|
|
100
|
-
- [ ]
|
|
109
|
+
- [ ] FT4 Support
|
|
101
110
|
|
|
102
111
|
## Build
|
|
103
112
|
|
package/dist/ft8ts.cjs
CHANGED
|
@@ -686,7 +686,7 @@ function bitsToUint(bits, start, len) {
|
|
|
686
686
|
}
|
|
687
687
|
return val;
|
|
688
688
|
}
|
|
689
|
-
function unpack28(n28) {
|
|
689
|
+
function unpack28(n28, book) {
|
|
690
690
|
if (n28 < 0 || n28 >= 268435456)
|
|
691
691
|
return { call: "", success: false };
|
|
692
692
|
if (n28 === 0)
|
|
@@ -700,7 +700,6 @@ function unpack28(n28) {
|
|
|
700
700
|
return { call: `CQ ${nqsy.toString().padStart(3, "0")}`, success: true };
|
|
701
701
|
}
|
|
702
702
|
if (n28 >= 1003 && n28 < NTOKENS) {
|
|
703
|
-
// CQ with 4-letter directed call
|
|
704
703
|
let m = n28 - 1003;
|
|
705
704
|
let chars = "";
|
|
706
705
|
for (let i = 3; i >= 0; i--) {
|
|
@@ -714,7 +713,10 @@ function unpack28(n28) {
|
|
|
714
713
|
return { call: "CQ", success: true };
|
|
715
714
|
}
|
|
716
715
|
if (n28 >= NTOKENS && n28 < NTOKENS + MAX22) {
|
|
717
|
-
|
|
716
|
+
const n22 = n28 - NTOKENS;
|
|
717
|
+
const resolved = book?.lookup22(n22);
|
|
718
|
+
if (resolved)
|
|
719
|
+
return { call: `<${resolved}>`, success: true };
|
|
718
720
|
return { call: "<...>", success: true };
|
|
719
721
|
}
|
|
720
722
|
// Standard callsign
|
|
@@ -794,8 +796,11 @@ function unpackText77(bits71) {
|
|
|
794
796
|
}
|
|
795
797
|
/**
|
|
796
798
|
* Unpack a 77-bit FT8 message into a human-readable string.
|
|
799
|
+
*
|
|
800
|
+
* When a {@link HashCallBook} is provided, hashed callsigns are resolved from
|
|
801
|
+
* the book, and newly decoded standard callsigns are saved into it.
|
|
797
802
|
*/
|
|
798
|
-
function unpack77(bits77) {
|
|
803
|
+
function unpack77(bits77, book) {
|
|
799
804
|
const n3 = bitsToUint(bits77, 71, 3);
|
|
800
805
|
const i3 = bitsToUint(bits77, 74, 3);
|
|
801
806
|
if (i3 === 0 && n3 === 0) {
|
|
@@ -813,8 +818,8 @@ function unpack77(bits77) {
|
|
|
813
818
|
const ipb = bits77[57];
|
|
814
819
|
const ir = bits77[58];
|
|
815
820
|
const igrid4 = bitsToUint(bits77, 59, 15);
|
|
816
|
-
const { call: call1, success: ok1 } = unpack28(n28a);
|
|
817
|
-
const { call: call2Raw, success: ok2 } = unpack28(n28b);
|
|
821
|
+
const { call: call1, success: ok1 } = unpack28(n28a, book);
|
|
822
|
+
const { call: call2Raw, success: ok2 } = unpack28(n28b, book);
|
|
818
823
|
if (!ok1 || !ok2)
|
|
819
824
|
return { msg: "", success: false };
|
|
820
825
|
let c1 = call1;
|
|
@@ -832,6 +837,9 @@ function unpack77(bits77) {
|
|
|
832
837
|
c2 += "/R";
|
|
833
838
|
if (ipb === 1 && i3 === 2 && c2.length >= 3)
|
|
834
839
|
c2 += "/P";
|
|
840
|
+
// Save the "from" call (call_2) into the hash book
|
|
841
|
+
if (book && c2.length >= 3)
|
|
842
|
+
book.save(c2);
|
|
835
843
|
}
|
|
836
844
|
if (igrid4 <= MAXGRID4) {
|
|
837
845
|
const { grid, success: gridOk } = toGrid4(igrid4);
|
|
@@ -864,6 +872,7 @@ function unpack77(bits77) {
|
|
|
864
872
|
}
|
|
865
873
|
if (i3 === 4) {
|
|
866
874
|
// Type 4: One nonstandard call
|
|
875
|
+
const n12 = bitsToUint(bits77, 0, 12);
|
|
867
876
|
let n58 = 0n;
|
|
868
877
|
for (let i = 0; i < 58; i++) {
|
|
869
878
|
n58 = n58 * 2n + BigInt(bits77[12 + i] ?? 0);
|
|
@@ -871,7 +880,6 @@ function unpack77(bits77) {
|
|
|
871
880
|
const iflip = bits77[70];
|
|
872
881
|
const nrpt = bitsToUint(bits77, 71, 2);
|
|
873
882
|
const icq = bits77[73];
|
|
874
|
-
// Decode n58 to 11-char string using C38 alphabet
|
|
875
883
|
const c11chars = [];
|
|
876
884
|
let remain = n58;
|
|
877
885
|
for (let i = 10; i >= 0; i--) {
|
|
@@ -880,12 +888,15 @@ function unpack77(bits77) {
|
|
|
880
888
|
c11chars.unshift(C38[j] ?? " ");
|
|
881
889
|
}
|
|
882
890
|
const c11 = c11chars.join("").trim();
|
|
883
|
-
const
|
|
891
|
+
const resolved = book?.lookup12(n12);
|
|
892
|
+
const call3 = resolved ? `<${resolved}>` : "<...>";
|
|
884
893
|
let call1;
|
|
885
894
|
let call2;
|
|
886
895
|
if (iflip === 0) {
|
|
887
896
|
call1 = call3;
|
|
888
897
|
call2 = c11;
|
|
898
|
+
if (book)
|
|
899
|
+
book.save(c11);
|
|
889
900
|
}
|
|
890
901
|
else {
|
|
891
902
|
call1 = c11;
|
|
@@ -920,6 +931,7 @@ function decode(samples, sampleRate = SAMPLE_RATE, options = {}) {
|
|
|
920
931
|
const syncmin = options.syncMin ?? 1.2;
|
|
921
932
|
const depth = options.depth ?? 2;
|
|
922
933
|
const maxCandidates = options.maxCandidates ?? 300;
|
|
934
|
+
const book = options.hashCallBook;
|
|
923
935
|
// Resample to 12000 Hz if needed
|
|
924
936
|
let dd;
|
|
925
937
|
if (sampleRate === SAMPLE_RATE) {
|
|
@@ -944,7 +956,7 @@ function decode(samples, sampleRate = SAMPLE_RATE, options = {}) {
|
|
|
944
956
|
const decoded = [];
|
|
945
957
|
const seenMessages = new Set();
|
|
946
958
|
for (const cand of candidates) {
|
|
947
|
-
const result = ft8b(dd, cxRe, cxIm, cand.freq, cand.dt, sbase, depth);
|
|
959
|
+
const result = ft8b(dd, cxRe, cxIm, cand.freq, cand.dt, sbase, depth, book);
|
|
948
960
|
if (!result)
|
|
949
961
|
continue;
|
|
950
962
|
if (seenMessages.has(result.msg))
|
|
@@ -1128,7 +1140,7 @@ function computeBaseline(savg, nfa, nfb, df, nh1) {
|
|
|
1128
1140
|
}
|
|
1129
1141
|
return sbase;
|
|
1130
1142
|
}
|
|
1131
|
-
function ft8b(
|
|
1143
|
+
function ft8b(_dd0, cxRe, cxIm, f1, xdt, _sbase, depth, book) {
|
|
1132
1144
|
const NFFT2 = 3200;
|
|
1133
1145
|
const NP2 = 2812;
|
|
1134
1146
|
const fs2 = SAMPLE_RATE / NDOWN;
|
|
@@ -1336,7 +1348,7 @@ function ft8b(dd0, cxRe, cxIm, f1, xdt, _sbase, depth) {
|
|
|
1336
1348
|
if (i3v === 0 && n3v === 2)
|
|
1337
1349
|
return null;
|
|
1338
1350
|
// Unpack
|
|
1339
|
-
const { msg, success } = unpack77(message77);
|
|
1351
|
+
const { msg, success } = unpack77(message77, book);
|
|
1340
1352
|
if (!success || msg.trim().length === 0)
|
|
1341
1353
|
return null;
|
|
1342
1354
|
// Estimate SNR
|
|
@@ -2203,6 +2215,113 @@ function encode(msg, options = {}) {
|
|
|
2203
2215
|
return generateFT8Waveform(encodeMessage(msg), options);
|
|
2204
2216
|
}
|
|
2205
2217
|
|
|
2218
|
+
/**
|
|
2219
|
+
* Hash call table – TypeScript port of the hash call storage from packjt77.f90
|
|
2220
|
+
*
|
|
2221
|
+
* In FT8, nonstandard callsigns are transmitted as hashes (10-, 12-, or 22-bit).
|
|
2222
|
+
* When a full callsign is decoded from a standard message, it is stored in this
|
|
2223
|
+
* table so that future hashed references to it can be resolved.
|
|
2224
|
+
*
|
|
2225
|
+
* Mirrors Fortran: save_hash_call, hash10, hash12, hash22, ihashcall
|
|
2226
|
+
*/
|
|
2227
|
+
const MAGIC = 47055833459n;
|
|
2228
|
+
const MAX_HASH22_ENTRIES = 1000;
|
|
2229
|
+
function ihashcall(c0, m) {
|
|
2230
|
+
const s = c0.padEnd(11, " ").slice(0, 11).toUpperCase();
|
|
2231
|
+
let n8 = 0n;
|
|
2232
|
+
for (let i = 0; i < 11; i++) {
|
|
2233
|
+
const j = C38.indexOf(s[i] ?? " ");
|
|
2234
|
+
n8 = 38n * n8 + BigInt(j < 0 ? 0 : j);
|
|
2235
|
+
}
|
|
2236
|
+
const prod = BigInt.asUintN(64, MAGIC * n8);
|
|
2237
|
+
return Number(prod >> BigInt(64 - m)) & ((1 << m) - 1);
|
|
2238
|
+
}
|
|
2239
|
+
/**
|
|
2240
|
+
* Maintains a callsign ↔ hash lookup table for resolving hashed FT8 callsigns.
|
|
2241
|
+
*
|
|
2242
|
+
* Usage:
|
|
2243
|
+
* ```ts
|
|
2244
|
+
* const book = new HashCallBook();
|
|
2245
|
+
* const decoded = decodeFT8(samples, sampleRate, { hashCallBook: book });
|
|
2246
|
+
* // `book` now contains callsigns learned from decoded messages.
|
|
2247
|
+
* // Subsequent calls reuse the same book to resolve hashed callsigns:
|
|
2248
|
+
* const decoded2 = decodeFT8(samples2, sampleRate, { hashCallBook: book });
|
|
2249
|
+
* ```
|
|
2250
|
+
*
|
|
2251
|
+
* You can also pre-populate the book with known callsigns:
|
|
2252
|
+
* ```ts
|
|
2253
|
+
* book.save("W9XYZ");
|
|
2254
|
+
* book.save("PJ4/K1ABC");
|
|
2255
|
+
* ```
|
|
2256
|
+
*/
|
|
2257
|
+
class HashCallBook {
|
|
2258
|
+
calls10 = new Map();
|
|
2259
|
+
calls12 = new Map();
|
|
2260
|
+
hash22Entries = [];
|
|
2261
|
+
/**
|
|
2262
|
+
* Store a callsign in all three hash tables (10, 12, 22-bit).
|
|
2263
|
+
* Strips angle brackets if present. Ignores `<...>` and blank/short strings.
|
|
2264
|
+
*/
|
|
2265
|
+
save(callsign) {
|
|
2266
|
+
let cw = callsign.trim().toUpperCase();
|
|
2267
|
+
if (cw === "" || cw === "<...>")
|
|
2268
|
+
return;
|
|
2269
|
+
if (cw.startsWith("<"))
|
|
2270
|
+
cw = cw.slice(1);
|
|
2271
|
+
const gt = cw.indexOf(">");
|
|
2272
|
+
if (gt >= 0)
|
|
2273
|
+
cw = cw.slice(0, gt);
|
|
2274
|
+
cw = cw.trim();
|
|
2275
|
+
if (cw.length < 3)
|
|
2276
|
+
return;
|
|
2277
|
+
const n10 = ihashcall(cw, 10);
|
|
2278
|
+
if (n10 >= 0 && n10 <= 1023)
|
|
2279
|
+
this.calls10.set(n10, cw);
|
|
2280
|
+
const n12 = ihashcall(cw, 12);
|
|
2281
|
+
if (n12 >= 0 && n12 <= 4095)
|
|
2282
|
+
this.calls12.set(n12, cw);
|
|
2283
|
+
const n22 = ihashcall(cw, 22);
|
|
2284
|
+
const existing = this.hash22Entries.findIndex((e) => e.hash === n22);
|
|
2285
|
+
if (existing >= 0) {
|
|
2286
|
+
this.hash22Entries[existing].call = cw;
|
|
2287
|
+
}
|
|
2288
|
+
else {
|
|
2289
|
+
if (this.hash22Entries.length >= MAX_HASH22_ENTRIES) {
|
|
2290
|
+
this.hash22Entries.pop();
|
|
2291
|
+
}
|
|
2292
|
+
this.hash22Entries.unshift({ hash: n22, call: cw });
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
/** Look up a callsign by its 10-bit hash. Returns `null` if not found. */
|
|
2296
|
+
lookup10(n10) {
|
|
2297
|
+
if (n10 < 0 || n10 > 1023)
|
|
2298
|
+
return null;
|
|
2299
|
+
return this.calls10.get(n10) ?? null;
|
|
2300
|
+
}
|
|
2301
|
+
/** Look up a callsign by its 12-bit hash. Returns `null` if not found. */
|
|
2302
|
+
lookup12(n12) {
|
|
2303
|
+
if (n12 < 0 || n12 > 4095)
|
|
2304
|
+
return null;
|
|
2305
|
+
return this.calls12.get(n12) ?? null;
|
|
2306
|
+
}
|
|
2307
|
+
/** Look up a callsign by its 22-bit hash. Returns `null` if not found. */
|
|
2308
|
+
lookup22(n22) {
|
|
2309
|
+
const entry = this.hash22Entries.find((e) => e.hash === n22);
|
|
2310
|
+
return entry?.call ?? null;
|
|
2311
|
+
}
|
|
2312
|
+
/** Number of entries in the 22-bit hash table. */
|
|
2313
|
+
get size() {
|
|
2314
|
+
return this.hash22Entries.length;
|
|
2315
|
+
}
|
|
2316
|
+
/** Remove all stored entries. */
|
|
2317
|
+
clear() {
|
|
2318
|
+
this.calls10.clear();
|
|
2319
|
+
this.calls12.clear();
|
|
2320
|
+
this.hash22Entries.length = 0;
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
exports.HashCallBook = HashCallBook;
|
|
2206
2325
|
exports.decodeFT8 = decode;
|
|
2207
2326
|
exports.encodeFT8 = encode;
|
|
2208
2327
|
//# sourceMappingURL=ft8ts.cjs.map
|