parent
a6f329e36c
commit
bf1cfa561a
@ -0,0 +1,332 @@ |
||||
// FT8Receive.ino 13 Oct 2022 Bob Larkin, W7PUA
|
||||
// Simple command-line reception of WSJT FT8 signals for
|
||||
// amateur radio.
|
||||
|
||||
/*
|
||||
* Huge thanks to Charlie Hill, W5BAA, for his Pocket FT8 work, much of which |
||||
* is the basis for this INO: https://github.com/Rotron/Pocket-FT8
|
||||
* That work started from the "FT8 Decoding Library" by |
||||
* Karlis Goba: https://github.com/kgoba/ft8_lib and thank you to both
|
||||
* contributors. |
||||
*/ |
||||
|
||||
/*
|
||||
* See Examples/Teensy/TimeTeensy3.ino for |
||||
* example code illustrating Time library with Real Time Clock. |
||||
*/ |
||||
|
||||
/* This INO uses the Teensy F32 Audio Library interface to test
|
||||
* the radioFT8Demodulator_F32 data collector. This should |
||||
* not be used with "#define W5BAA_INTERFACE" in the file |
||||
* radioFT8Demodulator_F32.h, as that interface is different. |
||||
*/ |
||||
|
||||
/* The F32 Audio library class, radioFT8Demodulator_F32, performs the
|
||||
* data collection and organization for FT8 reception. This occurs |
||||
* after every 128 audio samples, automatically. The sync and decode |
||||
* functions are time intensive and therefore not done with the audio |
||||
* interrupts. A complete set of Karlis Goba FT8 reception files is |
||||
* included with this INO and have been slightly renamed with an "R" |
||||
* at the end. The Goba functions have stayed the same and minimal |
||||
* changes are made to the functions. |
||||
* |
||||
* IMPORTANT -When one wants to build a full transmit and receive |
||||
* FT8 INO, maybe with waterfall, they should go back to the Goba and |
||||
* Hill files referenced above. This INO is intended as a minimal demonstration |
||||
* of FT8 reception with emphasis on testing the radioFT8Demodulator_F32 |
||||
* class. That said, be aware that these receive files include |
||||
* snr estimation not available from the other sets. |
||||
*/ |
||||
|
||||
#include "Arduino.h" |
||||
#include <TimeLib.h> |
||||
#include <OpenAudio_ArduinoLibrary.h> // Added for F32 Teensy library RSL |
||||
#include <Audio.h> |
||||
#include "AudioStream.h" |
||||
#include "arm_math.h" |
||||
|
||||
// The following debugging print dumps are available:
|
||||
// DEBUG1 - Main INO and overall control
|
||||
// DEBUG_N - Noise measurement data for snr estimates
|
||||
// DEBUG_D - Decode measurements
|
||||
// Uncomment the following for debugging:
|
||||
// #define DEBUG1
|
||||
// #define DEBUG_N
|
||||
// #define DEBUG_D
|
||||
|
||||
#define FFT_SIZE 2048 |
||||
#define block_size 128 |
||||
#define input_gulp_size 1024 |
||||
#define HIGH_FREQ_INDEX 368 |
||||
#define LOW_FREQ_INDEX 48 |
||||
#define FT8_FREQ_SPACING 6.25 // ??
|
||||
#define ft8_min_freq FT8_FREQ_SPACING * LOW_FREQ_INDEX |
||||
#define ft8_msg_samples 92 |
||||
|
||||
// Alternate interface format.
|
||||
// #define W5BAA
|
||||
|
||||
// Only 48 and 96 kHz audio sample rates are currently supported.
|
||||
const float32_t sample_rate_Hz = 48000.0f; |
||||
const int audio_block_samples = 128; |
||||
AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples); |
||||
|
||||
AudioInputI2S_F32 audioInI2S1(audio_settings); //xy=100,150
|
||||
AudioEffectGain_F32 gain1; //xy=250,150
|
||||
AudioAnalyzePeak_F32 peak1; //xy=400,250
|
||||
radioFT8Demodulator_F32 demod1; //xy=400,150
|
||||
AudioConnection_F32 patchCord1(audioInI2S1, 0, gain1, 0); |
||||
AudioConnection_F32 patchCord2(gain1, demod1); |
||||
AudioConnection_F32 patchCord3(gain1, peak1); |
||||
AudioControlSGTL5000 sgtl5000_1; //xy=100,250
|
||||
|
||||
// This is the big file of log powers
|
||||
uint8_t export_fft_power[ft8_msg_samples*HIGH_FREQ_INDEX*4] ; |
||||
// Pointer to 2048 float data for FFT array in radioDemodulator_F32
|
||||
float32_t* pData2K = NULL; |
||||
|
||||
float32_t noisePowerEstimateL = 0.0f; // Works when big signals are absent
|
||||
int16_t noisePwrDBIntL = 0; |
||||
float32_t noisePowerEstimateH = 0.0f; // Works for big signals and QRMt
|
||||
int16_t noisePwrDBIntH = 0; |
||||
float32_t noisePeakAveRatio = 0.0f; // > about 100 for big sigs
|
||||
|
||||
// /char Station_Call[11]; //six character call sign + /0
|
||||
// /char home_Locator[11];; // four character locator + /0
|
||||
// /char Locator[11]; // four character locator + /0
|
||||
char Station_Call[11]; // six character call sign + /0
|
||||
char home_Locator[11]; // four character locator + /0
|
||||
char Locator[11]; // four character locator + /0
|
||||
uint16_t currentFrequency; |
||||
|
||||
// Next 3 lines were uint32_t Sept 22 change to allow tOffset
|
||||
int32_t current_time, start_time, ft8_time, ft8_mod_time, ft8_mod_time_last; |
||||
int32_t days_fraction, hours_fraction, minute_fraction; |
||||
int32_t tOffset = 0; // Added Sept 22
|
||||
uint8_t ft8_hours, ft8_minutes, ft8_seconds; |
||||
int ft8_flag, FT_8_counter, ft8_marker, decode_flag; |
||||
int num_decoded_msg; |
||||
int xmit_flag, ft8_xmit_counter, Transmit_Armned; |
||||
int DSP_Flag; // =1 if new data is ready for FFT
|
||||
int master_decoded; |
||||
|
||||
// rcvFT8State
|
||||
#define FT8_RCV_IDLE 0 |
||||
#define FT8_RCV_DATA_COLLECT 1 |
||||
#define FT8_RCV_FIND_POWERS 2 |
||||
#define FT8_RCV_DECODE 3 |
||||
int rcvFT8State = FT8_RCV_IDLE; |
||||
int master_offset, offset_step; |
||||
int Target_Flag = 0; |
||||
|
||||
//From gen_ft8.cpp
|
||||
char Target_Call[7]; //six character call sign + /0
|
||||
char Target_Locator[5]; // four character locator + /0
|
||||
int Target_RSL; // four character RSL + /0
|
||||
|
||||
// Define FT8 symbol counts
|
||||
int ND = 58; |
||||
int NS = 21; |
||||
int NN = 79; |
||||
// Define the LDPC sizes
|
||||
int N = 174; |
||||
int K = 91; |
||||
int M = 83; |
||||
int K_BYTES = 12; |
||||
// Define CRC parameters
|
||||
uint16_t CRC_POLYNOMIAL = 0X2757; // CRC-14 polynomial without the leading (MSB) 1
|
||||
int CRC_WIDTH = 14; |
||||
|
||||
// Communicate amongst decode functions:
|
||||
typedef struct Candidate { |
||||
int16_t score; |
||||
int16_t time_offset; |
||||
int16_t freq_offset; |
||||
uint8_t time_sub; |
||||
uint8_t freq_sub; |
||||
uint8_t alt; // Added for convenience Teensy, RSL
|
||||
float32_t syncPower; // Added for Teensy, RSL
|
||||
} Candidate; |
||||
|
||||
uint8_t secLast = 0; |
||||
const int ledPin = 13; |
||||
bool showPower = false; |
||||
uint32_t tp = 0; |
||||
uint32_t tu; |
||||
uint32_t ct=0; |
||||
|
||||
void setup(void) { |
||||
// set the Time library to use Teensy 4.x's RTC to keep time
|
||||
setSyncProvider(getTeensy3Time); |
||||
AudioMemory_F32(50, audio_settings); |
||||
Serial.begin(9600); |
||||
delay(1000); |
||||
// Enable the audio shield, select input, and enable output
|
||||
sgtl5000_1.enable(); |
||||
sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN); |
||||
#ifndef W5BAA |
||||
pData2K = demod1.getDataPtr(); // 2048 floats in radioFT8Demodulator_F32
|
||||
#endif |
||||
demod1.initialize(); |
||||
demod1.setSampleRate_Hz(48000.0f); |
||||
init_DSP(); |
||||
gain1.setGain(1.0); |
||||
update_synchronization(); |
||||
Serial.println("FT8 Receive test"); |
||||
if (timeStatus()!= timeSet) |
||||
Serial.println("Unable to sync with the RTC"); |
||||
else |
||||
Serial.println("RTC has set the system time"); |
||||
//demod1.startDataCollect(); NOT FOR W5BAA interface
|
||||
} |
||||
|
||||
void loop(void) { |
||||
int16_t inCmd; |
||||
if( Serial.available() ) |
||||
{ |
||||
inCmd = Serial.read(); |
||||
if(inCmd=='=') //Set minute clock to zero
|
||||
{ |
||||
|
||||
} |
||||
else if(inCmd=='p' || inCmd=='P') // Increase clock 0.1 sec
|
||||
{ |
||||
tOffset += 100; |
||||
Serial.println("Increase clock 0.1 sec"); |
||||
Serial.println(0.001*(float)tOffset); |
||||
} |
||||
else if(inCmd=='l' || inCmd=='L') // Decrease clock 0.1 sec
|
||||
{ |
||||
tOffset -= 100; |
||||
Serial.println("Decrease clock 0.1 sec"); |
||||
Serial.println(0.001*(float)tOffset); |
||||
} |
||||
else if(inCmd=='-') // Increase clock 1 sec
|
||||
{ |
||||
tOffset += 1000; |
||||
Serial.println("Increase clock 1 sec"); |
||||
Serial.println(0.001*(float)tOffset); |
||||
} |
||||
else if(inCmd==',') // Decrease clock 1 sec
|
||||
{ |
||||
tOffset -= 1000; |
||||
Serial.println("Decrease clock 1 sec"); |
||||
Serial.println(0.001*(float)tOffset); |
||||
} |
||||
else if(inCmd=='c' || inCmd=='C') // Clock display
|
||||
{ |
||||
Serial.print("Time Offset, millisecods = "); |
||||
Serial.println(tOffset); |
||||
} |
||||
else if(inCmd=='e' || inCmd=='E') // Toggle power display
|
||||
{ |
||||
showPower = !showPower; |
||||
Serial.print("Show Power = "); |
||||
Serial.println(showPower); |
||||
} |
||||
else if(inCmd=='?') |
||||
{ |
||||
//Serial.println("= Set local FT-8 clock to 0 (60 sec range).");
|
||||
Serial.println("p, P Increase local FT8 clock by 0.1 sec"); |
||||
Serial.println("l, L Decrease local FT8 clock by 0.1 sec"); |
||||
Serial.println("- Increase local FT8 clock by 1 sec"); |
||||
Serial.println(", Decrease local FT8 clock by 1 sec"); |
||||
Serial.println("c, C Display offset"); |
||||
Serial.println("e, E Display received power"); |
||||
Serial.println("? Help Display (this message)"); |
||||
} |
||||
else if(inCmd>35) // Ignore anything below '#'
|
||||
Serial.println("Cmd ???"); |
||||
} // End, if Serial Available
|
||||
|
||||
// Print average power level to Serial Monitor
|
||||
// Useful for testing and synchronizing the FT8 clock. Shows total band power.
|
||||
if( showPower && demod1.powerAvailable() ) |
||||
{ |
||||
float32_t pwr=demod1.powerRead(); |
||||
float32_t fl; |
||||
if(second()>secLast || (second()==0 && secLast==59)) |
||||
{ |
||||
secLast = second(); |
||||
tp = millis(); |
||||
} |
||||
fl = millis() + tOffset - start_time; |
||||
while(fl>=15000.0f) |
||||
fl -= 15000.0f; |
||||
Serial.print(0.001f*fl); |
||||
Serial.print(" "); |
||||
|
||||
// Serial.print(0.001*(millis() - tp)+(float)second()); Serial.print(" ");
|
||||
Serial.print(pwr); Serial.print(" "); |
||||
for(int jj=0; jj<2*(30-(int)(-0.5*pwr)); jj++) |
||||
Serial.print("*"); |
||||
Serial.println(); |
||||
} |
||||
|
||||
update_synchronization(); |
||||
ft8_mod_time = ft8_time%15000; |
||||
if( ft8_mod_time<100 && ft8_mod_time_last>14900 ) |
||||
{ |
||||
ft8_mod_time_last = ft8_mod_time; |
||||
rcvFT8State = FT8_RCV_DATA_COLLECT; |
||||
demod1.startDataCollect(); // Turn on decimation and data
|
||||
#ifdef DEBUG1 |
||||
Serial.println("= = = = = SYNC TIME 15 = = = = ="); |
||||
#endif |
||||
digitalWrite(ledPin, HIGH); // set the LED on
|
||||
delay(100); |
||||
digitalWrite(ledPin, LOW); // set the LED on
|
||||
} |
||||
else |
||||
ft8_mod_time_last = ft8_mod_time; |
||||
|
||||
if(rcvFT8State==FT8_RCV_DATA_COLLECT && demod1.available()) |
||||
{ |
||||
// Here every 80 mSec for FFT
|
||||
rcvFT8State = FT8_RCV_FIND_POWERS; |
||||
// 1472 * 92 = 135424
|
||||
//master_offset = offset_step * FT_8_counter; //offset_step=1472
|
||||
master_offset = 736 * (demod1.getFFTCount() -1); |
||||
#ifdef DEBUG1 |
||||
Serial.print("master offset = "); Serial.println(master_offset); |
||||
Serial.print("Extract Power, fft count = "); Serial.println( demod1.getFFTCount() ); |
||||
#endif |
||||
extract_power(master_offset); // Do FFT and log powers
|
||||
rcvFT8State = FT8_RCV_DATA_COLLECT; |
||||
#ifdef DEBUG1 |
||||
Serial.println("Power array updated"); |
||||
#endif |
||||
} |
||||
|
||||
if(rcvFT8State!=FT8_RCV_IDLE && demod1.getFFTCount() >= 184)
|
||||
{ |
||||
rcvFT8State = FT8_RCV_DECODE; |
||||
#ifdef DEBUG1 |
||||
Serial.println("FT8 Decode"); |
||||
#endif |
||||
tu = micros(); |
||||
num_decoded_msg = ft8_decode(); |
||||
rcvFT8State = FT8_RCV_IDLE; |
||||
master_decoded = num_decoded_msg; |
||||
#ifdef DEBUG1 |
||||
Serial.print("ft8_decode Time, uSec = "); |
||||
Serial.println(micros() - tu); |
||||
#endif |
||||
} |
||||
|
||||
delay(1); |
||||
} // End loop()
|
||||
|
||||
time_t getTeensy3Time() { |
||||
return Teensy3Clock.get(); |
||||
} |
||||
|
||||
// This starts the receiving data collection every 15 sec
|
||||
void update_synchronization() { |
||||
current_time = millis() + tOffset; |
||||
ft8_time = current_time - start_time; |
||||
ft8_hours = (int8_t)(ft8_time/3600000); |
||||
hours_fraction = ft8_time % 3600000; |
||||
ft8_minutes = (int8_t) (hours_fraction/60000); |
||||
ft8_seconds = (int8_t)((hours_fraction % 60000)/1000); |
||||
} |
Binary file not shown.
@ -0,0 +1,303 @@ |
||||
/*
|
||||
* Process_DSP_R.ino |
||||
* Basically the Hill code with changes for Teensy floating point |
||||
* OpenAudio_ArduinoLibrary. |
||||
* Bob Larkin W7PUA, September 2022. |
||||
* |
||||
*/ |
||||
/* Thank you to Charlie Hill, W5BAA, https://github.com/Rotron/Pocket-FT8
|
||||
* for the conversion to Teensy operation, as well as |
||||
* to Kฤrlis Goba, YL3JG, https://github.com/kgoba/ft8_lib.
|
||||
* Thanks to all the contributors to the Joe Taylor WSJT project. |
||||
* See "The FT4 and FT8 Communication Protocols," Steve Franks, K9AN, |
||||
* Bill Somerville, G4WJS and Joe Taylor, K1JT, QEX July/August 2020 |
||||
* pp 7-17 as well as https://www.physics.princeton.edu/pulsar/K1JT
|
||||
*/ |
||||
|
||||
/* ***** MIT License ***
|
||||
Copyright (C) 2021, Charles Hill |
||||
Copyright (C) 2022, Bob Larkin on changes for F32 library |
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
||||
*/ |
||||
/* NOTE - The frequency range used here is the same as used by others.
|
||||
* This is about bin 128 to 768, or 400 Hz to 2400 Hz. |
||||
* Stations do operate outside this range. It would be easy to |
||||
* increase the range here. The library function radioFT8Demodulator_F32 is filtered |
||||
* to pass all frequencies up to, at least 2800 Hz. |
||||
*/ |
||||
|
||||
// Following are used inside extract_power()
|
||||
float32_t fft_buffer[2048]; |
||||
float fftOutput[2048]; |
||||
float window[2048]; // Change to 1024 by symmetry <<<<<<<<<<<<<<<<<<<
|
||||
arm_rfft_fast_instance_f32 Sfft; |
||||
|
||||
|
||||
float32_t powerSum = 0.0f; // Use these for snr estimate
|
||||
float32_t runningSum = 0.0f; |
||||
float32_t powerMax = 0.0f; |
||||
float32_t runningMax = 0.0f; |
||||
float32_t noiseBuffer[8]; // Circular storage
|
||||
uint16_t noiseBufferWrite = 0; // Array index
|
||||
bool noiseMeasured = false; // <<<<<<GLOBAL
|
||||
uint8_t noisePower8 = 0; // half dB per noise estimate GLOBAL
|
||||
|
||||
void init_DSP(void) { |
||||
arm_rfft_fast_init_f32(&Sfft, 2048); |
||||
for (int i = 0; i < FFT_SIZE; ++i) |
||||
window[i] = ft_blackman_i(i, FFT_SIZE); |
||||
offset_step = 1472; // (int) HIGH_FREQ_INDEX*4; /// 1472
|
||||
} |
||||
|
||||
float ft_blackman_i(int i, int N) { |
||||
const float alpha = 0.16f; // or 2860/18608
|
||||
const float a0 = (1 - alpha) / 2; |
||||
const float a1 = 1.0f / 2; |
||||
const float a2 = alpha / 2; |
||||
|
||||
float x1 = cosf(2 * (float)M_PI * i / (N - 1)); |
||||
//float x2 = cosf(4 * (float)M_PI * i / (N - 1));
|
||||
float x2 = 2*x1*x1 - 1; // Use double angle formula
|
||||
return a0 - a1*x1 + a2*x2; |
||||
} |
||||
|
||||
// Compute FFT magnitudes (log power) for each timeslot in the signal
|
||||
void extract_power( int offset) { |
||||
float32_t y[8]; |
||||
float32_t noiseCoeff[3]; |
||||
|
||||
/* Format of export_fft_power[] array:
|
||||
368 bytes of power for even time for 0.32 sec sample DESCRIBE BETTER <<<<<<<<<<<<<<<<<<<<<< |
||||
368 bytes of power for odd time for 0.32 sec sample |
||||
... |
||||
Repeated about 14.7/(0.08 sec) = 184 times. (Transmitted message length is 12.96 sec) |
||||
Total bytes 4 * 368 * 92 = 135424 |
||||
|
||||
The power byte is log encoded with a half dB MSB. This can handle a |
||||
dynamic range of 256/2 = 128 dB. |
||||
*/ |
||||
|
||||
for(int i=0; i<2048; i++) |
||||
{ |
||||
fft_buffer[i] = window[i]*pData2K[i]; // Protect pData2K from in-place FFT (17 uSec)
|
||||
} |
||||
|
||||
// (float32_t* pIn, float32_t* pOut, uint8_t ifftFlag)
|
||||
arm_rfft_fast_f32(&Sfft, fft_buffer, fftOutput, 0); |
||||
arm_cmplx_mag_squared_f32(fftOutput, fftOutput, 1024); |
||||
|
||||
// Variables for estimating noise level for SNR
|
||||
powerSum = 0.0f; |
||||
powerMax = 0.0f; |
||||
|
||||
for(int i=1; i<1024; i++) |
||||
{ |
||||
if(i>=128 && i<768) // Omit the first 400 Hz and last 800 Hz
|
||||
powerSum += fftOutput[i]; |
||||
if(fftOutput[i] > powerMax) |
||||
powerMax = fftOutput[i]; |
||||
// Next, 20*log10() (not 10*) is to the make 8-bit resolution 0.5 dB.
|
||||
// The floats range from nothing to 40*log10(1024)=120 for a
|
||||
// pure sine wave. For FT8, we never encounter this. To keep
|
||||
// the sine wave answer below 256 would use an upward
|
||||
// offset of 256-120=136. This totally prevents overload!
|
||||
// Borrow fft_buffer for a moment:
|
||||
fft_buffer[i] = 136.0f + 20.0f*log10f( 0.0000001f + fftOutput[i] ); |
||||
} |
||||
fft_buffer[0] = 0.000001; // Fake DC term
|
||||
|
||||
/* Noise needs to be estimated to determine snr. Two cases:
|
||||
* runningMax/runningSum < 100 This is weak signal case for which |
||||
* the runningSum must be used alone. |
||||
* runningMax/runningSum > 100 Here the 2 second quiet period can |
||||
* can be found and running Sum used |
||||
* when runningMax/runningSum is high. |
||||
*/ |
||||
runningSum = 0.80f*runningSum + 0.20f*powerSum; // Tracks changes in pwr
|
||||
runningMax = 0.99f*runningMax + 0.01f*powerMax; // Slow decay
|
||||
// Put the sum intocircular buffer
|
||||
noiseBuffer[ 0X0007 & noiseBufferWrite++ ] = 0.00156f*runningSum; |
||||
for(int kk=0; kk<8; kk++) |
||||
y[kk] = (float32_t)noiseBuffer[ 0X0007 & (kk + noiseBufferWrite) ]; |
||||
//fitCurve (int order, int nPoints, float32_t py[], int nCoeffs, float32_t *coeffs)
|
||||
fitCurve(2, 8, y, 3, noiseCoeff); |
||||
float32_t y9 = noiseCoeff[2] + 9.0f*noiseCoeff[1] + 81.0f*noiseCoeff[0]; |
||||
|
||||
if(runningMax > 100.0f*0.00156f*runningSum && y9 > 2.0f*noiseCoeff[2] && !noiseMeasured) |
||||
{ |
||||
// This measurement occurs once every 15 sec, but may be just before
|
||||
// or just after decode. Either way, the "latest" noise estimate is used.
|
||||
noiseMeasured = true; // Reset after decode()
|
||||
noisePowerEstimateH = 0.2f*(y[0]+y[1]+y[2]+y[3]+y[4]); |
||||
noisePwrDBIntH = (int16_t)(10.0f*log10f(noisePowerEstimateH)); |
||||
noisePeakAveRatio = runningMax/(0.00156*runningSum); |
||||
#ifdef DEBUG_N |
||||
Serial.println("Noise measurement between transmit time periods:"); |
||||
Serial.print(" rSum, rMax= "); Serial.print(0.00156*runningSum, 5);
|
||||
Serial.print(" "); Serial.print(runningMax, 5);
|
||||
Serial.print(" Ratio= "); Serial.print(noisePeakAveRatio, 3); |
||||
Serial.print(" Int noise= "); |
||||
Serial.println(noisePwrDBIntH); // dB increments
|
||||
#endif |
||||
} |
||||
|
||||
// Loop over two frequency bin offsets. This first picks up 367 even
|
||||
// numbered fft_buffer[] followed by 367 odd numbered bins. This is
|
||||
// a frequency shift of 3.125 Hz. With windowing, the bandwidth
|
||||
// of each FFT output is about 6 Hz, close to a match for the
|
||||
// 0.16 sec transmission time.
|
||||
/* First pass: j on (0, 367) j*2+freq_sub on (0, 734) (even)
|
||||
* Secnd pass: j on (0, 367) j*2+freq_sub on (1, 735) (odd) |
||||
*/ |
||||
for (int freq_sub=0; freq_sub<2; ++freq_sub) |
||||
{ |
||||
for (int j=0; j<368; ++j) |
||||
{ |
||||
export_fft_power[offset] = (uint8_t)fft_buffer[j*2 + freq_sub]; |
||||
++offset; |
||||
} |
||||
} |
||||
} // End extract_power()
|
||||
|
||||
// ===============================================================
|
||||
// CURVE FIT
|
||||
|
||||
/*
|
||||
curveFitting - Library for fitting curves to given |
||||
points using Least Squares method, with Cramer's rule |
||||
used to solve the linear equation. |
||||
Created by Rowan Easter-Robinson, August 23, 2018. |
||||
Released into the public domain. |
||||
|
||||
Converted to float32_t, made specific to FT8 case Bob L Oct 2022 |
||||
*/ |
||||
|
||||
void cpyArray(float32_t *src, float32_t*dest, int n){ |
||||
for (int i = 0; i < n*n; i++){ |
||||
dest[i] = src[i]; |
||||
} |
||||
} |
||||
|
||||
void subCol(float32_t *mat, float32_t* sub, uint8_t coln, uint8_t n){ |
||||
if (coln >= n) return; |
||||
for (int i = 0; i < n; i++){ |
||||
mat[(i*n)+coln] = sub[i]; |
||||
} |
||||
} |
||||
|
||||
/*Determinant algorithm taken from
|
||||
// https://codeforwin.org/2015/08/c-program-to-find-determinant-of-matrix.html */
|
||||
int trianglize(float32_t **m, int n) |
||||
{ |
||||
int sign = 1; |
||||
for (int i = 0; i < n; i++) { |
||||
int max = 0; |
||||
for (int row = i; row < n; row++) |
||||
if (fabs(m[row][i]) > fabs(m[max][i])) |
||||
max = row; |
||||
if (max) { |
||||
sign = -sign; |
||||
float32_t *tmp = m[i]; |
||||
m[i] = m[max], m[max] = tmp; |
||||
} |
||||
if (!m[i][i]) return 0; |
||||
for (int row = i + 1; row < n; row++) { |
||||
float32_t r = m[row][i] / m[i][i]; |
||||
if (!r) continue; |
||||
for (int col = i; col < n; col ++) |
||||
m[row][col] -= m[i][col] * r; |
||||
} |
||||
} |
||||
return sign; |
||||
} |
||||
|
||||
float32_t det(float32_t *in, int n) |
||||
{ |
||||
float32_t *m[n]; |
||||
m[0] = in; |
||||
|
||||
for (int i = 1; i < n; i++) |
||||
m[i] = m[i - 1] + n; |
||||
int sign = trianglize(m, n); |
||||
if (!sign) |
||||
return 0; |
||||
float32_t p = 1; |
||||
for (int i = 0; i < n; i++) |
||||
p *= m[i][i]; |
||||
return p * sign; |
||||
} |
||||
/*End of Determinant algorithm*/ |
||||
|
||||
//Raise x to power
|
||||
float32_t curveFitPower(float32_t base, int exponent){ |
||||
if (exponent == 0){ |
||||
return 1; |
||||
} else { |
||||
float32_t val = base; |
||||
for (int i = 1; i < exponent; i++){ |
||||
val = val * base; |
||||
} |
||||
return val; |
||||
} |
||||
} |
||||
|
||||
#define MAX_ORDER 4 |
||||
int fitCurve (int order, int nPoints, float32_t py[], int nCoeffs, float32_t *coeffs) { |
||||
int i, j; |
||||
float32_t T[MAX_ORDER] = {0}; //Values to generate RHS of linear equation
|
||||
float32_t S[MAX_ORDER*2+1] = {0}; //Values for LHS and RHS of linear equation
|
||||
float32_t denom; //denominator for Cramer's rule, determinant of LHS linear equation
|
||||
float32_t x, y; |
||||
float32_t px[nPoints]; //Generate X values, from 0 to n
|
||||
|
||||
for (i=0; i<nPoints; i++){ |
||||
px[i] = i; |
||||
} |
||||
|
||||
for (i=0; i<nPoints; i++) {//Generate matrix elements
|
||||
x = px[i]; |
||||
y = py[i]; |
||||
for (j = 0; j < (nCoeffs*2)-1; j++){ |
||||
S[j] += curveFitPower(x, j); // x^j iterated , S10 S20 S30 etc, x^0, x^1...
|
||||
} |
||||
for (j = 0; j < nCoeffs; j++){ |
||||
T[j] += y * curveFitPower(x, j); //y * x^j iterated, S01 S11 S21 etc, x^0*y, x^1*y, x^2*y...
|
||||
} |
||||
} |
||||
|
||||
float32_t masterMat[nCoeffs*nCoeffs]; //Master matrix LHS of linear equation
|
||||
for (i = 0; i < nCoeffs ;i++){//index by matrix row each time
|
||||
for (j = 0; j < nCoeffs; j++){//index within each row
|
||||
masterMat[i*nCoeffs+j] = S[i+j]; |
||||
} |
||||
} |
||||
|
||||
float32_t mat[nCoeffs*nCoeffs]; //Temp matrix as det() method alters the matrix given
|
||||
cpyArray(masterMat, mat, nCoeffs); |
||||
denom = det(mat, nCoeffs); |
||||
cpyArray(masterMat, mat, nCoeffs); |
||||
|
||||
//Generate cramers rule mats
|
||||
for (i = 0; i < nCoeffs; i++){ //Temporary matrix to substitute RHS of linear equation as per Cramer's rule
|
||||
subCol(mat, T, i, nCoeffs); |
||||
coeffs[nCoeffs-i-1] = det(mat, nCoeffs)/denom; //Coefficients are det(M_i)/det(Master)
|
||||
cpyArray(masterMat, mat, nCoeffs); |
||||
} |
||||
return 0; |
||||
} |
@ -0,0 +1,474 @@ |
||||
/*
|
||||
* constantsR.ino |
||||
* Basically the Goba constants.h and .c with minor changes for Teensy |
||||
* Arduino use along with the floating point OpenAudio_ArduinoLibrary. |
||||
* Bob Larkin W7PUA, September 2022. |
||||
* |
||||
*/ |
||||
|
||||
/* Thank you to Kฤrlis Goba, YL3JG, https://github.com/kgoba/ft8_lib
|
||||
* and to Charlie Hill, W5BAA, https://github.com/Rotron/Pocket-FT8
|
||||
* as well as the all the contributors to the Joe Taylor WSJT project. |
||||
* See "The FT4 and FT8 Communication Protocols," Steve Franks, K9AN, |
||||
* Bill Somerville, G4WJS and Joe Taylor, K1JT, QEX July/August 2020 |
||||
* pp 7-17 as well as https://www.physics.princeton.edu/pulsar/K1JT
|
||||
*/ |
||||
|
||||
/* ***** MIT License ***
|
||||
|
||||
Copyright (c) 2018 Kฤrlis Goba |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
||||
*/ |
||||
|
||||
#if 0 |
||||
// Retained here as useful notes:
|
||||
extern int K_BYTES; |
||||
extern uint8_t tones[79]; |
||||
|
||||
// Costas 7x7 tone pattern
|
||||
extern const uint8_t kCostas_map[7]; |
||||
|
||||
// Gray code map
|
||||
extern const uint8_t kGray_map[8]; |
||||
|
||||
// Parity generator matrix for (174,91) LDPC code, stored in bitpacked format (MSB first)
|
||||
//extern const uint8_t kGenerator[M][K_BYTES];
|
||||
extern uint8_t kGenerator[83][12]; |
||||
|
||||
// Column order (permutation) in which the bits in codeword are stored
|
||||
// (Not really used in FT8 v2 - instead the Nm, Mn and generator matrices are already permuted)
|
||||
//extern const uint8_t kColumn_order[N];
|
||||
extern uint8_t kColumn_order[174]; |
||||
|
||||
// this is the LDPC(174,91) parity check matrix.
|
||||
// 83 rows.
|
||||
// each row describes one parity check.
|
||||
// each number is an index into the codeword (1-origin).
|
||||
// the codeword bits mentioned in each row must xor to zero.
|
||||
// From WSJT-X's ldpc_174_91_c_reordered_parity.f90.
|
||||
//extern const uint8_t kNm[M][7];
|
||||
extern uint8_t kNm[83][7]; |
||||
|
||||
// Mn from WSJT-X's bpdecode174.f90.
|
||||
// each row corresponds to a codeword bit.
|
||||
// the numbers indicate which three parity
|
||||
// checks (rows in Nm) refer to the codeword bit.
|
||||
// 1-origin.
|
||||
|
||||
//extern const uint8_t kMn[N][3];
|
||||
extern uint8_t kMn[174][3]; |
||||
|
||||
// Number of rows (columns in C/C++) in the array Nm.
|
||||
//extern const uint8_t kNrw[M];
|
||||
extern uint8_t kNrw[83]; |
||||
#endif |
||||
|
||||
// See FT8Receive.ino for defines of some constant values.
|
||||
|
||||
// Costas 7x7 tone pattern
|
||||
const uint8_t kCostas_map[7] = { 3,1,4,0,6,5,2 }; |
||||
|
||||
// Gray code map
|
||||
const uint8_t kGray_map[8] = { 0,1,3,2,5,6,4,7 }; |
||||
|
||||
// Parity generator matrix for (174,91) LDPC code, stored in bitpacked format (MSB first)
|
||||
uint8_t kGenerator[83][12] = { |
||||
{ 0x83, 0x29, 0xce, 0x11, 0xbf, 0x31, 0xea, 0xf5, 0x09, 0xf2, 0x7f, 0xc0 }, |
||||
{ 0x76, 0x1c, 0x26, 0x4e, 0x25, 0xc2, 0x59, 0x33, 0x54, 0x93, 0x13, 0x20 }, |
||||
{ 0xdc, 0x26, 0x59, 0x02, 0xfb, 0x27, 0x7c, 0x64, 0x10, 0xa1, 0xbd, 0xc0 }, |
||||
{ 0x1b, 0x3f, 0x41, 0x78, 0x58, 0xcd, 0x2d, 0xd3, 0x3e, 0xc7, 0xf6, 0x20 }, |
||||
{ 0x09, 0xfd, 0xa4, 0xfe, 0xe0, 0x41, 0x95, 0xfd, 0x03, 0x47, 0x83, 0xa0 }, |
||||
{ 0x07, 0x7c, 0xcc, 0xc1, 0x1b, 0x88, 0x73, 0xed, 0x5c, 0x3d, 0x48, 0xa0 }, |
||||
{ 0x29, 0xb6, 0x2a, 0xfe, 0x3c, 0xa0, 0x36, 0xf4, 0xfe, 0x1a, 0x9d, 0xa0 }, |
||||
{ 0x60, 0x54, 0xfa, 0xf5, 0xf3, 0x5d, 0x96, 0xd3, 0xb0, 0xc8, 0xc3, 0xe0 }, |
||||
{ 0xe2, 0x07, 0x98, 0xe4, 0x31, 0x0e, 0xed, 0x27, 0x88, 0x4a, 0xe9, 0x00 }, |
||||
{ 0x77, 0x5c, 0x9c, 0x08, 0xe8, 0x0e, 0x26, 0xdd, 0xae, 0x56, 0x31, 0x80 }, |
||||
{ 0xb0, 0xb8, 0x11, 0x02, 0x8c, 0x2b, 0xf9, 0x97, 0x21, 0x34, 0x87, 0xc0 }, |
||||
{ 0x18, 0xa0, 0xc9, 0x23, 0x1f, 0xc6, 0x0a, 0xdf, 0x5c, 0x5e, 0xa3, 0x20 }, |
||||
{ 0x76, 0x47, 0x1e, 0x83, 0x02, 0xa0, 0x72, 0x1e, 0x01, 0xb1, 0x2b, 0x80 }, |
||||
{ 0xff, 0xbc, 0xcb, 0x80, 0xca, 0x83, 0x41, 0xfa, 0xfb, 0x47, 0xb2, 0xe0 }, |
||||
{ 0x66, 0xa7, 0x2a, 0x15, 0x8f, 0x93, 0x25, 0xa2, 0xbf, 0x67, 0x17, 0x00 }, |
||||
{ 0xc4, 0x24, 0x36, 0x89, 0xfe, 0x85, 0xb1, 0xc5, 0x13, 0x63, 0xa1, 0x80 }, |
||||
{ 0x0d, 0xff, 0x73, 0x94, 0x14, 0xd1, 0xa1, 0xb3, 0x4b, 0x1c, 0x27, 0x00 }, |
||||
{ 0x15, 0xb4, 0x88, 0x30, 0x63, 0x6c, 0x8b, 0x99, 0x89, 0x49, 0x72, 0xe0 }, |
||||
{ 0x29, 0xa8, 0x9c, 0x0d, 0x3d, 0xe8, 0x1d, 0x66, 0x54, 0x89, 0xb0, 0xe0 }, |
||||
{ 0x4f, 0x12, 0x6f, 0x37, 0xfa, 0x51, 0xcb, 0xe6, 0x1b, 0xd6, 0xb9, 0x40 }, |
||||
{ 0x99, 0xc4, 0x72, 0x39, 0xd0, 0xd9, 0x7d, 0x3c, 0x84, 0xe0, 0x94, 0x00 }, |
||||
{ 0x19, 0x19, 0xb7, 0x51, 0x19, 0x76, 0x56, 0x21, 0xbb, 0x4f, 0x1e, 0x80 }, |
||||
{ 0x09, 0xdb, 0x12, 0xd7, 0x31, 0xfa, 0xee, 0x0b, 0x86, 0xdf, 0x6b, 0x80 }, |
||||
{ 0x48, 0x8f, 0xc3, 0x3d, 0xf4, 0x3f, 0xbd, 0xee, 0xa4, 0xea, 0xfb, 0x40 }, |
||||
{ 0x82, 0x74, 0x23, 0xee, 0x40, 0xb6, 0x75, 0xf7, 0x56, 0xeb, 0x5f, 0xe0 }, |
||||
{ 0xab, 0xe1, 0x97, 0xc4, 0x84, 0xcb, 0x74, 0x75, 0x71, 0x44, 0xa9, 0xa0 }, |
||||
{ 0x2b, 0x50, 0x0e, 0x4b, 0xc0, 0xec, 0x5a, 0x6d, 0x2b, 0xdb, 0xdd, 0x00 }, |
||||
{ 0xc4, 0x74, 0xaa, 0x53, 0xd7, 0x02, 0x18, 0x76, 0x16, 0x69, 0x36, 0x00 }, |
||||
{ 0x8e, 0xba, 0x1a, 0x13, 0xdb, 0x33, 0x90, 0xbd, 0x67, 0x18, 0xce, 0xc0 }, |
||||
{ 0x75, 0x38, 0x44, 0x67, 0x3a, 0x27, 0x78, 0x2c, 0xc4, 0x20, 0x12, 0xe0 }, |
||||
{ 0x06, 0xff, 0x83, 0xa1, 0x45, 0xc3, 0x70, 0x35, 0xa5, 0xc1, 0x26, 0x80 }, |
||||
{ 0x3b, 0x37, 0x41, 0x78, 0x58, 0xcc, 0x2d, 0xd3, 0x3e, 0xc3, 0xf6, 0x20 }, |
||||
{ 0x9a, 0x4a, 0x5a, 0x28, 0xee, 0x17, 0xca, 0x9c, 0x32, 0x48, 0x42, 0xc0 }, |
||||
{ 0xbc, 0x29, 0xf4, 0x65, 0x30, 0x9c, 0x97, 0x7e, 0x89, 0x61, 0x0a, 0x40 }, |
||||
{ 0x26, 0x63, 0xae, 0x6d, 0xdf, 0x8b, 0x5c, 0xe2, 0xbb, 0x29, 0x48, 0x80 }, |
||||
{ 0x46, 0xf2, 0x31, 0xef, 0xe4, 0x57, 0x03, 0x4c, 0x18, 0x14, 0x41, 0x80 }, |
||||
{ 0x3f, 0xb2, 0xce, 0x85, 0xab, 0xe9, 0xb0, 0xc7, 0x2e, 0x06, 0xfb, 0xe0 }, |
||||
{ 0xde, 0x87, 0x48, 0x1f, 0x28, 0x2c, 0x15, 0x39, 0x71, 0xa0, 0xa2, 0xe0 }, |
||||
{ 0xfc, 0xd7, 0xcc, 0xf2, 0x3c, 0x69, 0xfa, 0x99, 0xbb, 0xa1, 0x41, 0x20 }, |
||||
{ 0xf0, 0x26, 0x14, 0x47, 0xe9, 0x49, 0x0c, 0xa8, 0xe4, 0x74, 0xce, 0xc0 }, |
||||
{ 0x44, 0x10, 0x11, 0x58, 0x18, 0x19, 0x6f, 0x95, 0xcd, 0xd7, 0x01, 0x20 }, |
||||
{ 0x08, 0x8f, 0xc3, 0x1d, 0xf4, 0xbf, 0xbd, 0xe2, 0xa4, 0xea, 0xfb, 0x40 }, |
||||
{ 0xb8, 0xfe, 0xf1, 0xb6, 0x30, 0x77, 0x29, 0xfb, 0x0a, 0x07, 0x8c, 0x00 }, |
||||
{ 0x5a, 0xfe, 0xa7, 0xac, 0xcc, 0xb7, 0x7b, 0xbc, 0x9d, 0x99, 0xa9, 0x00 }, |
||||
{ 0x49, 0xa7, 0x01, 0x6a, 0xc6, 0x53, 0xf6, 0x5e, 0xcd, 0xc9, 0x07, 0x60 }, |
||||
{ 0x19, 0x44, 0xd0, 0x85, 0xbe, 0x4e, 0x7d, 0xa8, 0xd6, 0xcc, 0x7d, 0x00 }, |
||||
{ 0x25, 0x1f, 0x62, 0xad, 0xc4, 0x03, 0x2f, 0x0e, 0xe7, 0x14, 0x00, 0x20 }, |
||||
{ 0x56, 0x47, 0x1f, 0x87, 0x02, 0xa0, 0x72, 0x1e, 0x00, 0xb1, 0x2b, 0x80 }, |
||||
{ 0x2b, 0x8e, 0x49, 0x23, 0xf2, 0xdd, 0x51, 0xe2, 0xd5, 0x37, 0xfa, 0x00 }, |
||||
{ 0x6b, 0x55, 0x0a, 0x40, 0xa6, 0x6f, 0x47, 0x55, 0xde, 0x95, 0xc2, 0x60 }, |
||||
{ 0xa1, 0x8a, 0xd2, 0x8d, 0x4e, 0x27, 0xfe, 0x92, 0xa4, 0xf6, 0xc8, 0x40 }, |
||||
{ 0x10, 0xc2, 0xe5, 0x86, 0x38, 0x8c, 0xb8, 0x2a, 0x3d, 0x80, 0x75, 0x80 }, |
||||
{ 0xef, 0x34, 0xa4, 0x18, 0x17, 0xee, 0x02, 0x13, 0x3d, 0xb2, 0xeb, 0x00 }, |
||||
{ 0x7e, 0x9c, 0x0c, 0x54, 0x32, 0x5a, 0x9c, 0x15, 0x83, 0x6e, 0x00, 0x00 }, |
||||
{ 0x36, 0x93, 0xe5, 0x72, 0xd1, 0xfd, 0xe4, 0xcd, 0xf0, 0x79, 0xe8, 0x60 }, |
||||
{ 0xbf, 0xb2, 0xce, 0xc5, 0xab, 0xe1, 0xb0, 0xc7, 0x2e, 0x07, 0xfb, 0xe0 }, |
||||
{ 0x7e, 0xe1, 0x82, 0x30, 0xc5, 0x83, 0xcc, 0xcc, 0x57, 0xd4, 0xb0, 0x80 }, |
||||
{ 0xa0, 0x66, 0xcb, 0x2f, 0xed, 0xaf, 0xc9, 0xf5, 0x26, 0x64, 0x12, 0x60 }, |
||||
{ 0xbb, 0x23, 0x72, 0x5a, 0xbc, 0x47, 0xcc, 0x5f, 0x4c, 0xc4, 0xcd, 0x20 }, |
||||
{ 0xde, 0xd9, 0xdb, 0xa3, 0xbe, 0xe4, 0x0c, 0x59, 0xb5, 0x60, 0x9b, 0x40 }, |
||||
{ 0xd9, 0xa7, 0x01, 0x6a, 0xc6, 0x53, 0xe6, 0xde, 0xcd, 0xc9, 0x03, 0x60 }, |
||||
{ 0x9a, 0xd4, 0x6a, 0xed, 0x5f, 0x70, 0x7f, 0x28, 0x0a, 0xb5, 0xfc, 0x40 }, |
||||
{ 0xe5, 0x92, 0x1c, 0x77, 0x82, 0x25, 0x87, 0x31, 0x6d, 0x7d, 0x3c, 0x20 }, |
||||
{ 0x4f, 0x14, 0xda, 0x82, 0x42, 0xa8, 0xb8, 0x6d, 0xca, 0x73, 0x35, 0x20 }, |
||||
{ 0x8b, 0x8b, 0x50, 0x7a, 0xd4, 0x67, 0xd4, 0x44, 0x1d, 0xf7, 0x70, 0xe0 }, |
||||
{ 0x22, 0x83, 0x1c, 0x9c, 0xf1, 0x16, 0x94, 0x67, 0xad, 0x04, 0xb6, 0x80 }, |
||||
{ 0x21, 0x3b, 0x83, 0x8f, 0xe2, 0xae, 0x54, 0xc3, 0x8e, 0xe7, 0x18, 0x00 }, |
||||
{ 0x5d, 0x92, 0x6b, 0x6d, 0xd7, 0x1f, 0x08, 0x51, 0x81, 0xa4, 0xe1, 0x20 }, |
||||
{ 0x66, 0xab, 0x79, 0xd4, 0xb2, 0x9e, 0xe6, 0xe6, 0x95, 0x09, 0xe5, 0x60 }, |
||||
{ 0x95, 0x81, 0x48, 0x68, 0x2d, 0x74, 0x8a, 0x38, 0xdd, 0x68, 0xba, 0xa0 }, |
||||
{ 0xb8, 0xce, 0x02, 0x0c, 0xf0, 0x69, 0xc3, 0x2a, 0x72, 0x3a, 0xb1, 0x40 }, |
||||
{ 0xf4, 0x33, 0x1d, 0x6d, 0x46, 0x16, 0x07, 0xe9, 0x57, 0x52, 0x74, 0x60 }, |
||||
{ 0x6d, 0xa2, 0x3b, 0xa4, 0x24, 0xb9, 0x59, 0x61, 0x33, 0xcf, 0x9c, 0x80 }, |
||||
{ 0xa6, 0x36, 0xbc, 0xbc, 0x7b, 0x30, 0xc5, 0xfb, 0xea, 0xe6, 0x7f, 0xe0 }, |
||||
{ 0x5c, 0xb0, 0xd8, 0x6a, 0x07, 0xdf, 0x65, 0x4a, 0x90, 0x89, 0xa2, 0x00 }, |
||||
{ 0xf1, 0x1f, 0x10, 0x68, 0x48, 0x78, 0x0f, 0xc9, 0xec, 0xdd, 0x80, 0xa0 }, |
||||
{ 0x1f, 0xbb, 0x53, 0x64, 0xfb, 0x8d, 0x2c, 0x9d, 0x73, 0x0d, 0x5b, 0xa0 }, |
||||
{ 0xfc, 0xb8, 0x6b, 0xc7, 0x0a, 0x50, 0xc9, 0xd0, 0x2a, 0x5d, 0x03, 0x40 }, |
||||
{ 0xa5, 0x34, 0x43, 0x30, 0x29, 0xea, 0xc1, 0x5f, 0x32, 0x2e, 0x34, 0xc0 }, |
||||
{ 0xc9, 0x89, 0xd9, 0xc7, 0xc3, 0xd3, 0xb8, 0xc5, 0x5d, 0x75, 0x13, 0x00 }, |
||||
{ 0x7b, 0xb3, 0x8b, 0x2f, 0x01, 0x86, 0xd4, 0x66, 0x43, 0xae, 0x96, 0x20 }, |
||||
{ 0x26, 0x44, 0xeb, 0xad, 0xeb, 0x44, 0xb9, 0x46, 0x7d, 0x1f, 0x42, 0xc0 }, |
||||
{ 0x60, 0x8c, 0xc8, 0x57, 0x59, 0x4b, 0xfb, 0xb5, 0x5d, 0x69, 0x60, 0x00 } |
||||
}; |
||||
|
||||
// Column order (permutation) in which the bits in codeword are stored
|
||||
// (Not really used in FT8 v2 - instead the Nm, Mn and generator matrices are already permuted)
|
||||
uint8_t kColumn_order[174] = { |
||||
0, 1, 2, 3, 28, 4, 5, 6, 7, 8, 9, 10, 11, 34, 12, 32, 13, 14, 15, 16, |
||||
17, 18, 36, 29, 43, 19, 20, 42, 21, 40, 30, 37, 22, 47, 61, 45, 44, 23, 41, 39, |
||||
49, 24, 46, 50, 48, 26, 31, 33, 51, 38, 52, 59, 55, 66, 57, 27, 60, 35, 54, 58, |
||||
25, 56, 62, 64, 67, 69, 63, 68, 70, 72, 65, 73, 75, 74, 71, 77, 78, 76, 79, 80, |
||||
53, 81, 83, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, |
||||
100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119, |
||||
120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139, |
||||
140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, |
||||
160,161,162,163,164,165,166,167,168,169,170,171,172,173 |
||||
}; |
||||
|
||||
|
||||
// this is the LDPC(174,91) parity check matrix.
|
||||
// 83 rows.
|
||||
// each row describes one parity check.
|
||||
// each number is an index into the codeword (1-origin).
|
||||
// the codeword bits mentioned in each row must xor to zero.
|
||||
// From WSJT-X's ldpc_174_91_c_reordered_parity.f90.
|
||||
uint8_t kNm[83][7] = { |
||||
{ 4, 31, 59, 91, 92, 96, 153 }, |
||||
{ 5, 32, 60, 93, 115, 146, 0 }, |
||||
{ 6, 24, 61, 94, 122, 151, 0 }, |
||||
{ 7, 33, 62, 95, 96, 143, 0 }, |
||||
{ 8, 25, 63, 83, 93, 96, 148 }, |
||||
{ 6, 32, 64, 97, 126, 138, 0 }, |
||||
{ 5, 34, 65, 78, 98, 107, 154 }, |
||||
{ 9, 35, 66, 99, 139, 146, 0 }, |
||||
{ 10, 36, 67, 100, 107, 126, 0 }, |
||||
{ 11, 37, 67, 87, 101, 139, 158 }, |
||||
{ 12, 38, 68, 102, 105, 155, 0 }, |
||||
{ 13, 39, 69, 103, 149, 162, 0 }, |
||||
{ 8, 40, 70, 82, 104, 114, 145 }, |
||||
{ 14, 41, 71, 88, 102, 123, 156 }, |
||||
{ 15, 42, 59, 106, 123, 159, 0 }, |
||||
{ 1, 33, 72, 106, 107, 157, 0 }, |
||||
{ 16, 43, 73, 108, 141, 160, 0 }, |
||||
{ 17, 37, 74, 81, 109, 131, 154 }, |
||||
{ 11, 44, 75, 110, 121, 166, 0 }, |
||||
{ 45, 55, 64, 111, 130, 161, 173 }, |
||||
{ 8, 46, 71, 112, 119, 166, 0 }, |
||||
{ 18, 36, 76, 89, 113, 114, 143 }, |
||||
{ 19, 38, 77, 104, 116, 163, 0 }, |
||||
{ 20, 47, 70, 92, 138, 165, 0 }, |
||||
{ 2, 48, 74, 113, 128, 160, 0 }, |
||||
{ 21, 45, 78, 83, 117, 121, 151 }, |
||||
{ 22, 47, 58, 118, 127, 164, 0 }, |
||||
{ 16, 39, 62, 112, 134, 158, 0 }, |
||||
{ 23, 43, 79, 120, 131, 145, 0 }, |
||||
{ 19, 35, 59, 73, 110, 125, 161 }, |
||||
{ 20, 36, 63, 94, 136, 161, 0 }, |
||||
{ 14, 31, 79, 98, 132, 164, 0 }, |
||||
{ 3, 44, 80, 124, 127, 169, 0 }, |
||||
{ 19, 46, 81, 117, 135, 167, 0 }, |
||||
{ 7, 49, 58, 90, 100, 105, 168 }, |
||||
{ 12, 50, 61, 118, 119, 144, 0 }, |
||||
{ 13, 51, 64, 114, 118, 157, 0 }, |
||||
{ 24, 52, 76, 129, 148, 149, 0 }, |
||||
{ 25, 53, 69, 90, 101, 130, 156 }, |
||||
{ 20, 46, 65, 80, 120, 140, 170 }, |
||||
{ 21, 54, 77, 100, 140, 171, 0 }, |
||||
{ 35, 82, 133, 142, 171, 174, 0 }, |
||||
{ 14, 30, 83, 113, 125, 170, 0 }, |
||||
{ 4, 29, 68, 120, 134, 173, 0 }, |
||||
{ 1, 4, 52, 57, 86, 136, 152 }, |
||||
{ 26, 51, 56, 91, 122, 137, 168 }, |
||||
{ 52, 84, 110, 115, 145, 168, 0 }, |
||||
{ 7, 50, 81, 99, 132, 173, 0 }, |
||||
{ 23, 55, 67, 95, 172, 174, 0 }, |
||||
{ 26, 41, 77, 109, 141, 148, 0 }, |
||||
{ 2, 27, 41, 61, 62, 115, 133 }, |
||||
{ 27, 40, 56, 124, 125, 126, 0 }, |
||||
{ 18, 49, 55, 124, 141, 167, 0 }, |
||||
{ 6, 33, 85, 108, 116, 156, 0 }, |
||||
{ 28, 48, 70, 85, 105, 129, 158 }, |
||||
{ 9, 54, 63, 131, 147, 155, 0 }, |
||||
{ 22, 53, 68, 109, 121, 174, 0 }, |
||||
{ 3, 13, 48, 78, 95, 123, 0 }, |
||||
{ 31, 69, 133, 150, 155, 169, 0 }, |
||||
{ 12, 43, 66, 89, 97, 135, 159 }, |
||||
{ 5, 39, 75, 102, 136, 167, 0 }, |
||||
{ 2, 54, 86, 101, 135, 164, 0 }, |
||||
{ 15, 56, 87, 108, 119, 171, 0 }, |
||||
{ 10, 44, 82, 91, 111, 144, 149 }, |
||||
{ 23, 34, 71, 94, 127, 153, 0 }, |
||||
{ 11, 49, 88, 92, 142, 157, 0 }, |
||||
{ 29, 34, 87, 97, 147, 162, 0 }, |
||||
{ 30, 50, 60, 86, 137, 142, 162 }, |
||||
{ 10, 53, 66, 84, 112, 128, 165 }, |
||||
{ 22, 57, 85, 93, 140, 159, 0 }, |
||||
{ 28, 32, 72, 103, 132, 166, 0 }, |
||||
{ 28, 29, 84, 88, 117, 143, 150 }, |
||||
{ 1, 26, 45, 80, 128, 147, 0 }, |
||||
{ 17, 27, 89, 103, 116, 153, 0 }, |
||||
{ 51, 57, 98, 163, 165, 172, 0 }, |
||||
{ 21, 37, 73, 138, 152, 169, 0 }, |
||||
{ 16, 47, 76, 130, 137, 154, 0 }, |
||||
{ 3, 24, 30, 72, 104, 139, 0 }, |
||||
{ 9, 40, 90, 106, 134, 151, 0 }, |
||||
{ 15, 58, 60, 74, 111, 150, 163 }, |
||||
{ 18, 42, 79, 144, 146, 152, 0 }, |
||||
{ 25, 38, 65, 99, 122, 160, 0 }, |
||||
{ 17, 42, 75, 129, 170, 172, 0 } |
||||
}; |
||||
|
||||
// Mn from WSJT-X's bpdecode174.f90.
|
||||
// each row corresponds to a codeword bit.
|
||||
// the numbers indicate which three parity
|
||||
// checks (rows in Nm) refer to the codeword bit.
|
||||
// 1-origin.
|
||||
uint8_t kMn[174][3] = { |
||||
{ 16, 45, 73 }, |
||||
{ 25, 51, 62 }, |
||||
{ 33, 58, 78 }, |
||||
{ 1, 44, 45 }, |
||||
{ 2, 7, 61 }, |
||||
{ 3, 6, 54 }, |
||||
{ 4, 35, 48 }, |
||||
{ 5, 13, 21 }, |
||||
{ 8, 56, 79 }, |
||||
{ 9, 64, 69 }, |
||||
{ 10, 19, 66 }, |
||||
{ 11, 36, 60 }, |
||||
{ 12, 37, 58 }, |
||||
{ 14, 32, 43 }, |
||||
{ 15, 63, 80 }, |
||||
{ 17, 28, 77 }, |
||||
{ 18, 74, 83 }, |
||||
{ 22, 53, 81 }, |
||||
{ 23, 30, 34 }, |
||||
{ 24, 31, 40 }, |
||||
{ 26, 41, 76 }, |
||||
{ 27, 57, 70 }, |
||||
{ 29, 49, 65 }, |
||||
{ 3, 38, 78 }, |
||||
{ 5, 39, 82 }, |
||||
{ 46, 50, 73 }, |
||||
{ 51, 52, 74 }, |
||||
{ 55, 71, 72 }, |
||||
{ 44, 67, 72 }, |
||||
{ 43, 68, 78 }, |
||||
{ 1, 32, 59 }, |
||||
{ 2, 6, 71 }, |
||||
{ 4, 16, 54 }, |
||||
{ 7, 65, 67 }, |
||||
{ 8, 30, 42 }, |
||||
{ 9, 22, 31 }, |
||||
{ 10, 18, 76 }, |
||||
{ 11, 23, 82 }, |
||||
{ 12, 28, 61 }, |
||||
{ 13, 52, 79 }, |
||||
{ 14, 50, 51 }, |
||||
{ 15, 81, 83 }, |
||||
{ 17, 29, 60 }, |
||||
{ 19, 33, 64 }, |
||||
{ 20, 26, 73 }, |
||||
{ 21, 34, 40 }, |
||||
{ 24, 27, 77 }, |
||||
{ 25, 55, 58 }, |
||||
{ 35, 53, 66 }, |
||||
{ 36, 48, 68 }, |
||||
{ 37, 46, 75 }, |
||||
{ 38, 45, 47 }, |
||||
{ 39, 57, 69 }, |
||||
{ 41, 56, 62 }, |
||||
{ 20, 49, 53 }, |
||||
{ 46, 52, 63 }, |
||||
{ 45, 70, 75 }, |
||||
{ 27, 35, 80 }, |
||||
{ 1, 15, 30 }, |
||||
{ 2, 68, 80 }, |
||||
{ 3, 36, 51 }, |
||||
{ 4, 28, 51 }, |
||||
{ 5, 31, 56 }, |
||||
{ 6, 20, 37 }, |
||||
{ 7, 40, 82 }, |
||||
{ 8, 60, 69 }, |
||||
{ 9, 10, 49 }, |
||||
{ 11, 44, 57 }, |
||||
{ 12, 39, 59 }, |
||||
{ 13, 24, 55 }, |
||||
{ 14, 21, 65 }, |
||||
{ 16, 71, 78 }, |
||||
{ 17, 30, 76 }, |
||||
{ 18, 25, 80 }, |
||||
{ 19, 61, 83 }, |
||||
{ 22, 38, 77 }, |
||||
{ 23, 41, 50 }, |
||||
{ 7, 26, 58 }, |
||||
{ 29, 32, 81 }, |
||||
{ 33, 40, 73 }, |
||||
{ 18, 34, 48 }, |
||||
{ 13, 42, 64 }, |
||||
{ 5, 26, 43 }, |
||||
{ 47, 69, 72 }, |
||||
{ 54, 55, 70 }, |
||||
{ 45, 62, 68 }, |
||||
{ 10, 63, 67 }, |
||||
{ 14, 66, 72 }, |
||||
{ 22, 60, 74 }, |
||||
{ 35, 39, 79 }, |
||||
{ 1, 46, 64 }, |
||||
{ 1, 24, 66 }, |
||||
{ 2, 5, 70 }, |
||||
{ 3, 31, 65 }, |
||||
{ 4, 49, 58 }, |
||||
{ 1, 4, 5 }, |
||||
{ 6, 60, 67 }, |
||||
{ 7, 32, 75 }, |
||||
{ 8, 48, 82 }, |
||||
{ 9, 35, 41 }, |
||||
{ 10, 39, 62 }, |
||||
{ 11, 14, 61 }, |
||||
{ 12, 71, 74 }, |
||||
{ 13, 23, 78 }, |
||||
{ 11, 35, 55 }, |
||||
{ 15, 16, 79 }, |
||||
{ 7, 9, 16 }, |
||||
{ 17, 54, 63 }, |
||||
{ 18, 50, 57 }, |
||||
{ 19, 30, 47 }, |
||||
{ 20, 64, 80 }, |
||||
{ 21, 28, 69 }, |
||||
{ 22, 25, 43 }, |
||||
{ 13, 22, 37 }, |
||||
{ 2, 47, 51 }, |
||||
{ 23, 54, 74 }, |
||||
{ 26, 34, 72 }, |
||||
{ 27, 36, 37 }, |
||||
{ 21, 36, 63 }, |
||||
{ 29, 40, 44 }, |
||||
{ 19, 26, 57 }, |
||||
{ 3, 46, 82 }, |
||||
{ 14, 15, 58 }, |
||||
{ 33, 52, 53 }, |
||||
{ 30, 43, 52 }, |
||||
{ 6, 9, 52 }, |
||||
{ 27, 33, 65 }, |
||||
{ 25, 69, 73 }, |
||||
{ 38, 55, 83 }, |
||||
{ 20, 39, 77 }, |
||||
{ 18, 29, 56 }, |
||||
{ 32, 48, 71 }, |
||||
{ 42, 51, 59 }, |
||||
{ 28, 44, 79 }, |
||||
{ 34, 60, 62 }, |
||||
{ 31, 45, 61 }, |
||||
{ 46, 68, 77 }, |
||||
{ 6, 24, 76 }, |
||||
{ 8, 10, 78 }, |
||||
{ 40, 41, 70 }, |
||||
{ 17, 50, 53 }, |
||||
{ 42, 66, 68 }, |
||||
{ 4, 22, 72 }, |
||||
{ 36, 64, 81 }, |
||||
{ 13, 29, 47 }, |
||||
{ 2, 8, 81 }, |
||||
{ 56, 67, 73 }, |
||||
{ 5, 38, 50 }, |
||||
{ 12, 38, 64 }, |
||||
{ 59, 72, 80 }, |
||||
{ 3, 26, 79 }, |
||||
{ 45, 76, 81 }, |
||||
{ 1, 65, 74 }, |
||||
{ 7, 18, 77 }, |
||||
{ 11, 56, 59 }, |
||||
{ 14, 39, 54 }, |
||||
{ 16, 37, 66 }, |
||||
{ 10, 28, 55 }, |
||||
{ 15, 60, 70 }, |
||||
{ 17, 25, 82 }, |
||||
{ 20, 30, 31 }, |
||||
{ 12, 67, 68 }, |
||||
{ 23, 75, 80 }, |
||||
{ 27, 32, 62 }, |
||||
{ 24, 69, 75 }, |
||||
{ 19, 21, 71 }, |
||||
{ 34, 53, 61 }, |
||||
{ 35, 46, 47 }, |
||||
{ 33, 59, 76 }, |
||||
{ 40, 43, 83 }, |
||||
{ 41, 42, 63 }, |
||||
{ 49, 75, 83 }, |
||||
{ 20, 44, 48 }, |
||||
{ 42, 49, 57 } |
||||
}; |
||||
|
||||
uint8_t kNrw[83] = { |
||||
7,6,6,6,7,6,7,6,6,7,6,6,7,7,6,6, |
||||