mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-02-03 09:44:01 -05:00
FT8 demod: initial commit of FT8 library with minimal changes and benchmark test
This commit is contained in:
parent
6455c3ad3a
commit
902e58b46b
@ -781,6 +781,10 @@ add_subdirectory(sdrbench)
|
||||
|
||||
add_subdirectory(modemm17)
|
||||
|
||||
if (LINUX)
|
||||
add_subdirectory(ft8)
|
||||
endif()
|
||||
|
||||
if (BUILD_GUI)
|
||||
add_subdirectory(sdrgui)
|
||||
add_subdirectory(plugins plugins)
|
||||
|
29
ft8/CMakeLists.txt
Normal file
29
ft8/CMakeLists.txt
Normal file
@ -0,0 +1,29 @@
|
||||
project(ft8)
|
||||
|
||||
set(ft8_SOURCES
|
||||
fft.cpp
|
||||
ft8.cpp
|
||||
libldpc.cpp
|
||||
osd.cpp
|
||||
unpack.cpp
|
||||
util.cpp
|
||||
)
|
||||
|
||||
set(ft8_HEADERS
|
||||
fft.h
|
||||
ft8.h
|
||||
libldpc.h
|
||||
osd.h
|
||||
unpack.h
|
||||
util.h
|
||||
)
|
||||
|
||||
add_library(ft8 SHARED
|
||||
${ft8_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(ft8
|
||||
${FFTW3F_LIBRARIES}
|
||||
)
|
||||
|
||||
install(TARGETS ft8 DESTINATION ${INSTALL_LIB_DIR})
|
300
ft8/arrays.h
Normal file
300
ft8/arrays.h
Normal file
@ -0,0 +1,300 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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 },
|
||||
};
|
||||
|
||||
// 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 },
|
||||
};
|
||||
|
||||
} // namespace FT8
|
644
ft8/fft.cpp
Normal file
644
ft8/fft.cpp
Normal file
@ -0,0 +1,644 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// 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 "fft.h"
|
||||
#include <mutex>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "util.h"
|
||||
|
||||
#define TIMING 0
|
||||
|
||||
namespace FT8 {
|
||||
|
||||
// MEASURE=0, ESTIMATE=64, PATIENT=32
|
||||
int fftw_type = FFTW_ESTIMATE;
|
||||
|
||||
// a cached fftw plan, for both of:
|
||||
// fftwf_plan_dft_r2c_1d(n, m_in, m_out, FFTW_ESTIMATE);
|
||||
// fftwf_plan_dft_c2r_1d(n, m_in, m_out, FFTW_ESTIMATE);
|
||||
class Plan
|
||||
{
|
||||
public:
|
||||
int n_;
|
||||
int type_;
|
||||
|
||||
//
|
||||
// real -> complex
|
||||
//
|
||||
fftwf_complex *c_; // (n_ / 2) + 1 of these
|
||||
float *r_; // n_ of these
|
||||
fftwf_plan fwd_; // forward plan
|
||||
fftwf_plan rev_; // reverse plan
|
||||
|
||||
//
|
||||
// complex -> complex
|
||||
//
|
||||
fftwf_complex *cc1_; // n
|
||||
fftwf_complex *cc2_; // n
|
||||
fftwf_plan cfwd_; // forward plan
|
||||
fftwf_plan crev_; // reverse plan
|
||||
|
||||
// how much CPU time spent in FFTs that use this plan.
|
||||
#if TIMING
|
||||
float time_;
|
||||
#endif
|
||||
const char *why_;
|
||||
int uses_;
|
||||
};
|
||||
|
||||
static std::mutex plansmu;
|
||||
static Plan *plans[1000];
|
||||
static int nplans;
|
||||
static int plan_master_pid = 0;
|
||||
|
||||
Plan *get_plan(int n, const char *why)
|
||||
{
|
||||
// cache fftw plans in the parent process,
|
||||
// so they will already be there for fork()ed children.
|
||||
|
||||
plansmu.lock();
|
||||
|
||||
if (plan_master_pid == 0)
|
||||
{
|
||||
plan_master_pid = getpid();
|
||||
}
|
||||
|
||||
for (int i = 0; i < nplans; i++)
|
||||
{
|
||||
if (plans[i]->n_ == n && plans[i]->type_ == fftw_type
|
||||
#if TIMING
|
||||
&& strcmp(plans[i]->why_, why) == 0
|
||||
#endif
|
||||
)
|
||||
{
|
||||
Plan *p = plans[i];
|
||||
p->uses_ += 1;
|
||||
plansmu.unlock();
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
float t0 = now();
|
||||
|
||||
// fftw_make_planner_thread_safe();
|
||||
|
||||
// the fftw planner is not thread-safe.
|
||||
// can't rely on plansmu because both ft8.so
|
||||
// and snd.so may be using separate copies of fft.cc.
|
||||
// the lock file really should be per process.
|
||||
// FIXME: Qt-fy this
|
||||
int lockfd = creat("/tmp/fft-plan-lock", 0666);
|
||||
assert(lockfd >= 0);
|
||||
fchmod(lockfd, 0666);
|
||||
int lockret = flock(lockfd, LOCK_EX);
|
||||
assert(lockret == 0);
|
||||
|
||||
fftwf_set_timelimit(5);
|
||||
|
||||
//
|
||||
// real -> complex
|
||||
//
|
||||
|
||||
Plan *p = new Plan;
|
||||
|
||||
p->n_ = n;
|
||||
#if TIMING
|
||||
p->time_ = 0;
|
||||
#endif
|
||||
p->uses_ = 1;
|
||||
p->why_ = why;
|
||||
p->r_ = (float *)fftwf_malloc(n * sizeof(float));
|
||||
assert(p->r_);
|
||||
p->c_ = (fftwf_complex *)fftwf_malloc(((n / 2) + 1) * sizeof(fftwf_complex));
|
||||
assert(p->c_);
|
||||
|
||||
// FFTW_ESTIMATE
|
||||
// FFTW_MEASURE
|
||||
// FFTW_PATIENT
|
||||
// FFTW_EXHAUSTIVE
|
||||
int type = fftw_type;
|
||||
if (getpid() != plan_master_pid)
|
||||
{
|
||||
type = FFTW_ESTIMATE;
|
||||
}
|
||||
p->type_ = type;
|
||||
p->fwd_ = fftwf_plan_dft_r2c_1d(n, p->r_, p->c_, type);
|
||||
assert(p->fwd_);
|
||||
p->rev_ = fftwf_plan_dft_c2r_1d(n, p->c_, p->r_, type);
|
||||
assert(p->rev_);
|
||||
|
||||
//
|
||||
// complex -> complex
|
||||
//
|
||||
p->cc1_ = (fftwf_complex *)fftwf_malloc(n * sizeof(fftwf_complex));
|
||||
assert(p->cc1_);
|
||||
p->cc2_ = (fftwf_complex *)fftwf_malloc(n * sizeof(fftwf_complex));
|
||||
assert(p->cc2_);
|
||||
p->cfwd_ = fftwf_plan_dft_1d(n, p->cc1_, p->cc2_, FFTW_FORWARD, type);
|
||||
assert(p->cfwd_);
|
||||
p->crev_ = fftwf_plan_dft_1d(n, p->cc2_, p->cc1_, FFTW_BACKWARD, type);
|
||||
assert(p->crev_);
|
||||
|
||||
flock(lockfd, LOCK_UN);
|
||||
close(lockfd);
|
||||
|
||||
assert(nplans + 1 < 1000);
|
||||
|
||||
plans[nplans] = p;
|
||||
__sync_synchronize();
|
||||
nplans += 1;
|
||||
|
||||
if (0 && getpid() == plan_master_pid)
|
||||
{
|
||||
float t1 = now();
|
||||
fprintf(stderr, "miss pid=%d master=%d n=%d t=%.3f total=%d type=%d, %s\n",
|
||||
getpid(), plan_master_pid, n, t1 - t0, nplans, type, why);
|
||||
}
|
||||
|
||||
plansmu.unlock();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
//
|
||||
// do just one FFT on samples[i0..i0+block]
|
||||
// real inputs, complex outputs.
|
||||
// output has (block / 2) + 1 points.
|
||||
//
|
||||
std::vector<std::complex<float>> one_fft(
|
||||
const std::vector<float> &samples,
|
||||
int i0,
|
||||
int block,
|
||||
const char *why,
|
||||
Plan *p
|
||||
)
|
||||
{
|
||||
assert(i0 >= 0);
|
||||
assert(block > 1);
|
||||
|
||||
int nsamples = samples.size();
|
||||
int nbins = (block / 2) + 1;
|
||||
|
||||
if (p)
|
||||
{
|
||||
assert(p->n_ == block);
|
||||
p->uses_ += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = get_plan(block, why);
|
||||
}
|
||||
fftwf_plan m_plan = p->fwd_;
|
||||
|
||||
#if TIMING
|
||||
float t0 = now();
|
||||
#endif
|
||||
|
||||
assert((int)samples.size() - i0 >= block);
|
||||
|
||||
int m_in_allocated = 0;
|
||||
float *m_in = (float *)samples.data() + i0;
|
||||
|
||||
if ((((unsigned long long)m_in) % 16) != 0)
|
||||
{
|
||||
// m_in must be on a 16-byte boundary for FFTW.
|
||||
m_in = (float *)fftwf_malloc(sizeof(float) * p->n_);
|
||||
assert(m_in);
|
||||
m_in_allocated = 1;
|
||||
for (int i = 0; i < block; i++)
|
||||
{
|
||||
if (i0 + i < nsamples)
|
||||
{
|
||||
m_in[i] = samples[i0 + i];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_in[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fftwf_complex *m_out = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * ((p->n_ / 2) + 1));
|
||||
assert(m_out);
|
||||
|
||||
fftwf_execute_dft_r2c(m_plan, m_in, m_out);
|
||||
|
||||
std::vector<std::complex<float>> out(nbins);
|
||||
|
||||
for (int bi = 0; bi < nbins; bi++)
|
||||
{
|
||||
float re = m_out[bi][0];
|
||||
float im = m_out[bi][1];
|
||||
out[bi] = std::complex<float>(re, im);
|
||||
}
|
||||
|
||||
if (m_in_allocated)
|
||||
fftwf_free(m_in);
|
||||
fftwf_free(m_out);
|
||||
|
||||
#if TIMING
|
||||
p->time_ += now() - t0;
|
||||
#endif
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
//
|
||||
// do a full set of FFTs, one per symbol-time.
|
||||
// bins[time][frequency]
|
||||
//
|
||||
ffts_t ffts(const std::vector<float> &samples, int i0, int block, const char *why)
|
||||
{
|
||||
assert(i0 >= 0);
|
||||
assert(block > 1 && (block % 2) == 0);
|
||||
|
||||
int nsamples = samples.size();
|
||||
int nbins = (block / 2) + 1;
|
||||
int nblocks = (nsamples - i0) / block;
|
||||
ffts_t bins(nblocks);
|
||||
for (int si = 0; si < nblocks; si++)
|
||||
{
|
||||
bins[si].resize(nbins);
|
||||
}
|
||||
|
||||
Plan *p = get_plan(block, why);
|
||||
fftwf_plan m_plan = p->fwd_;
|
||||
|
||||
#if TIMING
|
||||
float t0 = now();
|
||||
#endif
|
||||
|
||||
// allocate our own b/c using p->m_in and p->m_out isn't thread-safe.
|
||||
float *m_in = (float *)fftwf_malloc(sizeof(float) * p->n_);
|
||||
fftwf_complex *m_out = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * ((p->n_ / 2) + 1));
|
||||
assert(m_in && m_out);
|
||||
|
||||
// float *m_in = p->r_;
|
||||
// fftw_complex *m_out = p->c_;
|
||||
|
||||
for (int si = 0; si < nblocks; si++)
|
||||
{
|
||||
int off = i0 + si * block;
|
||||
for (int i = 0; i < block; i++)
|
||||
{
|
||||
if (off + i < nsamples)
|
||||
{
|
||||
float x = samples[off + i];
|
||||
m_in[i] = x;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_in[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fftwf_execute_dft_r2c(m_plan, m_in, m_out);
|
||||
|
||||
for (int bi = 0; bi < nbins; bi++)
|
||||
{
|
||||
float re = m_out[bi][0];
|
||||
float im = m_out[bi][1];
|
||||
std::complex<float> c(re, im);
|
||||
bins[si][bi] = c;
|
||||
}
|
||||
}
|
||||
|
||||
fftwf_free(m_in);
|
||||
fftwf_free(m_out);
|
||||
|
||||
#if TIMING
|
||||
p->time_ += now() - t0;
|
||||
#endif
|
||||
|
||||
return bins;
|
||||
}
|
||||
|
||||
//
|
||||
// do just one FFT on samples[i0..i0+block]
|
||||
// real inputs, complex outputs.
|
||||
// output has block points.
|
||||
//
|
||||
std::vector<std::complex<float>> one_fft_c(
|
||||
const std::vector<float> &samples,
|
||||
int i0,
|
||||
int block,
|
||||
const char *why
|
||||
)
|
||||
{
|
||||
assert(i0 >= 0);
|
||||
assert(block > 1);
|
||||
|
||||
int nsamples = samples.size();
|
||||
|
||||
Plan *p = get_plan(block, why);
|
||||
fftwf_plan m_plan = p->cfwd_;
|
||||
|
||||
#if TIMING
|
||||
float t0 = now();
|
||||
#endif
|
||||
|
||||
fftwf_complex *m_in = (fftwf_complex *)fftwf_malloc(block * sizeof(fftwf_complex));
|
||||
fftwf_complex *m_out = (fftwf_complex *)fftwf_malloc(block * sizeof(fftwf_complex));
|
||||
assert(m_in && m_out);
|
||||
|
||||
for (int i = 0; i < block; i++)
|
||||
{
|
||||
if (i0 + i < nsamples)
|
||||
{
|
||||
m_in[i][0] = samples[i0 + i]; // real
|
||||
}
|
||||
else
|
||||
{
|
||||
m_in[i][0] = 0;
|
||||
}
|
||||
m_in[i][1] = 0; // imaginary
|
||||
}
|
||||
|
||||
fftwf_execute_dft(m_plan, m_in, m_out);
|
||||
|
||||
std::vector<std::complex<float>> out(block);
|
||||
|
||||
float norm = 1.0 / sqrt(block);
|
||||
for (int bi = 0; bi < block; bi++)
|
||||
{
|
||||
float re = m_out[bi][0];
|
||||
float im = m_out[bi][1];
|
||||
std::complex<float> c(re, im);
|
||||
c *= norm;
|
||||
out[bi] = c;
|
||||
}
|
||||
|
||||
fftwf_free(m_in);
|
||||
fftwf_free(m_out);
|
||||
|
||||
#if TIMING
|
||||
p->time_ += now() - t0;
|
||||
#endif
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<std::complex<float>> one_fft_cc(
|
||||
const std::vector<std::complex<float>> &samples,
|
||||
int i0,
|
||||
int block,
|
||||
const char *why
|
||||
)
|
||||
{
|
||||
assert(i0 >= 0);
|
||||
assert(block > 1);
|
||||
|
||||
int nsamples = samples.size();
|
||||
|
||||
Plan *p = get_plan(block, why);
|
||||
fftwf_plan m_plan = p->cfwd_;
|
||||
|
||||
#if TIMING
|
||||
float t0 = now();
|
||||
#endif
|
||||
|
||||
fftwf_complex *m_in = (fftwf_complex *)fftwf_malloc(block * sizeof(fftwf_complex));
|
||||
fftwf_complex *m_out = (fftwf_complex *)fftwf_malloc(block * sizeof(fftwf_complex));
|
||||
assert(m_in && m_out);
|
||||
|
||||
for (int i = 0; i < block; i++)
|
||||
{
|
||||
if (i0 + i < nsamples)
|
||||
{
|
||||
m_in[i][0] = samples[i0 + i].real();
|
||||
m_in[i][1] = samples[i0 + i].imag();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_in[i][0] = 0;
|
||||
m_in[i][1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fftwf_execute_dft(m_plan, m_in, m_out);
|
||||
|
||||
std::vector<std::complex<float>> out(block);
|
||||
|
||||
// float norm = 1.0 / sqrt(block);
|
||||
for (int bi = 0; bi < block; bi++)
|
||||
{
|
||||
float re = m_out[bi][0];
|
||||
float im = m_out[bi][1];
|
||||
std::complex<float> c(re, im);
|
||||
// c *= norm;
|
||||
out[bi] = c;
|
||||
}
|
||||
|
||||
fftwf_free(m_in);
|
||||
fftwf_free(m_out);
|
||||
|
||||
#if TIMING
|
||||
p->time_ += now() - t0;
|
||||
#endif
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<std::complex<float>> one_ifft_cc(
|
||||
const std::vector<std::complex<float>> &bins,
|
||||
const char *why
|
||||
)
|
||||
{
|
||||
int block = bins.size();
|
||||
|
||||
Plan *p = get_plan(block, why);
|
||||
fftwf_plan m_plan = p->crev_;
|
||||
|
||||
#if TIMING
|
||||
float t0 = now();
|
||||
#endif
|
||||
|
||||
fftwf_complex *m_in = (fftwf_complex *)fftwf_malloc(block * sizeof(fftwf_complex));
|
||||
fftwf_complex *m_out = (fftwf_complex *)fftwf_malloc(block * sizeof(fftwf_complex));
|
||||
assert(m_in && m_out);
|
||||
|
||||
for (int bi = 0; bi < block; bi++)
|
||||
{
|
||||
float re = bins[bi].real();
|
||||
float im = bins[bi].imag();
|
||||
m_in[bi][0] = re;
|
||||
m_in[bi][1] = im;
|
||||
}
|
||||
|
||||
fftwf_execute_dft(m_plan, m_in, m_out);
|
||||
|
||||
std::vector<std::complex<float>> out(block);
|
||||
float norm = 1.0 / sqrt(block);
|
||||
for (int i = 0; i < block; i++)
|
||||
{
|
||||
float re = m_out[i][0];
|
||||
float im = m_out[i][1];
|
||||
std::complex<float> c(re, im);
|
||||
c *= norm;
|
||||
out[i] = c;
|
||||
}
|
||||
|
||||
fftwf_free(m_in);
|
||||
fftwf_free(m_out);
|
||||
|
||||
#if TIMING
|
||||
p->time_ += now() - t0;
|
||||
#endif
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<float> one_ifft(const std::vector<std::complex<float>> &bins, const char *why)
|
||||
{
|
||||
int nbins = bins.size();
|
||||
int block = (nbins - 1) * 2;
|
||||
|
||||
Plan *p = get_plan(block, why);
|
||||
fftwf_plan m_plan = p->rev_;
|
||||
|
||||
#if TIMING
|
||||
float t0 = now();
|
||||
#endif
|
||||
|
||||
fftwf_complex *m_in = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * ((p->n_ / 2) + 1));
|
||||
float *m_out = (float *)fftwf_malloc(sizeof(float) * p->n_);
|
||||
|
||||
for (int bi = 0; bi < nbins; bi++)
|
||||
{
|
||||
float re = bins[bi].real();
|
||||
float im = bins[bi].imag();
|
||||
m_in[bi][0] = re;
|
||||
m_in[bi][1] = im;
|
||||
}
|
||||
|
||||
fftwf_execute_dft_c2r(m_plan, m_in, m_out);
|
||||
|
||||
std::vector<float> out(block);
|
||||
for (int i = 0; i < block; i++)
|
||||
{
|
||||
out[i] = m_out[i];
|
||||
}
|
||||
|
||||
fftwf_free(m_in);
|
||||
fftwf_free(m_out);
|
||||
|
||||
#if TIMING
|
||||
p->time_ += now() - t0;
|
||||
#endif
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
//
|
||||
// return the analytic signal for signal x,
|
||||
// just like scipy.signal.hilbert(), from which
|
||||
// this code is copied.
|
||||
//
|
||||
// the return value is x + iy, where y is the hilbert transform of x.
|
||||
//
|
||||
std::vector<std::complex<float>> analytic(const std::vector<float> &x, const char *why)
|
||||
{
|
||||
ulong n = x.size();
|
||||
|
||||
std::vector<std::complex<float>> y = one_fft_c(x, 0, n, why);
|
||||
assert(y.size() == n);
|
||||
|
||||
// leave y[0] alone.
|
||||
// float the first (positive) half of the spectrum.
|
||||
// zero out the second (negative) half of the spectrum.
|
||||
// y[n/2] is the nyquist bucket if n is even; leave it alone.
|
||||
if ((n % 2) == 0)
|
||||
{
|
||||
for (ulong i = 1; i < n / 2; i++)
|
||||
y[i] *= 2;
|
||||
for (ulong i = n / 2 + 1; i < n; i++)
|
||||
y[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ulong i = 1; i < (n + 1) / 2; i++)
|
||||
y[i] *= 2;
|
||||
for (ulong i = (n + 1) / 2; i < n; i++)
|
||||
y[i] = 0;
|
||||
}
|
||||
|
||||
std::vector<std::complex<float>> z = one_ifft_cc(y, why);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
//
|
||||
// general-purpose shift x in frequency by hz.
|
||||
// uses hilbert transform to avoid sidebands.
|
||||
// but it does wrap around at 0 hz and the nyquist frequency.
|
||||
//
|
||||
// note analytic() does an FFT over the whole signal, which
|
||||
// is expensive, and often re-used, but it turns out it
|
||||
// isn't a big factor in overall run-time.
|
||||
//
|
||||
// like weakutil.py's freq_shift().
|
||||
//
|
||||
std::vector<float> hilbert_shift(const std::vector<float> &x, float hz0, float hz1, int rate)
|
||||
{
|
||||
// y = scipy.signal.hilbert(x)
|
||||
std::vector<std::complex<float>> y = analytic(x, "hilbert_shift");
|
||||
assert(y.size() == x.size());
|
||||
|
||||
float dt = 1.0 / rate;
|
||||
int n = x.size();
|
||||
|
||||
std::vector<float> ret(n);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
// complex "local oscillator" at hz.
|
||||
float hz = hz0 + (i / (float)n) * (hz1 - hz0);
|
||||
std::complex<float> lo = std::exp(std::complex<float>(0.0, 2 * M_PI * hz * dt * i));
|
||||
ret[i] = (lo * y[i]).real();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
fft_stats()
|
||||
{
|
||||
for (int i = 0; i < nplans; i++)
|
||||
{
|
||||
Plan *p = plans[i];
|
||||
printf("%-13s %6d %9d %6.3f\n",
|
||||
p->why_,
|
||||
p->n_,
|
||||
p->uses_,
|
||||
#if TIMING
|
||||
p->time_
|
||||
#else
|
||||
0.0
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace FT8
|
46
ft8/fft.h
Normal file
46
ft8/fft.h
Normal file
@ -0,0 +1,46 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef FFT_H
|
||||
#define FFT_H
|
||||
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
#include <fftw3.h>
|
||||
|
||||
namespace FT8
|
||||
{
|
||||
class Plan;
|
||||
Plan *get_plan(int n, const char *why);
|
||||
|
||||
std::vector<std::complex<float>> one_fft(const std::vector<float> &samples, int i0, int block, const char *why, Plan *p);
|
||||
std::vector<float> one_ifft(const std::vector<std::complex<float>> &bins, const char *why);
|
||||
typedef std::vector<std::vector<std::complex<float>>> ffts_t;
|
||||
ffts_t ffts(const std::vector<float> &samples, int i0, int block, const char *why);
|
||||
std::vector<std::complex<float>> one_fft_c(const std::vector<float> &samples, int i0, int block, const char *why);
|
||||
std::vector<std::complex<float>> one_fft_cc(const std::vector<std::complex<float>> &samples, int i0, int block, const char *why);
|
||||
std::vector<std::complex<float>> one_ifft_cc(const std::vector<std::complex<float>> &bins, const char *why);
|
||||
std::vector<std::complex<float>> analytic(const std::vector<float> &x, const char *why);
|
||||
std::vector<float> hilbert_shift(const std::vector<float> &x, float hz0, float hz1, int rate);
|
||||
|
||||
} // namespace FT8
|
||||
|
||||
#endif
|
3914
ft8/ft8.cpp
Normal file
3914
ft8/ft8.cpp
Normal file
File diff suppressed because it is too large
Load Diff
65
ft8/ft8.h
Normal file
65
ft8/ft8.h
Normal file
@ -0,0 +1,65 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef ft8_h
|
||||
#define ft8_h
|
||||
|
||||
namespace FT8 {
|
||||
// Callback function to get the results
|
||||
typedef int (*cb_t)(
|
||||
int *a91,
|
||||
float hz0,
|
||||
float hz1,
|
||||
float off,
|
||||
const char *,
|
||||
float snr,
|
||||
int pass,
|
||||
int correct_bits
|
||||
);
|
||||
// same as Python class CDECODE
|
||||
//
|
||||
struct cdecode
|
||||
{
|
||||
float hz0;
|
||||
float hz1;
|
||||
float off;
|
||||
int *bits; // 174
|
||||
};
|
||||
|
||||
void entry(
|
||||
float xsamples[],
|
||||
int nsamples,
|
||||
int start,
|
||||
int rate,
|
||||
float min_hz,
|
||||
float max_hz,
|
||||
int hints1[],
|
||||
int hints2[],
|
||||
float time_left,
|
||||
float total_time_left,
|
||||
cb_t cb,
|
||||
int,
|
||||
struct cdecode *
|
||||
);
|
||||
|
||||
float set(char *param, char *val);
|
||||
} // namespace FT8
|
||||
|
||||
#endif
|
747
ft8/libldpc.cpp
Normal file
747
ft8/libldpc.cpp
Normal file
@ -0,0 +1,747 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Low Density Parity Check (LDPC) decoder for new FT8.
|
||||
//
|
||||
// given a 174-bit codeword as an array of log-likelihood of zero,
|
||||
// return a 174-bit corrected codeword, or zero-length array.
|
||||
// first 91 bits are the (systematic) plain-text.
|
||||
// codeword[i] = log ( P(x=0) / P(x=1) )
|
||||
//
|
||||
// this is an implementation of the sum-product algorithm
|
||||
// from Sarah Johnson's Iterative Error Correction book, and
|
||||
// Bernhard Leiner's http://www.bernh.net/media/download/papers/ldpc.pdf
|
||||
//
|
||||
// cc -O3 libldpc.c -shared -fPIC -o libldpc.so
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "arrays.h"
|
||||
|
||||
// float, long float, __float128
|
||||
#define REAL float
|
||||
|
||||
namespace FT8
|
||||
{
|
||||
//
|
||||
// does a 174-bit codeword pass the FT8's LDPC parity checks?
|
||||
// returns the number of parity checks that passed.
|
||||
// 83 means total success.
|
||||
//
|
||||
int ldpc_check(int codeword[])
|
||||
{
|
||||
int score = 0;
|
||||
|
||||
// Nm[83][7]
|
||||
for (int j = 0; j < 83; j++)
|
||||
{
|
||||
int x = 0;
|
||||
for (int ii1 = 0; ii1 < 7; ii1++)
|
||||
{
|
||||
int i1 = Nm[j][ii1] - 1;
|
||||
if (i1 >= 0)
|
||||
{
|
||||
x ^= codeword[i1];
|
||||
}
|
||||
}
|
||||
if (x == 0)
|
||||
score++;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
// llcodeword is 174 log-likelihoods.
|
||||
// plain is a return value, 174 ints, to be 0 or 1.
|
||||
// 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)
|
||||
{
|
||||
REAL m[83][174];
|
||||
REAL e[83][174];
|
||||
REAL codeword[174];
|
||||
int best_score = -1;
|
||||
int best_cw[174];
|
||||
|
||||
// to translate from log-likelihood x to probability p,
|
||||
// p = e**x / (1 + e**x)
|
||||
// it's P(zero), not P(one).
|
||||
for (int i = 0; i < 174; i++)
|
||||
{
|
||||
REAL ex = expl(llcodeword[i]);
|
||||
REAL p = ex / (1.0 + ex);
|
||||
codeword[i] = p;
|
||||
}
|
||||
|
||||
// m[j][i] tells the j'th check bit the P(zero) of
|
||||
// each of its codeword inputs, based on check
|
||||
// bits other than j.
|
||||
for (int i = 0; i < 174; i++)
|
||||
for (int j = 0; j < 83; j++)
|
||||
m[j][i] = codeword[i];
|
||||
|
||||
// e[j][i]: each check j tells each codeword bit i the
|
||||
// probability of the bit being zero based
|
||||
// on the *other* bits contributing to that check.
|
||||
for (int i = 0; i < 174; i++)
|
||||
for (int j = 0; j < 83; j++)
|
||||
e[j][i] = 0.0;
|
||||
|
||||
for (int iter = 0; iter < iters; iter++)
|
||||
{
|
||||
|
||||
for (int j = 0; j < 83; j++)
|
||||
{
|
||||
for (int ii1 = 0; ii1 < 7; ii1++)
|
||||
{
|
||||
int i1 = 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;
|
||||
if (i2 >= 0 && i2 != i1)
|
||||
{
|
||||
// tmp ranges from 1.0 to -1.0, for
|
||||
// definitely zero to definitely one.
|
||||
float tmp = 1.0 - 2.0 * (1.0 - m[j][i2]);
|
||||
a *= tmp;
|
||||
}
|
||||
}
|
||||
// a ranges from 1.0 to -1.0, meaning
|
||||
// bit i1 should be zero .. one.
|
||||
// so e[j][i1] will be 0.0 .. 1.0 meaning
|
||||
// bit i1 is one .. zero.
|
||||
REAL tmp = 0.5 + 0.5 * a;
|
||||
e[j][i1] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
int cw[174];
|
||||
for (int i = 0; i < 174; i++)
|
||||
{
|
||||
REAL q0 = codeword[i];
|
||||
REAL q1 = 1.0 - q0;
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
int j2 = Mn[i][j] - 1;
|
||||
q0 *= e[j2][i];
|
||||
q1 *= 1.0 - e[j2][i];
|
||||
}
|
||||
// REAL p = q0 / (q0 + q1);
|
||||
REAL p;
|
||||
if (q0 == 0.0)
|
||||
{
|
||||
p = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = 1.0 / (1.0 + (q1 / q0));
|
||||
}
|
||||
cw[i] = (p <= 0.5);
|
||||
}
|
||||
int score = ldpc_check(cw);
|
||||
if (score == 83)
|
||||
{
|
||||
for (int i = 0; i < 174; i++)
|
||||
plain[i] = cw[i];
|
||||
*ok = 83;
|
||||
return;
|
||||
}
|
||||
|
||||
if (score > best_score)
|
||||
{
|
||||
for (int i = 0; i < 174; i++)
|
||||
best_cw[i] = cw[i];
|
||||
best_score = score;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 174; i++)
|
||||
{
|
||||
for (int ji1 = 0; ji1 < 3; ji1++)
|
||||
{
|
||||
int j1 = 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;
|
||||
if (j1 != j2)
|
||||
{
|
||||
q0 *= e[j2][i];
|
||||
q1 *= 1.0 - e[j2][i];
|
||||
}
|
||||
}
|
||||
// REAL p = q0 / (q0 + q1);
|
||||
REAL p;
|
||||
if (q0 == 0.0)
|
||||
{
|
||||
p = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = 1.0 / (1.0 + (q1 / q0));
|
||||
}
|
||||
m[j1][i] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decode didn't work, return best guess.
|
||||
for (int i = 0; i < 174; i++)
|
||||
plain[i] = best_cw[i];
|
||||
|
||||
*ok = best_score;
|
||||
}
|
||||
|
||||
// thank you Douglas Bagnall
|
||||
// https://math.stackexchange.com/a/446411
|
||||
float fast_tanh(float x)
|
||||
{
|
||||
if (x < -7.6)
|
||||
{
|
||||
return -0.999;
|
||||
}
|
||||
if (x > 7.6)
|
||||
{
|
||||
return 0.999;
|
||||
}
|
||||
float x2 = x * x;
|
||||
float a = x * (135135.0f + x2 * (17325.0f + x2 * (378.0f + x2)));
|
||||
float b = 135135.0f + x2 * (62370.0f + x2 * (3150.0f + x2 * 28.0f));
|
||||
return a / b;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#define TANGRAN 0.01
|
||||
static float tanhtable[];
|
||||
|
||||
float
|
||||
table_tanh(float x)
|
||||
{
|
||||
int ind = (x - (-5.0)) / TANGRAN;
|
||||
if(ind < 0){
|
||||
return -1.0;
|
||||
}
|
||||
if(ind >= 1000){
|
||||
return 1.0;
|
||||
}
|
||||
return tanhtable[ind];
|
||||
}
|
||||
#endif
|
||||
|
||||
// codeword is 174 log-likelihoods.
|
||||
// plain is a return value, 174 ints, to be 0 or 1.
|
||||
// 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)
|
||||
{
|
||||
REAL m[83][174];
|
||||
REAL e[83][174];
|
||||
int best_score = -1;
|
||||
int best_cw[174];
|
||||
|
||||
for (int i = 0; i < 174; i++)
|
||||
for (int j = 0; j < 83; j++)
|
||||
m[j][i] = codeword[i];
|
||||
|
||||
for (int i = 0; i < 174; i++)
|
||||
for (int j = 0; j < 83; j++)
|
||||
e[j][i] = 0.0;
|
||||
|
||||
for (int iter = 0; iter < iters; iter++)
|
||||
{
|
||||
for (int j = 0; j < 83; j++)
|
||||
{
|
||||
for (int ii1 = 0; ii1 < 7; ii1++)
|
||||
{
|
||||
int i1 = 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;
|
||||
if (i2 >= 0 && i2 != i1)
|
||||
{
|
||||
// a *= table_tanh(m[j][i2] / 2.0);
|
||||
a *= fast_tanh(m[j][i2] / 2.0);
|
||||
}
|
||||
}
|
||||
REAL tmp;
|
||||
if (a >= 0.999)
|
||||
{
|
||||
tmp = 7.6;
|
||||
}
|
||||
else if (a <= -0.999)
|
||||
{
|
||||
tmp = -7.6;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = log((1 + a) / (1 - a));
|
||||
}
|
||||
e[j][i1] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
int cw[174];
|
||||
for (int i = 0; i < 174; i++)
|
||||
{
|
||||
REAL l = codeword[i];
|
||||
for (int j = 0; j < 3; j++)
|
||||
l += e[Mn[i][j] - 1][i];
|
||||
cw[i] = (l <= 0.0);
|
||||
}
|
||||
int score = ldpc_check(cw);
|
||||
if (score == 83)
|
||||
{
|
||||
for (int i = 0; i < 174; i++)
|
||||
plain[i] = cw[i];
|
||||
*ok = 83;
|
||||
return;
|
||||
}
|
||||
|
||||
if (score > best_score)
|
||||
{
|
||||
for (int i = 0; i < 174; i++)
|
||||
best_cw[i] = cw[i];
|
||||
best_score = score;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 174; i++)
|
||||
{
|
||||
for (int ji1 = 0; ji1 < 3; ji1++)
|
||||
{
|
||||
int j1 = Mn[i][ji1] - 1;
|
||||
REAL l = codeword[i];
|
||||
for (int ji2 = 0; ji2 < 3; ji2++)
|
||||
{
|
||||
int j2 = Mn[i][ji2] - 1;
|
||||
if (j1 != j2)
|
||||
{
|
||||
l += e[j2][i];
|
||||
}
|
||||
}
|
||||
m[j1][i] = l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decode didn't work, return best guess.
|
||||
for (int i = 0; i < 174; i++)
|
||||
plain[i] = best_cw[i];
|
||||
|
||||
*ok = best_score;
|
||||
}
|
||||
|
||||
//
|
||||
// check the FT8 CRC-14
|
||||
//
|
||||
|
||||
void 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 };
|
||||
|
||||
// the new FT8 polynomial for 14-bit CRC, 0x2757,
|
||||
// with leading 1 bit.
|
||||
int div[] = {1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1};
|
||||
|
||||
// append 14 zeros.
|
||||
int *msg = (int *)malloc(sizeof(int) * (msglen + 14));
|
||||
for (int i = 0; i < msglen + 14; i++)
|
||||
{
|
||||
if (i < msglen)
|
||||
{
|
||||
msg[i] = msg1[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
msg[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < msglen; i++)
|
||||
{
|
||||
if (msg[i])
|
||||
{
|
||||
for (int j = 0; j < 15; j++)
|
||||
{
|
||||
msg[i + j] = (msg[i + j] + div[j]) % 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 14; i++)
|
||||
{
|
||||
out[i] = msg[msglen + i];
|
||||
}
|
||||
|
||||
free(msg);
|
||||
}
|
||||
|
||||
// rows is 91, cols is 174.
|
||||
// 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)
|
||||
// gauss_jordan(int rows, int cols, int m[cols][2*rows], int which[rows], int *ok)
|
||||
{
|
||||
*ok = 0;
|
||||
|
||||
assert(rows == 91);
|
||||
assert(cols == 174);
|
||||
|
||||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
if (m[row][row] != 1)
|
||||
{
|
||||
for (int row1 = row + 1; row1 < cols; row1++)
|
||||
{
|
||||
if (m[row1][row] == 1)
|
||||
{
|
||||
// swap m[row] and m[row1]
|
||||
for (int col = 0; col < 2 * rows; col++)
|
||||
{
|
||||
int tmp = m[row][col];
|
||||
m[row][col] = m[row1][col];
|
||||
m[row1][col] = tmp;
|
||||
}
|
||||
int tmp = which[row];
|
||||
which[row] = which[row1];
|
||||
which[row1] = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m[row][row] != 1)
|
||||
{
|
||||
// could not invert
|
||||
*ok = 0;
|
||||
return;
|
||||
}
|
||||
// lazy creation of identity matrix in the upper-right quarter
|
||||
m[row][rows + row] = (m[row][rows + row] + 1) % 2;
|
||||
// now eliminate
|
||||
for (int row1 = 0; row1 < cols; row1++)
|
||||
{
|
||||
if (row1 == row)
|
||||
continue;
|
||||
if (m[row1][row] != 0)
|
||||
{
|
||||
for (int col = 0; col < 2 * rows; col++)
|
||||
{
|
||||
m[row1][col] = (m[row1][col] + m[row][col]) % 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*ok = 1;
|
||||
}
|
||||
|
||||
// # given a 174-bit codeword as an array of log-likelihood of zero,
|
||||
// # return a 87-bit plain text, or zero-length array.
|
||||
// # this is an implementation of the sum-product algorithm
|
||||
// # from Sarah Johnson's Iterative Error Correction book.
|
||||
// # codeword[i] = log ( P(x=0) / P(x=1) )
|
||||
// def ldpc_decode(self, codeword):
|
||||
// # 174 codeword bits
|
||||
// # 87 parity checks
|
||||
//
|
||||
// # Mji
|
||||
// # each codeword bit i tells each parity check j
|
||||
// # what the bit's log-likelihood of being 0 is
|
||||
// # based on information *other* than from that
|
||||
// # parity check.
|
||||
// m = numpy.zeros((87, 174))
|
||||
//
|
||||
// # Eji
|
||||
// # each check j tells each codeword bit i the
|
||||
// # log likelihood of the bit being zero based
|
||||
// # on the *other* bits in that check.
|
||||
// e = numpy.zeros((87, 174))
|
||||
//
|
||||
// for i in range(0, 174):
|
||||
// for j in range(0, 87):
|
||||
// m[j][i] = codeword[i]
|
||||
//
|
||||
// for iter in range(0, 50):
|
||||
// # messages from checks to bits.
|
||||
// # for each parity check
|
||||
// for j in range(0, 87):
|
||||
// # for each bit mentioned in this parity check
|
||||
// for i in Nm[j]:
|
||||
// if i <= 0:
|
||||
// continue
|
||||
// a = 1
|
||||
// # for each other bit mentioned in this parity check
|
||||
// for ii in Nm[j]:
|
||||
// if ii != i:
|
||||
// a *= math.tanh(m[j][ii-1] / 2.0)
|
||||
// e[j][i-1] = math.log((1 + a) / (1 - a))
|
||||
//
|
||||
// # decide if we are done -- compute the corrected codeword,
|
||||
// # see if the parity check succeeds.
|
||||
// cw = numpy.zeros(174, dtype=numpy.int32)
|
||||
// for i in range(0, 174):
|
||||
// # sum the log likelihoods for codeword bit i being 0.
|
||||
// l = codeword[i]
|
||||
// for j in Mn[i]:
|
||||
// l += e[j-1][i]
|
||||
// if l > 0:
|
||||
// cw[i] = 0
|
||||
// else:
|
||||
// cw[i] = 1
|
||||
// if self.ldpc_check(cw):
|
||||
// # success!
|
||||
// # it's a systematic code, though the plain-text bits are scattered.
|
||||
// # collect them.
|
||||
// decoded = cw[colorder]
|
||||
// decoded = decoded[-87:]
|
||||
// return decoded
|
||||
//
|
||||
// # messages from bits to checks.
|
||||
// for i in range(0, 174):
|
||||
// for j in Mn[i]:
|
||||
// l = codeword[i]
|
||||
// for jj in Mn[i]:
|
||||
// if jj != j:
|
||||
// l += e[jj-1][i]
|
||||
// m[j-1][i] = l
|
||||
//
|
||||
// # could not decode.
|
||||
// return numpy.array([])
|
||||
|
||||
#if 0
|
||||
static float tanhtable[] = {
|
||||
-0.99990920, -0.99990737, -0.99990550, -0.99990359, -0.99990164,
|
||||
-0.99989966, -0.99989763, -0.99989556, -0.99989345, -0.99989130,
|
||||
-0.99988910, -0.99988686, -0.99988458, -0.99988225, -0.99987987,
|
||||
-0.99987744, -0.99987496, -0.99987244, -0.99986986, -0.99986723,
|
||||
-0.99986455, -0.99986182, -0.99985902, -0.99985618, -0.99985327,
|
||||
-0.99985031, -0.99984728, -0.99984420, -0.99984105, -0.99983784,
|
||||
-0.99983457, -0.99983122, -0.99982781, -0.99982434, -0.99982079,
|
||||
-0.99981717, -0.99981348, -0.99980971, -0.99980586, -0.99980194,
|
||||
-0.99979794, -0.99979386, -0.99978970, -0.99978545, -0.99978111,
|
||||
-0.99977669, -0.99977218, -0.99976758, -0.99976289, -0.99975810,
|
||||
-0.99975321, -0.99974823, -0.99974314, -0.99973795, -0.99973266,
|
||||
-0.99972726, -0.99972175, -0.99971613, -0.99971040, -0.99970455,
|
||||
-0.99969858, -0.99969249, -0.99968628, -0.99967994, -0.99967348,
|
||||
-0.99966688, -0.99966016, -0.99965329, -0.99964629, -0.99963914,
|
||||
-0.99963186, -0.99962442, -0.99961683, -0.99960910, -0.99960120,
|
||||
-0.99959315, -0.99958493, -0.99957655, -0.99956799, -0.99955927,
|
||||
-0.99955037, -0.99954129, -0.99953202, -0.99952257, -0.99951293,
|
||||
-0.99950309, -0.99949305, -0.99948282, -0.99947237, -0.99946171,
|
||||
-0.99945084, -0.99943975, -0.99942844, -0.99941690, -0.99940512,
|
||||
-0.99939311, -0.99938085, -0.99936835, -0.99935559, -0.99934258,
|
||||
-0.99932930, -0.99931576, -0.99930194, -0.99928784, -0.99927346,
|
||||
-0.99925879, -0.99924382, -0.99922855, -0.99921297, -0.99919708,
|
||||
-0.99918087, -0.99916432, -0.99914745, -0.99913024, -0.99911267,
|
||||
-0.99909476, -0.99907648, -0.99905783, -0.99903881, -0.99901940,
|
||||
-0.99899960, -0.99897940, -0.99895879, -0.99893777, -0.99891632,
|
||||
-0.99889444, -0.99887212, -0.99884935, -0.99882612, -0.99880242,
|
||||
-0.99877824, -0.99875358, -0.99872841, -0.99870274, -0.99867655,
|
||||
-0.99864983, -0.99862258, -0.99859477, -0.99856640, -0.99853747,
|
||||
-0.99850794, -0.99847782, -0.99844710, -0.99841575, -0.99838377,
|
||||
-0.99835115, -0.99831787, -0.99828392, -0.99824928, -0.99821395,
|
||||
-0.99817790, -0.99814112, -0.99810361, -0.99806533, -0.99802629,
|
||||
-0.99798646, -0.99794582, -0.99790437, -0.99786208, -0.99781894,
|
||||
-0.99777493, -0.99773003, -0.99768423, -0.99763750, -0.99758983,
|
||||
-0.99754120, -0.99749159, -0.99744099, -0.99738936, -0.99733669,
|
||||
-0.99728296, -0.99722815, -0.99717223, -0.99711519, -0.99705700,
|
||||
-0.99699764, -0.99693708, -0.99687530, -0.99681228, -0.99674798,
|
||||
-0.99668240, -0.99661549, -0.99654724, -0.99647761, -0.99640658,
|
||||
-0.99633412, -0.99626020, -0.99618480, -0.99610788, -0.99602941,
|
||||
-0.99594936, -0.99586770, -0.99578440, -0.99569942, -0.99561273,
|
||||
-0.99552430, -0.99543409, -0.99534207, -0.99524820, -0.99515244,
|
||||
-0.99505475, -0.99495511, -0.99485345, -0.99474976, -0.99464398,
|
||||
-0.99453608, -0.99442601, -0.99431373, -0.99419919, -0.99408235,
|
||||
-0.99396317, -0.99384159, -0.99371757, -0.99359107, -0.99346202,
|
||||
-0.99333039, -0.99319611, -0.99305914, -0.99291942, -0.99277690,
|
||||
-0.99263152, -0.99248323, -0.99233196, -0.99217766, -0.99202027,
|
||||
-0.99185972, -0.99169596, -0.99152892, -0.99135853, -0.99118473,
|
||||
-0.99100745, -0.99082663, -0.99064218, -0.99045404, -0.99026214,
|
||||
-0.99006640, -0.98986674, -0.98966309, -0.98945538, -0.98924351,
|
||||
-0.98902740, -0.98880698, -0.98858216, -0.98835285, -0.98811896,
|
||||
-0.98788040, -0.98763708, -0.98738891, -0.98713578, -0.98687761,
|
||||
-0.98661430, -0.98634574, -0.98607182, -0.98579245, -0.98550752,
|
||||
-0.98521692, -0.98492053, -0.98461825, -0.98430995, -0.98399553,
|
||||
-0.98367486, -0.98334781, -0.98301427, -0.98267411, -0.98232720,
|
||||
-0.98197340, -0.98161259, -0.98124462, -0.98086936, -0.98048667,
|
||||
-0.98009640, -0.97969840, -0.97929252, -0.97887862, -0.97845654,
|
||||
-0.97802611, -0.97758719, -0.97713959, -0.97668317, -0.97621774,
|
||||
-0.97574313, -0.97525917, -0.97476568, -0.97426247, -0.97374936,
|
||||
-0.97322616, -0.97269268, -0.97214872, -0.97159408, -0.97102855,
|
||||
-0.97045194, -0.96986402, -0.96926459, -0.96865342, -0.96803030,
|
||||
-0.96739500, -0.96674729, -0.96608693, -0.96541369, -0.96472732,
|
||||
-0.96402758, -0.96331422, -0.96258698, -0.96184561, -0.96108983,
|
||||
-0.96031939, -0.95953401, -0.95873341, -0.95791731, -0.95708542,
|
||||
-0.95623746, -0.95537312, -0.95449211, -0.95359412, -0.95267884,
|
||||
-0.95174596, -0.95079514, -0.94982608, -0.94883842, -0.94783185,
|
||||
-0.94680601, -0.94576057, -0.94469516, -0.94360942, -0.94250301,
|
||||
-0.94137554, -0.94022664, -0.93905593, -0.93786303, -0.93664754,
|
||||
-0.93540907, -0.93414721, -0.93286155, -0.93155168, -0.93021718,
|
||||
-0.92885762, -0.92747257, -0.92606158, -0.92462422, -0.92316003,
|
||||
-0.92166855, -0.92014933, -0.91860189, -0.91702576, -0.91542046,
|
||||
-0.91378549, -0.91212037, -0.91042459, -0.90869766, -0.90693905,
|
||||
-0.90514825, -0.90332474, -0.90146799, -0.89957745, -0.89765260,
|
||||
-0.89569287, -0.89369773, -0.89166660, -0.88959892, -0.88749413,
|
||||
-0.88535165, -0.88317089, -0.88095127, -0.87869219, -0.87639307,
|
||||
-0.87405329, -0.87167225, -0.86924933, -0.86678393, -0.86427541,
|
||||
-0.86172316, -0.85912654, -0.85648492, -0.85379765, -0.85106411,
|
||||
-0.84828364, -0.84545560, -0.84257933, -0.83965418, -0.83667949,
|
||||
-0.83365461, -0.83057887, -0.82745161, -0.82427217, -0.82103988,
|
||||
-0.81775408, -0.81441409, -0.81101926, -0.80756892, -0.80406239,
|
||||
-0.80049902, -0.79687814, -0.79319910, -0.78946122, -0.78566386,
|
||||
-0.78180636, -0.77788807, -0.77390834, -0.76986654, -0.76576202,
|
||||
-0.76159416, -0.75736232, -0.75306590, -0.74870429, -0.74427687,
|
||||
-0.73978305, -0.73522225, -0.73059390, -0.72589741, -0.72113225,
|
||||
-0.71629787, -0.71139373, -0.70641932, -0.70137413, -0.69625767,
|
||||
-0.69106947, -0.68580906, -0.68047601, -0.67506987, -0.66959026,
|
||||
-0.66403677, -0.65840904, -0.65270671, -0.64692945, -0.64107696,
|
||||
-0.63514895, -0.62914516, -0.62306535, -0.61690930, -0.61067683,
|
||||
-0.60436778, -0.59798200, -0.59151940, -0.58497988, -0.57836341,
|
||||
-0.57166997, -0.56489955, -0.55805222, -0.55112803, -0.54412710,
|
||||
-0.53704957, -0.52989561, -0.52266543, -0.51535928, -0.50797743,
|
||||
-0.50052021, -0.49298797, -0.48538109, -0.47770001, -0.46994520,
|
||||
-0.46211716, -0.45421643, -0.44624361, -0.43819931, -0.43008421,
|
||||
-0.42189901, -0.41364444, -0.40532131, -0.39693043, -0.38847268,
|
||||
-0.37994896, -0.37136023, -0.36270747, -0.35399171, -0.34521403,
|
||||
-0.33637554, -0.32747739, -0.31852078, -0.30950692, -0.30043710,
|
||||
-0.29131261, -0.28213481, -0.27290508, -0.26362484, -0.25429553,
|
||||
-0.24491866, -0.23549575, -0.22602835, -0.21651806, -0.20696650,
|
||||
-0.19737532, -0.18774621, -0.17808087, -0.16838105, -0.15864850,
|
||||
-0.14888503, -0.13909245, -0.12927258, -0.11942730, -0.10955847,
|
||||
-0.09966799, -0.08975778, -0.07982977, -0.06988589, -0.05992810,
|
||||
-0.04995837, -0.03997868, -0.02999100, -0.01999733, -0.00999967,
|
||||
-0.00000000, 0.00999967, 0.01999733, 0.02999100, 0.03997868,
|
||||
0.04995837, 0.05992810, 0.06988589, 0.07982977, 0.08975778,
|
||||
0.09966799, 0.10955847, 0.11942730, 0.12927258, 0.13909245,
|
||||
0.14888503, 0.15864850, 0.16838105, 0.17808087, 0.18774621,
|
||||
0.19737532, 0.20696650, 0.21651806, 0.22602835, 0.23549575,
|
||||
0.24491866, 0.25429553, 0.26362484, 0.27290508, 0.28213481,
|
||||
0.29131261, 0.30043710, 0.30950692, 0.31852078, 0.32747739,
|
||||
0.33637554, 0.34521403, 0.35399171, 0.36270747, 0.37136023,
|
||||
0.37994896, 0.38847268, 0.39693043, 0.40532131, 0.41364444,
|
||||
0.42189901, 0.43008421, 0.43819931, 0.44624361, 0.45421643,
|
||||
0.46211716, 0.46994520, 0.47770001, 0.48538109, 0.49298797,
|
||||
0.50052021, 0.50797743, 0.51535928, 0.52266543, 0.52989561,
|
||||
0.53704957, 0.54412710, 0.55112803, 0.55805222, 0.56489955,
|
||||
0.57166997, 0.57836341, 0.58497988, 0.59151940, 0.59798200,
|
||||
0.60436778, 0.61067683, 0.61690930, 0.62306535, 0.62914516,
|
||||
0.63514895, 0.64107696, 0.64692945, 0.65270671, 0.65840904,
|
||||
0.66403677, 0.66959026, 0.67506987, 0.68047601, 0.68580906,
|
||||
0.69106947, 0.69625767, 0.70137413, 0.70641932, 0.71139373,
|
||||
0.71629787, 0.72113225, 0.72589741, 0.73059390, 0.73522225,
|
||||
0.73978305, 0.74427687, 0.74870429, 0.75306590, 0.75736232,
|
||||
0.76159416, 0.76576202, 0.76986654, 0.77390834, 0.77788807,
|
||||
0.78180636, 0.78566386, 0.78946122, 0.79319910, 0.79687814,
|
||||
0.80049902, 0.80406239, 0.80756892, 0.81101926, 0.81441409,
|
||||
0.81775408, 0.82103988, 0.82427217, 0.82745161, 0.83057887,
|
||||
0.83365461, 0.83667949, 0.83965418, 0.84257933, 0.84545560,
|
||||
0.84828364, 0.85106411, 0.85379765, 0.85648492, 0.85912654,
|
||||
0.86172316, 0.86427541, 0.86678393, 0.86924933, 0.87167225,
|
||||
0.87405329, 0.87639307, 0.87869219, 0.88095127, 0.88317089,
|
||||
0.88535165, 0.88749413, 0.88959892, 0.89166660, 0.89369773,
|
||||
0.89569287, 0.89765260, 0.89957745, 0.90146799, 0.90332474,
|
||||
0.90514825, 0.90693905, 0.90869766, 0.91042459, 0.91212037,
|
||||
0.91378549, 0.91542046, 0.91702576, 0.91860189, 0.92014933,
|
||||
0.92166855, 0.92316003, 0.92462422, 0.92606158, 0.92747257,
|
||||
0.92885762, 0.93021718, 0.93155168, 0.93286155, 0.93414721,
|
||||
0.93540907, 0.93664754, 0.93786303, 0.93905593, 0.94022664,
|
||||
0.94137554, 0.94250301, 0.94360942, 0.94469516, 0.94576057,
|
||||
0.94680601, 0.94783185, 0.94883842, 0.94982608, 0.95079514,
|
||||
0.95174596, 0.95267884, 0.95359412, 0.95449211, 0.95537312,
|
||||
0.95623746, 0.95708542, 0.95791731, 0.95873341, 0.95953401,
|
||||
0.96031939, 0.96108983, 0.96184561, 0.96258698, 0.96331422,
|
||||
0.96402758, 0.96472732, 0.96541369, 0.96608693, 0.96674729,
|
||||
0.96739500, 0.96803030, 0.96865342, 0.96926459, 0.96986402,
|
||||
0.97045194, 0.97102855, 0.97159408, 0.97214872, 0.97269268,
|
||||
0.97322616, 0.97374936, 0.97426247, 0.97476568, 0.97525917,
|
||||
0.97574313, 0.97621774, 0.97668317, 0.97713959, 0.97758719,
|
||||
0.97802611, 0.97845654, 0.97887862, 0.97929252, 0.97969840,
|
||||
0.98009640, 0.98048667, 0.98086936, 0.98124462, 0.98161259,
|
||||
0.98197340, 0.98232720, 0.98267411, 0.98301427, 0.98334781,
|
||||
0.98367486, 0.98399553, 0.98430995, 0.98461825, 0.98492053,
|
||||
0.98521692, 0.98550752, 0.98579245, 0.98607182, 0.98634574,
|
||||
0.98661430, 0.98687761, 0.98713578, 0.98738891, 0.98763708,
|
||||
0.98788040, 0.98811896, 0.98835285, 0.98858216, 0.98880698,
|
||||
0.98902740, 0.98924351, 0.98945538, 0.98966309, 0.98986674,
|
||||
0.99006640, 0.99026214, 0.99045404, 0.99064218, 0.99082663,
|
||||
0.99100745, 0.99118473, 0.99135853, 0.99152892, 0.99169596,
|
||||
0.99185972, 0.99202027, 0.99217766, 0.99233196, 0.99248323,
|
||||
0.99263152, 0.99277690, 0.99291942, 0.99305914, 0.99319611,
|
||||
0.99333039, 0.99346202, 0.99359107, 0.99371757, 0.99384159,
|
||||
0.99396317, 0.99408235, 0.99419919, 0.99431373, 0.99442601,
|
||||
0.99453608, 0.99464398, 0.99474976, 0.99485345, 0.99495511,
|
||||
0.99505475, 0.99515244, 0.99524820, 0.99534207, 0.99543409,
|
||||
0.99552430, 0.99561273, 0.99569942, 0.99578440, 0.99586770,
|
||||
0.99594936, 0.99602941, 0.99610788, 0.99618480, 0.99626020,
|
||||
0.99633412, 0.99640658, 0.99647761, 0.99654724, 0.99661549,
|
||||
0.99668240, 0.99674798, 0.99681228, 0.99687530, 0.99693708,
|
||||
0.99699764, 0.99705700, 0.99711519, 0.99717223, 0.99722815,
|
||||
0.99728296, 0.99733669, 0.99738936, 0.99744099, 0.99749159,
|
||||
0.99754120, 0.99758983, 0.99763750, 0.99768423, 0.99773003,
|
||||
0.99777493, 0.99781894, 0.99786208, 0.99790437, 0.99794582,
|
||||
0.99798646, 0.99802629, 0.99806533, 0.99810361, 0.99814112,
|
||||
0.99817790, 0.99821395, 0.99824928, 0.99828392, 0.99831787,
|
||||
0.99835115, 0.99838377, 0.99841575, 0.99844710, 0.99847782,
|
||||
0.99850794, 0.99853747, 0.99856640, 0.99859477, 0.99862258,
|
||||
0.99864983, 0.99867655, 0.99870274, 0.99872841, 0.99875358,
|
||||
0.99877824, 0.99880242, 0.99882612, 0.99884935, 0.99887212,
|
||||
0.99889444, 0.99891632, 0.99893777, 0.99895879, 0.99897940,
|
||||
0.99899960, 0.99901940, 0.99903881, 0.99905783, 0.99907648,
|
||||
0.99909476, 0.99911267, 0.99913024, 0.99914745, 0.99916432,
|
||||
0.99918087, 0.99919708, 0.99921297, 0.99922855, 0.99924382,
|
||||
0.99925879, 0.99927346, 0.99928784, 0.99930194, 0.99931576,
|
||||
0.99932930, 0.99934258, 0.99935559, 0.99936835, 0.99938085,
|
||||
0.99939311, 0.99940512, 0.99941690, 0.99942844, 0.99943975,
|
||||
0.99945084, 0.99946171, 0.99947237, 0.99948282, 0.99949305,
|
||||
0.99950309, 0.99951293, 0.99952257, 0.99953202, 0.99954129,
|
||||
0.99955037, 0.99955927, 0.99956799, 0.99957655, 0.99958493,
|
||||
0.99959315, 0.99960120, 0.99960910, 0.99961683, 0.99962442,
|
||||
0.99963186, 0.99963914, 0.99964629, 0.99965329, 0.99966016,
|
||||
0.99966688, 0.99967348, 0.99967994, 0.99968628, 0.99969249,
|
||||
0.99969858, 0.99970455, 0.99971040, 0.99971613, 0.99972175,
|
||||
0.99972726, 0.99973266, 0.99973795, 0.99974314, 0.99974823,
|
||||
0.99975321, 0.99975810, 0.99976289, 0.99976758, 0.99977218,
|
||||
0.99977669, 0.99978111, 0.99978545, 0.99978970, 0.99979386,
|
||||
0.99979794, 0.99980194, 0.99980586, 0.99980971, 0.99981348,
|
||||
0.99981717, 0.99982079, 0.99982434, 0.99982781, 0.99983122,
|
||||
0.99983457, 0.99983784, 0.99984105, 0.99984420, 0.99984728,
|
||||
0.99985031, 0.99985327, 0.99985618, 0.99985902, 0.99986182,
|
||||
0.99986455, 0.99986723, 0.99986986, 0.99987244, 0.99987496,
|
||||
0.99987744, 0.99987987, 0.99988225, 0.99988458, 0.99988686,
|
||||
0.99988910, 0.99989130, 0.99989345, 0.99989556, 0.99989763,
|
||||
0.99989966, 0.99990164, 0.99990359, 0.99990550, 0.99990737,
|
||||
0.99990920,
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace FT8
|
36
ft8/libldpc.h
Normal file
36
ft8/libldpc.h
Normal file
@ -0,0 +1,36 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef libldpc_h
|
||||
#define libldpc_h
|
||||
|
||||
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);
|
||||
|
||||
|
||||
} // namespace FT8
|
||||
|
||||
#endif // libldpc_h
|
489
ft8/osd.cpp
Normal file
489
ft8/osd.cpp
Normal file
@ -0,0 +1,489 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// ordered statistics decoder for LDPC and new FT8.
|
||||
// idea from wsjt-x.
|
||||
//
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "libldpc.h"
|
||||
#include "osd.h"
|
||||
|
||||
namespace FT8 {
|
||||
//
|
||||
// check the FT8 CRC-14
|
||||
//
|
||||
int check_crc(const int a91[91])
|
||||
{
|
||||
int aa[91];
|
||||
int non_zero = 0;
|
||||
for (int i = 0; i < 91; i++)
|
||||
{
|
||||
if (i < 77)
|
||||
{
|
||||
aa[i] = a91[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
aa[i] = 0;
|
||||
}
|
||||
if (aa[i])
|
||||
non_zero++;
|
||||
}
|
||||
int out1[14];
|
||||
|
||||
// don't bother with all-zero messages.
|
||||
if (non_zero == 0)
|
||||
return 0;
|
||||
|
||||
// why 82? why not 77?
|
||||
ft8_crc(aa, 82, out1);
|
||||
|
||||
for (int i = 0; i < 14; i++)
|
||||
{
|
||||
if (out1[i] != a91[91 - 14 + i])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// plain is 91 bits of plain-text.
|
||||
// returns a 174-bit codeword.
|
||||
// mimics wsjt-x's encode174_91.f90.
|
||||
void ldpc_encode(int plain[91], int codeword[174])
|
||||
{
|
||||
// the systematic 91 bits.
|
||||
for (int i = 0; i < 91; i++)
|
||||
{
|
||||
codeword[i] = plain[i];
|
||||
}
|
||||
|
||||
// the 174-91 bits of redundancy.
|
||||
for (int i = 0; i + 91 < 174; i++)
|
||||
{
|
||||
int sum = 0;
|
||||
for (int j = 0; j < 91; j++)
|
||||
{
|
||||
sum += gen_sys[i + 91][j] * plain[j];
|
||||
codeword[i + 91] = sum % 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// xplain is a possible original codeword.
|
||||
// ll174 is what was received.
|
||||
// ldpc-encode xplain; how close is the
|
||||
// result to what we received?
|
||||
float osd_score(int xplain[91], float ll174[174])
|
||||
{
|
||||
int xcode[174];
|
||||
ldpc_encode(xplain, xcode);
|
||||
|
||||
float score = 0;
|
||||
for (int i = 0; i < 174; i++)
|
||||
{
|
||||
if (xcode[i])
|
||||
{
|
||||
// one-bit, expect ll to be negative.
|
||||
score -= ll174[i] * 4.6;
|
||||
}
|
||||
else
|
||||
{
|
||||
// zero-bit, expect ll to be positive.
|
||||
score += ll174[i] * 4.6;
|
||||
}
|
||||
}
|
||||
|
||||
return -score;
|
||||
}
|
||||
|
||||
// does a decode look plausible?
|
||||
int osd_check(const int plain[91])
|
||||
{
|
||||
int allzero = 1;
|
||||
for (int i = 0; i < 91; i++)
|
||||
{
|
||||
if (plain[i] != 0)
|
||||
{
|
||||
allzero = 0;
|
||||
}
|
||||
}
|
||||
if (allzero)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (check_crc(plain) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void matmul(int a[91][91], int b[91], int c[91])
|
||||
{
|
||||
for (int i = 0; i < 91; i++)
|
||||
{
|
||||
int sum = 0;
|
||||
for (int j = 0; j < 91; j++)
|
||||
{
|
||||
sum += a[i][j] * b[j];
|
||||
}
|
||||
c[i] = sum % 2;
|
||||
}
|
||||
}
|
||||
|
||||
// ordered statistics decoder for LDPC and new FT8.
|
||||
// idea from wsjt-x.
|
||||
// codeword[i] = log ( P(x=0) / P(x=1) )
|
||||
// codeword has 174 bits.
|
||||
// first 91 bits are plaintext, remaining 83 are parity.
|
||||
// returns 0 or 1, with decoded plain bits in out91[].
|
||||
// and actual depth used in *out_depth.
|
||||
int osd_decode(float codeword[174], int depth, int out[91], int *out_depth)
|
||||
{
|
||||
// strength = abs(codeword)
|
||||
float strength[174];
|
||||
for (int i = 0; i < 174; i++)
|
||||
{
|
||||
float x = codeword[i];
|
||||
strength[i] = (x < 0 ? -x : x);
|
||||
}
|
||||
|
||||
// sort, strongest first; we'll use strongest 91.
|
||||
std::vector<int> which(174);
|
||||
for (int i = 0; i < 174; i++)
|
||||
which[i] = i;
|
||||
std::sort(which.begin(),
|
||||
which.end(),
|
||||
[=](int a, int b)
|
||||
{
|
||||
return strength[a] > strength[b];
|
||||
});
|
||||
|
||||
// gen_sys[174 rows][91 cols] has a row per each of the 174 codeword bits,
|
||||
// indicating how to generate it by xor with each of the 91 plain bits.
|
||||
|
||||
// generator matrix, reordered strongest codeword bit first.
|
||||
int b[174][91 * 2];
|
||||
for (int i = 0; i < 174; i++)
|
||||
{
|
||||
int ii = which[i];
|
||||
for (int j = 0; j < 91 * 2; j++)
|
||||
{
|
||||
if (j < 91)
|
||||
{
|
||||
b[i][j] = gen_sys[ii][j];
|
||||
}
|
||||
else
|
||||
{
|
||||
b[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int xwhich[174];
|
||||
for (int i = 0; i < 174; i++)
|
||||
xwhich[i] = which[i];
|
||||
|
||||
int ok = 0;
|
||||
gauss_jordan(91, 174, b, xwhich, &ok);
|
||||
if (ok == 0)
|
||||
{
|
||||
fprintf(stderr, "gauss_jordan failed\n");
|
||||
}
|
||||
|
||||
int gen1_inv[91][91];
|
||||
for (int i = 0; i < 91; i++)
|
||||
{
|
||||
for (int j = 0; j < 91; j++)
|
||||
{
|
||||
gen1_inv[i][j] = b[i][91 + j];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 174; i++)
|
||||
{
|
||||
which[i] = xwhich[i];
|
||||
}
|
||||
|
||||
// y1 is the received bits, same order as gen1_inv,
|
||||
// more or less strongest-first, converted from
|
||||
// log-likihood to 0/1.
|
||||
int y1[91];
|
||||
for (int i = 0; i < 91; i++)
|
||||
{
|
||||
int j = which[i];
|
||||
y1[i] = (codeword[j] < 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
int best_plain[91];
|
||||
float best_score = 0;
|
||||
int got_a_best = 0;
|
||||
int best_depth = -1;
|
||||
|
||||
// can we decode without flipping any bits?
|
||||
int xplain[91];
|
||||
matmul(gen1_inv, y1, xplain); // also does mod 2
|
||||
|
||||
int osd_thresh = -500;
|
||||
|
||||
float xscore = osd_score(xplain, codeword);
|
||||
int ch = osd_check(xplain);
|
||||
if (xscore < osd_thresh && ch)
|
||||
{
|
||||
if (got_a_best == 0 || xscore < best_score)
|
||||
{
|
||||
if (1)
|
||||
{
|
||||
// just accept this, since no bits had to be flipped.
|
||||
memcpy(out, xplain, sizeof(xplain));
|
||||
*out_depth = 0;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
got_a_best = 1;
|
||||
memcpy(best_plain, xplain, sizeof(best_plain));
|
||||
best_score = xscore;
|
||||
best_depth = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flip a few bits, see if decode works.
|
||||
for (int ii = 0; ii < depth; ii++)
|
||||
{
|
||||
int i = 91 - 1 - ii;
|
||||
y1[i] ^= 1;
|
||||
matmul(gen1_inv, y1, xplain);
|
||||
y1[i] ^= 1;
|
||||
float xscore = osd_score(xplain, codeword);
|
||||
int ch = osd_check(xplain);
|
||||
if (xscore < osd_thresh && ch)
|
||||
{
|
||||
if (got_a_best == 0 || xscore < best_score)
|
||||
{
|
||||
got_a_best = 1;
|
||||
memcpy(best_plain, xplain, sizeof(best_plain));
|
||||
best_score = xscore;
|
||||
best_depth = ii;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (got_a_best)
|
||||
{
|
||||
memcpy(out, best_plain, sizeof(best_plain));
|
||||
*out_depth = best_depth;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int gen_sys[174][91] = {
|
||||
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, },
|
||||
{ 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, },
|
||||
{ 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, },
|
||||
{ 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, },
|
||||
{ 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, },
|
||||
{ 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, },
|
||||
{ 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, },
|
||||
{ 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, },
|
||||
{ 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, },
|
||||
{ 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, },
|
||||
{ 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, },
|
||||
{ 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, },
|
||||
{ 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, },
|
||||
{ 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, },
|
||||
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, },
|
||||
{ 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, },
|
||||
{ 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, },
|
||||
{ 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, },
|
||||
{ 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, },
|
||||
{ 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, },
|
||||
{ 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, },
|
||||
{ 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, },
|
||||
{ 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, },
|
||||
{ 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, },
|
||||
{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, },
|
||||
{ 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, },
|
||||
{ 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, },
|
||||
{ 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, },
|
||||
{ 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, },
|
||||
{ 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, },
|
||||
{ 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, },
|
||||
{ 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, },
|
||||
{ 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, },
|
||||
{ 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, },
|
||||
{ 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, },
|
||||
{ 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, },
|
||||
{ 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, },
|
||||
{ 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, },
|
||||
{ 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, },
|
||||
{ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, },
|
||||
{ 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, },
|
||||
{ 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, },
|
||||
{ 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, },
|
||||
{ 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, },
|
||||
{ 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, },
|
||||
{ 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, },
|
||||
{ 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, },
|
||||
{ 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, },
|
||||
{ 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, },
|
||||
{ 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, },
|
||||
{ 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, },
|
||||
{ 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, },
|
||||
{ 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, },
|
||||
{ 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, },
|
||||
{ 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, },
|
||||
{ 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, },
|
||||
{ 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, },
|
||||
{ 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, },
|
||||
{ 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, },
|
||||
{ 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, },
|
||||
{ 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, },
|
||||
{ 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, },
|
||||
{ 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, },
|
||||
{ 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, },
|
||||
{ 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, },
|
||||
{ 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, },
|
||||
{ 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, },
|
||||
{ 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, },
|
||||
{ 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, },
|
||||
{ 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, },
|
||||
{ 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, },
|
||||
{ 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, },
|
||||
{ 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, },
|
||||
{ 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, },
|
||||
{ 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, },
|
||||
{ 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, },
|
||||
{ 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, },
|
||||
{ 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, },
|
||||
{ 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, },
|
||||
{ 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, },
|
||||
{ 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, },
|
||||
{ 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
};
|
||||
|
||||
} // namespace FT8
|
37
ft8/osd.h
Normal file
37
ft8/osd.h
Normal file
@ -0,0 +1,37 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef osd_h
|
||||
#define osd_h
|
||||
|
||||
namespace FT8 {
|
||||
|
||||
extern int gen_sys[174][91];
|
||||
int check_crc(const int a91[91]);
|
||||
void ldpc_encode(int plain[91], int codeword[174]);
|
||||
float osd_score(int xplain[91], float ll174[174]);
|
||||
int osd_check(const int plain[91]);
|
||||
void matmul(int a[91][91], int b[91], int c[91]);
|
||||
int osd_decode(float codeword[174], int depth, int out[91], int *out_depth);
|
||||
|
||||
} // namepsace FT8
|
||||
|
||||
#endif // osd_h
|
||||
|
551
ft8/unpack.cpp
Normal file
551
ft8/unpack.cpp
Normal file
@ -0,0 +1,551 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// 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 <string>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "unpack.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace FT8 {
|
||||
|
||||
//
|
||||
// turn bits into a 128-bit integer.
|
||||
// most significant bit first.
|
||||
//
|
||||
__int128 un(int a77[], int start, int len)
|
||||
{
|
||||
__int128 x = 0;
|
||||
|
||||
assert(len < (int)sizeof(x) * 8 && start >= 0 && start + len <= 77);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
x <<= 1;
|
||||
x |= a77[start + i];
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
std::mutex hashes_mu;
|
||||
std::map<int, std::string> hashes12;
|
||||
std::map<int, std::string> hashes22;
|
||||
|
||||
int ihashcall(std::string call, int m)
|
||||
{
|
||||
while (call.size() > 0 && call[0] == ' ')
|
||||
call.erase(0, 1);
|
||||
while (call.size() > 0 && call[call.size() - 1] == ' ')
|
||||
call.erase(call.end() - 1);
|
||||
|
||||
const char *chars = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/";
|
||||
|
||||
while (call.size() < 11)
|
||||
call += " ";
|
||||
|
||||
unsigned long long x = 0;
|
||||
for (int i = 0; i < 11; i++)
|
||||
{
|
||||
int c = call[i];
|
||||
const char *p = strchr(chars, c);
|
||||
assert(p);
|
||||
int j = p - chars;
|
||||
x = 38 * x + j;
|
||||
}
|
||||
x = x * 47055833459LL;
|
||||
x = x >> (64 - m);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
#define NGBASE (180 * 180)
|
||||
#define NTOKENS 2063592
|
||||
#define MAX22 4194304
|
||||
|
||||
//
|
||||
// turn 28 bits of packed call into the call
|
||||
//
|
||||
std::string unpackcall(int x)
|
||||
{
|
||||
char tmp[64];
|
||||
|
||||
const char *c1 = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const char *c2 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const char *c3 = "0123456789";
|
||||
const char *c4 = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
if (x == 0)
|
||||
return "DE";
|
||||
if (x == 1)
|
||||
return "QRZ";
|
||||
if (x == 2)
|
||||
return "CQ";
|
||||
if (x <= 1002)
|
||||
{
|
||||
sprintf(tmp, "CQ %d", x - 3);
|
||||
return tmp;
|
||||
}
|
||||
if (x <= 532443)
|
||||
{
|
||||
x -= 1003;
|
||||
int ci1 = x / (27 * 27 * 27);
|
||||
x %= 27 * 27 * 27;
|
||||
int ci2 = x / (27 * 27);
|
||||
x %= 27 * 27;
|
||||
int ci3 = x / 27;
|
||||
x %= 27;
|
||||
int ci4 = x;
|
||||
sprintf(tmp, "CQ %c%c%c%c", c4[ci1], c4[ci2], c4[ci3], c4[ci4]);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
if (x < NTOKENS)
|
||||
{
|
||||
return "<TOKEN>";
|
||||
}
|
||||
|
||||
x -= NTOKENS;
|
||||
|
||||
if (x < MAX22)
|
||||
{
|
||||
// 22-bit hash...
|
||||
std::string s;
|
||||
hashes_mu.lock();
|
||||
if (hashes22.count(x) > 0)
|
||||
{
|
||||
s = hashes22[x];
|
||||
}
|
||||
else
|
||||
{
|
||||
s = "<...22>";
|
||||
}
|
||||
hashes_mu.unlock();
|
||||
return s;
|
||||
}
|
||||
|
||||
x -= MAX22;
|
||||
|
||||
char a[7];
|
||||
|
||||
a[5] = c4[x % 27];
|
||||
x = x / 27;
|
||||
a[4] = c4[x % 27];
|
||||
x = x / 27;
|
||||
a[3] = c4[x % 27];
|
||||
x = x / 27;
|
||||
a[2] = c3[x % 10];
|
||||
x = x / 10;
|
||||
a[1] = c2[x % 36];
|
||||
x = x / 36;
|
||||
a[0] = c1[x];
|
||||
|
||||
a[6] = '\0';
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
// unpack a 15-bit grid square &c.
|
||||
// 77-bit version, from inspection of packjt77.f90.
|
||||
// ir is the bit after the two 28+1-bit callee/caller.
|
||||
// i3 is the message type, usually 1.
|
||||
std::string unpackgrid(int ng, int ir, int i3)
|
||||
{
|
||||
(void) i3;
|
||||
|
||||
if (ng < NGBASE)
|
||||
{
|
||||
// maidenhead grid system:
|
||||
// latitude from south pole to north pole.
|
||||
// longitude eastward from anti-meridian.
|
||||
// first: 20 degrees longitude.
|
||||
// second: 10 degrees latitude.
|
||||
// third: 2 degrees longitude.
|
||||
// fourth: 1 degree latitude.
|
||||
// so there are 18*18*10*10 possibilities.
|
||||
int x1 = ng / (18 * 10 * 10);
|
||||
ng %= 18 * 10 * 10;
|
||||
int x2 = ng / (10 * 10);
|
||||
ng %= 10 * 10;
|
||||
int x3 = ng / 10;
|
||||
ng %= 10;
|
||||
int x4 = ng;
|
||||
char tmp[5];
|
||||
tmp[0] = 'A' + x1;
|
||||
tmp[1] = 'A' + x2;
|
||||
tmp[2] = '0' + x3;
|
||||
tmp[3] = '0' + x4;
|
||||
tmp[4] = '\0';
|
||||
return tmp;
|
||||
}
|
||||
|
||||
ng -= NGBASE;
|
||||
|
||||
if (ng == 1)
|
||||
{
|
||||
return " "; // ???
|
||||
}
|
||||
if (ng == 2)
|
||||
{
|
||||
return "RRR ";
|
||||
}
|
||||
if (ng == 3)
|
||||
{
|
||||
return "RR73";
|
||||
}
|
||||
if (ng == 4)
|
||||
{
|
||||
return "73 ";
|
||||
}
|
||||
|
||||
int db = ng - 35;
|
||||
char tmp[16];
|
||||
if (db >= 0)
|
||||
{
|
||||
sprintf(tmp, "%s+%02d", ir ? "R" : "", db);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(tmp, "%s-%02d", ir ? "R" : "", 0 - db);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void remember_call(std::string call)
|
||||
{
|
||||
hashes_mu.lock();
|
||||
if (call.size() >= 3 && call[0] != '<')
|
||||
{
|
||||
hashes22[ihashcall(call, 22)] = call;
|
||||
hashes12[ihashcall(call, 12)] = call;
|
||||
}
|
||||
hashes_mu.unlock();
|
||||
}
|
||||
|
||||
//
|
||||
// i3 == 4
|
||||
// a call that doesn't fit in 28 bits.
|
||||
// 12 bits: hash of a previous call
|
||||
// 58 bits: 11 characters
|
||||
// 1 bit: swap
|
||||
// 2 bits: 1 RRR, 2 RR73, 3 73
|
||||
// 1 bit: 1 means CQ
|
||||
std::string unpack_4(int a77[])
|
||||
{
|
||||
// 38 possible characters:
|
||||
const char *chars = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/";
|
||||
|
||||
long long n58 = un(a77, 12, 58);
|
||||
char call[16];
|
||||
for (int i = 0; i < 11; i++)
|
||||
{
|
||||
call[10 - i] = chars[n58 % 38];
|
||||
n58 = n58 / 38;
|
||||
}
|
||||
call[11] = '\0';
|
||||
|
||||
remember_call(call);
|
||||
|
||||
if (un(a77, 73, 1) == 1)
|
||||
{
|
||||
return std::string("CQ ") + call;
|
||||
}
|
||||
|
||||
int x12 = un(a77, 0, 12);
|
||||
// 12-bit hash
|
||||
hashes_mu.lock();
|
||||
std::string ocall;
|
||||
if (hashes12.count(x12) > 0)
|
||||
{
|
||||
ocall = hashes12[x12];
|
||||
}
|
||||
else
|
||||
{
|
||||
ocall = "<...12>";
|
||||
}
|
||||
hashes_mu.unlock();
|
||||
|
||||
int swap = un(a77, 70, 1);
|
||||
std::string msg;
|
||||
if (swap)
|
||||
{
|
||||
msg = std::string(call) + " " + ocall;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = std::string(ocall) + " " + call;
|
||||
}
|
||||
|
||||
int suffix = un(a77, 71, 2);
|
||||
if (suffix == 1)
|
||||
{
|
||||
msg += " RRR";
|
||||
}
|
||||
else if (suffix == 2)
|
||||
{
|
||||
msg += " RR73";
|
||||
}
|
||||
else if (suffix == 3)
|
||||
{
|
||||
msg += " 73";
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
//
|
||||
// i3=1
|
||||
//
|
||||
std::string unpack_1(int a77[])
|
||||
{
|
||||
// type 1:
|
||||
// 28 call1
|
||||
// 1 P/R
|
||||
// 28 call2
|
||||
// 1 P/R
|
||||
// 1 ???
|
||||
// 15 grid
|
||||
// 3 type
|
||||
|
||||
int i = 0;
|
||||
int call1 = un(a77, i, 28);
|
||||
i += 28;
|
||||
int rover1 = a77[i];
|
||||
i += 1;
|
||||
int call2 = un(a77, i, 28);
|
||||
i += 28;
|
||||
int rover2 = a77[i];
|
||||
i += 1;
|
||||
int ir = a77[i];
|
||||
i += 1;
|
||||
int grid = un(a77, i, 15);
|
||||
i += 15;
|
||||
int i3 = un(a77, i, 3);
|
||||
i += 3;
|
||||
assert((i3 == 1 || i3 == 2) && i == 77);
|
||||
|
||||
std::string call1text = trim(unpackcall(call1));
|
||||
std::string call2text = trim(unpackcall(call2));
|
||||
std::string gridtext = unpackgrid(grid, ir, i3);
|
||||
|
||||
remember_call(call1text);
|
||||
remember_call(call2text);
|
||||
|
||||
const char *pr = (i3 == 1 ? "/R" : "/P");
|
||||
|
||||
return call1text + (rover1 ? pr : "") + " " + call2text + (rover2 ? pr : "") + " " + gridtext;
|
||||
}
|
||||
|
||||
// free text
|
||||
// 71 bits, 13 characters, each one of 42 choices.
|
||||
// reversed.
|
||||
// details from wsjt-x's packjt77.f90
|
||||
std::string unpack_0_0(int a77[])
|
||||
{
|
||||
// the 42 possible characters.
|
||||
const char *cc = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?";
|
||||
__int128 x = un(a77, 0, 71);
|
||||
std::string msg = "0123456789123";
|
||||
for (int i = 0; i < 13; i++)
|
||||
{
|
||||
msg[13 - 1 - i] = cc[x % 42];
|
||||
x = x / 42;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
// ARRL RTTY Round-Up states/provinces
|
||||
const char *ru_states[] = {
|
||||
"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"};
|
||||
|
||||
// i3=3
|
||||
// 3 TU; W9XYZ K1ABC R 579 MA 1 28 28 1 3 13 74 ARRL RTTY Roundup
|
||||
// 1 TU
|
||||
// 28 call1
|
||||
// 28 call2
|
||||
// 1 R
|
||||
// 3 RST 529 to 599
|
||||
// 13 state/province/serialnumber
|
||||
std::string unpack_3(int a77[])
|
||||
{
|
||||
int i = 0;
|
||||
int tu = a77[i];
|
||||
i += 1;
|
||||
int call1 = un(a77, i, 28);
|
||||
i += 28;
|
||||
int call2 = un(a77, i, 28);
|
||||
i += 28;
|
||||
int r = a77[i];
|
||||
i += 1;
|
||||
int rst = un(a77, i, 3);
|
||||
i += 3;
|
||||
int serial = un(a77, i, 13);
|
||||
i += 13;
|
||||
|
||||
std::string call1text = unpackcall(call1);
|
||||
std::string call2text = unpackcall(call2);
|
||||
|
||||
rst = 529 + 10 * rst;
|
||||
|
||||
int statei = serial - 8001;
|
||||
std::string serialstr;
|
||||
int nstates = sizeof(ru_states) / sizeof(ru_states[0]);
|
||||
if (serial > 8000 && statei < nstates)
|
||||
{
|
||||
serialstr = ru_states[statei];
|
||||
}
|
||||
else
|
||||
{
|
||||
char tmp[32];
|
||||
sprintf(tmp, "%04d", serial);
|
||||
serialstr = tmp;
|
||||
}
|
||||
|
||||
std::string msg;
|
||||
|
||||
if (tu)
|
||||
{
|
||||
msg += "TU; ";
|
||||
}
|
||||
msg += call1text + " " + call2text + " ";
|
||||
if (r)
|
||||
{
|
||||
msg += "R ";
|
||||
}
|
||||
{
|
||||
char tmp[16];
|
||||
sprintf(tmp, "%d ", rst);
|
||||
msg += tmp;
|
||||
}
|
||||
msg += serialstr;
|
||||
|
||||
remember_call(call1text);
|
||||
remember_call(call2text);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
// ARRL Field Day sections
|
||||
const char *sections[] = {
|
||||
"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 "};
|
||||
|
||||
// i3 = 0, n3 = 3 or 4: ARRL Field Day
|
||||
// 0.3 WA9XYZ KA1ABC R 16A EMA 28 28 1 4 3 7 71 ARRL Field Day
|
||||
// 0.4 WA9XYZ KA1ABC R 32A EMA 28 28 1 4 3 7 71 ARRL Field Day
|
||||
std::string unpack_0_3(int a77[], int n3)
|
||||
{
|
||||
int i = 0;
|
||||
int call1 = un(a77, i, 28);
|
||||
i += 28;
|
||||
int call2 = un(a77, i, 28);
|
||||
i += 28;
|
||||
int R = un(a77, i, 1);
|
||||
i += 1;
|
||||
int n_transmitters = un(a77, i, 4);
|
||||
if (n3 == 4)
|
||||
n_transmitters += 16;
|
||||
i += 4;
|
||||
int clss = un(a77, i, 3); // class
|
||||
i += 3;
|
||||
int section = un(a77, i, 7); // ARRL section
|
||||
i += 7;
|
||||
|
||||
std::string msg;
|
||||
msg += unpackcall(call1);
|
||||
msg += " ";
|
||||
msg += unpackcall(call2);
|
||||
msg += " ";
|
||||
if (R)
|
||||
msg += "R ";
|
||||
{
|
||||
char tmp[16];
|
||||
sprintf(tmp, "%d%c ", n_transmitters + 1, clss + 'A');
|
||||
msg += tmp;
|
||||
}
|
||||
if (section - 1 >= 0 && section - 1 < (int)(sizeof(sections) / sizeof(sections[0])))
|
||||
{
|
||||
msg += sections[section - 1];
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
//
|
||||
// unpack an FT8 message.
|
||||
// a77 is 91 bits -- 77 plus the 14-bit CRC.
|
||||
// CRC and LDPC have already been checked.
|
||||
// details from wsjt-x's packjt77.f90 and 77bit.txt.
|
||||
//
|
||||
std::string unpack(int a77[])
|
||||
{
|
||||
int i3 = un(a77, 74, 3);
|
||||
int n3 = un(a77, 71, 3);
|
||||
|
||||
if (i3 == 0 && n3 == 0)
|
||||
{
|
||||
// free text
|
||||
return unpack_0_0(a77);
|
||||
}
|
||||
|
||||
if (i3 == 0 && (n3 == 3 || n3 == 4))
|
||||
{
|
||||
// ARRL Field Day
|
||||
return unpack_0_3(a77, n3);
|
||||
}
|
||||
|
||||
if (i3 == 1 || i3 == 2)
|
||||
{
|
||||
// ordinary message
|
||||
return unpack_1(a77);
|
||||
}
|
||||
|
||||
if (i3 == 3)
|
||||
{
|
||||
// RTTY Round-Up
|
||||
return unpack_3(a77);
|
||||
}
|
||||
|
||||
if (i3 == 4)
|
||||
{
|
||||
// call that doesn't fit in 28 bits
|
||||
return unpack_4(a77);
|
||||
}
|
||||
|
||||
char tmp[64];
|
||||
sprintf(tmp, "UNK i3=%d n3=%d", i3, n3);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
} // namespace FT8
|
30
ft8/unpack.h
Normal file
30
ft8/unpack.h
Normal file
@ -0,0 +1,30 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef unpack_h
|
||||
#define unpack_h
|
||||
|
||||
namespace FT8 {
|
||||
|
||||
std::string unpack(int a91[]);
|
||||
|
||||
} // namespace FT8
|
||||
|
||||
#endif
|
408
ft8/util.cpp
Normal file
408
ft8/util.cpp
Normal file
@ -0,0 +1,408 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// 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 <sndfile.h>
|
||||
#include <sys/time.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <complex>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include "util.h"
|
||||
|
||||
namespace FT8 {
|
||||
|
||||
float now()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
return tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||
}
|
||||
|
||||
void writewav(const std::vector<float> &samples, const char *filename, int rate)
|
||||
{
|
||||
float mx = 0;
|
||||
for (ulong i = 0; i < samples.size(); i++)
|
||||
{
|
||||
mx = std::max(mx, std::abs(samples[i]));
|
||||
}
|
||||
std::vector<float> v(samples.size());
|
||||
for (ulong i = 0; i < samples.size(); i++)
|
||||
{
|
||||
v[i] = (samples[i] / mx) * 0.95;
|
||||
}
|
||||
|
||||
SF_INFO sf;
|
||||
sf.channels = 1;
|
||||
sf.samplerate = rate;
|
||||
sf.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
|
||||
SNDFILE *f = sf_open(filename, SFM_WRITE, &sf);
|
||||
assert(f);
|
||||
sf_write_float(f, v.data(), v.size());
|
||||
sf_write_sync(f);
|
||||
sf_close(f);
|
||||
}
|
||||
|
||||
std::vector<float> readwav(const char *filename, int &rate_out)
|
||||
{
|
||||
SF_INFO info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
SNDFILE *sf = sf_open(filename, SFM_READ, &info);
|
||||
if (sf == 0)
|
||||
{
|
||||
fprintf(stderr, "cannot open %s\n", filename);
|
||||
exit(1); // XXX
|
||||
}
|
||||
rate_out = info.samplerate;
|
||||
|
||||
std::vector<float> out;
|
||||
|
||||
while (1)
|
||||
{
|
||||
float buf[512];
|
||||
int n = sf_read_float(sf, buf, 512);
|
||||
if (n <= 0)
|
||||
break;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
out.push_back(buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
sf_close(sf);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void writetxt(std::vector<float> v, const char *filename)
|
||||
{
|
||||
FILE *fp = fopen(filename, "w");
|
||||
if (fp == 0)
|
||||
{
|
||||
fprintf(stderr, "could not write %s\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
for (ulong i = 0; i < v.size(); i++)
|
||||
{
|
||||
fprintf(fp, "%f\n", v[i]);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
//
|
||||
// Goertzel Algorithm for a Non-integer Frequency Index, Rick Lyons
|
||||
// https://www.dsprelated.com/showarticle/495.php
|
||||
//
|
||||
std::complex<float> goertzel(std::vector<float> v, int rate, int i0, int n, float hz)
|
||||
{
|
||||
// float radians_per_sample = (hz * 2 * M_PI) / rate;
|
||||
// float k = radians_per_sample * n;
|
||||
float bin_hz = rate / (float)n;
|
||||
float k = hz / bin_hz;
|
||||
|
||||
float alpha = 2 * M_PI * k / n;
|
||||
float beta = 2 * M_PI * k * (n - 1.0) / n;
|
||||
|
||||
float two_cos_alpha = 2 * cos(alpha);
|
||||
float a = cos(beta);
|
||||
float b = -sin(beta);
|
||||
float c = sin(alpha) * sin(beta) - cos(alpha) * cos(beta);
|
||||
float d = sin(2 * M_PI * k);
|
||||
|
||||
float w1 = 0;
|
||||
float w2 = 0;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
float w0 = v[i0 + i] + two_cos_alpha * w1 - w2;
|
||||
w2 = w1;
|
||||
w1 = w0;
|
||||
}
|
||||
|
||||
float re = w1 * a + w2 * c;
|
||||
float im = w1 * b + w2 * d;
|
||||
|
||||
return std::complex<float>(re, im);
|
||||
}
|
||||
|
||||
float vmax(const std::vector<float> &v)
|
||||
{
|
||||
float mx = 0;
|
||||
int got = 0;
|
||||
for (int i = 0; i < (int)v.size(); i++)
|
||||
{
|
||||
if (got == 0 || v[i] > mx)
|
||||
{
|
||||
got = 1;
|
||||
mx = v[i];
|
||||
}
|
||||
}
|
||||
return mx;
|
||||
}
|
||||
|
||||
std::vector<float> vreal(const std::vector<std::complex<float>> &a)
|
||||
{
|
||||
std::vector<float> b(a.size());
|
||||
for (int i = 0; i < (int)a.size(); i++)
|
||||
{
|
||||
b[i] = a[i].real();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
std::vector<float> vimag(const std::vector<std::complex<float>> &a)
|
||||
{
|
||||
std::vector<float> b(a.size());
|
||||
for (int i = 0; i < (int)a.size(); i++)
|
||||
{
|
||||
b[i] = a[i].imag();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// generate 8-FSK, at 25 hz, bin size 6.25 hz,
|
||||
// 200 samples/second, 32 samples/symbol.
|
||||
// used as reference to detect pairs of symbols.
|
||||
// superseded by gfsk().
|
||||
std::vector<std::complex<float>> fsk_c(const std::vector<int> &syms)
|
||||
{
|
||||
int n = syms.size();
|
||||
std::vector<std::complex<float>> v(n * 32);
|
||||
float theta = 0;
|
||||
for (int si = 0; si < n; si++)
|
||||
{
|
||||
float hz = 25 + syms[si] * 6.25;
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
v[si * 32 + i] = std::complex<float>(cos(theta), sin(theta));
|
||||
theta += 2 * M_PI / (200 / hz);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// copied from wsjt-x ft2/gfsk_pulse.f90.
|
||||
// b is 1.0 for FT4; 2.0 for FT8.
|
||||
float gfsk_point(float b, float t)
|
||||
{
|
||||
float c = M_PI * sqrt(2.0 / log(2.0));
|
||||
float x = 0.5 * (erf(c * b * (t + 0.5)) - erf(c * b * (t - 0.5)));
|
||||
return x;
|
||||
}
|
||||
|
||||
// the smoothing window for gfsk.
|
||||
// run the window over impulses of symbol frequencies,
|
||||
// each impulse at the center of its symbol time.
|
||||
// three symbols wide.
|
||||
// most of the pulse is in the center symbol.
|
||||
// b is 1.0 for FT4; 2.0 for FT8.
|
||||
std::vector<float> gfsk_window(int samples_per_symbol, float b)
|
||||
{
|
||||
std::vector<float> v(3 * samples_per_symbol);
|
||||
float sum = 0;
|
||||
for (int i = 0; i < (int)v.size(); i++)
|
||||
{
|
||||
float x = i / (float)samples_per_symbol;
|
||||
x -= 1.5;
|
||||
float y = gfsk_point(b, x);
|
||||
v[i] = y;
|
||||
sum += y;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)v.size(); i++)
|
||||
{
|
||||
v[i] /= sum;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
// gaussian-smoothed fsk.
|
||||
// the gaussian smooths the instantaneous frequencies,
|
||||
// so that the transitions between symbols don't
|
||||
// cause clicks.
|
||||
// gwin is gfsk_window(32, 2.0)
|
||||
std::vector<std::complex<float>> gfsk_c(
|
||||
const std::vector<int> &symbols,
|
||||
float hz0, float hz1,
|
||||
float spacing, int rate, int symsamples,
|
||||
float phase0,
|
||||
const std::vector<float> &gwin
|
||||
)
|
||||
{
|
||||
assert((gwin.size() % 2) == 0);
|
||||
|
||||
// compute frequency for each symbol.
|
||||
// generate a spike in the middle of each symbol time;
|
||||
// the gaussian filter will turn it into a waveform.
|
||||
std::vector<float> hzv(symsamples * (symbols.size() + 2), 0.0);
|
||||
for (int bi = 0; bi < (int)symbols.size(); bi++)
|
||||
{
|
||||
float base_hz = hz0 + (hz1 - hz0) * (bi / (float)symbols.size());
|
||||
float fr = base_hz + (symbols[bi] * spacing);
|
||||
int mid = symsamples * (bi + 1) + symsamples / 2;
|
||||
// the window has even size, so split the impulse over
|
||||
// the two middle samples to be symmetric.
|
||||
hzv[mid] = fr * symsamples / 2.0;
|
||||
hzv[mid - 1] = fr * symsamples / 2.0;
|
||||
}
|
||||
|
||||
// repeat first and last symbols
|
||||
for (int i = 0; i < symsamples; i++)
|
||||
{
|
||||
hzv[i] = hzv[i + symsamples];
|
||||
hzv[symsamples * (symbols.size() + 1) + i] = hzv[symsamples * symbols.size() + i];
|
||||
}
|
||||
|
||||
// run the per-sample frequency vector through
|
||||
// the gaussian filter.
|
||||
int half = gwin.size() / 2;
|
||||
std::vector<float> o(hzv.size());
|
||||
for (int i = 0; i < (int)o.size(); i++)
|
||||
{
|
||||
float sum = 0;
|
||||
for (int j = 0; j < (int)gwin.size(); j++)
|
||||
{
|
||||
int k = i - half + j;
|
||||
if (k >= 0 && k < (int)hzv.size())
|
||||
{
|
||||
sum += hzv[k] * gwin[j];
|
||||
}
|
||||
}
|
||||
o[i] = sum;
|
||||
}
|
||||
|
||||
// drop repeated first and last symbols
|
||||
std::vector<float> oo(symsamples * symbols.size());
|
||||
for (int i = 0; i < (int)oo.size(); i++)
|
||||
{
|
||||
oo[i] = o[i + symsamples];
|
||||
}
|
||||
|
||||
// now oo[i] contains the frequency for the i'th sample.
|
||||
|
||||
std::vector<std::complex<float>> v(symsamples * symbols.size());
|
||||
float theta = phase0;
|
||||
for (int i = 0; i < (int)v.size(); i++)
|
||||
{
|
||||
v[i] = std::complex<float>(cos(theta), sin(theta));
|
||||
float hz = oo[i];
|
||||
theta += 2 * M_PI / (rate / hz);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
// gaussian-smoothed fsk.
|
||||
// the gaussian smooths the instantaneous frequencies,
|
||||
// so that the transitions between symbols don't
|
||||
// cause clicks.
|
||||
// gwin is gfsk_window(32, 2.0)
|
||||
std::vector<float> gfsk_r(
|
||||
const std::vector<int> &symbols,
|
||||
float hz0, float hz1,
|
||||
float spacing, int rate, int symsamples,
|
||||
float phase0,
|
||||
const std::vector<float> &gwin
|
||||
)
|
||||
{
|
||||
assert((gwin.size() % 2) == 0);
|
||||
|
||||
// compute frequency for each symbol.
|
||||
// generate a spike in the middle of each symbol time;
|
||||
// the gaussian filter will turn it into a waveform.
|
||||
std::vector<float> hzv(symsamples * (symbols.size() + 2), 0.0);
|
||||
for (int bi = 0; bi < (int)symbols.size(); bi++)
|
||||
{
|
||||
float base_hz = hz0 + (hz1 - hz0) * (bi / (float)symbols.size());
|
||||
float fr = base_hz + (symbols[bi] * spacing);
|
||||
int mid = symsamples * (bi + 1) + symsamples / 2;
|
||||
// the window has even size, so split the impulse over
|
||||
// the two middle samples to be symmetric.
|
||||
hzv[mid] = fr * symsamples / 2.0;
|
||||
hzv[mid - 1] = fr * symsamples / 2.0;
|
||||
}
|
||||
|
||||
// repeat first and last symbols
|
||||
for (int i = 0; i < symsamples; i++)
|
||||
{
|
||||
hzv[i] = hzv[i + symsamples];
|
||||
hzv[symsamples * (symbols.size() + 1) + i] = hzv[symsamples * symbols.size() + i];
|
||||
}
|
||||
|
||||
// run the per-sample frequency vector through
|
||||
// the gaussian filter.
|
||||
int half = gwin.size() / 2;
|
||||
std::vector<float> o(hzv.size());
|
||||
for (int i = 0; i < (int)o.size(); i++)
|
||||
{
|
||||
float sum = 0;
|
||||
for (int j = 0; j < (int)gwin.size(); j++)
|
||||
{
|
||||
int k = i - half + j;
|
||||
if (k >= 0 && k < (int)hzv.size())
|
||||
{
|
||||
sum += hzv[k] * gwin[j];
|
||||
}
|
||||
}
|
||||
o[i] = sum;
|
||||
}
|
||||
|
||||
// drop repeated first and last symbols
|
||||
std::vector<float> oo(symsamples * symbols.size());
|
||||
for (int i = 0; i < (int)oo.size(); i++)
|
||||
{
|
||||
oo[i] = o[i + symsamples];
|
||||
}
|
||||
|
||||
// now oo[i] contains the frequency for the i'th sample.
|
||||
|
||||
std::vector<float> v(symsamples * symbols.size());
|
||||
float theta = phase0;
|
||||
for (int i = 0; i < (int)v.size(); i++)
|
||||
{
|
||||
v[i] = cos(theta);
|
||||
float hz = oo[i];
|
||||
theta += 2 * M_PI / (rate / hz);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
const std::string WHITESPACE = " \n\r\t\f\v";
|
||||
|
||||
std::string ltrim(const std::string &s)
|
||||
{
|
||||
size_t start = s.find_first_not_of(WHITESPACE);
|
||||
return (start == std::string::npos) ? "" : s.substr(start);
|
||||
}
|
||||
|
||||
std::string rtrim(const std::string &s)
|
||||
{
|
||||
size_t end = s.find_last_not_of(WHITESPACE);
|
||||
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
||||
}
|
||||
|
||||
std::string trim(const std::string &s) {
|
||||
return rtrim(ltrim(s));
|
||||
}
|
||||
|
||||
} // namespace FT8
|
58
ft8/util.h
Normal file
58
ft8/util.h
Normal file
@ -0,0 +1,58 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
|
||||
namespace FT8
|
||||
{
|
||||
float now();
|
||||
void writewav(const std::vector<float> &samples, const char *filename, int rate);
|
||||
std::vector<float> readwav(const char *filename, int &rate_out);
|
||||
void writetxt(std::vector<float> v, const char *filename);
|
||||
std::complex<float> goertzel(std::vector<float> v, int rate, int i0, int n, float hz);
|
||||
float vmax(const std::vector<float> &v);
|
||||
std::vector<float> vreal(const std::vector<std::complex<float>> &a);
|
||||
std::vector<float> vimag(const std::vector<std::complex<float>> &a);
|
||||
std::vector<std::complex<float>> gfsk_c(
|
||||
const std::vector<int> &symbols,
|
||||
float hz0, float hz1,
|
||||
float spacing, int rate, int symsamples,
|
||||
float phase0,
|
||||
const std::vector<float> &gwin
|
||||
);
|
||||
std::vector<float> gfsk_r(
|
||||
const std::vector<int> &symbols,
|
||||
float hz0, float hz1,
|
||||
float spacing, int rate, int symsamples,
|
||||
float phase0,
|
||||
const std::vector<float> &gwin
|
||||
);
|
||||
std::vector<float> gfsk_window(int samples_per_symbol, float b);
|
||||
std::string trim(const std::string &s);
|
||||
|
||||
typedef unsigned long ulong;
|
||||
typedef unsigned int uint;
|
||||
} // namespace FT8
|
||||
|
||||
#endif
|
@ -4,6 +4,7 @@ set(sdrbench_SOURCES
|
||||
mainbench.cpp
|
||||
parserbench.cpp
|
||||
test_golay2312.cpp
|
||||
test_ft8.cpp
|
||||
)
|
||||
|
||||
set(sdrbench_HEADERS
|
||||
@ -16,16 +17,20 @@ add_library(sdrbench SHARED
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${FFTW3F_INCLUDE_DIRS}
|
||||
${CMAKE_SOURCE_DIR}/exports
|
||||
${CMAKE_SOURCE_DIR}/sdrbase
|
||||
${CMAKE_SOURCE_DIR}/logging
|
||||
${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(sdrbench
|
||||
${FFTW3F_LIBRARIES}
|
||||
Qt::Core
|
||||
Qt::Gui
|
||||
sdrbase
|
||||
logging
|
||||
ft8
|
||||
)
|
||||
|
||||
install(TARGETS sdrbench DESTINATION ${INSTALL_LIB_DIR})
|
||||
|
@ -62,6 +62,8 @@ void MainBench::run()
|
||||
testDecimateFF();
|
||||
} else if (m_parser.getTestType() == ParserBench::TestGolay2312) {
|
||||
testGolay2312();
|
||||
} else if (m_parser.getTestType() == ParserBench::TestFT8) {
|
||||
testFT8();
|
||||
} else {
|
||||
qDebug() << "MainBench::run: unknown test type: " << m_parser.getTestType();
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ private:
|
||||
void testDecimateFI();
|
||||
void testDecimateFF();
|
||||
void testGolay2312();
|
||||
void testFT8();
|
||||
void decimateII(const qint16 *buf, int len);
|
||||
void decimateInfII(const qint16 *buf, int len);
|
||||
void decimateSupII(const qint16 *buf, int len);
|
||||
@ -62,6 +63,7 @@ private:
|
||||
void decimateFF(const float *buf, int len);
|
||||
void printResults(const QString& prefix, qint64 nsecs);
|
||||
|
||||
|
||||
static MainBench *m_instance;
|
||||
qtwebapp::LoggerWithFile *m_logger;
|
||||
const ParserBench& m_parser;
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
ParserBench::ParserBench() :
|
||||
m_testOption(QStringList() << "t" << "test",
|
||||
"Test type: decimateii, decimatefi, decimateff, decimateif, decimateinfii, decimatesupii, ambe, golay2312",
|
||||
"Test type: decimateii, decimatefi, decimateff, decimateif, decimateinfii, decimatesupii, ambe, golay2312, ft8"
|
||||
"test",
|
||||
"decimateii"),
|
||||
m_nbSamplesOption(QStringList() << "n" << "nb-samples",
|
||||
@ -127,6 +127,8 @@ ParserBench::TestType ParserBench::getTestType() const
|
||||
return TestDecimatorsSupII;
|
||||
} else if (m_testStr == "golay2312") {
|
||||
return TestGolay2312;
|
||||
} else if (m_testStr == "ft8") {
|
||||
return TestFT8;
|
||||
} else {
|
||||
return TestDecimatorsII;
|
||||
}
|
||||
|
@ -35,7 +35,8 @@ public:
|
||||
TestDecimatorsFF,
|
||||
TestDecimatorsInfII,
|
||||
TestDecimatorsSupII,
|
||||
TestGolay2312
|
||||
TestGolay2312,
|
||||
TestFT8
|
||||
} TestType;
|
||||
|
||||
ParserBench();
|
||||
|
111
sdrbench/test_ft8.cpp
Normal file
111
sdrbench/test_ft8.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// 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 "mainbench.h"
|
||||
#ifdef LINUX
|
||||
#include "ft8/ft8.h"
|
||||
#include "ft8/util.h"
|
||||
#include "ft8/unpack.h"
|
||||
|
||||
#include <QMutex>
|
||||
#endif
|
||||
|
||||
#ifndef LINUX
|
||||
void MainBench::testFT8()
|
||||
{
|
||||
qDebug("Implemented in Linux only");
|
||||
}
|
||||
#else
|
||||
|
||||
QMutex cycle_mu;
|
||||
volatile int cycle_count;
|
||||
time_t saved_cycle_start;
|
||||
std::map<std::string, bool> cycle_already;
|
||||
|
||||
int hcb(
|
||||
int *a91,
|
||||
float hz0,
|
||||
float hz1,
|
||||
float off,
|
||||
const char *comment,
|
||||
float snr,
|
||||
int pass,
|
||||
int correct_bits)
|
||||
{
|
||||
(void) hz1;
|
||||
(void) comment;
|
||||
(void) pass;
|
||||
|
||||
std::string msg = FT8::unpack(a91);
|
||||
|
||||
cycle_mu.lock();
|
||||
|
||||
if (cycle_already.count(msg) > 0)
|
||||
{
|
||||
// already decoded this message on this cycle
|
||||
cycle_mu.unlock();
|
||||
return 1; // 1 => already seen, don't subtract.
|
||||
}
|
||||
|
||||
cycle_already[msg] = true;
|
||||
cycle_count += 1;
|
||||
|
||||
cycle_mu.unlock();
|
||||
|
||||
struct tm result;
|
||||
gmtime_r(&saved_cycle_start, &result);
|
||||
|
||||
printf("%02d%02d%02d %3d %3d %5.2f %6.1f %s\n",
|
||||
result.tm_hour,
|
||||
result.tm_min,
|
||||
result.tm_sec,
|
||||
(int)snr,
|
||||
correct_bits,
|
||||
off - 0.5,
|
||||
hz0,
|
||||
msg.c_str());
|
||||
fflush(stdout);
|
||||
|
||||
return 2; // 2 => new decode, do subtract.
|
||||
}
|
||||
|
||||
void MainBench::testFT8()
|
||||
{
|
||||
qDebug("MainBench::testFT8: start");
|
||||
int hints[2] = { 2, 0 }; // CQ
|
||||
double budget = 5; // compute for this many seconds per cycle
|
||||
|
||||
int rate;
|
||||
std::vector<float> s = FT8::readwav("/home/f4exb/.local/share/WSJT-X/save/230105_091630.wav", rate); // FIXME: download file
|
||||
FT8::entry(
|
||||
s.data(),
|
||||
s.size(),
|
||||
0.5 * rate,
|
||||
rate,
|
||||
150,
|
||||
3600, // 2900,
|
||||
hints,
|
||||
hints,
|
||||
budget,
|
||||
budget,
|
||||
hcb,
|
||||
0,
|
||||
(struct FT8::cdecode *) 0
|
||||
);
|
||||
qDebug("MainBench::testFT8: end");
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user