| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | // 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/>.          //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // An FT8 receiver in C++.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Many ideas and protocol details borrowed from Franke
 | 
					
						
							|  |  |  | // and Taylor's WSJT-X code.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Robert Morris, AB1HL
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <assert.h>
 | 
					
						
							|  |  |  | #include <math.h>
 | 
					
						
							|  |  |  | #include <complex>
 | 
					
						
							|  |  |  | #include <fftw3.h>
 | 
					
						
							|  |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | #include <complex>
 | 
					
						
							|  |  |  | #include <random>
 | 
					
						
							|  |  |  | #include <functional>
 | 
					
						
							|  |  |  | #include <map>
 | 
					
						
							| 
									
										
										
										
											2023-01-12 01:02:38 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <QThread>
 | 
					
						
							| 
									
										
										
										
											2023-01-11 00:21:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | #include "util.h"
 | 
					
						
							|  |  |  | #include "ft8.h"
 | 
					
						
							|  |  |  | #include "libldpc.h"
 | 
					
						
							|  |  |  | #include "osd.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace FT8 { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // return a Hamming window of length n.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<float> hamming(int n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     std::vector<float> h(n); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (int k = 0; k < n; k++) { | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         h[k] = 0.54 - 0.46 * cos(2 * M_PI * k / (n - 1.0)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     return h; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // blackman window
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<float> blackman(int n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     std::vector<float> h(n); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (int k = 0; k < n; k++) { | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         h[k] = 0.42 - 0.5 * cos(2 * M_PI * k / n) + 0.08 * cos(4 * M_PI * k / n); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     return h; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // symmetric blackman window
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<float> sym_blackman(int n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     std::vector<float> h(n); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (int k = 0; k < (n / 2) + 1; k++) { | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         h[k] = 0.42 - 0.5 * cos(2 * M_PI * k / n) + 0.08 * cos(4 * M_PI * k / n); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (int k = n - 1; k >= (n / 2) + 1; --k) { | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         h[k] = h[(n - 1) - k]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     return h; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // blackman-harris window
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<float> blackmanharris(int n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     float a0 = 0.35875; | 
					
						
							|  |  |  |     float a1 = 0.48829; | 
					
						
							|  |  |  |     float a2 = 0.14128; | 
					
						
							|  |  |  |     float a3 = 0.01168; | 
					
						
							|  |  |  |     std::vector<float> h(n); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     for (int k = 0; k < n; k++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // symmetric
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         h[k] = a0 - a1 * cos(2 * M_PI * k / (n - 1)) + a2 * cos(4 * M_PI * k / (n - 1)) - a3 * cos(6 * M_PI * k / (n - 1)); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         // periodic
 | 
					
						
							|  |  |  |         // h[k] =
 | 
					
						
							|  |  |  |         //  a0
 | 
					
						
							|  |  |  |         //  - a1 * cos(2 * M_PI * k / n)
 | 
					
						
							|  |  |  |         //  + a2 * cos(4 * M_PI * k / n)
 | 
					
						
							|  |  |  |         //  - a3 * cos(6 * M_PI * k / n);
 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     return h; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | Stats::Stats(int how, float log_tail, float log_rate) : | 
					
						
							|  |  |  |     sum_(0), | 
					
						
							|  |  |  |     finalized_(false), | 
					
						
							|  |  |  |     how_(how), | 
					
						
							|  |  |  |     log_tail_(log_tail), | 
					
						
							|  |  |  |     log_rate_(log_rate) | 
					
						
							|  |  |  | {} | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | void Stats::add(float x) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     a_.push_back(x); | 
					
						
							|  |  |  |     sum_ += x; | 
					
						
							|  |  |  |     finalized_ = false; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | void Stats::finalize() | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     finalized_ = true; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int n = a_.size(); | 
					
						
							|  |  |  |     mean_ = sum_ / n; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float var = 0; | 
					
						
							|  |  |  |     float bsum = 0; | 
					
						
							|  |  |  |     for (int i = 0; i < n; i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         float y = a_[i] - mean_; | 
					
						
							|  |  |  |         var += y * y; | 
					
						
							|  |  |  |         bsum += fabs(y); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     var /= n; | 
					
						
							|  |  |  |     stddev_ = sqrt(var); | 
					
						
							|  |  |  |     b_ = bsum / n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // prepare for binary search to find where values lie
 | 
					
						
							|  |  |  |     // in the distribution.
 | 
					
						
							|  |  |  |     if (how_ != 0 && how_ != 5) | 
					
						
							|  |  |  |         std::sort(a_.begin(), a_.end()); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | float Stats::mean() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!finalized_) { | 
					
						
							|  |  |  |         finalize(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return mean_; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | float Stats::stddev() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!finalized_) { | 
					
						
							|  |  |  |         finalize(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return stddev_; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // fraction of distribution that's less than x.
 | 
					
						
							|  |  |  | // assumes normal distribution.
 | 
					
						
							|  |  |  | // this is PHI(x), or the CDF at x,
 | 
					
						
							|  |  |  | // or the integral from -infinity
 | 
					
						
							|  |  |  | // to x of the PDF.
 | 
					
						
							|  |  |  | float Stats::gaussian_problt(float x) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     float SDs = (x - mean()) / stddev(); | 
					
						
							|  |  |  |     float frac = 0.5 * (1.0 + erf(SDs / sqrt(2.0))); | 
					
						
							|  |  |  |     return frac; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | // https://en.wikipedia.org/wiki/Laplace_distribution
 | 
					
						
							|  |  |  | // m and b from page 116 of Mark Owen's Practical Signal Processing.
 | 
					
						
							|  |  |  | float Stats::laplace_problt(float x) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     float m = mean(); | 
					
						
							|  |  |  |     float cdf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (x < m) { | 
					
						
							|  |  |  |         cdf = 0.5 * exp((x - m) / b_); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         cdf = 1.0 - 0.5 * exp(-(x - m) / b_); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return cdf; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // look into the actual distribution.
 | 
					
						
							|  |  |  | float Stats::problt(float x) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!finalized_) { | 
					
						
							|  |  |  |         finalize(); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (how_ == 0) { | 
					
						
							|  |  |  |         return gaussian_problt(x); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (how_ == 5) { | 
					
						
							|  |  |  |         return laplace_problt(x); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // binary search.
 | 
					
						
							|  |  |  |     auto it = std::lower_bound(a_.begin(), a_.end(), x); | 
					
						
							|  |  |  |     int i = it - a_.begin(); | 
					
						
							|  |  |  |     int n = a_.size(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (how_ == 1) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // index into the distribution.
 | 
					
						
							|  |  |  |         // works poorly for values that are off the ends
 | 
					
						
							|  |  |  |         // of the distribution, since those are all
 | 
					
						
							|  |  |  |         // mapped to 0.0 or 1.0, regardless of magnitude.
 | 
					
						
							|  |  |  |         return i / (float)n; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (how_ == 2) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // use a kind of logistic regression for
 | 
					
						
							|  |  |  |         // values near the edges of the distribution.
 | 
					
						
							|  |  |  |         if (i < log_tail_ * n) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             float x0 = a_[(int)(log_tail_ * n)]; | 
					
						
							|  |  |  |             float y = 1.0 / (1.0 + exp(-log_rate_ * (x - x0))); | 
					
						
							|  |  |  |             // y is 0..0.5
 | 
					
						
							|  |  |  |             y /= 5; | 
					
						
							|  |  |  |             return y; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (i > (1 - log_tail_) * n) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             float x0 = a_[(int)((1 - log_tail_) * n)]; | 
					
						
							|  |  |  |             float y = 1.0 / (1.0 + exp(-log_rate_ * (x - x0))); | 
					
						
							|  |  |  |             // y is 0.5..1
 | 
					
						
							|  |  |  |             // we want (1-log_tail)..1
 | 
					
						
							|  |  |  |             y -= 0.5; | 
					
						
							|  |  |  |             y *= 2; | 
					
						
							|  |  |  |             y *= log_tail_; | 
					
						
							|  |  |  |             y += (1 - log_tail_); | 
					
						
							|  |  |  |             return y; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             return i / (float)n; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (how_ == 3) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // gaussian for values near the edge of the distribution.
 | 
					
						
							|  |  |  |         if (i < log_tail_ * n) { | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             return gaussian_problt(x); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         } else if (i > (1 - log_tail_) * n) { | 
					
						
							|  |  |  |             return gaussian_problt(x); | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             return i / (float)n; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (how_ == 4) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // gaussian for values outside the distribution.
 | 
					
						
							|  |  |  |         if (x < a_[0] || x > a_.back()) { | 
					
						
							|  |  |  |             return gaussian_problt(x); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return i / (float)n; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | // a-priori probability of each of the 174 LDPC codeword
 | 
					
						
							|  |  |  | // bits being one. measured from reconstructed correct
 | 
					
						
							|  |  |  | // codewords, into ft8bits, then python bprob.py.
 | 
					
						
							|  |  |  | // from ft8-n4
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | const double FT8::apriori174[] = { | 
					
						
							| 
									
										
										
										
											2023-01-08 23:15:31 +01:00
										 |  |  |   0.47, 0.32, 0.29, 0.37, 0.52, 0.36, 0.40, 0.42, 0.42, 0.53, 0.44, | 
					
						
							|  |  |  |   0.44, 0.39, 0.46, 0.39, 0.38, 0.42, 0.43, 0.45, 0.51, 0.42, 0.48, | 
					
						
							|  |  |  |   0.31, 0.45, 0.47, 0.53, 0.59, 0.41, 0.03, 0.50, 0.30, 0.26, 0.40, | 
					
						
							|  |  |  |   0.65, 0.34, 0.49, 0.46, 0.49, 0.69, 0.40, 0.45, 0.45, 0.60, 0.46, | 
					
						
							|  |  |  |   0.43, 0.49, 0.56, 0.45, 0.55, 0.51, 0.46, 0.37, 0.55, 0.52, 0.56, | 
					
						
							|  |  |  |   0.55, 0.50, 0.01, 0.19, 0.70, 0.88, 0.75, 0.75, 0.74, 0.73, 0.18, | 
					
						
							|  |  |  |   0.71, 0.35, 0.60, 0.58, 0.36, 0.60, 0.38, 0.50, 0.02, 0.01, 0.98, | 
					
						
							|  |  |  |   0.48, 0.49, 0.54, 0.50, 0.49, 0.53, 0.50, 0.49, 0.49, 0.51, 0.51, | 
					
						
							|  |  |  |   0.51, 0.47, 0.50, 0.53, 0.51, 0.46, 0.51, 0.51, 0.48, 0.51, 0.52, | 
					
						
							|  |  |  |   0.50, 0.52, 0.51, 0.50, 0.49, 0.53, 0.52, 0.50, 0.46, 0.47, 0.48, | 
					
						
							|  |  |  |   0.52, 0.50, 0.49, 0.51, 0.49, 0.49, 0.50, 0.50, 0.50, 0.50, 0.51, | 
					
						
							|  |  |  |   0.50, 0.49, 0.49, 0.55, 0.49, 0.51, 0.48, 0.55, 0.49, 0.48, 0.50, | 
					
						
							|  |  |  |   0.51, 0.50, 0.51, 0.50, 0.51, 0.53, 0.49, 0.54, 0.50, 0.48, 0.49, | 
					
						
							|  |  |  |   0.46, 0.51, 0.51, 0.52, 0.49, 0.51, 0.49, 0.51, 0.50, 0.49, 0.50, | 
					
						
							|  |  |  |   0.50, 0.47, 0.49, 0.52, 0.49, 0.51, 0.49, 0.48, 0.52, 0.48, 0.49, | 
					
						
							|  |  |  |   0.47, 0.50, 0.48, 0.50, 0.49, 0.51, 0.51, 0.51, 0.49, | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | FT8::FT8( | 
					
						
							|  |  |  |     const std::vector<float> &samples, | 
					
						
							|  |  |  |     float min_hz, | 
					
						
							|  |  |  |     float max_hz, | 
					
						
							|  |  |  |     int start, | 
					
						
							|  |  |  |     int rate, | 
					
						
							|  |  |  |     int hints1[], | 
					
						
							|  |  |  |     int hints2[], | 
					
						
							|  |  |  |     double deadline, | 
					
						
							|  |  |  |     double final_deadline, | 
					
						
							|  |  |  |     CallbackInterface *cb, | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     std::vector<cdecode> prevdecs, | 
					
						
							|  |  |  |     FFTEngine *fftEngine | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     samples_ = samples; | 
					
						
							|  |  |  |     min_hz_ = min_hz; | 
					
						
							|  |  |  |     max_hz_ = max_hz; | 
					
						
							|  |  |  |     prevdecs_ = prevdecs; | 
					
						
							|  |  |  |     start_ = start; | 
					
						
							|  |  |  |     rate_ = rate; | 
					
						
							|  |  |  |     deadline_ = deadline; | 
					
						
							|  |  |  |     final_deadline_ = final_deadline; | 
					
						
							|  |  |  |     cb_ = cb; | 
					
						
							|  |  |  |     down_hz_ = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int i = 0; hints1[i]; i++) { | 
					
						
							|  |  |  |         hints1_.push_back(hints1[i]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int i = 0; hints2[i]; i++) { | 
					
						
							|  |  |  |         hints2_.push_back(hints2[i]); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     hack_size_ = -1; | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     hack_data_ = nullptr; | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     hack_off_ = -1; | 
					
						
							|  |  |  |     hack_len_ = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     plan32_ = nullptr; | 
					
						
							|  |  |  |     fftEngine_ = fftEngine; | 
					
						
							| 
									
										
										
										
											2023-01-12 01:02:38 +01:00
										 |  |  |     npasses_ = 1; | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | FT8::~FT8() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-12 01:02:38 +01:00
										 |  |  | void FT8::start_work() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     go(npasses_); | 
					
						
							|  |  |  |     emit finished(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // strength of costas block of signal with tone 0 at bi0,
 | 
					
						
							|  |  |  | // and symbol zero at si0.
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  | float FT8::one_coarse_strength(const FFTEngine::ffts_t &bins, int bi0, int si0) | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int costas[] = {3, 1, 4, 0, 6, 5, 2}; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     assert(si0 >= 0 && si0 + 72 + 8 <= (int)bins.size()); | 
					
						
							|  |  |  |     assert(bi0 >= 0 && bi0 + 8 <= (int)bins[0].size()); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float sig = 0.0; | 
					
						
							|  |  |  |     float noise = 0.0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.coarse_all >= 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         for (int si = 0; si < 79; si++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float mx; | 
					
						
							|  |  |  |             int mxi = -1; | 
					
						
							|  |  |  |             float sum = 0; | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             for (int i = 0; i < 8; i++) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 float x = std::abs(bins[si0 + si][bi0 + i]); | 
					
						
							|  |  |  |                 sum += x; | 
					
						
							|  |  |  |                 if (mxi < 0 || x > mx) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     mxi = i; | 
					
						
							|  |  |  |                     mx = x; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             if (si >= 0 && si < 7) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 float x = std::abs(bins[si0 + si][bi0 + costas[si - 0]]); | 
					
						
							|  |  |  |                 sig += x; | 
					
						
							|  |  |  |                 noise += sum - x; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (si >= 36 && si < 36 + 7) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 float x = std::abs(bins[si0 + si][bi0 + costas[si - 36]]); | 
					
						
							|  |  |  |                 sig += x; | 
					
						
							|  |  |  |                 noise += sum - x; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (si >= 72 && si < 72 + 7) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 float x = std::abs(bins[si0 + si][bi0 + costas[si - 72]]); | 
					
						
							|  |  |  |                 sig += x; | 
					
						
							|  |  |  |                 noise += sum - x; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 sig += params.coarse_all * mx; | 
					
						
							|  |  |  |                 noise += params.coarse_all * (sum - mx); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // coarse_all = -1
 | 
					
						
							|  |  |  |         // just costas symbols
 | 
					
						
							|  |  |  |         for (int si = 0; si < 7; si++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             for (int bi = 0; bi < 8; bi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 float x = 0; | 
					
						
							|  |  |  |                 x += std::abs(bins[si0 + si][bi0 + bi]); | 
					
						
							|  |  |  |                 x += std::abs(bins[si0 + 36 + si][bi0 + bi]); | 
					
						
							|  |  |  |                 x += std::abs(bins[si0 + 72 + si][bi0 + bi]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (bi == costas[si]) { | 
					
						
							|  |  |  |                     sig += x; | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     noise += x; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.coarse_strength_how == 0) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         return sig - noise; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     else if (params.coarse_strength_how == 1) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         return sig - noise / 7; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.coarse_strength_how == 2) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         return sig / (noise / 7); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.coarse_strength_how == 3) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig / (sig + (noise / 7)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.coarse_strength_how == 4) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.coarse_strength_how == 5) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig / (sig + noise); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.coarse_strength_how == 6) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // this is it.
 | 
					
						
							|  |  |  |         return sig / noise; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | // return symbol length in samples at the given rate.
 | 
					
						
							|  |  |  | // insist on integer symbol lengths so that we can
 | 
					
						
							|  |  |  | // use whole FFT bins.
 | 
					
						
							|  |  |  | int FT8::blocksize(int rate) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // FT8 symbol length is 1920 at 12000 samples/second.
 | 
					
						
							|  |  |  |     int xblock = 1920 / (12000.0 / rate); | 
					
						
							|  |  |  |     assert(xblock == (int)xblock); | 
					
						
							|  |  |  |     int block = xblock; | 
					
						
							|  |  |  |     return block; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2023-01-11 00:21:47 +01:00
										 |  |  | // look for potential psignals by searching FFT bins for Costas symbol
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | // blocks. returns a vector of candidate positions.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  | std::vector<Strength> FT8::coarse(const FFTEngine::ffts_t &bins, int si0, int si1) | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int block = blocksize(rate_); | 
					
						
							|  |  |  |     int nbins = bins[0].size(); | 
					
						
							|  |  |  |     float bin_hz = rate_ / (float)block; | 
					
						
							|  |  |  |     int min_bin = min_hz_ / bin_hz; | 
					
						
							|  |  |  |     int max_bin = max_hz_ / bin_hz; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     std::vector<Strength> strengths; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (int bi = min_bin; bi < max_bin && bi + 8 <= nbins; bi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         std::vector<Strength> sv; | 
					
						
							|  |  |  |         for (int si = si0; si < si1 && si + 79 < (int)bins.size(); si++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float s = one_coarse_strength(bins, bi, si); | 
					
						
							|  |  |  |             Strength st; | 
					
						
							|  |  |  |             st.strength_ = s; | 
					
						
							|  |  |  |             st.hz_ = bi * 6.25; | 
					
						
							|  |  |  |             st.off_ = si * block; | 
					
						
							|  |  |  |             sv.push_back(st); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (sv.size() < 1) | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // save best ncoarse offsets, but require that they be separated
 | 
					
						
							|  |  |  |         // by at least one symbol time.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         std::sort(sv.begin(), sv.end(), | 
					
						
							|  |  |  |                     [](const Strength &a, const Strength &b) -> bool | 
					
						
							|  |  |  |                     { return a.strength_ > b.strength_; }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         strengths.push_back(sv[0]); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         int nn = 1; | 
					
						
							|  |  |  |         for (int i = 1; nn < params.ncoarse && i < (int)sv.size(); i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             if (std::abs(sv[i].off_ - sv[0].off_) > params.ncoarse_blocks * block) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 strengths.push_back(sv[i]); | 
					
						
							|  |  |  |                 nn++; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return strengths; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // reduce the sample rate from arate to brate.
 | 
					
						
							|  |  |  | // center hz0..hz1 in the new nyquist range.
 | 
					
						
							|  |  |  | // but first filter to that range.
 | 
					
						
							|  |  |  | // sets delta_hz to hz moved down.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<float> FT8::reduce_rate( | 
					
						
							|  |  |  |     const std::vector<float> &a, | 
					
						
							|  |  |  |     float hz0, | 
					
						
							|  |  |  |     float hz1, | 
					
						
							|  |  |  |     int arate, | 
					
						
							|  |  |  |     int brate, | 
					
						
							|  |  |  |     float &delta_hz | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(brate < arate); | 
					
						
							|  |  |  |     assert(hz1 - hz0 <= brate / 2); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // the pass band is hz0..hz1
 | 
					
						
							|  |  |  |     // stop bands are 0..hz00 and hz11..nyquist.
 | 
					
						
							|  |  |  |     float hz00, hz11; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     hz0 = std::max(0.0f, hz0 - params.reduce_extra); | 
					
						
							|  |  |  |     hz1 = std::min(arate / 2.0f, hz1 + params.reduce_extra); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.reduce_shoulder > 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         hz00 = hz0 - params.reduce_shoulder; | 
					
						
							|  |  |  |         hz11 = hz1 + params.reduce_shoulder; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         float mid = (hz0 + hz1) / 2; | 
					
						
							|  |  |  |         hz00 = mid - (brate * params.reduce_factor); | 
					
						
							|  |  |  |         hz00 = std::min(hz00, hz0); | 
					
						
							|  |  |  |         hz11 = mid + (brate * params.reduce_factor); | 
					
						
							|  |  |  |         hz11 = std::max(hz11, hz1); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int alen = a.size(); | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     std::vector<std::complex<float>> bins1 = fftEngine_->one_fft( | 
					
						
							|  |  |  |         a, 0, alen, "reduce_rate1", 0); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int nbins1 = bins1.size(); | 
					
						
							|  |  |  |     float bin_hz = arate / (float)alen; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.reduce_how == 2) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // band-pass filter the FFT output.
 | 
					
						
							|  |  |  |         bins1 = fbandpass(bins1, bin_hz, | 
					
						
							|  |  |  |                             hz00, | 
					
						
							|  |  |  |                             hz0, | 
					
						
							|  |  |  |                             hz1, | 
					
						
							|  |  |  |                             hz11); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.reduce_how == 3) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         for (int i = 0; i < nbins1; i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             if (i < (hz0 / bin_hz)) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 bins1[i] = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (i > (hz1 / bin_hz)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 bins1[i] = 0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // shift down.
 | 
					
						
							|  |  |  |     int omid = ((hz0 + hz1) / 2) / bin_hz; | 
					
						
							|  |  |  |     int nmid = (brate / 4.0) / bin_hz; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int delta = omid - nmid; // amount to move down
 | 
					
						
							|  |  |  |     assert(delta < nbins1); | 
					
						
							|  |  |  |     int blen = round(alen * (brate / (float)arate)); | 
					
						
							|  |  |  |     std::vector<std::complex<float>> bbins(blen / 2 + 1); | 
					
						
							|  |  |  |     for (int i = 0; i < (int)bbins.size(); i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (delta > 0) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             bbins[i] = bins1[i + delta]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             bbins[i] = bins1[i]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // use ifft to reduce the rate.
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     std::vector<float> vvv = fftEngine_->one_ifft(bbins, "reduce_rate2"); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     delta_hz = delta * bin_hz; | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return vvv; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | void FT8::go(int npasses) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // cache to avoid cost of fftw planner mutex.
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     plan32_ = fftEngine_->get_plan(32, "cache32"); | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         fprintf(stderr, "go: %.0f .. %.0f, %.0f, rate=%d\n", | 
					
						
							|  |  |  |                 min_hz_, max_hz_, max_hz_ - min_hz_, rate_); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // trim to make samples_ a good size for FFTW.
 | 
					
						
							|  |  |  |     int nice_sizes[] = {18000, 18225, 36000, 36450, | 
					
						
							|  |  |  |                         54000, 54675, 72000, 72900, | 
					
						
							|  |  |  |                         144000, 145800, 216000, 218700, | 
					
						
							|  |  |  |                         0}; | 
					
						
							|  |  |  |     int nice = -1; | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (int i = 0; nice_sizes[i]; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         int sz = nice_sizes[i]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (fabs(samples_.size() - sz) < 0.05 * samples_.size()) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             nice = sz; | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (nice != -1) { | 
					
						
							|  |  |  |         samples_.resize(nice); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     assert(min_hz_ >= 0 && max_hz_ + 50 <= rate_ / 2); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // can we reduce the sample rate?
 | 
					
						
							|  |  |  |     int nrate = -1; | 
					
						
							|  |  |  |     for (int xrate = 100; xrate < rate_; xrate += 100) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (xrate < rate_ && (params.oddrate || (rate_ % xrate) == 0)) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             if (((max_hz_ - min_hz_) + 50 + 2 * params.go_extra) < params.nyquist * (xrate / 2)) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 nrate = xrate; | 
					
						
							|  |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.do_reduce && nrate > 0 && nrate < rate_ * 0.75) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // filter and reduce the sample rate from rate_ to nrate.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         double t0 = now(); | 
					
						
							|  |  |  |         int osize = samples_.size(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         float delta_hz; // how much it moved down
 | 
					
						
							|  |  |  |         samples_ = reduce_rate( | 
					
						
							|  |  |  |             samples_, | 
					
						
							|  |  |  |             min_hz_ - 3.1 - params.go_extra, | 
					
						
							|  |  |  |             max_hz_ + 50 - 3.1 + params.go_extra, | 
					
						
							|  |  |  |             rate_, | 
					
						
							|  |  |  |             nrate, | 
					
						
							|  |  |  |             delta_hz | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         double t1 = now(); | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (t1 - t0 > 0.1) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             fprintf(stderr, "reduce oops, size %d -> %d, rate %d -> %d, took %.2f\n", | 
					
						
							|  |  |  |                     osize, | 
					
						
							|  |  |  |                     (int)samples_.size(), | 
					
						
							|  |  |  |                     rate_, | 
					
						
							|  |  |  |                     nrate, | 
					
						
							|  |  |  |                     t1 - t0); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (0) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             fprintf(stderr, "%.0f..%.0f, range %.0f, rate %d -> %d, delta hz %.0f, %.6f sec\n", | 
					
						
							|  |  |  |                     min_hz_, max_hz_, | 
					
						
							|  |  |  |                     max_hz_ - min_hz_, | 
					
						
							|  |  |  |                     rate_, nrate, delta_hz, t1 - t0); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (delta_hz > 0) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             down_hz_ = delta_hz; // to adjust hz for Python.
 | 
					
						
							|  |  |  |             min_hz_ -= down_hz_; | 
					
						
							|  |  |  |             max_hz_ -= down_hz_; | 
					
						
							|  |  |  |             for (int i = 0; i < (int)prevdecs_.size(); i++) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 prevdecs_[i].hz0 -= delta_hz; | 
					
						
							|  |  |  |                 prevdecs_[i].hz1 -= delta_hz; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         assert(max_hz_ + 50 < nrate / 2); | 
					
						
							|  |  |  |         assert(min_hz_ >= 0); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         float ratio = nrate / (float)rate_; | 
					
						
							|  |  |  |         rate_ = nrate; | 
					
						
							|  |  |  |         start_ = round(start_ * ratio); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int block = blocksize(rate_); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // start_ is the sample number of 0.5 seconds, the nominal start time.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // make sure there's at least tplus*rate_ samples after the end.
 | 
					
						
							|  |  |  |     if (start_ + params.tplus * rate_ + 79 * block + block > samples_.size()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         int need = start_ + params.tplus * rate_ + 79 * block - samples_.size(); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // round up to a whole second, to ease fft plan caching.
 | 
					
						
							|  |  |  |         if ((need % rate_) != 0) | 
					
						
							|  |  |  |             need += rate_ - (need % rate_); | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         std::default_random_engine generator; | 
					
						
							|  |  |  |         std::uniform_int_distribution<int> distribution(0, samples_.size() - 1); | 
					
						
							|  |  |  |         auto rnd = std::bind(distribution, generator); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         std::vector<float> v(need); | 
					
						
							|  |  |  |         for (int i = 0; i < need; i++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // v[i] = 0;
 | 
					
						
							|  |  |  |             v[i] = samples_[rnd()]; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         samples_.insert(samples_.end(), v.begin(), v.end()); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int si0 = (start_ - params.tminus * rate_) / block; | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (si0 < 0) { | 
					
						
							|  |  |  |         si0 = 0; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int si1 = (start_ + params.tplus * rate_) / block; | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // a copy from which to subtract.
 | 
					
						
							|  |  |  |     nsamples_ = samples_; | 
					
						
							|  |  |  |     int any = 0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (int i = 0; i < (int)prevdecs_.size(); i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         auto d = prevdecs_[i]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (d.hz0 >= min_hz_ && d.hz0 <= max_hz_) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // reconstruct correct 79 symbols from LDPC output.
 | 
					
						
							|  |  |  |             std::vector<int> re79 = recode(d.bits); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             // fine up hz/off again now that we have more samples
 | 
					
						
							|  |  |  |             float best_hz = (d.hz0 + d.hz1) / 2.0; | 
					
						
							|  |  |  |             float best_off = d.off; // seconds
 | 
					
						
							|  |  |  |             search_both_known(samples_, rate_, re79, | 
					
						
							|  |  |  |                                 best_hz, | 
					
						
							|  |  |  |                                 best_off, | 
					
						
							|  |  |  |                                 best_hz, best_off); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             // subtract from nsamples_.
 | 
					
						
							|  |  |  |             subtract(re79, best_hz, best_hz, best_off); | 
					
						
							|  |  |  |             any += 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (any) { | 
					
						
							|  |  |  |         samples_ = nsamples_; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (pass_ = 0; pass_ < npasses; pass_++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         double total_remaining = deadline_ - now(); | 
					
						
							|  |  |  |         double remaining = total_remaining / (npasses - pass_); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (pass_ == 0) { | 
					
						
							|  |  |  |             remaining *= params.pass0_frac; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         double deadline = now() + remaining; | 
					
						
							|  |  |  |         int new_decodes = 0; | 
					
						
							|  |  |  |         samples_ = nsamples_; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         std::vector<Strength> order; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |         // search coarsely for Costas blocks.
 | 
					
						
							|  |  |  |         // in fractions of bins in off and hz.
 | 
					
						
							|  |  |  |         //
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // just do this once, re-use for every fractional fft_shift
 | 
					
						
							|  |  |  |         // and down_v7_f() to 200 sps.
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |         std::vector<std::complex<float>> bins = fftEngine_->one_fft( | 
					
						
							|  |  |  |             samples_, 0, samples_.size(), "go1", 0); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         for (int hz_frac_i = 0; hz_frac_i < params.coarse_hz_n; hz_frac_i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             // shift down by hz_frac
 | 
					
						
							|  |  |  |             float hz_frac = hz_frac_i * (6.25 / params.coarse_hz_n); | 
					
						
							|  |  |  |             std::vector<float> samples1; | 
					
						
							|  |  |  |             if (hz_frac_i == 0) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 samples1 = samples_; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 samples1 = fft_shift_f(bins, rate_, hz_frac); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (int off_frac_i = 0; off_frac_i < params.coarse_off_n; off_frac_i++) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 int off_frac = off_frac_i * (block / params.coarse_off_n); | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |                 FFTEngine::ffts_t bins = fftEngine_->ffts(samples1, off_frac, block, "go2"); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 std::vector<Strength> oo = coarse(bins, si0, si1); | 
					
						
							|  |  |  |                 for (int i = 0; i < (int)oo.size(); i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     oo[i].hz_ += hz_frac; | 
					
						
							|  |  |  |                     oo[i].off_ += off_frac; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 order.insert(order.end(), oo.begin(), oo.end()); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // sort strongest-first.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         std::sort(order.begin(), order.end(), | 
					
						
							|  |  |  |                     [](const Strength &a, const Strength &b) -> bool | 
					
						
							|  |  |  |                     { return a.strength_ > b.strength_; }); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         char already[2000]; // XXX
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         for (int i = 0; i < (int)(sizeof(already) / sizeof(already[0])); i++) { | 
					
						
							|  |  |  |             already[i] = 0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         for (int ii = 0; ii < (int)order.size(); ii++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             double tt = now(); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             if (ii > 0 && | 
					
						
							|  |  |  |                 tt > deadline && | 
					
						
							|  |  |  |                 (tt > deadline_ || new_decodes >= params.pass_threshold) && | 
					
						
							|  |  |  |                 (pass_ < npasses - 1 || tt > final_deadline_) | 
					
						
							|  |  |  |             ) { | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float hz = order[ii].hz_; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             if (already[(int)round(hz / params.already_hz)]) { | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             int off = order[ii].off_; | 
					
						
							|  |  |  |             int ret = one(bins, samples_.size(), hz, off); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             if (ret) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 if (ret == 2) { | 
					
						
							|  |  |  |                     new_decodes++; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 already[(int)round(hz / params.already_hz)] = 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } // pass
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // what's the strength of the Costas sync blocks of
 | 
					
						
							|  |  |  | // the signal starting at hz and off?
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | float FT8::one_strength(const std::vector<float> &samples200, float hz, int off) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int bin0 = round(hz / 6.25); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int costas[] = {3, 1, 4, 0, 6, 5, 2}; | 
					
						
							|  |  |  |     int starts[] = {0, 36, 72}; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float sig = 0; | 
					
						
							|  |  |  |     float noise = 0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (int which = 0; which < 3; which++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         int start = starts[which]; | 
					
						
							|  |  |  |         for (int si = 0; si < 7; si++) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |             auto fft = fftEngine_->one_fft(samples200, off + (si + start) * 32, 32, "one_strength", plan32_); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             for (int bi = 0; bi < 8; bi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 float x = std::abs(fft[bin0 + bi]); | 
					
						
							|  |  |  |                 if (bi == costas[si]) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     sig += x; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     noise += x; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.strength_how == 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig - noise; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.strength_how == 1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig - noise / 7; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.strength_how == 2) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig / (noise / 7); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.strength_how == 3) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig / (sig + (noise / 7)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.strength_how == 4) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.strength_how == 5) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig / (sig + noise); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.strength_how == 6) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig / noise; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // given a complete known signal's symbols in syms,
 | 
					
						
							|  |  |  | // how strong is it? used to look for the best
 | 
					
						
							|  |  |  | // offset and frequency at which to subtract a
 | 
					
						
							|  |  |  | // decoded signal.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | float FT8::one_strength_known( | 
					
						
							|  |  |  |     const std::vector<float> &samples, | 
					
						
							|  |  |  |     int rate, | 
					
						
							|  |  |  |     const std::vector<int> &syms, | 
					
						
							|  |  |  |     float hz, | 
					
						
							|  |  |  |     int off | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int block = blocksize(rate); | 
					
						
							|  |  |  |     assert(syms.size() == 79); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int bin0 = round(hz / 6.25); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float sig = 0; | 
					
						
							|  |  |  |     float noise = 0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float sum7 = 0; | 
					
						
							|  |  |  |     std::complex<float> prev = 0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (int si = 0; si < 79; si += params.known_sparse) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |         auto fft = fftEngine_->one_fft(samples, off + si * block, block, "one_strength_known", 0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (params.known_strength_how == 7) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             std::complex<float> c = fft[bin0 + syms[si]]; | 
					
						
							|  |  |  |             if (si > 0) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 sum7 += std::abs(c - prev); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             prev = c; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             for (int bi = 0; bi < 8; bi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 float x = std::abs(fft[bin0 + bi]); | 
					
						
							|  |  |  |                 if (bi == syms[si]) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     sig += x; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     noise += x; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.known_strength_how == 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig - noise; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.known_strength_how == 1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig - noise / 7; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.known_strength_how == 2) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig / (noise / 7); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.known_strength_how == 3) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig / (sig + (noise / 7)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.known_strength_how == 4) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.known_strength_how == 5) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig / (sig + noise); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     else if (params.known_strength_how == 6) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return sig / noise; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (params.known_strength_how == 7) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return -sum7; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int FT8::search_time_fine( | 
					
						
							|  |  |  |     const std::vector<float> &samples200, | 
					
						
							|  |  |  |     int off0, | 
					
						
							|  |  |  |     int offN, | 
					
						
							|  |  |  |     float hz, | 
					
						
							|  |  |  |     int gran, | 
					
						
							|  |  |  |     float &str | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (off0 < 0) | 
					
						
							|  |  |  |         off0 = 0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // shift in frequency to put hz at 25.
 | 
					
						
							|  |  |  |     // only shift the samples we need, both for speed,
 | 
					
						
							|  |  |  |     // and try to always shift down the same number of samples
 | 
					
						
							|  |  |  |     // to make it easier to cache fftw plans.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int len = (offN - off0) + 79 * 32 + 32; | 
					
						
							|  |  |  |     if (off0 + len > (int)samples200.size()) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // len = samples200.size() - off0;
 | 
					
						
							|  |  |  |         // don't provoke random-length FFTs.
 | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     std::vector<float> downsamples200 = shift200(samples200, off0, len, hz); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int best_off = -1; | 
					
						
							|  |  |  |     float best_sum = 0.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int g = 0; g <= (offN - off0) && g + 79 * 32 <= len; g += gran) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float sum = one_strength(downsamples200, 25, g); | 
					
						
							|  |  |  |         if (sum > best_sum || best_off == -1) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             best_off = g; | 
					
						
							|  |  |  |             best_sum = sum; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     str = best_sum; | 
					
						
							|  |  |  |     assert(best_off >= 0); | 
					
						
							|  |  |  |     return off0 + best_off; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int FT8::search_time_fine_known( | 
					
						
							|  |  |  |     const std::vector<std::complex<float>> &bins, | 
					
						
							|  |  |  |     int rate, | 
					
						
							|  |  |  |     const std::vector<int> &syms, | 
					
						
							|  |  |  |     int off0, | 
					
						
							|  |  |  |     int offN, | 
					
						
							|  |  |  |     float hz, | 
					
						
							|  |  |  |     int gran, | 
					
						
							|  |  |  |     float &str | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (off0 < 0) | 
					
						
							|  |  |  |         off0 = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // nearest FFT bin center.
 | 
					
						
							|  |  |  |     float hz0 = round(hz / 6.25) * 6.25; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // move hz to hz0, so it is centered in a symbol-sized bin.
 | 
					
						
							|  |  |  |     std::vector<float> downsamples = fft_shift_f(bins, rate, hz - hz0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int best_off = -1; | 
					
						
							|  |  |  |     int block = blocksize(rate); | 
					
						
							|  |  |  |     float best_sum = 0.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int g = off0; g <= offN; g += gran) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (g >= 0 && g + 79 * block <= (int)downsamples.size()) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             float sum = one_strength_known(downsamples, rate, syms, hz0, g); | 
					
						
							|  |  |  |             if (sum > best_sum || best_off == -1) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 best_off = g; | 
					
						
							|  |  |  |                 best_sum = sum; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (best_off < 0) | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     str = best_sum; | 
					
						
							|  |  |  |     return best_off; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // search for costas blocks in an MxN time/frequency grid.
 | 
					
						
							|  |  |  | // hz0 +/- hz_win in hz_inc increments. hz0 should be near 25.
 | 
					
						
							|  |  |  | // off0 +/- off_win in off_inc incremenents.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<Strength> FT8::search_both( | 
					
						
							|  |  |  |     const std::vector<float> &samples200, | 
					
						
							|  |  |  |     float hz0, | 
					
						
							|  |  |  |     int hz_n, | 
					
						
							|  |  |  |     float hz_win, | 
					
						
							|  |  |  |     int off0, | 
					
						
							|  |  |  |     int off_n, | 
					
						
							|  |  |  |     int off_win | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(hz0 >= 25 - 6.25 / 2 && hz0 <= 25 + 6.25 / 2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     std::vector<Strength> strengths; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     float hz_inc = 2 * hz_win / hz_n; | 
					
						
							|  |  |  |     int off_inc = round(2 * off_win / (float)off_n); | 
					
						
							|  |  |  |     if (off_inc < 1) | 
					
						
							|  |  |  |         off_inc = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (float hz = hz0 - hz_win; hz <= hz0 + hz_win + 0.01; hz += hz_inc) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         float str = 0; | 
					
						
							|  |  |  |         int off = search_time_fine(samples200, off0 - off_win, off0 + off_win, hz, | 
					
						
							|  |  |  |                                     off_inc, str); | 
					
						
							|  |  |  |         if (off >= 0) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             Strength st; | 
					
						
							|  |  |  |             st.hz_ = hz; | 
					
						
							|  |  |  |             st.off_ = off; | 
					
						
							|  |  |  |             st.strength_ = str; | 
					
						
							|  |  |  |             strengths.push_back(st); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return strengths; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | void FT8::search_both_known( | 
					
						
							|  |  |  |     const std::vector<float> &samples, | 
					
						
							|  |  |  |     int rate, | 
					
						
							|  |  |  |     const std::vector<int> &syms, | 
					
						
							|  |  |  |     float hz0, | 
					
						
							|  |  |  |     float off_secs0, // seconds
 | 
					
						
							|  |  |  |     float &hz_out, | 
					
						
							|  |  |  |     float &off_out | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(hz0 >= 0 && hz0 + 50 < rate / 2); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int off0 = round(off_secs0 * (float)rate); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int off_win = params.third_off_win * blocksize(rate_); | 
					
						
							|  |  |  |     if (off_win < 1) | 
					
						
							|  |  |  |         off_win = 1; | 
					
						
							|  |  |  |     int off_inc = trunc((2.0 * off_win) / (params.third_off_n - 1.0)); | 
					
						
							|  |  |  |     if (off_inc < 1) | 
					
						
							|  |  |  |         off_inc = 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int got_best = 0; | 
					
						
							|  |  |  |     float best_hz = 0; | 
					
						
							|  |  |  |     int best_off = 0; | 
					
						
							|  |  |  |     float best_strength = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     std::vector<std::complex<float>> bins = fftEngine_->one_fft(samples, 0, samples.size(), "stfk", 0); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     float hz_start, hz_inc, hz_end; | 
					
						
							|  |  |  |     if (params.third_hz_n > 1) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         hz_inc = (2.0 * params.third_hz_win) / (params.third_hz_n - 1.0); | 
					
						
							|  |  |  |         hz_start = hz0 - params.third_hz_win; | 
					
						
							|  |  |  |         hz_end = hz0 + params.third_hz_win; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         hz_inc = 1; | 
					
						
							|  |  |  |         hz_start = hz0; | 
					
						
							|  |  |  |         hz_end = hz0; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (float hz = hz_start; hz <= hz_end + 0.0001; hz += hz_inc) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float strength = 0; | 
					
						
							|  |  |  |         int off = search_time_fine_known(bins, rate, syms, | 
					
						
							|  |  |  |                                             off0 - off_win, off0 + off_win, hz, | 
					
						
							|  |  |  |                                             off_inc, strength); | 
					
						
							|  |  |  |         if (off >= 0 && (got_best == 0 || strength > best_strength)) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             got_best = 1; | 
					
						
							|  |  |  |             best_hz = hz; | 
					
						
							|  |  |  |             best_off = off; | 
					
						
							|  |  |  |             best_strength = strength; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (got_best) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         hz_out = best_hz; | 
					
						
							|  |  |  |         off_out = best_off / (float)rate; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // shift frequency by shifting the bins of one giant FFT.
 | 
					
						
							|  |  |  | // so no problem with phase mismatch &c at block boundaries.
 | 
					
						
							|  |  |  | // surprisingly fast at 200 samples/second.
 | 
					
						
							|  |  |  | // shifts *down* by hz.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<float> FT8::fft_shift( | 
					
						
							|  |  |  |     const std::vector<float> &samples, | 
					
						
							|  |  |  |     int off, | 
					
						
							|  |  |  |     int len, | 
					
						
							|  |  |  |     int rate, | 
					
						
							|  |  |  |     float hz | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     std::vector<std::complex<float>> bins; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // horrible hack to avoid repeated FFTs on the same input.
 | 
					
						
							|  |  |  |     hack_mu_.lock(); | 
					
						
							|  |  |  |     if ((int)samples.size() == hack_size_ && samples.data() == hack_data_ && | 
					
						
							|  |  |  |         off == hack_off_ && len == hack_len_ && | 
					
						
							|  |  |  |         samples[0] == hack_0_ && samples[1] == hack_1_) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         bins = hack_bins_; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |         bins = fftEngine_->one_fft(samples, off, len, "fft_shift", 0); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         hack_bins_ = bins; | 
					
						
							|  |  |  |         hack_size_ = samples.size(); | 
					
						
							|  |  |  |         hack_off_ = off; | 
					
						
							|  |  |  |         hack_len_ = len; | 
					
						
							|  |  |  |         hack_0_ = samples[0]; | 
					
						
							|  |  |  |         hack_1_ = samples[1]; | 
					
						
							|  |  |  |         hack_data_ = samples.data(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     hack_mu_.unlock(); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return fft_shift_f(bins, rate, hz); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // shift down by hz.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<float> FT8::fft_shift_f( | 
					
						
							|  |  |  |     const std::vector<std::complex<float>> &bins, | 
					
						
							|  |  |  |     int rate, | 
					
						
							|  |  |  |     float hz | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int nbins = bins.size(); | 
					
						
							|  |  |  |     int len = (nbins - 1) * 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     float bin_hz = rate / (float)len; | 
					
						
							|  |  |  |     int down = round(hz / bin_hz); | 
					
						
							|  |  |  |     std::vector<std::complex<float>> bins1(nbins); | 
					
						
							|  |  |  |     for (int i = 0; i < nbins; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         int j = i + down; | 
					
						
							|  |  |  |         if (j >= 0 && j < nbins) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             bins1[i] = bins[j]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             bins1[i] = 0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     std::vector<float> out = fftEngine_->one_ifft(bins1, "fft_shift"); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return out; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | // shift the frequency by a fraction of 6.25,
 | 
					
						
							|  |  |  | // to center hz on bin 4 (25 hz).
 | 
					
						
							|  |  |  | std::vector<float> FT8::shift200( | 
					
						
							|  |  |  |     const std::vector<float> &samples200, | 
					
						
							|  |  |  |     int off, | 
					
						
							|  |  |  |     int len, | 
					
						
							|  |  |  |     float hz | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (std::abs(hz - 25) < 0.001 && off == 0 && len == (int)samples200.size()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return samples200; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         return fft_shift(samples200, off, len, 200, hz - 25.0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // return hilbert_shift(samples200, hz - 25.0, hz - 25.0, 200);
 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | // returns a mini-FFT of 79 8-tone symbols.
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  | FFTEngine::ffts_t FT8::extract(const std::vector<float> &samples200, float, int off) | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     FFTEngine::ffts_t bins3 = fftEngine_->ffts(samples200, off, 32, "extract"); | 
					
						
							|  |  |  |     FFTEngine::ffts_t m79(79); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m79[si].resize(8); | 
					
						
							|  |  |  |         if (si < (int)bins3.size()) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							|  |  |  |             for (int bi = 0; bi < 8; bi++) | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 auto x = bins3[si][4 + bi]; | 
					
						
							|  |  |  |                 m79[si][bi] = x; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             for (int bi = 0; bi < 8; bi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 m79[si][bi] = 0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return m79; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // m79 is a 79x8 array of complex.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  | FFTEngine::ffts_t FT8::un_gray_code_c(const FFTEngine::ffts_t &m79) | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     FFTEngine::ffts_t m79a(79); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     int map[] = {0, 1, 3, 2, 6, 4, 5, 7}; | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m79a[si].resize(8); | 
					
						
							|  |  |  |         for (int bi = 0; bi < 8; bi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             m79a[si][map[bi]] = m79[si][bi]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return m79a; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // m79 is a 79x8 array of float.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<std::vector<float>> FT8::un_gray_code_r(const std::vector<std::vector<float>> &m79) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     std::vector<std::vector<float>> m79a(79); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int map[] = {0, 1, 3, 2, 6, 4, 5, 7}; | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m79a[si].resize(8); | 
					
						
							|  |  |  |         for (int bi = 0; bi < 8; bi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             m79a[si][map[bi]] = m79[si][bi]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return m79a; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // normalize levels by windowed median.
 | 
					
						
							|  |  |  | // this helps, but why?
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<std::vector<float>> FT8::convert_to_snr(const std::vector<std::vector<float>> &m79) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (params.snr_how < 0 || params.snr_win < 0) | 
					
						
							|  |  |  |         return m79; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // for each symbol time, what's its "noise" level?
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     std::vector<float> mm(79); | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         std::vector<float> v(8); | 
					
						
							|  |  |  |         float sum = 0.0; | 
					
						
							|  |  |  |         for (int bi = 0; bi < 8; bi++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             float x = m79[si][bi]; | 
					
						
							|  |  |  |             v[bi] = x; | 
					
						
							|  |  |  |             sum += x; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (params.snr_how != 1) | 
					
						
							|  |  |  |             std::sort(v.begin(), v.end()); | 
					
						
							|  |  |  |         if (params.snr_how == 0) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // median
 | 
					
						
							|  |  |  |             mm[si] = (v[3] + v[4]) / 2; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (params.snr_how == 1) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             mm[si] = sum / 8; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (params.snr_how == 2) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // all but strongest tone.
 | 
					
						
							|  |  |  |             mm[si] = (v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6]) / 7; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (params.snr_how == 3) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             mm[si] = v[0]; // weakest tone
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (params.snr_how == 4) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             mm[si] = v[7]; // strongest tone
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (params.snr_how == 5) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             mm[si] = v[6]; // second-strongest tone
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             mm[si] = 1.0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // we're going to take a windowed average.
 | 
					
						
							|  |  |  |     std::vector<float> winwin; | 
					
						
							|  |  |  |     if (params.snr_win > 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         winwin = blackman(2 * params.snr_win + 1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         winwin.push_back(1.0); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     std::vector<std::vector<float>> n79(79); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float sum = 0; | 
					
						
							|  |  |  |         for (int dd = si - params.snr_win; dd <= si + params.snr_win; dd++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             int wi = dd - (si - params.snr_win); | 
					
						
							|  |  |  |             if (dd >= 0 && dd < 79) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 sum += mm[dd] * winwin[wi]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             else if (dd < 0) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 sum += mm[0] * winwin[wi]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 sum += mm[78] * winwin[wi]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         n79[si].resize(8); | 
					
						
							|  |  |  |         for (int bi = 0; bi < 8; bi++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             n79[si][bi] = m79[si][bi] / sum; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return n79; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // normalize levels by windowed median.
 | 
					
						
							|  |  |  | // this helps, but why?
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<std::vector<std::complex<float>>> FT8::c_convert_to_snr( | 
					
						
							|  |  |  |     const std::vector<std::vector<std::complex<float>>> &m79 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (params.snr_how < 0 || params.snr_win < 0) | 
					
						
							|  |  |  |         return m79; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // for each symbol time, what's its "noise" level?
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     std::vector<float> mm(79); | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         std::vector<float> v(8); | 
					
						
							|  |  |  |         float sum = 0.0; | 
					
						
							|  |  |  |         for (int bi = 0; bi < 8; bi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float x = std::abs(m79[si][bi]); | 
					
						
							|  |  |  |             v[bi] = x; | 
					
						
							|  |  |  |             sum += x; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (params.snr_how != 1) | 
					
						
							|  |  |  |             std::sort(v.begin(), v.end()); | 
					
						
							|  |  |  |         if (params.snr_how == 0) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // median
 | 
					
						
							|  |  |  |             mm[si] = (v[3] + v[4]) / 2; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (params.snr_how == 1) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             mm[si] = sum / 8; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (params.snr_how == 2) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // all but strongest tone.
 | 
					
						
							|  |  |  |             mm[si] = (v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6]) / 7; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (params.snr_how == 3) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             mm[si] = v[0]; // weakest tone
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (params.snr_how == 4) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             mm[si] = v[7]; // strongest tone
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (params.snr_how == 5) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             mm[si] = v[6]; // second-strongest tone
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             mm[si] = 1.0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // we're going to take a windowed average.
 | 
					
						
							|  |  |  |     std::vector<float> winwin; | 
					
						
							|  |  |  |     if (params.snr_win > 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         winwin = blackman(2 * params.snr_win + 1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         winwin.push_back(1.0); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     std::vector<std::vector<std::complex<float>>> n79(79); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float sum = 0; | 
					
						
							|  |  |  |         for (int dd = si - params.snr_win; dd <= si + params.snr_win; dd++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             int wi = dd - (si - params.snr_win); | 
					
						
							|  |  |  |             if (dd >= 0 && dd < 79) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 sum += mm[dd] * winwin[wi]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             else if (dd < 0) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 sum += mm[0] * winwin[wi]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 sum += mm[78] * winwin[wi]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         n79[si].resize(8); | 
					
						
							|  |  |  |         for (int bi = 0; bi < 8; bi++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             n79[si][bi] = m79[si][bi] / sum; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return n79; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // statistics to decide soft probabilities,
 | 
					
						
							|  |  |  | // to drive LDPC decoder.
 | 
					
						
							|  |  |  | // distribution of strongest tones, and
 | 
					
						
							|  |  |  | // distribution of noise.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | void FT8::make_stats( | 
					
						
							|  |  |  |     const std::vector<std::vector<float>> &m79, | 
					
						
							|  |  |  |     Stats &bests, | 
					
						
							|  |  |  |     Stats &all | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int costas[] = {3, 1, 4, 0, 6, 5, 2}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (si < 7 || (si >= 36 && si < 36 + 7) || si >= 72) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // Costas.
 | 
					
						
							|  |  |  |             int ci; | 
					
						
							|  |  |  |             if (si >= 72) | 
					
						
							|  |  |  |                 ci = si - 72; | 
					
						
							|  |  |  |             else if (si >= 36) | 
					
						
							|  |  |  |                 ci = si - 36; | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |                 ci = si; | 
					
						
							|  |  |  |             for (int bi = 0; bi < 8; bi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 float x = m79[si][bi]; | 
					
						
							|  |  |  |                 all.add(x); | 
					
						
							|  |  |  |                 if (bi == costas[ci]) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     bests.add(x); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             float mx = 0; | 
					
						
							|  |  |  |             for (int bi = 0; bi < 8; bi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 float x = m79[si][bi]; | 
					
						
							|  |  |  |                 if (x > mx) | 
					
						
							|  |  |  |                     mx = x; | 
					
						
							|  |  |  |                 all.add(x); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             bests.add(mx); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // convert 79x8 complex FFT bins to magnitudes.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // exploits local phase coherence by decreasing magnitudes of bins
 | 
					
						
							|  |  |  | // whose phase is far from the phases of nearby strongest tones.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // relies on each tone being reasonably well centered in its FFT bin
 | 
					
						
							|  |  |  | // (in time and frequency) so that each tone completes an integer
 | 
					
						
							|  |  |  | // number of cycles and thus preserves phase from one symbol to the
 | 
					
						
							|  |  |  | // next.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  | std::vector<std::vector<float>> FT8::soft_c2m(const FFTEngine::ffts_t &c79) | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     std::vector<std::vector<float>> m79(79); | 
					
						
							|  |  |  |     std::vector<float> raw_phases(79); // of strongest tone in each symbol time
 | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         m79[si].resize(8); | 
					
						
							|  |  |  |         int mxi = -1; | 
					
						
							|  |  |  |         float mx; | 
					
						
							|  |  |  |         float mx_phase; | 
					
						
							|  |  |  |         for (int bi = 0; bi < 8; bi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float x = std::abs(c79[si][bi]); | 
					
						
							|  |  |  |             m79[si][bi] = x; | 
					
						
							|  |  |  |             if (mxi < 0 || x > mx) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 mxi = bi; | 
					
						
							|  |  |  |                 mx = x; | 
					
						
							|  |  |  |                 mx_phase = std::arg(c79[si][bi]); // -pi .. pi
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         raw_phases[si] = mx_phase; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.soft_phase_win <= 0) | 
					
						
							|  |  |  |         return m79; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // phase around each symbol.
 | 
					
						
							|  |  |  |     std::vector<float> phases(79); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // for each symbol time, median of nearby phases
 | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         std::vector<float> v; | 
					
						
							|  |  |  |         for (int si1 = si - params.soft_phase_win; si1 <= si + params.soft_phase_win; si1++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             if (si1 >= 0 && si1 < 79) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 float x = raw_phases[si1]; | 
					
						
							|  |  |  |                 v.push_back(x); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // choose the phase that has the lowest total distance to other
 | 
					
						
							|  |  |  |         // phases. like median but avoids -pi..pi wrap-around.
 | 
					
						
							|  |  |  |         int n = v.size(); | 
					
						
							|  |  |  |         int best = -1; | 
					
						
							|  |  |  |         float best_score = 0; | 
					
						
							|  |  |  |         for (int i = 0; i < n; i++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             float score = 0; | 
					
						
							|  |  |  |             for (int j = 0; j < n; j++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 if (i == j) | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 float d = fabs(v[i] - v[j]); | 
					
						
							|  |  |  |                 if (d > M_PI) | 
					
						
							|  |  |  |                     d = 2 * M_PI - d; | 
					
						
							|  |  |  |                 score += d; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (best == -1 || score < best_score) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 best = i; | 
					
						
							|  |  |  |                 best_score = score; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         phases[si] = v[best]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // project each tone against the median phase around that symbol time.
 | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         for (int bi = 0; bi < 8; bi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float mag = std::abs(c79[si][bi]); | 
					
						
							|  |  |  |             float angle = std::arg(c79[si][bi]); | 
					
						
							|  |  |  |             float d = angle - phases[si]; | 
					
						
							|  |  |  |             float factor = 0.1; | 
					
						
							|  |  |  |             if (d < M_PI / 2 && d > -M_PI / 2) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 factor = cos(d); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             m79[si][bi] = factor * mag; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return m79; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // guess the probability that a bit is zero vs one,
 | 
					
						
							|  |  |  | // based on strengths of strongest tones that would
 | 
					
						
							|  |  |  | // give it those values. for soft LDPC decoding.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // returns log-likelihood, zero is positive, one is negative.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | float FT8::bayes( | 
					
						
							|  |  |  |     float best_zero, | 
					
						
							|  |  |  |     float best_one, | 
					
						
							|  |  |  |     int lli, | 
					
						
							|  |  |  |     Stats &bests, | 
					
						
							|  |  |  |     Stats &all | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     float maxlog = 4.97; | 
					
						
							|  |  |  |     float ll = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     float pzero = 0.5; | 
					
						
							|  |  |  |     float pone = 0.5; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (params.use_apriori) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         pzero = 1.0 - apriori174[lli]; | 
					
						
							|  |  |  |         pone = apriori174[lli]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // Bayes combining rule normalization from:
 | 
					
						
							|  |  |  |     // http://cs.wellesley.edu/~anderson/writing/naive-bayes.pdf
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // a = P(zero)P(e0|zero)P(e1|zero)
 | 
					
						
							|  |  |  |     // b = P(one)P(e0|one)P(e1|one)
 | 
					
						
							|  |  |  |     // p = a / (a + b)
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // also see Mark Owen's book Practical Signal Processing,
 | 
					
						
							|  |  |  |     // Chapter 6.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // zero
 | 
					
						
							|  |  |  |     float a = pzero * | 
					
						
							|  |  |  |                 bests.problt(best_zero) * | 
					
						
							|  |  |  |                 (1.0 - all.problt(best_one)); | 
					
						
							|  |  |  |     if (params.bayes_how == 1) | 
					
						
							|  |  |  |         a *= all.problt(all.mean() + (best_zero - best_one)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // one
 | 
					
						
							|  |  |  |     float b = pone * | 
					
						
							|  |  |  |                 bests.problt(best_one) * | 
					
						
							|  |  |  |                 (1.0 - all.problt(best_zero)); | 
					
						
							|  |  |  |     if (params.bayes_how == 1) | 
					
						
							|  |  |  |         b *= all.problt(all.mean() + (best_one - best_zero)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     float p; | 
					
						
							|  |  |  |     if (a + b == 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         p = 0.5; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         p = a / (a + b); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (1 - p == 0.0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         ll = maxlog; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         ll = log(p / (1 - p)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (ll > maxlog) | 
					
						
							|  |  |  |         ll = maxlog; | 
					
						
							|  |  |  |     if (ll < -maxlog) | 
					
						
							|  |  |  |         ll = -maxlog; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return ll; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // c79 is 79x8 complex tones, before un-gray-coding.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  | void FT8::soft_decode(const FFTEngine::ffts_t &c79, float ll174[]) | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     std::vector<std::vector<float>> m79(79); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // m79 = absolute values of c79.
 | 
					
						
							|  |  |  |     // still pre-un-gray-coding so we know which
 | 
					
						
							|  |  |  |     // are the correct Costas tones.
 | 
					
						
							|  |  |  |     m79 = soft_c2m(c79); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     m79 = convert_to_snr(m79); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // statistics to decide soft probabilities.
 | 
					
						
							|  |  |  |     // distribution of strongest tones, and
 | 
					
						
							|  |  |  |     // distribution of noise.
 | 
					
						
							|  |  |  |     Stats bests(params.problt_how_sig, params.log_tail, params.log_rate); | 
					
						
							|  |  |  |     Stats all(params.problt_how_noise, params.log_tail, params.log_rate); | 
					
						
							|  |  |  |     make_stats(m79, bests, all); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     m79 = un_gray_code_r(m79); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int lli = 0; | 
					
						
							|  |  |  |     for (int i79 = 0; i79 < 79; i79++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (i79 < 7 || (i79 >= 36 && i79 < 36 + 7) || i79 >= 72) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // Costas, skip
 | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // for each of the three bits, look at the strongest tone
 | 
					
						
							|  |  |  |         // that would make it a zero, and the strongest tone that
 | 
					
						
							|  |  |  |         // would make it a one. use Bayes to decide which is more
 | 
					
						
							|  |  |  |         // likely, comparing each against the distribution of noise
 | 
					
						
							|  |  |  |         // and the distribution of strongest tones.
 | 
					
						
							|  |  |  |         // most-significant-bit first.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (int biti = 0; biti < 3; biti++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // tone numbers that make this bit zero or one.
 | 
					
						
							|  |  |  |             int zeroi[4]; | 
					
						
							|  |  |  |             int onei[4]; | 
					
						
							|  |  |  |             if (biti == 0) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // high bit
 | 
					
						
							|  |  |  |                 zeroi[0] = 0; | 
					
						
							|  |  |  |                 zeroi[1] = 1; | 
					
						
							|  |  |  |                 zeroi[2] = 2; | 
					
						
							|  |  |  |                 zeroi[3] = 3; | 
					
						
							|  |  |  |                 onei[0] = 4; | 
					
						
							|  |  |  |                 onei[1] = 5; | 
					
						
							|  |  |  |                 onei[2] = 6; | 
					
						
							|  |  |  |                 onei[3] = 7; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (biti == 1) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // middle bit
 | 
					
						
							|  |  |  |                 zeroi[0] = 0; | 
					
						
							|  |  |  |                 zeroi[1] = 1; | 
					
						
							|  |  |  |                 zeroi[2] = 4; | 
					
						
							|  |  |  |                 zeroi[3] = 5; | 
					
						
							|  |  |  |                 onei[0] = 2; | 
					
						
							|  |  |  |                 onei[1] = 3; | 
					
						
							|  |  |  |                 onei[2] = 6; | 
					
						
							|  |  |  |                 onei[3] = 7; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (biti == 2) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // low bit
 | 
					
						
							|  |  |  |                 zeroi[0] = 0; | 
					
						
							|  |  |  |                 zeroi[1] = 2; | 
					
						
							|  |  |  |                 zeroi[2] = 4; | 
					
						
							|  |  |  |                 zeroi[3] = 6; | 
					
						
							|  |  |  |                 onei[0] = 1; | 
					
						
							|  |  |  |                 onei[1] = 3; | 
					
						
							|  |  |  |                 onei[2] = 5; | 
					
						
							|  |  |  |                 onei[3] = 7; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // strongest tone that would make this bit be zero.
 | 
					
						
							|  |  |  |             int got_best_zero = 0; | 
					
						
							|  |  |  |             float best_zero = 0; | 
					
						
							|  |  |  |             for (int i = 0; i < 4; i++) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 float x = m79[i79][zeroi[i]]; | 
					
						
							|  |  |  |                 if (got_best_zero == 0 || x > best_zero) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     got_best_zero = 1; | 
					
						
							|  |  |  |                     best_zero = x; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             // strongest tone that would make this bit be one.
 | 
					
						
							|  |  |  |             int got_best_one = 0; | 
					
						
							|  |  |  |             float best_one = 0; | 
					
						
							|  |  |  |             for (int i = 0; i < 4; i++) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 float x = m79[i79][onei[i]]; | 
					
						
							|  |  |  |                 if (got_best_one == 0 || x > best_one) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     got_best_one = 1; | 
					
						
							|  |  |  |                     best_one = x; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float ll = bayes(best_zero, best_one, lli, bests, all); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             ll174[lli++] = ll; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(lli == 174); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // c79 is 79x8 complex tones, before un-gray-coding.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  | void FT8::c_soft_decode(const FFTEngine::ffts_t &c79x, float ll174[]) | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     FFTEngine::ffts_t c79 = c_convert_to_snr(c79x); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int costas[] = {3, 1, 4, 0, 6, 5, 2}; | 
					
						
							|  |  |  |     std::complex<float> maxes[79]; | 
					
						
							|  |  |  |     for (int i = 0; i < 79; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         std::complex<float> m; | 
					
						
							|  |  |  |         if (i < 7) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // Costas.
 | 
					
						
							|  |  |  |             m = c79[i][costas[i]]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (i >= 36 && i < 36 + 7) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // Costas.
 | 
					
						
							|  |  |  |             m = c79[i][costas[i - 36]]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (i >= 72) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // Costas.
 | 
					
						
							|  |  |  |             m = c79[i][costas[i - 72]]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             int got = 0; | 
					
						
							|  |  |  |             for (int j = 0; j < 8; j++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 if (got == 0 || std::abs(c79[i][j]) > std::abs(m)) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     got = 1; | 
					
						
							|  |  |  |                     m = c79[i][j]; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         maxes[i] = m; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     std::vector<std::vector<float>> m79(79); | 
					
						
							|  |  |  |     for (int i = 0; i < 79; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m79[i].resize(8); | 
					
						
							|  |  |  |         for (int j = 0; j < 8; j++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             std::complex<float> c = c79[i][j]; | 
					
						
							|  |  |  |             int n = 0; | 
					
						
							|  |  |  |             float sum = 0; | 
					
						
							|  |  |  |             for (int k = i - params.c_soft_win; k <= i + params.c_soft_win; k++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 if (k < 0 || k >= 79) | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 if (k == i) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     sum -= params.c_soft_weight * std::abs(c); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     // we're expecting all genuine tones to have
 | 
					
						
							|  |  |  |                     // about the same phase and magnitude.
 | 
					
						
							|  |  |  |                     // so set m79[i][j] to the distance from the
 | 
					
						
							|  |  |  |                     // phase/magnitude predicted by surrounding
 | 
					
						
							|  |  |  |                     // genuine-looking tones.
 | 
					
						
							|  |  |  |                     std::complex<float> c1 = maxes[k]; | 
					
						
							|  |  |  |                     std::complex<float> d = c1 - c; | 
					
						
							|  |  |  |                     sum += std::abs(d); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 n += 1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             m79[i][j] = 0 - (sum / n); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // statistics to decide soft probabilities.
 | 
					
						
							|  |  |  |     // distribution of strongest tones, and
 | 
					
						
							|  |  |  |     // distribution of noise.
 | 
					
						
							|  |  |  |     Stats bests(params.problt_how_sig, params.log_tail, params.log_rate); | 
					
						
							|  |  |  |     Stats all(params.problt_how_noise, params.log_tail, params.log_rate); | 
					
						
							|  |  |  |     make_stats(m79, bests, all); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     m79 = un_gray_code_r(m79); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int lli = 0; | 
					
						
							|  |  |  |     for (int i79 = 0; i79 < 79; i79++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (i79 < 7 || (i79 >= 36 && i79 < 36 + 7) || i79 >= 72) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // Costas, skip
 | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // for each of the three bits, look at the strongest tone
 | 
					
						
							|  |  |  |         // that would make it a zero, and the strongest tone that
 | 
					
						
							|  |  |  |         // would make it a one. use Bayes to decide which is more
 | 
					
						
							|  |  |  |         // likely, comparing each against the distribution of noise
 | 
					
						
							|  |  |  |         // and the distribution of strongest tones.
 | 
					
						
							|  |  |  |         // most-significant-bit first.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (int biti = 0; biti < 3; biti++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // tone numbers that make this bit zero or one.
 | 
					
						
							|  |  |  |             int zeroi[4]; | 
					
						
							|  |  |  |             int onei[4]; | 
					
						
							|  |  |  |             if (biti == 0) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // high bit
 | 
					
						
							|  |  |  |                 zeroi[0] = 0; | 
					
						
							|  |  |  |                 zeroi[1] = 1; | 
					
						
							|  |  |  |                 zeroi[2] = 2; | 
					
						
							|  |  |  |                 zeroi[3] = 3; | 
					
						
							|  |  |  |                 onei[0] = 4; | 
					
						
							|  |  |  |                 onei[1] = 5; | 
					
						
							|  |  |  |                 onei[2] = 6; | 
					
						
							|  |  |  |                 onei[3] = 7; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (biti == 1) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // middle bit
 | 
					
						
							|  |  |  |                 zeroi[0] = 0; | 
					
						
							|  |  |  |                 zeroi[1] = 1; | 
					
						
							|  |  |  |                 zeroi[2] = 4; | 
					
						
							|  |  |  |                 zeroi[3] = 5; | 
					
						
							|  |  |  |                 onei[0] = 2; | 
					
						
							|  |  |  |                 onei[1] = 3; | 
					
						
							|  |  |  |                 onei[2] = 6; | 
					
						
							|  |  |  |                 onei[3] = 7; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (biti == 2) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // low bit
 | 
					
						
							|  |  |  |                 zeroi[0] = 0; | 
					
						
							|  |  |  |                 zeroi[1] = 2; | 
					
						
							|  |  |  |                 zeroi[2] = 4; | 
					
						
							|  |  |  |                 zeroi[3] = 6; | 
					
						
							|  |  |  |                 onei[0] = 1; | 
					
						
							|  |  |  |                 onei[1] = 3; | 
					
						
							|  |  |  |                 onei[2] = 5; | 
					
						
							|  |  |  |                 onei[3] = 7; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // strongest tone that would make this bit be zero.
 | 
					
						
							|  |  |  |             int got_best_zero = 0; | 
					
						
							|  |  |  |             float best_zero = 0; | 
					
						
							|  |  |  |             for (int i = 0; i < 4; i++) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 float x = m79[i79][zeroi[i]]; | 
					
						
							|  |  |  |                 if (got_best_zero == 0 || x > best_zero) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     got_best_zero = 1; | 
					
						
							|  |  |  |                     best_zero = x; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             // strongest tone that would make this bit be one.
 | 
					
						
							|  |  |  |             int got_best_one = 0; | 
					
						
							|  |  |  |             float best_one = 0; | 
					
						
							|  |  |  |             for (int i = 0; i < 4; i++) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 float x = m79[i79][onei[i]]; | 
					
						
							|  |  |  |                 if (got_best_one == 0 || x > best_one) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     got_best_one = 1; | 
					
						
							|  |  |  |                     best_one = x; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float ll = bayes(best_zero, best_one, lli, bests, all); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             ll174[lli++] = ll; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     assert(lli == 174); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // turn 79 symbol numbers into 174 bits.
 | 
					
						
							|  |  |  | // strip out the three Costas sync blocks,
 | 
					
						
							|  |  |  | // leaving 58 symbol numbers.
 | 
					
						
							|  |  |  | // each represents three bits.
 | 
					
						
							|  |  |  | // (all post-un-gray-code).
 | 
					
						
							|  |  |  | // str is per-symbol strength; must be positive.
 | 
					
						
							|  |  |  | // each returned element is < 0 for 1, > 0 for zero,
 | 
					
						
							|  |  |  | // scaled by str.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<float> FT8::extract_bits(const std::vector<int> &syms, const std::vector<float> str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(syms.size() == 79); | 
					
						
							|  |  |  |     assert(str.size() == 79); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     std::vector<float> bits; | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (si < 7 || (si >= 36 && si < 36 + 7) || si >= 72) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             // costas -- skip
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             bits.push_back((syms[si] & 4) == 0 ? str[si] : -str[si]); | 
					
						
							|  |  |  |             bits.push_back((syms[si] & 2) == 0 ? str[si] : -str[si]); | 
					
						
							|  |  |  |             bits.push_back((syms[si] & 1) == 0 ? str[si] : -str[si]); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return bits; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // decode successive pairs of symbols. exploits the likelyhood
 | 
					
						
							|  |  |  | // that they have the same phase, by summing the complex
 | 
					
						
							|  |  |  | // correlations for each possible pair and using the max.
 | 
					
						
							|  |  |  | void FT8::soft_decode_pairs( | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     const FFTEngine::ffts_t &m79x, | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float ll174[] | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     FFTEngine::ffts_t m79 = c_convert_to_snr(m79x); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     struct BitInfo | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float zero; // strongest correlation that makes it zero
 | 
					
						
							|  |  |  |         float one;  // and one
 | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     std::vector<BitInfo> bitinfo(79 * 3); | 
					
						
							|  |  |  |     for (int i = 0; i < (int)bitinfo.size(); i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         bitinfo[i].zero = 0; | 
					
						
							|  |  |  |         bitinfo[i].one = 0; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     Stats all(params.problt_how_noise, params.log_tail, params.log_rate); | 
					
						
							|  |  |  |     Stats bests(params.problt_how_sig, params.log_tail, params.log_rate); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int map[] = {0, 1, 3, 2, 6, 4, 5, 7}; // un-gray-code
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (int si = 0; si < 79; si += 2) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float mx = 0; | 
					
						
							|  |  |  |         float corrs[8 * 8]; | 
					
						
							|  |  |  |         for (int s1 = 0; s1 < 8; s1++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             for (int s2 = 0; s2 < 8; s2++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 // sum up the correlations.
 | 
					
						
							|  |  |  |                 std::complex<float> csum = m79[si][s1]; | 
					
						
							|  |  |  |                 if (si + 1 < 79) | 
					
						
							|  |  |  |                     csum += m79[si + 1][s2]; | 
					
						
							|  |  |  |                 float x = std::abs(csum); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 corrs[s1 * 8 + s2] = x; | 
					
						
							|  |  |  |                 if (x > mx) | 
					
						
							|  |  |  |                     mx = x; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 all.add(x); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 // first symbol
 | 
					
						
							|  |  |  |                 int i = map[s1]; | 
					
						
							|  |  |  |                 for (int bit = 0; bit < 3; bit++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     int bitind = (si + 0) * 3 + (2 - bit); | 
					
						
							|  |  |  |                     if ((i & (1 << bit))) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         // symbol i would make this bit a one.
 | 
					
						
							|  |  |  |                         if (x > bitinfo[bitind].one) | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             bitinfo[bitind].one = x; | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                         // symbol i would make this bit a zero.
 | 
					
						
							|  |  |  |                         if (x > bitinfo[bitind].zero) | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             bitinfo[bitind].zero = x; | 
					
						
							|  |  |  |                         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 // second symbol
 | 
					
						
							|  |  |  |                 if (si + 1 < 79) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     i = map[s2]; | 
					
						
							|  |  |  |                     for (int bit = 0; bit < 3; bit++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                         int bitind = (si + 1) * 3 + (2 - bit); | 
					
						
							|  |  |  |                         if ((i & (1 << bit))) | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             // symbol i would make this bit a one.
 | 
					
						
							|  |  |  |                             if (x > bitinfo[bitind].one) | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                                 bitinfo[bitind].one = x; | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         else | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             // symbol i would make this bit a zero.
 | 
					
						
							|  |  |  |                             if (x > bitinfo[bitind].zero) | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                                 bitinfo[bitind].zero = x; | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (si == 0 || si == 36 || si == 72) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             bests.add(corrs[3 * 8 + 1]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (si == 2 || si == 38 || si == 74) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             bests.add(corrs[4 * 8 + 0]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (si == 4 || si == 40 || si == 76) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             bests.add(corrs[6 * 8 + 5]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             bests.add(mx); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int lli = 0; | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (si < 7 || (si >= 36 && si < 36 + 7) || si >= 72) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             // costas
 | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for (int i = 0; i < 3; i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float best_zero = bitinfo[si * 3 + i].zero; | 
					
						
							|  |  |  |             float best_one = bitinfo[si * 3 + i].one; | 
					
						
							|  |  |  |             // ll174[lli++] = best_zero > best_one ? 4.99 : -4.99;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             float ll = bayes(best_zero, best_one, lli, bests, all); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             ll174[lli++] = ll; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |     assert(lli == 174); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FT8::soft_decode_triples( | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     const FFTEngine::ffts_t &m79x, | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float ll174[] | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     FFTEngine::ffts_t m79 = c_convert_to_snr(m79x); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     struct BitInfo | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float zero; // strongest correlation that makes it zero
 | 
					
						
							|  |  |  |         float one;  // and one
 | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     std::vector<BitInfo> bitinfo(79 * 3); | 
					
						
							|  |  |  |     for (int i = 0; i < (int)bitinfo.size(); i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         bitinfo[i].zero = 0; | 
					
						
							|  |  |  |         bitinfo[i].one = 0; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     Stats all(params.problt_how_noise, params.log_tail, params.log_rate); | 
					
						
							|  |  |  |     Stats bests(params.problt_how_sig, params.log_tail, params.log_rate); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int map[] = {0, 1, 3, 2, 6, 4, 5, 7}; // un-gray-code
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (int si = 0; si < 79; si += 3) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float mx = 0; | 
					
						
							|  |  |  |         float corrs[8 * 8 * 8]; | 
					
						
							|  |  |  |         for (int s1 = 0; s1 < 8; s1++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             for (int s2 = 0; s2 < 8; s2++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 for (int s3 = 0; s3 < 8; s3++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							|  |  |  |                     std::complex<float> csum = m79[si][s1]; | 
					
						
							|  |  |  |                     if (si + 1 < 79) | 
					
						
							|  |  |  |                         csum += m79[si + 1][s2]; | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     if (si + 2 < 79) | 
					
						
							|  |  |  |                         csum += m79[si + 2][s3]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     float x = std::abs(csum); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     corrs[s1 * 64 + s2 * 8 + s3] = x; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     if (x > mx) | 
					
						
							|  |  |  |                         mx = x; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     all.add(x); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     // first symbol
 | 
					
						
							|  |  |  |                     int i = map[s1]; | 
					
						
							|  |  |  |                     for (int bit = 0; bit < 3; bit++) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         int bitind = (si + 0) * 3 + (2 - bit); | 
					
						
							|  |  |  |                         if ((i & (1 << bit))) | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             // symbol i would make this bit a one.
 | 
					
						
							|  |  |  |                             if (x > bitinfo[bitind].one) | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                                 bitinfo[bitind].one = x; | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         else | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             // symbol i would make this bit a zero.
 | 
					
						
							|  |  |  |                             if (x > bitinfo[bitind].zero) | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                                 bitinfo[bitind].zero = x; | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     // second symbol
 | 
					
						
							|  |  |  |                     if (si + 1 < 79) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         i = map[s2]; | 
					
						
							|  |  |  |                         for (int bit = 0; bit < 3; bit++) | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             int bitind = (si + 1) * 3 + (2 - bit); | 
					
						
							|  |  |  |                             if ((i & (1 << bit))) | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                                 // symbol i would make this bit a one.
 | 
					
						
							|  |  |  |                                 if (x > bitinfo[bitind].one) | 
					
						
							|  |  |  |                                 { | 
					
						
							|  |  |  |                                     bitinfo[bitind].one = x; | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                             else | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                                 // symbol i would make this bit a zero.
 | 
					
						
							|  |  |  |                                 if (x > bitinfo[bitind].zero) | 
					
						
							|  |  |  |                                 { | 
					
						
							|  |  |  |                                     bitinfo[bitind].zero = x; | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     // third symbol
 | 
					
						
							|  |  |  |                     if (si + 2 < 79) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                         i = map[s3]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                         for (int bit = 0; bit < 3; bit++) | 
					
						
							|  |  |  |                         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                             int bitind = (si + 2) * 3 + (2 - bit); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                             if ((i & (1 << bit))) | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                                 // symbol i would make this bit a one.
 | 
					
						
							|  |  |  |                                 if (x > bitinfo[bitind].one) | 
					
						
							|  |  |  |                                 { | 
					
						
							|  |  |  |                                     bitinfo[bitind].one = x; | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                             else | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                                 // symbol i would make this bit a zero.
 | 
					
						
							|  |  |  |                                 if (x > bitinfo[bitind].zero) | 
					
						
							|  |  |  |                                 { | 
					
						
							|  |  |  |                                     bitinfo[bitind].zero = x; | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // costas: 3, 1, 4, 0, 6, 5, 2
 | 
					
						
							|  |  |  |         if (si == 0 || si == 36 || si == 72) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             bests.add(corrs[3 * 64 + 1 * 8 + 4]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (si == 3 || si == 39 || si == 75) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             bests.add(corrs[0 * 64 + 6 * 8 + 5]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             bests.add(mx); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int lli = 0; | 
					
						
							|  |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (si < 7 || (si >= 36 && si < 36 + 7) || si >= 72) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             // costas
 | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for (int i = 0; i < 3; i++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             float best_zero = bitinfo[si * 3 + i].zero; | 
					
						
							|  |  |  |             float best_one = bitinfo[si * 3 + i].one; | 
					
						
							|  |  |  |             // ll174[lli++] = best_zero > best_one ? 4.99 : -4.99;
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float ll = bayes(best_zero, best_one, lli, bests, all); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             ll174[lli++] = ll; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     assert(lli == 174); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // given log likelyhood for each bit, try LDPC and OSD decoders.
 | 
					
						
							|  |  |  | // on success, puts corrected 174 bits into a174[].
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | int FT8::decode(const float ll174[], int a174[], int use_osd, std::string &comment) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     void ldpc_decode(float llcodeword[], int iters, int plain[], int *ok); | 
					
						
							|  |  |  |     void ldpc_decode_log(float codeword[], int iters, int plain[], int *ok); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int plain[174];  // will be 0/1 bits.
 | 
					
						
							|  |  |  |     int ldpc_ok = 0; // 83 will mean success.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     ldpc_decode((float *)ll174, params.ldpc_iters, plain, &ldpc_ok); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int ok_thresh = 83; // 83 is perfect
 | 
					
						
							|  |  |  |     if (ldpc_ok >= ok_thresh) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // plain[] is 91 systematic data bits, 83 parity bits.
 | 
					
						
							|  |  |  |         for (int i = 0; i < 174; i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             a174[i] = plain[i]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (check_crc(a174)) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             // success!
 | 
					
						
							|  |  |  |             return 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (use_osd && params.osd_depth >= 0 && ldpc_ok >= params.osd_ldpc_thresh) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         extern int osd_decode(float codeword[174], int depth, int out[91], int *); | 
					
						
							|  |  |  |         extern void ldpc_encode(int plain[91], int codeword[174]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         int oplain[91]; | 
					
						
							|  |  |  |         int got_depth = -1; | 
					
						
							|  |  |  |         int osd_ok = osd_decode((float *)ll174, params.osd_depth, oplain, &got_depth); | 
					
						
							|  |  |  |         if (osd_ok) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // reconstruct all 174.
 | 
					
						
							|  |  |  |             comment += "OSD-" + std::to_string(got_depth) + "-" + std::to_string(ldpc_ok); | 
					
						
							|  |  |  |             ldpc_encode(oplain, a174); | 
					
						
							|  |  |  |             return 1; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // bandpass filter some FFT bins.
 | 
					
						
							|  |  |  | // smooth transition from stop-band to pass-band,
 | 
					
						
							|  |  |  | // so that it's not a brick-wall filter, so that it
 | 
					
						
							|  |  |  | // doesn't ring.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<std::complex<float>> FT8::fbandpass( | 
					
						
							|  |  |  |     const std::vector<std::complex<float>> &bins0, | 
					
						
							|  |  |  |     float bin_hz, | 
					
						
							|  |  |  |     float low_outer,  // start of transition
 | 
					
						
							|  |  |  |     float low_inner,  // start of flat area
 | 
					
						
							|  |  |  |     float high_inner, // end of flat area
 | 
					
						
							|  |  |  |     float high_outer  // end of transition
 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // assert(low_outer >= 0);
 | 
					
						
							|  |  |  |     assert(low_outer <= low_inner); | 
					
						
							|  |  |  |     assert(low_inner <= high_inner); | 
					
						
							|  |  |  |     assert(high_inner <= high_outer); | 
					
						
							|  |  |  |     // assert(high_outer <= bin_hz * bins0.size());
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int nbins = bins0.size(); | 
					
						
							|  |  |  |     std::vector<std::complex<float>> bins1(nbins); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int i = 0; i < nbins; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float ihz = i * bin_hz; | 
					
						
							|  |  |  |         // cos(x)+flat+cos(x) taper
 | 
					
						
							|  |  |  |         float factor; | 
					
						
							|  |  |  |         if (ihz <= low_outer || ihz >= high_outer) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             factor = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (ihz >= low_outer && ihz < low_inner) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // rising shoulder
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | #if 1
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             factor = (ihz - low_outer) / (low_inner - low_outer); // 0 .. 1
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float theta = (ihz - low_outer) / (low_inner - low_outer);    // 0 .. 1
 | 
					
						
							|  |  |  |             theta -= 1;                                                    // -1 .. 0
 | 
					
						
							|  |  |  |             theta *= 3.14159;                                              // -pi .. 0
 | 
					
						
							|  |  |  |             factor = cos(theta);                                           // -1 .. 1
 | 
					
						
							|  |  |  |             factor = (factor + 1) / 2;                                     // 0 .. 1
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         else if (ihz > high_inner && ihz <= high_outer) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // falling shoulder
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | #if 1
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             factor = (high_outer - ihz) / (high_outer - high_inner); // 1 .. 0
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float theta = (high_outer - ihz) / (high_outer - high_inner); // 1 .. 0
 | 
					
						
							|  |  |  |             theta = 1.0 - theta;                                           // 0 .. 1
 | 
					
						
							|  |  |  |             theta *= 3.14159;                                              // 0 .. pi
 | 
					
						
							|  |  |  |             factor = cos(theta);                                           // 1 .. -1
 | 
					
						
							|  |  |  |             factor = (factor + 1) / 2;                                     // 1 .. 0
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | #endif
 | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             factor = 1.0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         bins1[i] = bins0[i] * factor; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return bins1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // move hz down to 25, filter+convert to 200 samples/second.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // like fft_shift(). one big FFT, move bins down and
 | 
					
						
							|  |  |  | // zero out those outside the band, then IFFT,
 | 
					
						
							|  |  |  | // then re-sample.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX maybe merge w/ fft_shift() / shift200().
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<float> FT8::down_v7(const std::vector<float> &samples, float hz) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int len = samples.size(); | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     std::vector<std::complex<float>> bins = fftEngine_->one_fft(samples, 0, len, "down_v7a", 0); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return down_v7_f(bins, len, hz); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<float> FT8::down_v7_f(const std::vector<std::complex<float>> &bins, int len, float hz) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int nbins = bins.size(); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float bin_hz = rate_ / (float)len; | 
					
						
							|  |  |  |     int down = round((hz - 25) / bin_hz); | 
					
						
							|  |  |  |     std::vector<std::complex<float>> bins1(nbins); | 
					
						
							|  |  |  |     for (int i = 0; i < nbins; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         int j = i + down; | 
					
						
							|  |  |  |         if (j >= 0 && j < nbins) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             bins1[i] = bins[j]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             bins1[i] = 0; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // now filter to fit in 200 samples/second.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     float low_inner = 25.0 - params.shoulder200_extra; | 
					
						
							|  |  |  |     float low_outer = low_inner - params.shoulder200; | 
					
						
							|  |  |  |     if (low_outer < 0) | 
					
						
							|  |  |  |         low_outer = 0; | 
					
						
							|  |  |  |     float high_inner = 75 - 6.25 + params.shoulder200_extra; | 
					
						
							|  |  |  |     float high_outer = high_inner + params.shoulder200; | 
					
						
							|  |  |  |     if (high_outer > 100) | 
					
						
							|  |  |  |         high_outer = 100; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bins1 = fbandpass(bins1, bin_hz, | 
					
						
							|  |  |  |                         low_outer, low_inner, | 
					
						
							|  |  |  |                         high_inner, high_outer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // convert back to time domain and down-sample to 200 samples/second.
 | 
					
						
							|  |  |  |     int blen = round(len * (200.0 / rate_)); | 
					
						
							|  |  |  |     std::vector<std::complex<float>> bbins(blen / 2 + 1); | 
					
						
							|  |  |  |     for (int i = 0; i < (int)bbins.size(); i++) | 
					
						
							|  |  |  |         bbins[i] = bins1[i]; | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     std::vector<float> out = fftEngine_->one_ifft(bbins, "down_v7b"); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return out; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // putative start of signal is at hz and symbol si0.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // return 2 if it decodes to a brand-new message.
 | 
					
						
							|  |  |  | // return 1 if it decodes but we've already seen it,
 | 
					
						
							|  |  |  | //   perhaps in a different pass.
 | 
					
						
							|  |  |  | // return 0 if we could not decode.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX merge with one_iter().
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | int FT8::one(const std::vector<std::complex<float>> &bins, int len, float hz, int off) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // set up to search for best frequency and time offset.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // move down to 25 hz and re-sample to 200 samples/second,
 | 
					
						
							|  |  |  |     // i.e. 32 samples/symbol.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     std::vector<float> samples200 = down_v7_f(bins, len, hz); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int off200 = round((off / (float)rate_) * 200.0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int ret = one_iter(samples200, off200, hz); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | // return 2 if it decodes to a brand-new message.
 | 
					
						
							|  |  |  | // return 1 if it decodes but we've already seen it,
 | 
					
						
							|  |  |  | //   perhaps in a different pass.
 | 
					
						
							|  |  |  | // return 0 if we could not decode.
 | 
					
						
							|  |  |  | int FT8::one_iter(const std::vector<float> &samples200, int best_off, float hz_for_cb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (params.do_second) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         std::vector<Strength> strengths = | 
					
						
							|  |  |  |             search_both(samples200, | 
					
						
							|  |  |  |                         25, params.second_hz_n, params.second_hz_win, | 
					
						
							|  |  |  |                         best_off, params.second_off_n, params.second_off_win * 32); | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |         // sort strongest-first.
 | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |         std::sort(strengths.begin(), strengths.end(), | 
					
						
							|  |  |  |                     [](const Strength &a, const Strength &b) -> bool | 
					
						
							|  |  |  |                     { return a.strength_ > b.strength_; }); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         for (int i = 0; i < (int)strengths.size() && i < params.second_count; i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float hz = strengths[i].hz_; | 
					
						
							|  |  |  |             int off = strengths[i].off_; | 
					
						
							|  |  |  |             int ret = one_iter1(samples200, off, hz, hz_for_cb, hz_for_cb); | 
					
						
							|  |  |  |             if (ret > 0) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 return ret; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         int ret = one_iter1(samples200, best_off, 25, hz_for_cb, hz_for_cb); | 
					
						
							|  |  |  |         return ret; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // estimate SNR, yielding numbers vaguely similar to WSJT-X.
 | 
					
						
							|  |  |  | // m79 is a 79x8 complex FFT output.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  | float FT8::guess_snr(const FFTEngine::ffts_t &m79) | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     int costas[] = {3, 1, 4, 0, 6, 5, 2}; | 
					
						
							| 
									
										
										
										
											2023-01-11 00:21:47 +01:00
										 |  |  |     float pnoises = 0; | 
					
						
							|  |  |  |     float psignals = 0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (int i = 0; i < 7; i++) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-11 00:21:47 +01:00
										 |  |  |         psignals += std::abs(m79[i][costas[i]]); | 
					
						
							|  |  |  |         psignals += std::abs(m79[36 + i][costas[i]]); | 
					
						
							|  |  |  |         psignals += std::abs(m79[72 + i][costas[i]]); | 
					
						
							|  |  |  |         pnoises += std::abs(m79[i][(costas[i] + 4) % 8]); | 
					
						
							|  |  |  |         pnoises += std::abs(m79[36 + i][(costas[i] + 4) % 8]); | 
					
						
							|  |  |  |         pnoises += std::abs(m79[72 + i][(costas[i] + 4) % 8]); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (int i = 0; i < 79; i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (i < 7 || (i >= 36 && i < 36 + 7) || (i >= 72 && i < 72 + 7)) | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         std::vector<float> v(8); | 
					
						
							|  |  |  |         for (int j = 0; j < 8; j++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             v[j] = std::abs(m79[i][j]); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         std::sort(v.begin(), v.end()); | 
					
						
							| 
									
										
										
										
											2023-01-11 00:21:47 +01:00
										 |  |  |         psignals += v[7]; // strongest tone, probably the signal
 | 
					
						
							|  |  |  |         pnoises += (v[2] + v[3] + v[4]) / 3; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-11 00:21:47 +01:00
										 |  |  |     pnoises /= 79; | 
					
						
							|  |  |  |     psignals /= 79; | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-11 00:21:47 +01:00
										 |  |  |     pnoises *= pnoises; // square yields power
 | 
					
						
							|  |  |  |     psignals *= psignals; | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-11 00:21:47 +01:00
										 |  |  |     float raw = psignals / pnoises; | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     raw -= 1; // turn (s+n)/n into s/n
 | 
					
						
							|  |  |  |     if (raw < 0.1) | 
					
						
							|  |  |  |         raw = 0.1; | 
					
						
							|  |  |  |     raw /= (2500.0 / 2.7); // 2.7 hz noise b/w -> 2500 hz b/w
 | 
					
						
							|  |  |  |     float snr = 10 * log10(raw); | 
					
						
							|  |  |  |     snr += 5; | 
					
						
							|  |  |  |     snr *= 1.4; | 
					
						
							|  |  |  |     return snr; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // compare phases of successive symbols to guess whether
 | 
					
						
							|  |  |  | // the starting offset is a little too high or low.
 | 
					
						
							|  |  |  | // we expect each symbol to have the same phase.
 | 
					
						
							|  |  |  | // an error in causes the phase to advance at a steady rate.
 | 
					
						
							|  |  |  | // so if hz is wrong, we expect the phase to advance
 | 
					
						
							|  |  |  | // or retard at a steady pace.
 | 
					
						
							|  |  |  | // an error in offset causes each symbol to start at
 | 
					
						
							|  |  |  | // a phase that depends on the symbol's frequency;
 | 
					
						
							|  |  |  | // a particular offset error causes a phase error
 | 
					
						
							|  |  |  | // that depends on frequency.
 | 
					
						
							|  |  |  | // hz0 is actual FFT bin number of m79[...][0] (always 4).
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // the output adj_hz is relative to the FFT bin center;
 | 
					
						
							|  |  |  | // a positive number means the real signal seems to be
 | 
					
						
							|  |  |  | // a bit higher in frequency that the bin center.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // adj_off is the amount to change the offset, in samples.
 | 
					
						
							|  |  |  | // should be subtracted from offset.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  | void FT8::fine(const FFTEngine::ffts_t &m79, int, float &adj_hz, float &adj_off) | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     adj_hz = 0.0; | 
					
						
							|  |  |  |     adj_off = 0.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // tone number for each of the 79 symbols.
 | 
					
						
							|  |  |  |     int sym[79]; | 
					
						
							|  |  |  |     float symval[79]; | 
					
						
							|  |  |  |     float symphase[79]; | 
					
						
							|  |  |  |     int costas[] = {3, 1, 4, 0, 6, 5, 2}; | 
					
						
							|  |  |  |     for (int i = 0; i < 79; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (i < 7) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             sym[i] = costas[i]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         else if (i >= 36 && i < 36 + 7) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             sym[i] = costas[i - 36]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (i >= 72) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             sym[i] = costas[i - 72]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             int mxj = -1; | 
					
						
							|  |  |  |             float mx = 0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             for (int j = 0; j < 8; j++) | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 float x = std::abs(m79[i][j]); | 
					
						
							|  |  |  |                 if (mxj < 0 || x > mx) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     mx = x; | 
					
						
							|  |  |  |                     mxj = j; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             sym[i] = mxj; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         symphase[i] = std::arg(m79[i][sym[i]]); | 
					
						
							|  |  |  |         symval[i] = std::abs(m79[i][sym[i]]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     float sum = 0; | 
					
						
							|  |  |  |     float weight_sum = 0; | 
					
						
							|  |  |  |     for (int i = 0; i < 79 - 1; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float d = symphase[i + 1] - symphase[i]; | 
					
						
							|  |  |  |         while (d > M_PI) | 
					
						
							|  |  |  |             d -= 2 * M_PI; | 
					
						
							|  |  |  |         while (d < -M_PI) | 
					
						
							|  |  |  |             d += 2 * M_PI; | 
					
						
							|  |  |  |         float w = symval[i]; | 
					
						
							|  |  |  |         sum += d * w; | 
					
						
							|  |  |  |         weight_sum += w; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     float mean = sum / weight_sum; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float err_rad = mean; // radians per symbol time
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float err_hz = (err_rad / (2 * M_PI)) / 0.16; // cycles per symbol time
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // if each symbol's phase is a bit more than we expect,
 | 
					
						
							|  |  |  |     // that means the real frequency is a bit higher
 | 
					
						
							|  |  |  |     // than we thought, so increase our estimate.
 | 
					
						
							|  |  |  |     adj_hz = err_hz; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // now think about offset error.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // the higher tones have many cycles per
 | 
					
						
							|  |  |  |     // symbol -- e.g. tone 7 has 11 cycles
 | 
					
						
							|  |  |  |     // in each symbol. a one- or two-sample
 | 
					
						
							|  |  |  |     // offset error at such a high tone will
 | 
					
						
							|  |  |  |     // change the phase by pi or more,
 | 
					
						
							|  |  |  |     // which makes the phase-to-samples
 | 
					
						
							|  |  |  |     // conversion ambiguous. so only try
 | 
					
						
							|  |  |  |     // to distinguish early-ontime-late,
 | 
					
						
							|  |  |  |     // not the amount.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     int nearly = 0; | 
					
						
							|  |  |  |     int nlate = 0; | 
					
						
							|  |  |  |     float early = 0.0; | 
					
						
							|  |  |  |     float late = 0.0; | 
					
						
							|  |  |  |     for (int i = 1; i < 79; i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         float ph0 = std::arg(m79[i - 1][sym[i - 1]]); | 
					
						
							|  |  |  |         float ph = std::arg(m79[i][sym[i]]); | 
					
						
							|  |  |  |         float d = ph - ph0; | 
					
						
							|  |  |  |         d -= err_rad; // correct for hz error.
 | 
					
						
							|  |  |  |         while (d > M_PI) | 
					
						
							|  |  |  |             d -= 2 * M_PI; | 
					
						
							|  |  |  |         while (d < -M_PI) | 
					
						
							|  |  |  |             d += 2 * M_PI; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // if off is correct, each symbol will have the same phase (modulo
 | 
					
						
							|  |  |  |         // the above hz correction), since each FFT bin holds an integer
 | 
					
						
							|  |  |  |         // number of cycles.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // if off is too small, the phase is altered by the trailing part
 | 
					
						
							|  |  |  |         // of the previous symbol. if the previous tone was lower,
 | 
					
						
							|  |  |  |         // the phase won't have advanced as much as expected, and
 | 
					
						
							|  |  |  |         // this symbol's phase will be lower than the previous phase.
 | 
					
						
							|  |  |  |         // if the previous tone was higher, the phase will be more
 | 
					
						
							|  |  |  |         // advanced than expected. thus off too small leads to
 | 
					
						
							|  |  |  |         // a phase difference that's the reverse of the tone difference.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // if off is too high, then the FFT started a little way into
 | 
					
						
							|  |  |  |         // this symbol, which causes the phase to be advanced a bit.
 | 
					
						
							|  |  |  |         // of course the previous symbol's phase was also advanced
 | 
					
						
							|  |  |  |         // too much. if this tone is higher than the previous symbol,
 | 
					
						
							|  |  |  |         // its phase will be more advanced than the previous. if
 | 
					
						
							|  |  |  |         // less, less.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // the point: if successive phases and tone differences
 | 
					
						
							|  |  |  |         // are positively correlated, off is too high. if negatively,
 | 
					
						
							|  |  |  |         // too low.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // fine_max_tone:
 | 
					
						
							|  |  |  |         // if late, ignore if a high tone, since ambiguous.
 | 
					
						
							|  |  |  |         // if early, ignore if prev is a high tone.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (sym[i] > sym[i - 1]) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             if (d > 0 && sym[i] <= params.fine_max_tone) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 nlate++; | 
					
						
							|  |  |  |                 late += d / std::abs(sym[i] - sym[i - 1]); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             if (d < 0 && sym[i - 1] <= params.fine_max_tone) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 nearly++; | 
					
						
							|  |  |  |                 early += fabs(d) / std::abs(sym[i] - sym[i - 1]); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         else if (sym[i] < sym[i - 1]) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (d > 0 && sym[i - 1] <= params.fine_max_tone) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 nearly++; | 
					
						
							|  |  |  |                 early += d / std::abs(sym[i] - sym[i - 1]); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             if (d < 0 && sym[i] <= params.fine_max_tone) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 nlate++; | 
					
						
							|  |  |  |                 late += fabs(d) / std::abs(sym[i] - sym[i - 1]); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (nearly > 0) | 
					
						
							|  |  |  |         early /= nearly; | 
					
						
							|  |  |  |     if (nlate > 0) | 
					
						
							|  |  |  |         late /= nlate; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-11 00:21:47 +01:00
										 |  |  |     // qDebug("early %d %.1f, late %d %.1f", nearly, early, nlate, late);
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // assumes 32 samples/symbol.
 | 
					
						
							|  |  |  |     if (nearly > 2 * nlate) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         adj_off = round(32 * early / params.fine_thresh); | 
					
						
							|  |  |  |         if (adj_off > params.fine_max_off) | 
					
						
							|  |  |  |             adj_off = params.fine_max_off; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (nlate > 2 * nearly) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         adj_off = 0 - round(32 * late / params.fine_thresh); | 
					
						
							|  |  |  |         if (fabs(adj_off) > params.fine_max_off) | 
					
						
							|  |  |  |             adj_off =- params.fine_max_off; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // the signal is at roughly 25 hz in samples200.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // return 2 if it decodes to a brand-new message.
 | 
					
						
							|  |  |  | // return 1 if it decodes but we've already seen it,
 | 
					
						
							|  |  |  | //   perhaps in a different pass.
 | 
					
						
							|  |  |  | // return 0 if we could not decode.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | int FT8::one_iter1( | 
					
						
							|  |  |  |     const std::vector<float> &samples200x, | 
					
						
							|  |  |  |     int best_off, | 
					
						
							|  |  |  |     float best_hz, | 
					
						
							|  |  |  |     float hz0_for_cb, | 
					
						
							|  |  |  |     float hz1_for_cb | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // put best_hz in the middle of bin 4, at 25.0.
 | 
					
						
							|  |  |  |     std::vector<float> samples200 = shift200(samples200x, 0, samples200x.size(), | 
					
						
							|  |  |  |                                                 best_hz); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // mini 79x8 FFT.
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     FFTEngine::ffts_t m79 = extract(samples200, 25, best_off); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // look at symbol-to-symbol phase change to try
 | 
					
						
							|  |  |  |     // to improve best_hz and best_off.
 | 
					
						
							|  |  |  |     if (params.do_fine_hz || params.do_fine_off) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float adj_hz = 0; | 
					
						
							|  |  |  |         float adj_off = 0; | 
					
						
							|  |  |  |         fine(m79, 4, adj_hz, adj_off); | 
					
						
							|  |  |  |         if (params.do_fine_hz == 0) | 
					
						
							|  |  |  |             adj_hz = 0; | 
					
						
							|  |  |  |         if (params.do_fine_off == 0) | 
					
						
							|  |  |  |             adj_off = 0; | 
					
						
							|  |  |  |         if (fabs(adj_hz) < 6.25 / 4 && fabs(adj_off) < 4) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             best_hz += adj_hz; | 
					
						
							|  |  |  |             best_off += round(adj_off); | 
					
						
							|  |  |  |             if (best_off < 0) | 
					
						
							|  |  |  |                 best_off = 0; | 
					
						
							|  |  |  |             samples200 = shift200(samples200x, 0, samples200x.size(), best_hz); | 
					
						
							|  |  |  |             m79 = extract(samples200, 25, best_off); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float ll174[174]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.soft_ones) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (params.soft_ones == 1) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             soft_decode(m79, ll174); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             c_soft_decode(m79, ll174); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         int ret = try_decode(samples200, ll174, best_hz, best_off, | 
					
						
							|  |  |  |                                 hz0_for_cb, hz1_for_cb, 1, "", m79); | 
					
						
							|  |  |  |         if (ret) | 
					
						
							|  |  |  |             return ret; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.soft_pairs) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float p174[174]; | 
					
						
							|  |  |  |         soft_decode_pairs(m79, p174); | 
					
						
							|  |  |  |         int ret = try_decode(samples200, p174, best_hz, best_off, | 
					
						
							|  |  |  |                                 hz0_for_cb, hz1_for_cb, 1, "", m79); | 
					
						
							|  |  |  |         if (ret) | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         if (params.soft_ones == 0) | 
					
						
							| 
									
										
										
										
											2023-01-11 17:08:57 +01:00
										 |  |  |             std::copy(p174, p174 + 174, ll174); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.soft_triples) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         float p174[174]; | 
					
						
							|  |  |  |         soft_decode_triples(m79, p174); | 
					
						
							|  |  |  |         int ret = try_decode(samples200, p174, best_hz, best_off, | 
					
						
							|  |  |  |                                 hz0_for_cb, hz1_for_cb, 1, "", m79); | 
					
						
							|  |  |  |         if (ret) | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.use_hints) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         for (int hi = 0; hi < (int)hints1_.size(); hi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             int h = hints1_[hi]; // 28-bit number, goes in ll174 0..28
 | 
					
						
							|  |  |  |             if (params.use_hints == 2 && h != 2) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 // just CQ
 | 
					
						
							|  |  |  |                 continue; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float n174[174]; | 
					
						
							|  |  |  |             for (int i = 0; i < 174; i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 if (i < 28) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     int bit = h & (1 << 27); | 
					
						
							|  |  |  |                     if (bit) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                         n174[i] = -4.97; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     } | 
					
						
							|  |  |  |                     else | 
					
						
							|  |  |  |                     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                         n174[i] = 4.97; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     h <<= 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     n174[i] = ll174[i]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             int ret = try_decode(samples200, n174, best_hz, best_off, | 
					
						
							|  |  |  |                                     hz0_for_cb, hz1_for_cb, 0, "hint1", m79); | 
					
						
							|  |  |  |             if (ret) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 return ret; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (params.use_hints == 1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         for (int hi = 0; hi < (int)hints2_.size(); hi++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             int h = hints2_[hi]; // 28-bit number, goes in ll174 29:29+28
 | 
					
						
							|  |  |  |             float n174[174]; | 
					
						
							|  |  |  |             for (int i = 0; i < 174; i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 if (i >= 29 && i < 29 + 28) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     int bit = h & (1 << 27); | 
					
						
							|  |  |  |                     if (bit) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                         n174[i] = -4.97; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     } | 
					
						
							|  |  |  |                     else | 
					
						
							|  |  |  |                     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                         n174[i] = 4.97; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     h <<= 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 else | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                     n174[i] = ll174[i]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             int ret = try_decode(samples200, n174, best_hz, best_off, | 
					
						
							|  |  |  |                                     hz0_for_cb, hz1_for_cb, 0, "hint2", m79); | 
					
						
							|  |  |  |             if (ret) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 return ret; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // subtract a corrected decoded signal from nsamples_,
 | 
					
						
							|  |  |  | // perhaps revealing a weaker signal underneath,
 | 
					
						
							|  |  |  | // to be decoded in a subsequent pass.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // re79[] holds the error-corrected symbol numbers.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | void FT8::subtract( | 
					
						
							|  |  |  |     const std::vector<int> re79, | 
					
						
							|  |  |  |     float hz0, | 
					
						
							|  |  |  |     float hz1, | 
					
						
							|  |  |  |     float off_sec | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int block = blocksize(rate_); | 
					
						
							|  |  |  |     float bin_hz = rate_ / (float)block; | 
					
						
							|  |  |  |     int off0 = off_sec * rate_; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float mhz = (hz0 + hz1) / 2.0; | 
					
						
							|  |  |  |     int bin0 = round(mhz / bin_hz); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // move nsamples so that signal is centered in bin0.
 | 
					
						
							|  |  |  |     float diff0 = (bin0 * bin_hz) - hz0; | 
					
						
							|  |  |  |     float diff1 = (bin0 * bin_hz) - hz1; | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     std::vector<float> moved = fftEngine_->hilbert_shift(nsamples_, diff0, diff1, rate_); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     FFTEngine::ffts_t bins = fftEngine_->ffts(moved, off0, block, "subtract"); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (bin0 + 8 > (int)bins[0].size()) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     if ((int)bins.size() < 79) | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     std::vector<float> phases(79); | 
					
						
							|  |  |  |     std::vector<float> amps(79); | 
					
						
							|  |  |  |     for (int i = 0; i < 79; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         int sym = bin0 + re79[i]; | 
					
						
							|  |  |  |         std::complex<float> c = bins[i][sym]; | 
					
						
							|  |  |  |         phases[i] = std::arg(c); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // FFT multiplies magnitudes by number of bins,
 | 
					
						
							|  |  |  |         // or half the number of samples.
 | 
					
						
							|  |  |  |         amps[i] = std::abs(c) / (block / 2.0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int ramp = round(block * params.subtract_ramp); | 
					
						
							|  |  |  |     if (ramp < 1) | 
					
						
							|  |  |  |         ramp = 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     // initial ramp part of first symbol.
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         int sym = bin0 + re79[0]; | 
					
						
							|  |  |  |         float phase = phases[0]; | 
					
						
							|  |  |  |         float amp = amps[0]; | 
					
						
							|  |  |  |         float hz = 6.25 * sym; | 
					
						
							|  |  |  |         float dtheta = 2 * M_PI / (rate_ / hz); // advance per sample
 | 
					
						
							|  |  |  |         for (int jj = 0; jj < ramp; jj++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             float theta = phase + jj * dtheta; | 
					
						
							|  |  |  |             float x = amp * cos(theta); | 
					
						
							|  |  |  |             x *= jj / (float)ramp; | 
					
						
							|  |  |  |             int iii = off0 + block * 0 + jj; | 
					
						
							|  |  |  |             moved[iii] -= x; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (int si = 0; si < 79; si++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         int sym = bin0 + re79[si]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         float phase = phases[si]; | 
					
						
							|  |  |  |         float amp = amps[si]; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         float hz = 6.25 * sym; | 
					
						
							|  |  |  |         float dtheta = 2 * M_PI / (rate_ / hz); // advance per sample
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // we've already done the first ramp for this symbol.
 | 
					
						
							|  |  |  |         // now for the steady part between ramps.
 | 
					
						
							|  |  |  |         for (int jj = ramp; jj < block - ramp; jj++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             float theta = phase + jj * dtheta; | 
					
						
							|  |  |  |             float x = amp * cos(theta); | 
					
						
							|  |  |  |             int iii = off0 + block * si + jj; | 
					
						
							|  |  |  |             moved[iii] -= x; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // now the two ramps, from us to the next symbol.
 | 
					
						
							|  |  |  |         // we need to smoothly change the frequency,
 | 
					
						
							|  |  |  |         // approximating wsjt-x's gaussian frequency shift,
 | 
					
						
							|  |  |  |         // and also end up matching the next symbol's phase,
 | 
					
						
							|  |  |  |         // which is often different from this symbol due
 | 
					
						
							|  |  |  |         // to inaccuracies in hz or offset.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // at start of this symbol's off-ramp.
 | 
					
						
							|  |  |  |         float theta = phase + (block - ramp) * dtheta; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         float hz1; | 
					
						
							|  |  |  |         float phase1; | 
					
						
							|  |  |  |         if (si + 1 >= 79) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             hz1 = hz; | 
					
						
							|  |  |  |             phase1 = phase; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             int sym1 = bin0 + re79[si + 1]; | 
					
						
							|  |  |  |             hz1 = 6.25 * sym1; | 
					
						
							|  |  |  |             phase1 = phases[si + 1]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         float dtheta1 = 2 * M_PI / (rate_ / hz1); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // add this to dtheta for each sample, to gradually
 | 
					
						
							|  |  |  |         // change the frequency.
 | 
					
						
							|  |  |  |         float inc = (dtheta1 - dtheta) / (2.0 * ramp); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // after we've applied all those inc's, what will the
 | 
					
						
							|  |  |  |         // phase be at the end of the next symbol's initial ramp,
 | 
					
						
							|  |  |  |         // if we don't do anything to correct it?
 | 
					
						
							|  |  |  |         float actual = theta + dtheta * 2.0 * ramp + inc * 4.0 * ramp * ramp / 2.0; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // what phase does the next symbol want to be at when
 | 
					
						
							|  |  |  |         // its on-ramp finishes?
 | 
					
						
							|  |  |  |         float target = phase1 + dtheta1 * ramp; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // ???
 | 
					
						
							|  |  |  |         while (fabs(target - actual) > M_PI) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (target < actual) | 
					
						
							|  |  |  |                 target += 2 * M_PI; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             else | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 target -= 2 * M_PI; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // adj is to be spread evenly over the off-ramp and on-ramp samples.
 | 
					
						
							|  |  |  |         float adj = target - actual; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         int end = block + ramp; | 
					
						
							|  |  |  |         if (si == 79 - 1) | 
					
						
							|  |  |  |             end = block; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         for (int jj = block - ramp; jj < end; jj++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             int iii = off0 + block * si + jj; | 
					
						
							|  |  |  |             float x = amp * cos(theta); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             // trail off to zero at the very end.
 | 
					
						
							|  |  |  |             if (si == 79 - 1) | 
					
						
							|  |  |  |                 x *= 1.0 - ((jj - (block - ramp)) / (float)ramp); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             moved[iii] -= x; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             theta += dtheta; | 
					
						
							|  |  |  |             dtheta += inc; | 
					
						
							|  |  |  |             theta += adj / (2.0 * ramp); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     nsamples_ = fftEngine_->hilbert_shift(moved, -diff0, -diff1, rate_); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // decode, give to callback, and subtract.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // return 2 if it decodes to a brand-new message.
 | 
					
						
							|  |  |  | // return 1 if it decodes but we've already seen it,
 | 
					
						
							|  |  |  | //   perhaps in a different pass.
 | 
					
						
							|  |  |  | // return 0 if we could not decode.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | int FT8::try_decode( | 
					
						
							|  |  |  |     const std::vector<float> &samples200, | 
					
						
							|  |  |  |     float ll174[174], | 
					
						
							|  |  |  |     float best_hz, | 
					
						
							|  |  |  |     int best_off_samples, | 
					
						
							|  |  |  |     float hz0_for_cb, | 
					
						
							|  |  |  |     float, | 
					
						
							|  |  |  |     int use_osd, | 
					
						
							|  |  |  |     const char *comment1, | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |     const FFTEngine::ffts_t &m79 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int a174[174]; | 
					
						
							|  |  |  |     std::string comment(comment1); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     if (decode(ll174, a174, use_osd, comment)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // a174 is corrected 91 bits of plain message plus 83 bits of LDPC parity.
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // how many of the corrected 174 bits match the received signal in ll174?
 | 
					
						
							|  |  |  |         int correct_bits = 0; | 
					
						
							|  |  |  |         for (int i = 0; i < 174; i++) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (ll174[i] < 0 && a174[i] == 1) { | 
					
						
							|  |  |  |                 correct_bits += 1; | 
					
						
							|  |  |  |             } else if (ll174[i] > 0 && a174[i] == 0) { | 
					
						
							|  |  |  |                 correct_bits += 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // reconstruct correct 79 symbols from LDPC output.
 | 
					
						
							|  |  |  |         std::vector<int> re79 = recode(a174); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (params.do_third == 1) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // fine-tune offset and hz for better subtraction.
 | 
					
						
							|  |  |  |             float best_off = best_off_samples / 200.0; | 
					
						
							|  |  |  |             search_both_known( | 
					
						
							|  |  |  |                 samples200, | 
					
						
							|  |  |  |                 200, | 
					
						
							|  |  |  |                 re79, | 
					
						
							|  |  |  |                 best_hz, | 
					
						
							|  |  |  |                 best_off, | 
					
						
							|  |  |  |                 best_hz, | 
					
						
							|  |  |  |                 best_off | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |             best_off_samples = round(best_off * 200.0); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         // convert starting sample # from 200 samples/second back to rate_.
 | 
					
						
							|  |  |  |         // also hz.
 | 
					
						
							|  |  |  |         float best_off = best_off_samples / 200.0; // convert to seconds
 | 
					
						
							|  |  |  |         best_hz = hz0_for_cb + (best_hz - 25.0); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (params.do_third == 2) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // fine-tune offset and hz for better subtraction.
 | 
					
						
							|  |  |  |             search_both_known( | 
					
						
							|  |  |  |                 samples_, | 
					
						
							|  |  |  |                 rate_, | 
					
						
							|  |  |  |                 re79, | 
					
						
							|  |  |  |                 best_hz, | 
					
						
							|  |  |  |                 best_off, | 
					
						
							|  |  |  |                 best_hz, | 
					
						
							|  |  |  |                 best_off | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         float snr = guess_snr(m79); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (cb_) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             cb_mu_.lock(); | 
					
						
							|  |  |  |             int ret = cb_->hcb( | 
					
						
							|  |  |  |                 a174, | 
					
						
							|  |  |  |                 best_hz + down_hz_, | 
					
						
							|  |  |  |                 best_off, | 
					
						
							|  |  |  |                 comment.c_str(), | 
					
						
							|  |  |  |                 snr, | 
					
						
							|  |  |  |                 pass_, | 
					
						
							|  |  |  |                 correct_bits | 
					
						
							|  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             cb_mu_.unlock(); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             if (ret == 2) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |                 // a new decode. subtract it from nsamples_.
 | 
					
						
							|  |  |  |                 subtract(re79, best_hz, best_hz, best_off); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             return ret; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return 1; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | //
 | 
					
						
							|  |  |  | // given 174 bits corrected by LDPC, work
 | 
					
						
							|  |  |  | // backwards to the symbols that must have
 | 
					
						
							|  |  |  | // been sent.
 | 
					
						
							|  |  |  | // used to help ensure that subtraction subtracts
 | 
					
						
							|  |  |  | // at the right place.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | std::vector<int> FT8::recode(int a174[]) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int i174 = 0; | 
					
						
							|  |  |  |     int costas[] = {3, 1, 4, 0, 6, 5, 2}; | 
					
						
							|  |  |  |     std::vector<int> out79; | 
					
						
							|  |  |  |     for (int i79 = 0; i79 < 79; i79++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (i79 < 7) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |             out79.push_back(costas[i79]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (i79 >= 36 && i79 < 36 + 7) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             out79.push_back(costas[i79 - 36]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (i79 >= 72) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             out79.push_back(costas[i79 - 72]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             int sym = (a174[i174 + 0] << 2) | (a174[i174 + 1] << 1) | (a174[i174 + 2] << 0); | 
					
						
							|  |  |  |             i174 += 3; | 
					
						
							|  |  |  |             // gray code
 | 
					
						
							|  |  |  |             int map[] = {0, 1, 3, 2, 5, 6, 4, 7}; | 
					
						
							|  |  |  |             sym = map[sym]; | 
					
						
							|  |  |  |             out79.push_back(sym); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     assert(out79.size() == 79); | 
					
						
							|  |  |  |     assert(i174 == 174); | 
					
						
							|  |  |  |     return out79; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-12 01:02:38 +01:00
										 |  |  | FT8Decoder::~FT8Decoder() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     forceQuit(); // stop all remaining running threads if any
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2023-01-12 01:02:38 +01:00
										 |  |  | // Launch decoding
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  | void FT8Decoder::entry( | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     float xsamples[], | 
					
						
							|  |  |  |     int nsamples, | 
					
						
							|  |  |  |     int start, | 
					
						
							|  |  |  |     int rate, | 
					
						
							|  |  |  |     float min_hz, | 
					
						
							|  |  |  |     float max_hz, | 
					
						
							|  |  |  |     int hints1[], | 
					
						
							|  |  |  |     int hints2[], | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  |     double time_left, | 
					
						
							|  |  |  |     double total_time_left, | 
					
						
							| 
									
										
										
										
											2023-01-09 00:38:15 +01:00
										 |  |  |     CallbackInterface *cb, | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     int nprevdecs, | 
					
						
							|  |  |  |     struct cdecode *xprevdecs | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-08 21:41:43 +01:00
										 |  |  |     double t0 = now(); | 
					
						
							|  |  |  |     double deadline = t0 + time_left; | 
					
						
							|  |  |  |     double final_deadline = t0 + total_time_left; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // decodes from previous runs, for subtraction.
 | 
					
						
							|  |  |  |     std::vector<cdecode> prevdecs; | 
					
						
							| 
									
										
										
										
											2023-01-08 23:15:31 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (int i = 0; i < nprevdecs; i++) { | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         prevdecs.push_back(xprevdecs[i]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     std::vector<float> samples(nsamples); | 
					
						
							| 
									
										
										
										
											2023-01-08 23:15:31 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (int i = 0; i < nsamples; i++) { | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         samples[i] = xsamples[i]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 23:15:31 +01:00
										 |  |  |     if (min_hz < 0) { | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         min_hz = 0; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-08 23:15:31 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (max_hz > rate / 2) { | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         max_hz = rate / 2; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     float per = (max_hz - min_hz) / params.nthreads; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |     for (int i = 0; i < params.nthreads; i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							|  |  |  |         float hz0 = min_hz + i * per; | 
					
						
							| 
									
										
										
										
											2023-01-08 23:15:31 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (i > 0 || params.overlap_edges) { | 
					
						
							|  |  |  |             hz0 -= params.overlap; | 
					
						
							| 
									
										
										
										
											2023-01-08 23:15:31 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         float hz1 = min_hz + (i + 1) * per; | 
					
						
							| 
									
										
										
										
											2023-01-08 23:15:31 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         if (i != params.nthreads - 1 || params.overlap_edges) { | 
					
						
							|  |  |  |             hz1 += params.overlap; | 
					
						
							| 
									
										
										
										
											2023-01-08 23:15:31 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         hz0 = std::max(hz0, 0.0f); | 
					
						
							|  |  |  |         hz1 = std::min(hz1, (rate / 2.0f) - 50); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         FT8 *ft8 = new FT8( | 
					
						
							|  |  |  |             samples, | 
					
						
							|  |  |  |             hz0, | 
					
						
							|  |  |  |             hz1, | 
					
						
							|  |  |  |             start, | 
					
						
							|  |  |  |             rate, | 
					
						
							|  |  |  |             hints1, | 
					
						
							|  |  |  |             hints2, | 
					
						
							|  |  |  |             deadline, | 
					
						
							|  |  |  |             final_deadline, | 
					
						
							|  |  |  |             cb, | 
					
						
							| 
									
										
										
										
											2023-01-10 23:27:17 +01:00
										 |  |  |             prevdecs, | 
					
						
							|  |  |  |             &fftEngine | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         ft8->getParams() = getParams(); // transfer parameters
 | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-10 00:15:46 +01:00
										 |  |  |         int npasses = nprevdecs > 0 ? params.npasses_two : params.npasses_one; | 
					
						
							| 
									
										
										
										
											2023-01-12 01:02:38 +01:00
										 |  |  |         ft8->set_npasses(npasses); | 
					
						
							|  |  |  |         QThread *th = new QThread(); | 
					
						
							|  |  |  |         threads.push_back(th); | 
					
						
							|  |  |  |         // std::thread *th = new std::thread([ft8, npasses] () { ft8->go(npasses); });
 | 
					
						
							|  |  |  |         // thv.push_back(std::pair<FT8*, std::thread*>(ft8, th));
 | 
					
						
							|  |  |  |         ft8->moveToThread(th); | 
					
						
							|  |  |  |         QObject::connect(th, &QThread::started, ft8, &FT8::start_work); | 
					
						
							|  |  |  |         QObject::connect(ft8, &FT8::finished, th, &QThread::quit, Qt::DirectConnection); | 
					
						
							|  |  |  |         QObject::connect(th, &QThread::finished, ft8, &QObject::deleteLater); | 
					
						
							|  |  |  |         QObject::connect(th, &QThread::finished, th, &QThread::deleteLater); | 
					
						
							|  |  |  |         th->start(); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-12 01:02:38 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FT8Decoder::wait(double time_left) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     unsigned long thread_timeout = time_left * 1000; | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-12 01:02:38 +01:00
										 |  |  |     while (threads.size() != 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         bool success = threads.front()->wait(thread_timeout); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!success) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             qDebug("FT8::FT8Decoder::wait: thread timed out"); | 
					
						
							|  |  |  |             thread_timeout = 50; // only 50ms for the rest
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         threads.erase(threads.begin()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FT8Decoder::forceQuit() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     while (threads.size() != 0) | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-12 01:02:38 +01:00
										 |  |  |         threads.front()->quit(); | 
					
						
							|  |  |  |         threads.front()->wait(); | 
					
						
							|  |  |  |         threads.erase(threads.begin()); | 
					
						
							| 
									
										
										
										
											2023-01-08 19:03:29 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // namespace FT8
 |