| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							| 
									
										
										
										
											2023-11-19 05:24:06 +01:00
										 |  |  | // Copyright (C) 2023 Edouard Griffiths, F4EXB <f4exb06@gmail.com>               //
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon          //
 | 
					
						
							|  |  |  | // reformatted and adapted to Qt and SDRangel context                            //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This program is free software; you can redistribute it and/or modify          //
 | 
					
						
							|  |  |  | // it under the terms of the GNU General Public License as published by          //
 | 
					
						
							|  |  |  | // the Free Software Foundation as version 3 of the License, or                  //
 | 
					
						
							|  |  |  | // (at your option) any later version.                                           //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful,               //
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of                //
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
 | 
					
						
							|  |  |  | // GNU General Public License V3 for more details.                               //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU General Public License             //
 | 
					
						
							|  |  |  | // along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 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 "arrays.h"
 | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  | #include "libldpc.h"
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | // 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.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  | int LDPC::ldpc_check(int codeword[]) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int score = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Nm[83][7]
 | 
					
						
							|  |  |  |     for (int j = 0; j < 83; j++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         int x = 0; | 
					
						
							|  |  |  |         for (int ii1 = 0; ii1 < 7; ii1++) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  |             int i1 = Arrays::Nm[j][ii1] - 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             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.
 | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  | void LDPC::ldpc_decode(float llcodeword[], int iters, int plain[], int *ok) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     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++) | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  |                 int i1 = Arrays::Nm[j][ii1] - 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 if (i1 < 0) | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 REAL a = 1.0; | 
					
						
							|  |  |  |                 for (int ii2 = 0; ii2 < 7; ii2++) | 
					
						
							|  |  |  |                 { | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  |                     int i2 = Arrays::Nm[j][ii2] - 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     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++) | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  |                 int j2 = Arrays::Mn[i][j] - 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 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++) | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  |                 int j1 = Arrays::Mn[i][ji1] - 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 REAL q0 = codeword[i]; | 
					
						
							|  |  |  |                 REAL q1 = 1.0 - q0; | 
					
						
							|  |  |  |                 for (int ji2 = 0; ji2 < 3; ji2++) | 
					
						
							|  |  |  |                 { | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  |                     int j2 = Arrays::Mn[i][ji2] - 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     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
 | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  | float LDPC::fast_tanh(float x) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     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.
 | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  | void LDPC::ldpc_decode_log(float codeword[], int iters, int plain[], int *ok) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     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++) | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  |                 int i1 = Arrays::Nm[j][ii1] - 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 if (i1 < 0) | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 REAL a = 1.0; | 
					
						
							|  |  |  |                 for (int ii2 = 0; ii2 < 7; ii2++) | 
					
						
							|  |  |  |                 { | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  |                     int i2 = Arrays::Nm[j][ii2] - 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     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++) | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  |                 l += e[Arrays::Mn[i][j] - 1][i]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             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++) | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  |                 int j1 = Arrays::Mn[i][ji1] - 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 REAL l = codeword[i]; | 
					
						
							|  |  |  |                 for (int ji2 = 0; ji2 < 3; ji2++) | 
					
						
							|  |  |  |                 { | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  |                     int j2 = Arrays::Mn[i][ji2] - 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     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
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  | void LDPC::ft8_crc(int msg1[], int msglen, int out[14]) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     // 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)); | 
					
						
							| 
									
										
										
										
											2024-07-21 14:20:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     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; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-21 14:20:48 +02:00
										 |  |  |     for (int i = 0; i < msglen + 14; i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							|  |  |  |         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.
 | 
					
						
							| 
									
										
										
										
											2024-03-28 15:15:48 +01:00
										 |  |  | void LDPC::gauss_jordan(int rows, int cols, int m[174][2 * 91], int which[91], int *ok) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | // gauss_jordan(int rows, int cols, int m[cols][2*rows], int which[rows], int *ok)
 | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     *ok = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-24 04:05:35 +01:00
										 |  |  |     if ((rows != 91) || (cols != 174)) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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
 |