1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-21 23:55:13 -05:00

FT8: add encoding support to library and added more tests

This commit is contained in:
f4exb 2024-03-28 15:15:48 +01:00
parent c833432675
commit 2e9fc9db64
21 changed files with 1330 additions and 493 deletions

View File

@ -1,6 +1,7 @@
project(ft8)
set(ft8_SOURCES
arrays.cpp
fft.cpp
fftbuffers.cpp
ft8.cpp
@ -8,12 +9,14 @@ set(ft8_SOURCES
ft8plans.cpp
libldpc.cpp
osd.cpp
unpack.cpp
packing.cpp
pack0.cpp
unpack0.cpp
util.cpp
)
set(ft8_HEADERS
arrays.h
fft.h
fftbuffers.h
ft8.h
@ -21,7 +24,9 @@ set(ft8_HEADERS
ft8plans.h
libldpc.h
osd.h
unpack.h
packing.h
pack0.h
unpack0.h
util.h
)

390
ft8/arrays.cpp Normal file
View File

@ -0,0 +1,390 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
// reformatted and adapted to Qt and SDRangel context //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "arrays.h"
namespace FT8 {
//
// this is the LDPC(174,91) parity check matrix.
// each row describes one parity check.
// 83 rows.
// each number is an index into the codeword (1-origin).
// the codeword bits mentioned in each row must xor to zero.
// From WSJT-X's ldpc_174_91_c_reordered_parity.f90
//
const int Arrays::Nm[][7] = {
{ 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, 96, 143, 0 },
{ 8, 25, 63, 83, 93, 96, 148 },
{ 6, 32, 64, 97, 126, 138, 0 },
{ 5, 34, 65, 78, 98, 107, 154 },
{ 9, 35, 66, 99, 139, 146, 0 },
{ 10, 36, 67, 100, 107, 126, 0 },
{ 11, 37, 67, 87, 101, 139, 158 },
{ 12, 38, 68, 102, 105, 155, 0 },
{ 13, 39, 69, 103, 149, 162, 0 },
{ 8, 40, 70, 82, 104, 114, 145 },
{ 14, 41, 71, 88, 102, 123, 156 },
{ 15, 42, 59, 106, 123, 159, 0 },
{ 1, 33, 72, 106, 107, 157, 0 },
{ 16, 43, 73, 108, 141, 160, 0 },
{ 17, 37, 74, 81, 109, 131, 154 },
{ 11, 44, 75, 110, 121, 166, 0 },
{ 45, 55, 64, 111, 130, 161, 173 },
{ 8, 46, 71, 112, 119, 166, 0 },
{ 18, 36, 76, 89, 113, 114, 143 },
{ 19, 38, 77, 104, 116, 163, 0 },
{ 20, 47, 70, 92, 138, 165, 0 },
{ 2, 48, 74, 113, 128, 160, 0 },
{ 21, 45, 78, 83, 117, 121, 151 },
{ 22, 47, 58, 118, 127, 164, 0 },
{ 16, 39, 62, 112, 134, 158, 0 },
{ 23, 43, 79, 120, 131, 145, 0 },
{ 19, 35, 59, 73, 110, 125, 161 },
{ 20, 36, 63, 94, 136, 161, 0 },
{ 14, 31, 79, 98, 132, 164, 0 },
{ 3, 44, 80, 124, 127, 169, 0 },
{ 19, 46, 81, 117, 135, 167, 0 },
{ 7, 49, 58, 90, 100, 105, 168 },
{ 12, 50, 61, 118, 119, 144, 0 },
{ 13, 51, 64, 114, 118, 157, 0 },
{ 24, 52, 76, 129, 148, 149, 0 },
{ 25, 53, 69, 90, 101, 130, 156 },
{ 20, 46, 65, 80, 120, 140, 170 },
{ 21, 54, 77, 100, 140, 171, 0 },
{ 35, 82, 133, 142, 171, 174, 0 },
{ 14, 30, 83, 113, 125, 170, 0 },
{ 4, 29, 68, 120, 134, 173, 0 },
{ 1, 4, 52, 57, 86, 136, 152 },
{ 26, 51, 56, 91, 122, 137, 168 },
{ 52, 84, 110, 115, 145, 168, 0 },
{ 7, 50, 81, 99, 132, 173, 0 },
{ 23, 55, 67, 95, 172, 174, 0 },
{ 26, 41, 77, 109, 141, 148, 0 },
{ 2, 27, 41, 61, 62, 115, 133 },
{ 27, 40, 56, 124, 125, 126, 0 },
{ 18, 49, 55, 124, 141, 167, 0 },
{ 6, 33, 85, 108, 116, 156, 0 },
{ 28, 48, 70, 85, 105, 129, 158 },
{ 9, 54, 63, 131, 147, 155, 0 },
{ 22, 53, 68, 109, 121, 174, 0 },
{ 3, 13, 48, 78, 95, 123, 0 },
{ 31, 69, 133, 150, 155, 169, 0 },
{ 12, 43, 66, 89, 97, 135, 159 },
{ 5, 39, 75, 102, 136, 167, 0 },
{ 2, 54, 86, 101, 135, 164, 0 },
{ 15, 56, 87, 108, 119, 171, 0 },
{ 10, 44, 82, 91, 111, 144, 149 },
{ 23, 34, 71, 94, 127, 153, 0 },
{ 11, 49, 88, 92, 142, 157, 0 },
{ 29, 34, 87, 97, 147, 162, 0 },
{ 30, 50, 60, 86, 137, 142, 162 },
{ 10, 53, 66, 84, 112, 128, 165 },
{ 22, 57, 85, 93, 140, 159, 0 },
{ 28, 32, 72, 103, 132, 166, 0 },
{ 28, 29, 84, 88, 117, 143, 150 },
{ 1, 26, 45, 80, 128, 147, 0 },
{ 17, 27, 89, 103, 116, 153, 0 },
{ 51, 57, 98, 163, 165, 172, 0 },
{ 21, 37, 73, 138, 152, 169, 0 },
{ 16, 47, 76, 130, 137, 154, 0 },
{ 3, 24, 30, 72, 104, 139, 0 },
{ 9, 40, 90, 106, 134, 151, 0 },
{ 15, 58, 60, 74, 111, 150, 163 },
{ 18, 42, 79, 144, 146, 152, 0 },
{ 25, 38, 65, 99, 122, 160, 0 },
{ 17, 42, 75, 129, 170, 172, 0 },
};
// Mn from WSJT-X's ldpc_174_91_c_reordered_parity.f90
// each of the 174 rows corresponds to a codeword bit.
// the numbers indicate which three parity
// checks (rows in Nm) refer to the codeword bit.
// 1-origin.
const int Arrays::Mn[][3] = {
{ 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 },
};
// This is the LDPC(174, 91) generator matrix
// It has 83 rows and 91 columns (zero padded on the right thus 12 bytes)
// From WSJT-X's ldpc_174_91_c_generator.f90
const int Arrays::Gm[][12] = {
{ 0x83, 0x29, 0xce, 0x11, 0xbf, 0x31, 0xea, 0xf5, 0x09, 0xf2, 0x7f, 0xc0 },
{ 0x76, 0x1c, 0x26, 0x4e, 0x25, 0xc2, 0x59, 0x33, 0x54, 0x93, 0x13, 0x20 },
{ 0xdc, 0x26, 0x59, 0x02, 0xfb, 0x27, 0x7c, 0x64, 0x10, 0xa1, 0xbd, 0xc0 },
{ 0x1b, 0x3f, 0x41, 0x78, 0x58, 0xcd, 0x2d, 0xd3, 0x3e, 0xc7, 0xf6, 0x20 },
{ 0x09, 0xfd, 0xa4, 0xfe, 0xe0, 0x41, 0x95, 0xfd, 0x03, 0x47, 0x83, 0xa0 },
{ 0x07, 0x7c, 0xcc, 0xc1, 0x1b, 0x88, 0x73, 0xed, 0x5c, 0x3d, 0x48, 0xa0 },
{ 0x29, 0xb6, 0x2a, 0xfe, 0x3c, 0xa0, 0x36, 0xf4, 0xfe, 0x1a, 0x9d, 0xa0 },
{ 0x60, 0x54, 0xfa, 0xf5, 0xf3, 0x5d, 0x96, 0xd3, 0xb0, 0xc8, 0xc3, 0xe0 },
{ 0xe2, 0x07, 0x98, 0xe4, 0x31, 0x0e, 0xed, 0x27, 0x88, 0x4a, 0xe9, 0x00 },
{ 0x77, 0x5c, 0x9c, 0x08, 0xe8, 0x0e, 0x26, 0xdd, 0xae, 0x56, 0x31, 0x80 },
{ 0xb0, 0xb8, 0x11, 0x02, 0x8c, 0x2b, 0xf9, 0x97, 0x21, 0x34, 0x87, 0xc0 },
{ 0x18, 0xa0, 0xc9, 0x23, 0x1f, 0xc6, 0x0a, 0xdf, 0x5c, 0x5e, 0xa3, 0x20 },
{ 0x76, 0x47, 0x1e, 0x83, 0x02, 0xa0, 0x72, 0x1e, 0x01, 0xb1, 0x2b, 0x80 },
{ 0xff, 0xbc, 0xcb, 0x80, 0xca, 0x83, 0x41, 0xfa, 0xfb, 0x47, 0xb2, 0xe0 },
{ 0x66, 0xa7, 0x2a, 0x15, 0x8f, 0x93, 0x25, 0xa2, 0xbf, 0x67, 0x17, 0x00 },
{ 0xc4, 0x24, 0x36, 0x89, 0xfe, 0x85, 0xb1, 0xc5, 0x13, 0x63, 0xa1, 0x80 },
{ 0x0d, 0xff, 0x73, 0x94, 0x14, 0xd1, 0xa1, 0xb3, 0x4b, 0x1c, 0x27, 0x00 },
{ 0x15, 0xb4, 0x88, 0x30, 0x63, 0x6c, 0x8b, 0x99, 0x89, 0x49, 0x72, 0xe0 },
{ 0x29, 0xa8, 0x9c, 0x0d, 0x3d, 0xe8, 0x1d, 0x66, 0x54, 0x89, 0xb0, 0xe0 },
{ 0x4f, 0x12, 0x6f, 0x37, 0xfa, 0x51, 0xcb, 0xe6, 0x1b, 0xd6, 0xb9, 0x40 },
{ 0x99, 0xc4, 0x72, 0x39, 0xd0, 0xd9, 0x7d, 0x3c, 0x84, 0xe0, 0x94, 0x00 },
{ 0x19, 0x19, 0xb7, 0x51, 0x19, 0x76, 0x56, 0x21, 0xbb, 0x4f, 0x1e, 0x80 },
{ 0x09, 0xdb, 0x12, 0xd7, 0x31, 0xfa, 0xee, 0x0b, 0x86, 0xdf, 0x6b, 0x80 },
{ 0x48, 0x8f, 0xc3, 0x3d, 0xf4, 0x3f, 0xbd, 0xee, 0xa4, 0xea, 0xfb, 0x40 },
{ 0x82, 0x74, 0x23, 0xee, 0x40, 0xb6, 0x75, 0xf7, 0x56, 0xeb, 0x5f, 0xe0 },
{ 0xab, 0xe1, 0x97, 0xc4, 0x84, 0xcb, 0x74, 0x75, 0x71, 0x44, 0xa9, 0xa0 },
{ 0x2b, 0x50, 0x0e, 0x4b, 0xc0, 0xec, 0x5a, 0x6d, 0x2b, 0xdb, 0xdd, 0x00 },
{ 0xc4, 0x74, 0xaa, 0x53, 0xd7, 0x02, 0x18, 0x76, 0x16, 0x69, 0x36, 0x00 },
{ 0x8e, 0xba, 0x1a, 0x13, 0xdb, 0x33, 0x90, 0xbd, 0x67, 0x18, 0xce, 0xc0 },
{ 0x75, 0x38, 0x44, 0x67, 0x3a, 0x27, 0x78, 0x2c, 0xc4, 0x20, 0x12, 0xe0 },
{ 0x06, 0xff, 0x83, 0xa1, 0x45, 0xc3, 0x70, 0x35, 0xa5, 0xc1, 0x26, 0x80 },
{ 0x3b, 0x37, 0x41, 0x78, 0x58, 0xcc, 0x2d, 0xd3, 0x3e, 0xc3, 0xf6, 0x20 },
{ 0x9a, 0x4a, 0x5a, 0x28, 0xee, 0x17, 0xca, 0x9c, 0x32, 0x48, 0x42, 0xc0 },
{ 0xbc, 0x29, 0xf4, 0x65, 0x30, 0x9c, 0x97, 0x7e, 0x89, 0x61, 0x0a, 0x40 },
{ 0x26, 0x63, 0xae, 0x6d, 0xdf, 0x8b, 0x5c, 0xe2, 0xbb, 0x29, 0x48, 0x80 },
{ 0x46, 0xf2, 0x31, 0xef, 0xe4, 0x57, 0x03, 0x4c, 0x18, 0x14, 0x41, 0x80 },
{ 0x3f, 0xb2, 0xce, 0x85, 0xab, 0xe9, 0xb0, 0xc7, 0x2e, 0x06, 0xfb, 0xe0 },
{ 0xde, 0x87, 0x48, 0x1f, 0x28, 0x2c, 0x15, 0x39, 0x71, 0xa0, 0xa2, 0xe0 },
{ 0xfc, 0xd7, 0xcc, 0xf2, 0x3c, 0x69, 0xfa, 0x99, 0xbb, 0xa1, 0x41, 0x20 },
{ 0xf0, 0x26, 0x14, 0x47, 0xe9, 0x49, 0x0c, 0xa8, 0xe4, 0x74, 0xce, 0xc0 },
{ 0x44, 0x10, 0x11, 0x58, 0x18, 0x19, 0x6f, 0x95, 0xcd, 0xd7, 0x01, 0x20 },
{ 0x08, 0x8f, 0xc3, 0x1d, 0xf4, 0xbf, 0xbd, 0xe2, 0xa4, 0xea, 0xfb, 0x40 },
{ 0xb8, 0xfe, 0xf1, 0xb6, 0x30, 0x77, 0x29, 0xfb, 0x0a, 0x07, 0x8c, 0x00 },
{ 0x5a, 0xfe, 0xa7, 0xac, 0xcc, 0xb7, 0x7b, 0xbc, 0x9d, 0x99, 0xa9, 0x00 },
{ 0x49, 0xa7, 0x01, 0x6a, 0xc6, 0x53, 0xf6, 0x5e, 0xcd, 0xc9, 0x07, 0x60 },
{ 0x19, 0x44, 0xd0, 0x85, 0xbe, 0x4e, 0x7d, 0xa8, 0xd6, 0xcc, 0x7d, 0x00 },
{ 0x25, 0x1f, 0x62, 0xad, 0xc4, 0x03, 0x2f, 0x0e, 0xe7, 0x14, 0x00, 0x20 },
{ 0x56, 0x47, 0x1f, 0x87, 0x02, 0xa0, 0x72, 0x1e, 0x00, 0xb1, 0x2b, 0x80 },
{ 0x2b, 0x8e, 0x49, 0x23, 0xf2, 0xdd, 0x51, 0xe2, 0xd5, 0x37, 0xfa, 0x00 },
{ 0x6b, 0x55, 0x0a, 0x40, 0xa6, 0x6f, 0x47, 0x55, 0xde, 0x95, 0xc2, 0x60 },
{ 0xa1, 0x8a, 0xd2, 0x8d, 0x4e, 0x27, 0xfe, 0x92, 0xa4, 0xf6, 0xc8, 0x40 },
{ 0x10, 0xc2, 0xe5, 0x86, 0x38, 0x8c, 0xb8, 0x2a, 0x3d, 0x80, 0x75, 0x80 },
{ 0xef, 0x34, 0xa4, 0x18, 0x17, 0xee, 0x02, 0x13, 0x3d, 0xb2, 0xeb, 0x00 },
{ 0x7e, 0x9c, 0x0c, 0x54, 0x32, 0x5a, 0x9c, 0x15, 0x83, 0x6e, 0x00, 0x00 },
{ 0x36, 0x93, 0xe5, 0x72, 0xd1, 0xfd, 0xe4, 0xcd, 0xf0, 0x79, 0xe8, 0x60 },
{ 0xbf, 0xb2, 0xce, 0xc5, 0xab, 0xe1, 0xb0, 0xc7, 0x2e, 0x07, 0xfb, 0xe0 },
{ 0x7e, 0xe1, 0x82, 0x30, 0xc5, 0x83, 0xcc, 0xcc, 0x57, 0xd4, 0xb0, 0x80 },
{ 0xa0, 0x66, 0xcb, 0x2f, 0xed, 0xaf, 0xc9, 0xf5, 0x26, 0x64, 0x12, 0x60 },
{ 0xbb, 0x23, 0x72, 0x5a, 0xbc, 0x47, 0xcc, 0x5f, 0x4c, 0xc4, 0xcd, 0x20 },
{ 0xde, 0xd9, 0xdb, 0xa3, 0xbe, 0xe4, 0x0c, 0x59, 0xb5, 0x60, 0x9b, 0x40 },
{ 0xd9, 0xa7, 0x01, 0x6a, 0xc6, 0x53, 0xe6, 0xde, 0xcd, 0xc9, 0x03, 0x60 },
{ 0x9a, 0xd4, 0x6a, 0xed, 0x5f, 0x70, 0x7f, 0x28, 0x0a, 0xb5, 0xfc, 0x40 },
{ 0xe5, 0x92, 0x1c, 0x77, 0x82, 0x25, 0x87, 0x31, 0x6d, 0x7d, 0x3c, 0x20 },
{ 0x4f, 0x14, 0xda, 0x82, 0x42, 0xa8, 0xb8, 0x6d, 0xca, 0x73, 0x35, 0x20 },
{ 0x8b, 0x8b, 0x50, 0x7a, 0xd4, 0x67, 0xd4, 0x44, 0x1d, 0xf7, 0x70, 0xe0 },
{ 0x22, 0x83, 0x1c, 0x9c, 0xf1, 0x16, 0x94, 0x67, 0xad, 0x04, 0xb6, 0x80 },
{ 0x21, 0x3b, 0x83, 0x8f, 0xe2, 0xae, 0x54, 0xc3, 0x8e, 0xe7, 0x18, 0x00 },
{ 0x5d, 0x92, 0x6b, 0x6d, 0xd7, 0x1f, 0x08, 0x51, 0x81, 0xa4, 0xe1, 0x20 },
{ 0x66, 0xab, 0x79, 0xd4, 0xb2, 0x9e, 0xe6, 0xe6, 0x95, 0x09, 0xe5, 0x60 },
{ 0x95, 0x81, 0x48, 0x68, 0x2d, 0x74, 0x8a, 0x38, 0xdd, 0x68, 0xba, 0xa0 },
{ 0xb8, 0xce, 0x02, 0x0c, 0xf0, 0x69, 0xc3, 0x2a, 0x72, 0x3a, 0xb1, 0x40 },
{ 0xf4, 0x33, 0x1d, 0x6d, 0x46, 0x16, 0x07, 0xe9, 0x57, 0x52, 0x74, 0x60 },
{ 0x6d, 0xa2, 0x3b, 0xa4, 0x24, 0xb9, 0x59, 0x61, 0x33, 0xcf, 0x9c, 0x80 },
{ 0xa6, 0x36, 0xbc, 0xbc, 0x7b, 0x30, 0xc5, 0xfb, 0xea, 0xe6, 0x7f, 0xe0 },
{ 0x5c, 0xb0, 0xd8, 0x6a, 0x07, 0xdf, 0x65, 0x4a, 0x90, 0x89, 0xa2, 0x00 },
{ 0xf1, 0x1f, 0x10, 0x68, 0x48, 0x78, 0x0f, 0xc9, 0xec, 0xdd, 0x80, 0xa0 },
{ 0x1f, 0xbb, 0x53, 0x64, 0xfb, 0x8d, 0x2c, 0x9d, 0x73, 0x0d, 0x5b, 0xa0 },
{ 0xfc, 0xb8, 0x6b, 0xc7, 0x0a, 0x50, 0xc9, 0xd0, 0x2a, 0x5d, 0x03, 0x40 },
{ 0xa5, 0x34, 0x43, 0x30, 0x29, 0xea, 0xc1, 0x5f, 0x32, 0x2e, 0x34, 0xc0 },
{ 0xc9, 0x89, 0xd9, 0xc7, 0xc3, 0xd3, 0xb8, 0xc5, 0x5d, 0x75, 0x13, 0x00 },
{ 0x7b, 0xb3, 0x8b, 0x2f, 0x01, 0x86, 0xd4, 0x66, 0x43, 0xae, 0x96, 0x20 },
{ 0x26, 0x44, 0xeb, 0xad, 0xeb, 0x44, 0xb9, 0x46, 0x7d, 0x1f, 0x42, 0xc0 },
{ 0x60, 0x8c, 0xc8, 0x57, 0x59, 0x4b, 0xfb, 0xb5, 0x5d, 0x69, 0x60, 0x00 }
};
} // namespace FT8

View File

@ -18,282 +18,39 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef FT8_ARRAYS_H_
#define FT8_ARRAYS_H_
#include "export.h"
namespace FT8 {
//
// this is the LDPC(174,91) parity check matrix.
// each row describes one parity check.
// 83 rows.
// 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
//
int Nm[][7] = {
{ 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, 96, 143, 0 },
{ 8, 25, 63, 83, 93, 96, 148 },
{ 6, 32, 64, 97, 126, 138, 0 },
{ 5, 34, 65, 78, 98, 107, 154 },
{ 9, 35, 66, 99, 139, 146, 0 },
{ 10, 36, 67, 100, 107, 126, 0 },
{ 11, 37, 67, 87, 101, 139, 158 },
{ 12, 38, 68, 102, 105, 155, 0 },
{ 13, 39, 69, 103, 149, 162, 0 },
{ 8, 40, 70, 82, 104, 114, 145 },
{ 14, 41, 71, 88, 102, 123, 156 },
{ 15, 42, 59, 106, 123, 159, 0 },
{ 1, 33, 72, 106, 107, 157, 0 },
{ 16, 43, 73, 108, 141, 160, 0 },
{ 17, 37, 74, 81, 109, 131, 154 },
{ 11, 44, 75, 110, 121, 166, 0 },
{ 45, 55, 64, 111, 130, 161, 173 },
{ 8, 46, 71, 112, 119, 166, 0 },
{ 18, 36, 76, 89, 113, 114, 143 },
{ 19, 38, 77, 104, 116, 163, 0 },
{ 20, 47, 70, 92, 138, 165, 0 },
{ 2, 48, 74, 113, 128, 160, 0 },
{ 21, 45, 78, 83, 117, 121, 151 },
{ 22, 47, 58, 118, 127, 164, 0 },
{ 16, 39, 62, 112, 134, 158, 0 },
{ 23, 43, 79, 120, 131, 145, 0 },
{ 19, 35, 59, 73, 110, 125, 161 },
{ 20, 36, 63, 94, 136, 161, 0 },
{ 14, 31, 79, 98, 132, 164, 0 },
{ 3, 44, 80, 124, 127, 169, 0 },
{ 19, 46, 81, 117, 135, 167, 0 },
{ 7, 49, 58, 90, 100, 105, 168 },
{ 12, 50, 61, 118, 119, 144, 0 },
{ 13, 51, 64, 114, 118, 157, 0 },
{ 24, 52, 76, 129, 148, 149, 0 },
{ 25, 53, 69, 90, 101, 130, 156 },
{ 20, 46, 65, 80, 120, 140, 170 },
{ 21, 54, 77, 100, 140, 171, 0 },
{ 35, 82, 133, 142, 171, 174, 0 },
{ 14, 30, 83, 113, 125, 170, 0 },
{ 4, 29, 68, 120, 134, 173, 0 },
{ 1, 4, 52, 57, 86, 136, 152 },
{ 26, 51, 56, 91, 122, 137, 168 },
{ 52, 84, 110, 115, 145, 168, 0 },
{ 7, 50, 81, 99, 132, 173, 0 },
{ 23, 55, 67, 95, 172, 174, 0 },
{ 26, 41, 77, 109, 141, 148, 0 },
{ 2, 27, 41, 61, 62, 115, 133 },
{ 27, 40, 56, 124, 125, 126, 0 },
{ 18, 49, 55, 124, 141, 167, 0 },
{ 6, 33, 85, 108, 116, 156, 0 },
{ 28, 48, 70, 85, 105, 129, 158 },
{ 9, 54, 63, 131, 147, 155, 0 },
{ 22, 53, 68, 109, 121, 174, 0 },
{ 3, 13, 48, 78, 95, 123, 0 },
{ 31, 69, 133, 150, 155, 169, 0 },
{ 12, 43, 66, 89, 97, 135, 159 },
{ 5, 39, 75, 102, 136, 167, 0 },
{ 2, 54, 86, 101, 135, 164, 0 },
{ 15, 56, 87, 108, 119, 171, 0 },
{ 10, 44, 82, 91, 111, 144, 149 },
{ 23, 34, 71, 94, 127, 153, 0 },
{ 11, 49, 88, 92, 142, 157, 0 },
{ 29, 34, 87, 97, 147, 162, 0 },
{ 30, 50, 60, 86, 137, 142, 162 },
{ 10, 53, 66, 84, 112, 128, 165 },
{ 22, 57, 85, 93, 140, 159, 0 },
{ 28, 32, 72, 103, 132, 166, 0 },
{ 28, 29, 84, 88, 117, 143, 150 },
{ 1, 26, 45, 80, 128, 147, 0 },
{ 17, 27, 89, 103, 116, 153, 0 },
{ 51, 57, 98, 163, 165, 172, 0 },
{ 21, 37, 73, 138, 152, 169, 0 },
{ 16, 47, 76, 130, 137, 154, 0 },
{ 3, 24, 30, 72, 104, 139, 0 },
{ 9, 40, 90, 106, 134, 151, 0 },
{ 15, 58, 60, 74, 111, 150, 163 },
{ 18, 42, 79, 144, 146, 152, 0 },
{ 25, 38, 65, 99, 122, 160, 0 },
{ 17, 42, 75, 129, 170, 172, 0 },
};
class FT8_API Arrays
{
public:
//
// this is the LDPC(174,91) parity check matrix.
// each row describes one parity check.
// 83 rows.
// 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
//
static const int Nm[][7];
// Mn from WSJT-X's ldpc_174_91_c_reordered_parity.f90
// each of the 174 rows corresponds to a codeword bit.
// the numbers indicate which three parity
// checks (rows in Nm) refer to the codeword bit.
// 1-origin.
int Mn[][3] = {
{ 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 },
// Mn from WSJT-X's ldpc_174_91_c_reordered_parity.f90
// each of the 174 rows corresponds to a codeword bit.
// the numbers indicate which three parity
// checks (rows in Nm) refer to the codeword bit.
// 1-origin.
static const int Mn[][3];
// This is the LDPC(174, 91) generator matrix
// It has 83 rows and 91 columns (zero padded on the right thus 12 bytes)
// From WSJT-X's ldpc_174_91_c_generator.f90
static const int Gm[][12];
};
} // namespace FT8
#endif // FT8_ARRAYS_H_

View File

@ -44,6 +44,7 @@
#include "ft8.h"
#include "libldpc.h"
#include "osd.h"
#include "arrays.h"
namespace FT8 {
@ -2478,13 +2479,11 @@ void FT8::soft_decode_triples(
// given log likelyhood for each bit, try LDPC and OSD decoders.
// on success, puts corrected 174 bits into a174[].
//
int FT8::decode(const float ll174[], int a174[], int use_osd, std::string &comment)
int FT8::decode(const float ll174[], int a174[], FT8Params& _params, int use_osd, std::string &comment)
{
void ldpc_decode(float llcodeword[], int iters, int plain[], int *ok);
void ldpc_decode_log(float codeword[], int iters, int plain[], int *ok);
int plain[174]; // will be 0/1 bits.
int ldpc_ok = 0; // 83 will mean success.
ldpc_decode((float *)ll174, params.ldpc_iters, plain, &ldpc_ok);
LDPC::ldpc_decode((float *)ll174, _params.ldpc_iters, plain, &ldpc_ok);
int ok_thresh = 83; // 83 is perfect
if (ldpc_ok >= ok_thresh)
@ -2500,11 +2499,11 @@ int FT8::decode(const float ll174[], int a174[], int use_osd, std::string &comme
}
}
if (use_osd && params.osd_depth >= 0 && ldpc_ok >= params.osd_ldpc_thresh)
if (use_osd && _params.osd_depth >= 0 && ldpc_ok >= _params.osd_ldpc_thresh)
{
int oplain[91];
int got_depth = -1;
int osd_ok = OSD::osd_decode((float *)ll174, params.osd_depth, oplain, &got_depth);
int osd_ok = OSD::osd_decode((float *)ll174, _params.osd_depth, oplain, &got_depth);
if (osd_ok)
{
@ -2518,6 +2517,37 @@ int FT8::decode(const float ll174[], int a174[], int use_osd, std::string &comme
return 0;
}
//
// encode a 77 bit message into a 174 bit payload
// adds the 14 bit CRC to obtain 91 bits
// apply (174, 91) generator mastrix to obtain the 83 parity bits
// append the 83 bits to the 91 bits message + crc to obbain the 174 bit payload
//
void FT8::encode(int a174[], int s77[])
{
int a91[91]; // msg + CRC
std::fill(a91, a91 + 91, 0);
std::copy(s77, s77+77, a91); // copy msg
LDPC::ft8_crc(a91, 82, &a91[77]); // append CRC - to match OSD::check_crc
std::copy(a91, a91+91, a174); // copy msg + CRC
int sum, n, ni, b;
for (int i=0; i<83; i++)
{
sum = 0;
for (int j=0; j<91; j++)
{
n = j/8; // byte index in the generator matrix
ni = j%8; // bit index in the generator matrix byte LSB first
b = (Arrays::Gm[i][n] >> (7-ni)) & 1; // bit in the generator matrix
sum += a91[j] * b;
}
a174[91+i] = sum % 2; // sum modulo 2
}
}
//
// bandpass filter some FFT bins.
// smooth transition from stop-band to pass-band,
@ -3418,7 +3448,7 @@ int FT8::try_decode(
int a174[174];
std::string comment(comment1);
if (decode(ll174, a174, use_osd, comment))
if (decode(ll174, a174, params, use_osd, comment))
{
// a174 is corrected 91 bits of plain message plus 83 bits of LDPC parity.
// how many of the corrected 174 bits match the received signal in ll174?

View File

@ -309,6 +309,16 @@ public:
std::vector<Strength> coarse(const FFTEngine::ffts_t &bins, int si0, int si1);
FT8Params& getParams() { return params; }
//
// given log likelyhood for each bit, try LDPC and OSD decoders.
// on success, puts corrected 174 bits into a174[].
//
static int decode(const float ll174[], int a174[], FT8Params& params, int use_osd, std::string &comment);
// encode a 77 bit message into a 174 bit payload
// adds the 14 bit CRC to obtain 91 bits
// apply (174, 91) generator mastrix to obtain the 83 parity bits
// append the 83 bits to the 91 bits messag e+ crc to obbain the 174 bit payload
static void encode(int a174[], int s77[]);
private:
//
@ -505,11 +515,6 @@ private:
float ll174[]
);
//
// given log likelyhood for each bit, try LDPC and OSD decoders.
// on success, puts corrected 174 bits into a174[].
//
int decode(const float ll174[], int a174[], int use_osd, std::string &comment);
//
// bandpass filter some FFT bins.
// smooth transition from stop-band to pass-band,
// so that it's not a brick-wall filter, so that it

104
ft8/ldpcgen.py Executable file
View File

@ -0,0 +1,104 @@
#!/usr/bin/env python
# Generates (prints out) C structure of FT8 LDPC generator matrix
# Derived from WSJT-X ldpc_174_91_c_generator.f90
DATA_G = [
"8329ce11bf31eaf509f27fc",
"761c264e25c259335493132",
"dc265902fb277c6410a1bdc",
"1b3f417858cd2dd33ec7f62",
"09fda4fee04195fd034783a",
"077cccc11b8873ed5c3d48a",
"29b62afe3ca036f4fe1a9da",
"6054faf5f35d96d3b0c8c3e",
"e20798e4310eed27884ae90",
"775c9c08e80e26ddae56318",
"b0b811028c2bf997213487c",
"18a0c9231fc60adf5c5ea32",
"76471e8302a0721e01b12b8",
"ffbccb80ca8341fafb47b2e",
"66a72a158f9325a2bf67170",
"c4243689fe85b1c51363a18",
"0dff739414d1a1b34b1c270",
"15b48830636c8b99894972e",
"29a89c0d3de81d665489b0e",
"4f126f37fa51cbe61bd6b94",
"99c47239d0d97d3c84e0940",
"1919b75119765621bb4f1e8",
"09db12d731faee0b86df6b8",
"488fc33df43fbdeea4eafb4",
"827423ee40b675f756eb5fe",
"abe197c484cb74757144a9a",
"2b500e4bc0ec5a6d2bdbdd0",
"c474aa53d70218761669360",
"8eba1a13db3390bd6718cec",
"753844673a27782cc42012e",
"06ff83a145c37035a5c1268",
"3b37417858cc2dd33ec3f62",
"9a4a5a28ee17ca9c324842c",
"bc29f465309c977e89610a4",
"2663ae6ddf8b5ce2bb29488",
"46f231efe457034c1814418",
"3fb2ce85abe9b0c72e06fbe",
"de87481f282c153971a0a2e",
"fcd7ccf23c69fa99bba1412",
"f0261447e9490ca8e474cec",
"4410115818196f95cdd7012",
"088fc31df4bfbde2a4eafb4",
"b8fef1b6307729fb0a078c0",
"5afea7acccb77bbc9d99a90",
"49a7016ac653f65ecdc9076",
"1944d085be4e7da8d6cc7d0",
"251f62adc4032f0ee714002",
"56471f8702a0721e00b12b8",
"2b8e4923f2dd51e2d537fa0",
"6b550a40a66f4755de95c26",
"a18ad28d4e27fe92a4f6c84",
"10c2e586388cb82a3d80758",
"ef34a41817ee02133db2eb0",
"7e9c0c54325a9c15836e000",
"3693e572d1fde4cdf079e86",
"bfb2cec5abe1b0c72e07fbe",
"7ee18230c583cccc57d4b08",
"a066cb2fedafc9f52664126",
"bb23725abc47cc5f4cc4cd2",
"ded9dba3bee40c59b5609b4",
"d9a7016ac653e6decdc9036",
"9ad46aed5f707f280ab5fc4",
"e5921c77822587316d7d3c2",
"4f14da8242a8b86dca73352",
"8b8b507ad467d4441df770e",
"22831c9cf1169467ad04b68",
"213b838fe2ae54c38ee7180",
"5d926b6dd71f085181a4e12",
"66ab79d4b29ee6e69509e56",
"958148682d748a38dd68baa",
"b8ce020cf069c32a723ab14",
"f4331d6d461607e95752746",
"6da23ba424b9596133cf9c8",
"a636bcbc7b30c5fbeae67fe",
"5cb0d86a07df654a9089a20",
"f11f106848780fc9ecdd80a",
"1fbb5364fb8d2c9d730d5ba",
"fcb86bc70a50c9d02a5d034",
"a534433029eac15f322e34c",
"c989d9c7c3d3b8c55d75130",
"7bb38b2f0186d46643ae962",
"2644ebadeb44b9467d1f42c",
"608cc857594bfbb55d69600"
]
for data_g in DATA_G:
rest = data_g
rowstr = '{ '
while len(rest) > 0:
byte_hex = rest[:2]
if len(byte_hex) == 1:
rowstr += f'0x{byte_hex}0, '
else:
rowstr += f'0x{byte_hex}, '
rest = rest[2:]
rowstr = rowstr[:-2] + ' },'
print(rowstr)

View File

@ -37,6 +37,7 @@
#include <math.h>
#include <stdlib.h>
#include "arrays.h"
#include "libldpc.h"
// float, long float, __float128
#define REAL float
@ -48,7 +49,7 @@ namespace FT8
// returns the number of parity checks that passed.
// 83 means total success.
//
int ldpc_check(int codeword[])
int LDPC::ldpc_check(int codeword[])
{
int score = 0;
@ -58,7 +59,7 @@ int ldpc_check(int codeword[])
int x = 0;
for (int ii1 = 0; ii1 < 7; ii1++)
{
int i1 = Nm[j][ii1] - 1;
int i1 = Arrays::Nm[j][ii1] - 1;
if (i1 >= 0)
{
x ^= codeword[i1];
@ -75,7 +76,7 @@ int ldpc_check(int codeword[])
// iters is how hard to try.
// ok is the number of parity checks that worked out,
// ok == 83 means success.
void ldpc_decode(float llcodeword[], int iters, int plain[], int *ok)
void LDPC::ldpc_decode(float llcodeword[], int iters, int plain[], int *ok)
{
REAL m[83][174];
REAL e[83][174];
@ -114,13 +115,13 @@ void ldpc_decode(float llcodeword[], int iters, int plain[], int *ok)
{
for (int ii1 = 0; ii1 < 7; ii1++)
{
int i1 = Nm[j][ii1] - 1;
int i1 = Arrays::Nm[j][ii1] - 1;
if (i1 < 0)
continue;
REAL a = 1.0;
for (int ii2 = 0; ii2 < 7; ii2++)
{
int i2 = Nm[j][ii2] - 1;
int i2 = Arrays::Nm[j][ii2] - 1;
if (i2 >= 0 && i2 != i1)
{
// tmp ranges from 1.0 to -1.0, for
@ -145,7 +146,7 @@ void ldpc_decode(float llcodeword[], int iters, int plain[], int *ok)
REAL q1 = 1.0 - q0;
for (int j = 0; j < 3; j++)
{
int j2 = Mn[i][j] - 1;
int j2 = Arrays::Mn[i][j] - 1;
q0 *= e[j2][i];
q1 *= 1.0 - e[j2][i];
}
@ -181,12 +182,12 @@ void ldpc_decode(float llcodeword[], int iters, int plain[], int *ok)
{
for (int ji1 = 0; ji1 < 3; ji1++)
{
int j1 = Mn[i][ji1] - 1;
int j1 = Arrays::Mn[i][ji1] - 1;
REAL q0 = codeword[i];
REAL q1 = 1.0 - q0;
for (int ji2 = 0; ji2 < 3; ji2++)
{
int j2 = Mn[i][ji2] - 1;
int j2 = Arrays::Mn[i][ji2] - 1;
if (j1 != j2)
{
q0 *= e[j2][i];
@ -217,7 +218,7 @@ void ldpc_decode(float llcodeword[], int iters, int plain[], int *ok)
// thank you Douglas Bagnall
// https://math.stackexchange.com/a/446411
float fast_tanh(float x)
float LDPC::fast_tanh(float x)
{
if (x < -7.6)
{
@ -256,7 +257,7 @@ return tanhtable[ind];
// iters is how hard to try.
// ok is the number of parity checks that worked out,
// ok == 83 means success.
void ldpc_decode_log(float codeword[], int iters, int plain[], int *ok)
void LDPC::ldpc_decode_log(float codeword[], int iters, int plain[], int *ok)
{
REAL m[83][174];
REAL e[83][174];
@ -277,13 +278,13 @@ void ldpc_decode_log(float codeword[], int iters, int plain[], int *ok)
{
for (int ii1 = 0; ii1 < 7; ii1++)
{
int i1 = Nm[j][ii1] - 1;
int i1 = Arrays::Nm[j][ii1] - 1;
if (i1 < 0)
continue;
REAL a = 1.0;
for (int ii2 = 0; ii2 < 7; ii2++)
{
int i2 = Nm[j][ii2] - 1;
int i2 = Arrays::Nm[j][ii2] - 1;
if (i2 >= 0 && i2 != i1)
{
// a *= table_tanh(m[j][i2] / 2.0);
@ -312,7 +313,7 @@ void ldpc_decode_log(float codeword[], int iters, int plain[], int *ok)
{
REAL l = codeword[i];
for (int j = 0; j < 3; j++)
l += e[Mn[i][j] - 1][i];
l += e[Arrays::Mn[i][j] - 1][i];
cw[i] = (l <= 0.0);
}
int score = ldpc_check(cw);
@ -335,11 +336,11 @@ void ldpc_decode_log(float codeword[], int iters, int plain[], int *ok)
{
for (int ji1 = 0; ji1 < 3; ji1++)
{
int j1 = Mn[i][ji1] - 1;
int j1 = Arrays::Mn[i][ji1] - 1;
REAL l = codeword[i];
for (int ji2 = 0; ji2 < 3; ji2++)
{
int j2 = Mn[i][ji2] - 1;
int j2 = Arrays::Mn[i][ji2] - 1;
if (j1 != j2)
{
l += e[j2][i];
@ -361,7 +362,7 @@ void ldpc_decode_log(float codeword[], int iters, int plain[], int *ok)
// check the FT8 CRC-14
//
void ft8_crc(int msg1[], int msglen, int out[14])
void LDPC::ft8_crc(int msg1[], int msglen, int out[14])
{
// the old FT8 polynomial for 12-bit CRC, 0xc06.
// int div[] = { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 };
@ -407,7 +408,7 @@ void ft8_crc(int msg1[], int msglen, int out[14])
// m[174][2*91].
// m's right half should start out as zeros.
// m's upper-right quarter will be the desired inverse.
void gauss_jordan(int rows, int cols, int m[174][2 * 91], int which[91], int *ok)
void LDPC::gauss_jordan(int rows, int cols, int m[174][2 * 91], int which[91], int *ok)
// gauss_jordan(int rows, int cols, int m[cols][2*rows], int which[rows], int *ok)
{
*ok = 0;

View File

@ -24,13 +24,17 @@
namespace FT8 {
int ldpc_check(int codeword[]);
void ldpc_decode(float llcodeword[], int iters, int plain[], int *ok);
float fast_tanh(float x);
void ldpc_decode_log(float codeword[], int iters, int plain[], int *ok);
void ft8_crc(int msg1[], int msglen, int out[14]);
void gauss_jordan(int rows, int cols, int m[174][2 * 91], int which[91], int *ok);
class LDPC {
public:
static void ldpc_decode(float llcodeword[], int iters, int plain[], int *ok);
static void ldpc_decode_log(float codeword[], int iters, int plain[], int *ok);
static void ft8_crc(int msg1[], int msglen, int out[14]);
static void gauss_jordan(int rows, int cols, int m[174][2 * 91], int which[91], int *ok);
private:
static int ldpc_check(int codeword[]);
static float fast_tanh(float x);
};
} // namespace FT8

View File

@ -57,7 +57,7 @@ int OSD::check_crc(const int a91[91])
return 0;
// why 82? why not 77?
ft8_crc(aa, 82, out1);
LDPC::ft8_crc(aa, 82, out1);
for (int i = 0; i < 14; i++)
{
@ -216,7 +216,7 @@ int OSD::osd_decode(float codeword[174], int depth, int out[91], int *out_depth)
}
int ok = 0;
gauss_jordan(91, 174, b, xwhich, &ok);
LDPC::gauss_jordan(91, 174, b, xwhich, &ok);
if (ok == 0) {
fprintf(stderr, "gauss_jordan failed\n");

51
ft8/pack0.cpp Normal file
View File

@ -0,0 +1,51 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
// reformatted and adapted to Qt and SDRangel context //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "pack0.h"
namespace FT8 {
void pa128(int a77[], int start, int len, const boost::multiprecision::int128_t value)
{
boost::multiprecision::int128_t x = value;
int i = start + len;
while (x != 0)
{
i--;
a77[i] = (int) (x % 2);
x /= 2;
}
}
void pa64(int a77[], int start, int len, const uint64_t value)
{
uint64_t x = value;
int i = start + len;
while (x != 0)
{
i--;
a77[i] = x % 2;
x /= 2;
}
}
} // namespae FT8

28
ft8/pack0.h Normal file
View File

@ -0,0 +1,28 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
// reformatted and adapted to Qt and SDRangel context //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <boost/multiprecision/cpp_int.hpp>
namespace FT8
{
void pa128(int a77[], int start, int len, const boost::multiprecision::int128_t value);
void pa64(int a77[], int start, int len, const uint64_t value); // pack into bits MSB first
} // namespace FT8

View File

@ -18,9 +18,11 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <string>
#include <regex>
#include "unpack.h"
#include "packing.h"
#include "unpack0.h"
#include "pack0.h"
#include "util.h"
namespace FT8 {
@ -716,4 +718,220 @@ std::string Packing::unpack(int a77[], std::string& call1, std::string& call2, s
return std::string(tmp);
}
bool Packing::packcall_std(int& c28, const std::string& callstr)
{
c28 = 0;
if (callstr.size() == 2)
{
if (callstr == "DE") {
return true;
}
if (callstr == "CQ")
{
c28 = 2;
return true;
}
}
if (callstr == "QRZ")
{
c28 = 1;
return true;
}
if (callstr.rfind("CQ ", 0) == 0) // special CQ
{
std::regex cq_regex_num("CQ (\\d\\d\\d)");
std::regex cq_regex_alpha("CQ ([A-Z]+)");
std::smatch cq_match;
if (std::regex_match(callstr, cq_match, cq_regex_num))
{
std::string cq_num_arg = cq_match[1].str();
int cq_num = stoi(cq_num_arg);
c28 = 3 + cq_num;
return true;
}
if (std::regex_match(callstr, cq_match, cq_regex_alpha))
{
std::string cq_alpha_arg = cq_match[1].str();
if (cq_alpha_arg.size() > 4) {
return false;
}
int arg_value = 1;
for (auto c : cq_alpha_arg) {
arg_value *= int(c) - int('A') + 1;
}
if (cq_alpha_arg.size() == 1) {
c28 = 1003 + arg_value;
} else if (cq_alpha_arg.size() == 2) {
c28 = 1030 + arg_value;
} else if (cq_alpha_arg.size() == 3) {
c28 = 1759 + arg_value;
} else if (cq_alpha_arg.size() == 4) {
c28 = 21442 + arg_value;
}
return true;
}
}
if ((callstr.size() < 3) || (callstr.size() > 6)) { // standard callsigns are 3 to 6 characters
return false;
}
std::string call_prefix;
int call_num;
std::string call_suffix;
if (isdigit(callstr.at(0)))
{
std::regex call_regex("(\\d[A-Z])(\\d)([A-Z]{1,3})");
std::smatch call_match;
if (std::regex_match(callstr, call_match, call_regex))
{
call_prefix = call_match[1].str();
call_num = stoi(call_match[2].str());
call_suffix = call_match[3].str();
}
else
{
return false;
}
}
else
{
std::regex call_regex("([A-Z0-9]{1,2})(\\d)([A-Z]{1,3})");
std::smatch call_match;
if (std::regex_match(callstr, call_match, call_regex))
{
call_prefix = call_match[1].str();
call_num = stoi(call_match[2].str());
call_suffix = call_match[3].str();
if (isdigit(call_prefix.at(0))) { // In this case the first character cannot be a digit
return false;
}
}
else
{
return false;
}
}
// qDebug("Packing::packcall_std: %s %d %s", call_prefix.c_str(), call_num, call_suffix.c_str());
int i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0;
std::string alnums = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::string alphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (call_prefix.size() == 2)
{
i1 = alnums.find(call_prefix.at(0)) + 1;
i2 = alnums.find(call_prefix.at(1));
}
else
{
i2 = alnums.find(call_prefix.at(0));
}
i3 = call_num;
i4 = alphas.find(call_suffix.at(0)) + 1;
if (call_suffix.size() > 1) {
i5 = alphas.find(call_suffix.at(1)) + 1;
}
if (call_suffix.size() > 2) {
i6 = alphas.find(call_suffix.at(2)) + 1;
}
c28 = 2063592 + (1<<22) + 36*10*27*27*27*i1 + 10*27*27*27*i2 + 27*27*27*i3 + 27*27*i4 + 27*i5 + i6;
// qDebug("Packing::packcall c28: %d, i1: %d, i2: %d, i3: %d, i4: %d, i5: %d, i6: %d", c28, i1, i2, i3, i4, i5, i6);
return true;
}
bool Packing::packgrid(int& g15, const std::string& locstr)
{
static const int MAXGRID4 = 32400;
std::regex loc_regex("[A-R][A-R][0-9][0-9]");
std::smatch loc_match;
g15 = 0;
if (locstr.size() == 0)
{
g15 = MAXGRID4 + 1;
return true;
}
if (locstr == "RRR")
{
g15 = MAXGRID4 + 2;
return true;
}
if (locstr == "RR73")
{
g15 = MAXGRID4 + 3;
return true;
}
if (locstr == "73")
{
g15 = MAXGRID4 + 4;
return true;
}
if (std::regex_match(locstr, loc_match, loc_regex)) // Maidenhead 4 char locator
{
int i1 = int(locstr.at(0)) - int('A');
int i2 = int(locstr.at(1)) - int('A');
int i3 = int(locstr.at(2)) - int('0');
int i4 = int(locstr.at(3)) - int('0');
g15 = i1*18*10*10 + i2*10*10 + i3*10 + i4;
return true;
}
std::regex rpt_regex("([+-])(\\d)(\\d)");
if (std::regex_match(locstr, loc_match, rpt_regex)) // Report -30 to +99
{
int i1 = int(locstr.at(1)) - int('0');
int i2 = int(locstr.at(2)) - int('0');
int s = (locstr.at(0) == '-') ? -1 : 1;
g15 = MAXGRID4 + 35 + s*(i1*10 + i2);
return true;
}
return false;
}
bool Packing::packfree(int a77[], const std::string& msg)
{
std::string s = msg;
s.append(13, ' ');
s = s.substr(0, 13);
std::string a = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?";
boost::multiprecision::int128_t b = 1, x = 0;
for (int i=12; i>=0; i--)
{
int ai = a.find(s.at(i));
ai = (ai < 0) ? 0 : ai; // map unknown characters to blanks
x += ai * b;
b *= 42;
}
pa128(a77, 0, 71, x);
return true;
}
} // namespace FT8

View File

@ -35,6 +35,11 @@ class FT8_API Packing
{
public:
std::string unpack(int a91[], std::string& call1str, std::string& call2str, std::string& locstr, std::string& type);
static std::string unpack_0_0(int a77[], std::string& call1str, std::string& call2str, std::string& locstr);
std::string unpack_1(int a77[], std::string& call1str, std::string& call2str, std::string& locstr);
static bool packcall_std(int& c28, const std::string& callstr);
static bool packgrid(int& g15, const std::string& locstr);
static bool packfree(int a77[], const std::string& msg);
private:
static int ihashcall(std::string call, int m);
@ -42,17 +47,16 @@ private:
std::string unpackgrid15(int ng, int ir);
std::string unpackgrid25(int ng);
void remember_call(std::string call);
std::string unpack_0_0(int a77[], std::string& call1str, std::string& call2str, std::string& locstr);
std::string unpack_0_1(int a77[], std::string& call1str, std::string& call2str, std::string& locstr);
// 0.3 and 0.4
std::string unpack_0_3(int a77[], int n3, std::string& call1str, std::string& call2str, std::string& locstr);
std::string unpack_0_5(int a77[], std::string& call1str, std::string& call2str, std::string& locstr);
// 1 and 2
std::string unpack_1(int a77[], std::string& call1str, std::string& call2str, std::string& locstr);
std::string unpack_3(int a77[], std::string& call1str, std::string& call2str, std::string& locstr);
std::string unpack_4(int a77[], std::string& call1str, std::string& call2str, std::string& locstr);
std::string unpack_5(int a77[], std::string& call1str, std::string& call2str, std::string& locstr);
QMutex hashes_mu;
std::map<int, std::string> hashes10;
std::map<int, std::string> hashes12;

View File

@ -24,7 +24,7 @@
#include <QSet>
#include "ft8.h"
#include "unpack.h"
#include "packing.h"
class QDateTime;
class MessageQueue;

View File

@ -10,6 +10,7 @@ set(sdrbench_SOURCES
test_golay2312.cpp
test_ft8.cpp
test_callsign.cpp
test_ft8protocols.cpp
)
set(sdrbench_HEADERS

View File

@ -71,6 +71,8 @@ void MainBench::run()
testFT8(m_parser.getFileName(), m_parser.getArgsStr());
} else if (m_parser.getTestType() == ParserBench::TestCallsign) {
testCallsign(m_parser.getArgsStr());
} else if (m_parser.getTestType() == ParserBench::TestFT8Protocols) {
testFT8Protocols(m_parser.getArgsStr());
} else {
qDebug() << "MainBench::run: unknown test type: " << m_parser.getTestType();
}

View File

@ -58,6 +58,7 @@ private:
void testDecimateFF();
void testGolay2312();
void testFT8(const QString& wavFile, const QString& argsStr); //!< use with sdrbench/samples/ft8/230105_091630.wav in -f option
void testFT8Protocols(const QString& argsStr);
void testCallsign(const QString& argsStr);
void decimateII(const qint16 *buf, int len);
void decimateInfII(const qint16 *buf, int len);

View File

@ -24,7 +24,7 @@
ParserBench::ParserBench() :
m_testOption(QStringList() << "t" << "test",
"Test type: decimateii, decimatefi, decimateff, decimateif, decimateinfii, decimatesupii, ambe, golay2312, ft8, callsign"
"Test type: decimateii, decimatefi, decimateff, decimateif, decimateinfii, decimatesupii, ambe, golay2312, ft8, ft8protocols, callsign"
"test",
"decimateii"),
m_nbSamplesOption(QStringList() << "n" << "nb-samples",
@ -149,6 +149,8 @@ ParserBench::TestType ParserBench::getTestType() const
return TestFT8;
} else if (m_testStr == "callsign") {
return TestCallsign;
} else if (m_testStr == "ft8protocols") {
return TestFT8Protocols;
} else {
return TestDecimatorsII;
}

View File

@ -39,7 +39,8 @@ public:
TestDecimatorsSupII,
TestGolay2312,
TestFT8,
TestCallsign
TestCallsign,
TestFT8Protocols
} TestType;
ParserBench();

View File

@ -34,7 +34,7 @@ void MainBench::testFT8(const QString& wavFile, const QString& argsStr)
#else
#include "ft8/ft8.h"
#include "ft8/unpack.h"
#include "ft8/packing.h"
class TestFT8Callback : public FT8::CallbackInterface
{

View File

@ -0,0 +1,233 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <fstream>
#include <regex>
#include <QTextStream>
#include "mainbench.h"
#ifndef HAS_FT8
void MainBench::testFT8(const QString& wavFile, const QString& argsStr)
{
(void) wavFile;
(void) argsStr;
qWarning("MainBench::testFT8: this version has no FT8 support");
}
#else
#include "ft8/ft8.h"
#include "ft8/packing.h"
#include "ft8/pack0.h"
class TestFT8Protocols
{
public:
static void testMsg1(const QStringList& argElements, bool runLDPC = false);
static void testMsg00(const QStringList& argElements, bool runLDPC = false);
private:
static bool testLDPC(int a77[]);
static bool compare174(int a174[], int r174[]);
static void debugIntArray(int a[], int length);
};
void MainBench::testFT8Protocols(const QString& argsStr)
{
QStringList argElements = argsStr.split(','); // comma separated list of arguments
if (argElements.size() == 0)
{
qWarning("MainBench::testFT8Protocols: no arguments");
return;
}
QString& testType = argElements[0];
if (testType == "msg1") {
TestFT8Protocols::testMsg1(argElements); // type 1 message test
} else if (testType == "msg00") {
TestFT8Protocols::testMsg00(argElements); // type 0.0 message test
} else if (testType == "msg1L") {
TestFT8Protocols::testMsg1(argElements, true); // type 1 message test with LDPC encoding/decoding test
} else if (testType == "msg00L") {
TestFT8Protocols::testMsg00(argElements, true); // type 0.0 message test with LDPC encoding/decoding test
} else {
qWarning("MainBench::testFT8Protocols: unrecognized test type");
}
}
void TestFT8Protocols::testMsg1(const QStringList& argElements, bool runLDPC)
{
if (argElements.size() < 4)
{
qWarning("TestFT8Protocols::testMsg1: missing callsigns and locator/report in argument");
return;
}
int c28_1, c28_2, g15;
if (!FT8::Packing::packcall_std(c28_1, argElements[1].toStdString()))
{
qWarning("TestFT8Protocols::testMsg1: callsign %s is not a standard callsign", qPrintable(argElements[1]));
return;
}
if (!FT8::Packing::packcall_std(c28_2, argElements[2].toStdString()))
{
qWarning("TestFT8Protocols::testMsg1: callsign %s is not a standard callsign", qPrintable(argElements[2]));
return;
}
std::string locstr;
int report;
if (argElements[3].startsWith("R+") || argElements[3].startsWith("R-"))
{
report = 1;
locstr = argElements[3].mid(1).toStdString();
}
else
{
report = 0;
locstr = argElements[3].toStdString();
}
if (!FT8::Packing::packgrid(g15, locstr))
{
qWarning("TestFT8Protocols::testMsg1: locator or report %s is not valid", locstr.c_str());
return;
}
qDebug("TestFT8Protocols::testMsg1: c28_1: %d c28_2: %d g15: %d", c28_1, c28_2, g15);
int a77[77];
std::fill(a77, a77 + 77, 0);
FT8::pa64(a77, 0, 28, c28_1);
FT8::pa64(a77, 28+1, 28, c28_2);
a77[28+1+28+1] = report;
FT8::pa64(a77, 28+1+28+2, 15, g15);
FT8::pa64(a77, 28+1+28+2+15, 3, 1);
FT8::Packing packing;
std::string call1, call2, loc;
std::string msg = packing.unpack_1(a77, call1, call2, loc);
qInfo("TestFT8Protocols::testMsg1: msg: %s, call1: %s, call2: %s, loc: %s", msg.c_str(), call1.c_str(), call2.c_str(), loc.c_str());
if (runLDPC)
{
if (testLDPC(a77)) {
qInfo("TestFT8Protocols::testMsg1: LDPC test suceeded");
} else {
qWarning("TestFT8Protocols::testMsg1: LDPC test failed");
}
}
}
void TestFT8Protocols::testMsg00(const QStringList& argElements, bool runLDPC)
{
if (argElements.size() < 2)
{
qWarning("TestFT8Protocols::testMsg00: missing free text in argument");
return;
}
int a77[77];
std::fill(a77, a77 + 77, 0);
if (!FT8::Packing::packfree(a77, argElements[1].toStdString()))
{
qWarning("TestFT8Protocols::testMsg00: message %s is not valid", qPrintable(argElements[1]));
return;
}
std::string call1, call2, loc;
std::string msg = FT8::Packing::unpack_0_0(a77, call1, call2, loc);
qInfo("TestFT8Protocols::testMsg00: msg: %s, call1: %s", msg.c_str(), call1.c_str());
if (runLDPC)
{
if (testLDPC(a77)) {
qInfo("TestFT8Protocols::testMsg00: LDPC test suceeded");
} else {
qWarning("TestFT8Protocols::testMsg00: LDPC test failed");
}
}
}
bool TestFT8Protocols::testLDPC(int a77[])
{
int a174[174], r174[174];
FT8::FT8::encode(a174, a77);
FT8::FT8Params ft8Params;
float ll174[174];
FT8::FT8Params params;
std::string comments;
std::transform(
a174,
a174+174,
ll174,
[](const int& s) -> float { return s == 1 ? -1.0 : 1.0; }
);
if (FT8::FT8::decode(ll174, r174, params, 0, comments) == 0)
{
qInfo("TestFT8Protocols::testLDPC(: LDPC or CRC check failed");
return false;
}
else
{
return compare174(a174, r174);
}
}
bool TestFT8Protocols::compare174(int a174[], int r174[])
{
for (int i=0; i < 174; i++)
{
if (a174[i] != r174[i])
{
qDebug("TestFT8Protocols::compare174: failed at index %d: %d != %d", i, a174[i], r174[i]);
return false;
}
}
return true;
}
void TestFT8Protocols::debugIntArray(int a[], int length)
{
QString s;
QTextStream os(&s);
for (int i=0; i < length; i++) {
os << a[i] << " ";
}
qDebug("TestFT8Protocols::debugIntArray: %s", qPrintable(s));
}
#endif