Merge pull request #48 from N0BOY/dev

Dev 0.89 -> Release
pull/66/head v0.89
NØBOY 2023-07-08 22:03:56 -07:00 zatwierdzone przez GitHub
commit 0eae0821d9
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
537 zmienionych plików z 8835 dodań i 1595 usunięć

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

0
ft8CN/app/.gitignore vendored 100755 → 100644
Wyświetl plik

12
ft8CN/app/build.gradle 100755 → 100644
Wyświetl plik

@ -5,7 +5,7 @@ plugins {
id 'com.android.application'
}
def currentTime = getCurrentTime();
def currentTime = getCurrentTime()
static def getCurrentTime() {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd")
@ -22,7 +22,7 @@ android {
minSdk 23
targetSdk 33
versionCode 1
versionName '0.87'
versionName '0.89'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
dataBinding{
@ -35,14 +35,6 @@ android {
}
}
ndk{
abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
}
sourceSets{
main{
jniLibs.srcDirs=['libs']
}
}
signingConfig signingConfigs.debug
}

BIN
ft8CN/app/libs/arm64-v8a/libft8cn.so 100755 → 100644

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

BIN
ft8CN/app/libs/x86/libft8cn.so 100755 → 100644

Plik binarny nie jest wyświetlany.

BIN
ft8CN/app/libs/x86_64/libft8cn.so 100755 → 100644

Plik binarny nie jest wyświetlany.

0
ft8CN/app/proguard-rules.pro vendored 100755 → 100644
Wyświetl plik

Wyświetl plik

@ -1,26 +0,0 @@
package com.bg7yoz.ft8cn;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.bg7yoz.ft8cn", appContext.getPackageName());
}
}

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

@ -0,0 +1,5 @@
关于音频输出设置
采样位深也称采样精度FT8CN只有16位整型和32位浮点可选。采样位数是表示声音强度量化后的精细程度它的数值越大波动幅度的分辨率也就越高所发出声音的能力越强。
采样率:也称取样频率, 指每秒钟取得声音样本的次数。采样频率越高,声音的质量也就越好,但占的资源也多。

Wyświetl plik

@ -0,0 +1,5 @@
Audio Output Setting
Bit depth: Choose 16-bit int or 32-bit float. Bit depth dictates the number of possible amplitude values of audio sample. A higher bit depth will produce a higher resolution audio.
Sample rate: Sample rate refers to the number of samples that are present within one second of digital audio. Higher sample rate provides more accurate audio waveform, but consume more system resources.

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

0
ft8CN/app/src/main/assets/cty.dat 100755 → 100644
Wyświetl plik

Wyświetl plik

@ -0,0 +1,4 @@
关于解码模式
快速解码沿用原来FT8CN的解码对接收到的音频做一次性的解码。
多次解码:在快速解码的基础上,再做多次的解码,增加迭代的次数,并尝试解码频率有叠加的信号。
注:多次解码增加的运算量,总的解码时间会变长,会缩短设备的续航时间。

Wyświetl plik

@ -0,0 +1,4 @@
Decode mode:
Fast decode: Default mode, one decoding pass on received audio.
Deep decode: Recursive decoding that process received audio in multiple passes .
* Note: Deep decoding requires longer decoding time and more battery consumption.

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

@ -14,6 +14,17 @@ Please click "FAQ" if you have good suggestions or questions .
BG7YOZ
2022-07-01
2023-07-08(0.89)
1.增加多重解码功能,在多重解码模式下,提高解码深度,尝试解码叠加的信号。
2.解决导入ADI后通联过的分区没有及时更新的问题。
3.解决iCom电台在网络模式下发射音频会出现破音的情况。
4.解决在某些情况下RR73卡死的问题。
5.提高解码稳定性。
6.改正解码消息的comment字段有时不正确显示的问题。
7.修改导出日志的提示。
2023-05-02(0.88 Patch 2)
1.增加音频输出设置(位深、采样率)。
2.增加日志可以按条件查询并导出。

Wyświetl plik

Wyświetl plik

@ -41,7 +41,7 @@ YAESU FT-DX Other series,00,4800,3
KENWOOD(建伍) TK-90,00,9600,5
KENWOOD(建伍) TS-480,00,9600,7
KENWOOD(建伍) TS-590,00,9600,7
KENWOOD(建伍) TS-2000,00,9600,7
KENWOOD(建伍) TS-2000,00,9600,14
KN990,00,38400,1
Elecraft K3S\K3\KX3\KX2,00,38400,10
mcHF-QRP sdr,00,4800,1

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

Wyświetl plik

@ -0,0 +1,69 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.18.1)
# Declares and names the project.
project("ft8cn")
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
ft8cn
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
generate_ft8.cpp
ft8Listener.cpp
ft8Spectrum.cpp
fft/kiss_fftr.c
fft/kiss_fft.c
ft8/decode.c
ft8/crc.c
ft8/constants.c
ft8/ldpc.c
ft8/unpack.c
ft8/text.c
ft8/hash22.c
ft8/encode.c
ft8/pack.c
monitor_opr.c
ft8Decoder.c
ft8Encoder.c
spectrum_data.c
)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
ft8cn
# Links the target library to the log library
# included in the NDK.
${log-lib} )

Wyświetl plik

@ -0,0 +1,11 @@
//
// Created by jmsmf on 2022/4/22.
//
#ifndef NATIVEC_COMM_STR_H
#define NATIVEC_COMM_STR_H
#endif //NATIVEC_COMM_STR_H
const char ERROR_FILE_NAME_IS_NULL[] = "error:wav_path is null!!!";
const char ERROR_OPEN_FILE_FAILED[] ="Error : the content of the file does not meet the requirements.";
const char INFO_DECODE_OK[] ="decode OK!!!";

Wyświetl plik

@ -0,0 +1,3 @@
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

Wyświetl plik

@ -0,0 +1,24 @@
#pragma once
#include <stdio.h>
#include<android/log.h>
#define LOG_DEBUG 0
#define LOG_INFO 1
#define LOG_WARN 2
#define LOG_ERROR 3
#define LOG_FATAL 4
#define LOG_LEVEL LOG_DEBUG
//#define LOG_LEVEL LOG_INFO
//#define LOG(level, ...) if (level >= LOG_LEVEL) fprintf(stderr, __VA_ARGS__)
#define TAG "FT8_DECODER" // 这个是自定义的LOG的标识
#define LOG(level, ...) if (level >= LOG_LEVEL) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOG_PRINTF(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型

Wyświetl plik

@ -0,0 +1,131 @@
#include "wave.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
// Save signal in floating point format (-1 .. +1) as a WAVE file using 16-bit signed integers.
void save_wav(const float* signal, int num_samples, int sample_rate, const char* path)
{
char subChunk1ID[4] = { 'f', 'm', 't', ' ' };
uint32_t subChunk1Size = 16; // 16 for PCM
uint16_t audioFormat = 1; // PCM = 1
uint16_t numChannels = 1;
uint16_t bitsPerSample = 16;
uint32_t sampleRate = sample_rate;
uint16_t blockAlign = numChannels * bitsPerSample / 8;
uint32_t byteRate = sampleRate * blockAlign;
char subChunk2ID[4] = { 'd', 'a', 't', 'a' };
uint32_t subChunk2Size = num_samples * blockAlign;
char chunkID[4] = { 'R', 'I', 'F', 'F' };
uint32_t chunkSize = 4 + (8 + subChunk1Size) + (8 + subChunk2Size);
char format[4] = { 'W', 'A', 'V', 'E' };
int16_t* raw_data = (int16_t*)malloc(num_samples * blockAlign);
for (int i = 0; i < num_samples; i++)
{
float x = signal[i];
if (x > 1.0)
x = 1.0;
else if (x < -1.0)
x = -1.0;
raw_data[i] = (int)(x * 32767.0);
}
FILE* f = fopen(path, "wb");
// NOTE: works only on little-endian architecture
fwrite(chunkID, sizeof(chunkID), 1, f);
fwrite(&chunkSize, sizeof(chunkSize), 1, f);
fwrite(format, sizeof(format), 1, f);
fwrite(subChunk1ID, sizeof(subChunk1ID), 1, f);
fwrite(&subChunk1Size, sizeof(subChunk1Size), 1, f);
fwrite(&audioFormat, sizeof(audioFormat), 1, f);
fwrite(&numChannels, sizeof(numChannels), 1, f);
fwrite(&sampleRate, sizeof(sampleRate), 1, f);
fwrite(&byteRate, sizeof(byteRate), 1, f);
fwrite(&blockAlign, sizeof(blockAlign), 1, f);
fwrite(&bitsPerSample, sizeof(bitsPerSample), 1, f);
fwrite(subChunk2ID, sizeof(subChunk2ID), 1, f);
fwrite(&subChunk2Size, sizeof(subChunk2Size), 1, f);
fwrite(raw_data, blockAlign, num_samples, f);
fclose(f);
free(raw_data);
}
// Load signal in floating point format (-1 .. +1) as a WAVE file using 16-bit signed integers.
int load_wav(float* signal, int* num_samples, int* sample_rate, const char* path)
{
char subChunk1ID[4]; // = {'f', 'm', 't', ' '};
uint32_t subChunk1Size; // = 16; // 16 for PCM
uint16_t audioFormat; // = 1; // PCM = 1
uint16_t numChannels; // = 1;
uint16_t bitsPerSample; // = 16;
uint32_t sampleRate;
uint16_t blockAlign; // = numChannels * bitsPerSample / 8;
uint32_t byteRate; // = sampleRate * blockAlign;
char subChunk2ID[4]; // = {'d', 'a', 't', 'a'};
uint32_t subChunk2Size; // = num_samples * blockAlign;
char chunkID[4]; // = {'R', 'I', 'F', 'F'};
uint32_t chunkSize; // = 4 + (8 + subChunk1Size) + (8 + subChunk2Size);
char format[4]; // = {'W', 'A', 'V', 'E'};
FILE* f = fopen(path, "rb");
if (f==NULL){
return -1;
}
// NOTE: works only on little-endian architecture
fread((void*)chunkID, sizeof(chunkID), 1, f);
fread((void*)&chunkSize, sizeof(chunkSize), 1, f);
fread((void*)format, sizeof(format), 1, f);
fread((void*)subChunk1ID, sizeof(subChunk1ID), 1, f);
fread((void*)&subChunk1Size, sizeof(subChunk1Size), 1, f);
if (subChunk1Size != 16)
return -1;
fread((void*)&audioFormat, sizeof(audioFormat), 1, f);
fread((void*)&numChannels, sizeof(numChannels), 1, f);
fread((void*)&sampleRate, sizeof(sampleRate), 1, f);
fread((void*)&byteRate, sizeof(byteRate), 1, f);
fread((void*)&blockAlign, sizeof(blockAlign), 1, f);
fread((void*)&bitsPerSample, sizeof(bitsPerSample), 1, f);
if (audioFormat != 1 || numChannels != 1 || bitsPerSample != 16)
return -1;
fread((void*)subChunk2ID, sizeof(subChunk2ID), 1, f);
fread((void*)&subChunk2Size, sizeof(subChunk2Size), 1, f);
if (subChunk2Size / blockAlign > *num_samples)
return -2;
*num_samples = subChunk2Size / blockAlign;
*sample_rate = sampleRate;
int16_t* raw_data = (int16_t*)malloc(*num_samples * blockAlign);
fread((void*)raw_data, blockAlign, *num_samples, f);
for (int i = 0; i < *num_samples; i++)
{
signal[i] = raw_data[i] / 32768.0f;
}
free(raw_data);
fclose(f);
return 0;
}

Wyświetl plik

@ -0,0 +1,10 @@
#ifndef _INCLUDE_WAVE_H_
#define _INCLUDE_WAVE_H_
// Save signal in floating point format (-1 .. +1) as a WAVE file using 16-bit signed integers.
void save_wav(const float* signal, int num_samples, int sample_rate, const char* path);
// Load signal in floating point format (-1 .. +1) as a WAVE file using 16-bit signed integers.
int load_wav(float* signal, int* num_samples, int* sample_rate, const char* path);
#endif // _INCLUDE_WAVE_H_

Wyświetl plik

@ -0,0 +1,158 @@
/*
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
*
* SPDX-License-Identifier: BSD-3-Clause
* See COPYING file for more information.
*/
/* kiss_fft.h
defines kiss_fft_scalar as either short or a float type
and defines
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
#include "kiss_fft.h"
#include <limits.h>
#define MAXFACTORS 32
/* e.g. an fft of length 128 has 4 factors
as far as kissfft is concerned
4*4*4*2
*/
struct kiss_fft_state{
int nfft;
int inverse;
int factors[2*MAXFACTORS];
kiss_fft_cpx twiddles[1];
};
/*
Explanation of macros dealing with complex math:
C_MUL(m,a,b) : m = a*b
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
C_SUB( res, a,b) : res = a - b
C_SUBFROM( res , a) : res -= a
C_ADDTO( res , a) : res += a
* */
#ifdef FIXED_POINT
#if (FIXED_POINT==32)
# define FRACBITS 31
# define SAMPPROD int64_t
#define SAMP_MAX 2147483647
#else
# define FRACBITS 15
# define SAMPPROD int32_t
#define SAMP_MAX 32767
#endif
#define SAMP_MIN -SAMP_MAX
#if defined(CHECK_OVERFLOW)
# define CHECK_OVERFLOW_OP(a,op,b) \
if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \
fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); }
#endif
# define smul(a,b) ( (SAMPPROD)(a)*(b) )
# define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS )
# define S_MUL(a,b) sround( smul(a,b) )
# define C_MUL(m,a,b) \
do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \
(m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0)
# define DIVSCALAR(x,k) \
(x) = sround( smul( x, SAMP_MAX/k ) )
# define C_FIXDIV(c,div) \
do { DIVSCALAR( (c).r , div); \
DIVSCALAR( (c).i , div); }while (0)
# define C_MULBYSCALAR( c, s ) \
do{ (c).r = sround( smul( (c).r , s ) ) ;\
(c).i = sround( smul( (c).i , s ) ) ; }while(0)
#else /* not FIXED_POINT*/
# define S_MUL(a,b) ( (a)*(b) )
#define C_MUL(m,a,b) \
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
# define C_FIXDIV(c,div) /* NOOP */
# define C_MULBYSCALAR( c, s ) \
do{ (c).r *= (s);\
(c).i *= (s); }while(0)
#endif
#ifndef CHECK_OVERFLOW_OP
# define CHECK_OVERFLOW_OP(a,op,b) /* noop */
#endif
#define C_ADD( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,+,(b).r)\
CHECK_OVERFLOW_OP((a).i,+,(b).i)\
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
}while(0)
#define C_SUB( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,-,(b).r)\
CHECK_OVERFLOW_OP((a).i,-,(b).i)\
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
}while(0)
#define C_ADDTO( res , a)\
do { \
CHECK_OVERFLOW_OP((res).r,+,(a).r)\
CHECK_OVERFLOW_OP((res).i,+,(a).i)\
(res).r += (a).r; (res).i += (a).i;\
}while(0)
#define C_SUBFROM( res , a)\
do {\
CHECK_OVERFLOW_OP((res).r,-,(a).r)\
CHECK_OVERFLOW_OP((res).i,-,(a).i)\
(res).r -= (a).r; (res).i -= (a).i; \
}while(0)
#ifdef FIXED_POINT
# define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase))
# define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase))
# define HALF_OF(x) ((x)>>1)
#elif defined(USE_SIMD)
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
# define HALF_OF(x) ((x)*_mm_set1_ps(.5))
#else
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
# define HALF_OF(x) ((x)*.5)
#endif
#define kf_cexp(x,phase) \
do{ \
(x)->r = KISS_FFT_COS(phase);\
(x)->i = KISS_FFT_SIN(phase);\
}while(0)
/* a debugging function */
#define pcpx(c)\
fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) )
#ifdef KISS_FFT_USE_ALLOCA
// define this to allow use of alloca instead of malloc for temporary buffers
// Temporary buffers are used in two case:
// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5
// 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform.
#include <alloca.h>
#define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes)
#define KISS_FFT_TMP_FREE(ptr)
#else
#define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes)
#define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr)
#endif

Wyświetl plik

@ -0,0 +1,402 @@
/*
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
*
* SPDX-License-Identifier: BSD-3-Clause
* See COPYING file for more information.
*/
#include "_kiss_fft_guts.h"
/* The guts header contains all the multiplication and addition macros that are defined for
fixed or floating point complex numbers. It also delares the kf_ internal functions.
*/
static void kf_bfly2(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
int m
)
{
kiss_fft_cpx * Fout2;
kiss_fft_cpx * tw1 = st->twiddles;
kiss_fft_cpx t;
Fout2 = Fout + m;
do{
C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2);
C_MUL (t, *Fout2 , *tw1);
tw1 += fstride;
C_SUB( *Fout2 , *Fout , t );
C_ADDTO( *Fout , t );
++Fout2;
++Fout;
}while (--m);
}
static void kf_bfly4(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
const size_t m
)
{
kiss_fft_cpx *tw1,*tw2,*tw3;
kiss_fft_cpx scratch[6];
size_t k=m;
const size_t m2=2*m;
const size_t m3=3*m;
tw3 = tw2 = tw1 = st->twiddles;
do {
C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4);
C_MUL(scratch[0],Fout[m] , *tw1 );
C_MUL(scratch[1],Fout[m2] , *tw2 );
C_MUL(scratch[2],Fout[m3] , *tw3 );
C_SUB( scratch[5] , *Fout, scratch[1] );
C_ADDTO(*Fout, scratch[1]);
C_ADD( scratch[3] , scratch[0] , scratch[2] );
C_SUB( scratch[4] , scratch[0] , scratch[2] );
C_SUB( Fout[m2], *Fout, scratch[3] );
tw1 += fstride;
tw2 += fstride*2;
tw3 += fstride*3;
C_ADDTO( *Fout , scratch[3] );
if(st->inverse) {
Fout[m].r = scratch[5].r - scratch[4].i;
Fout[m].i = scratch[5].i + scratch[4].r;
Fout[m3].r = scratch[5].r + scratch[4].i;
Fout[m3].i = scratch[5].i - scratch[4].r;
}else{
Fout[m].r = scratch[5].r + scratch[4].i;
Fout[m].i = scratch[5].i - scratch[4].r;
Fout[m3].r = scratch[5].r - scratch[4].i;
Fout[m3].i = scratch[5].i + scratch[4].r;
}
++Fout;
}while(--k);
}
static void kf_bfly3(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
size_t m
)
{
size_t k=m;
const size_t m2 = 2*m;
kiss_fft_cpx *tw1,*tw2;
kiss_fft_cpx scratch[5];
kiss_fft_cpx epi3;
epi3 = st->twiddles[fstride*m];
tw1=tw2=st->twiddles;
do{
C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3);
C_MUL(scratch[1],Fout[m] , *tw1);
C_MUL(scratch[2],Fout[m2] , *tw2);
C_ADD(scratch[3],scratch[1],scratch[2]);
C_SUB(scratch[0],scratch[1],scratch[2]);
tw1 += fstride;
tw2 += fstride*2;
Fout[m].r = Fout->r - HALF_OF(scratch[3].r);
Fout[m].i = Fout->i - HALF_OF(scratch[3].i);
C_MULBYSCALAR( scratch[0] , epi3.i );
C_ADDTO(*Fout,scratch[3]);
Fout[m2].r = Fout[m].r + scratch[0].i;
Fout[m2].i = Fout[m].i - scratch[0].r;
Fout[m].r -= scratch[0].i;
Fout[m].i += scratch[0].r;
++Fout;
}while(--k);
}
static void kf_bfly5(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
int m
)
{
kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
int u;
kiss_fft_cpx scratch[13];
kiss_fft_cpx * twiddles = st->twiddles;
kiss_fft_cpx *tw;
kiss_fft_cpx ya,yb;
ya = twiddles[fstride*m];
yb = twiddles[fstride*2*m];
Fout0=Fout;
Fout1=Fout0+m;
Fout2=Fout0+2*m;
Fout3=Fout0+3*m;
Fout4=Fout0+4*m;
tw=st->twiddles;
for ( u=0; u<m; ++u ) {
C_FIXDIV( *Fout0,5); C_FIXDIV( *Fout1,5); C_FIXDIV( *Fout2,5); C_FIXDIV( *Fout3,5); C_FIXDIV( *Fout4,5);
scratch[0] = *Fout0;
C_MUL(scratch[1] ,*Fout1, tw[u*fstride]);
C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]);
C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]);
C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]);
C_ADD( scratch[7],scratch[1],scratch[4]);
C_SUB( scratch[10],scratch[1],scratch[4]);
C_ADD( scratch[8],scratch[2],scratch[3]);
C_SUB( scratch[9],scratch[2],scratch[3]);
Fout0->r += scratch[7].r + scratch[8].r;
Fout0->i += scratch[7].i + scratch[8].i;
scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r);
scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r);
scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i);
scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i);
C_SUB(*Fout1,scratch[5],scratch[6]);
C_ADD(*Fout4,scratch[5],scratch[6]);
scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r);
scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r);
scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i);
scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i);
C_ADD(*Fout2,scratch[11],scratch[12]);
C_SUB(*Fout3,scratch[11],scratch[12]);
++Fout0;++Fout1;++Fout2;++Fout3;++Fout4;
}
}
/* perform the butterfly for one stage of a mixed radix FFT */
static void kf_bfly_generic(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
int m,
int p
)
{
int u,k,q1,q;
kiss_fft_cpx * twiddles = st->twiddles;
kiss_fft_cpx t;
int Norig = st->nfft;
kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p);
for ( u=0; u<m; ++u ) {
k=u;
for ( q1=0 ; q1<p ; ++q1 ) {
scratch[q1] = Fout[ k ];
C_FIXDIV(scratch[q1],p);
k += m;
}
k=u;
for ( q1=0 ; q1<p ; ++q1 ) {
int twidx=0;
Fout[ k ] = scratch[0];
for (q=1;q<p;++q ) {
twidx += fstride * k;
if (twidx>=Norig) twidx-=Norig;
C_MUL(t,scratch[q] , twiddles[twidx] );
C_ADDTO( Fout[ k ] ,t);
}
k += m;
}
}
KISS_FFT_TMP_FREE(scratch);
}
static
void kf_work(
kiss_fft_cpx * Fout,
const kiss_fft_cpx * f,
const size_t fstride,
int in_stride,
int * factors,
const kiss_fft_cfg st
)
{
kiss_fft_cpx * Fout_beg=Fout;
const int p=*factors++; /* the radix */
const int m=*factors++; /* stage's fft length/p */
const kiss_fft_cpx * Fout_end = Fout + p*m;
#ifdef _OPENMP
// use openmp extensions at the
// top-level (not recursive)
if (fstride==1 && p<=5)
{
int k;
// execute the p different work units in different threads
# pragma omp parallel for
for (k=0;k<p;++k)
kf_work( Fout +k*m, f+ fstride*in_stride*k,fstride*p,in_stride,factors,st);
// all threads have joined by this point
switch (p) {
case 2: kf_bfly2(Fout,fstride,st,m); break;
case 3: kf_bfly3(Fout,fstride,st,m); break;
case 4: kf_bfly4(Fout,fstride,st,m); break;
case 5: kf_bfly5(Fout,fstride,st,m); break;
default: kf_bfly_generic(Fout,fstride,st,m,p); break;
}
return;
}
#endif
if (m==1) {
do{
*Fout = *f;
f += fstride*in_stride;
}while(++Fout != Fout_end );
}else{
do{
// recursive call:
// DFT of size m*p performed by doing
// p instances of smaller DFTs of size m,
// each one takes a decimated version of the input
kf_work( Fout , f, fstride*p, in_stride, factors,st);
f += fstride*in_stride;
}while( (Fout += m) != Fout_end );
}
Fout=Fout_beg;
// recombine the p smaller DFTs
switch (p) {
case 2: kf_bfly2(Fout,fstride,st,m); break;
case 3: kf_bfly3(Fout,fstride,st,m); break;
case 4: kf_bfly4(Fout,fstride,st,m); break;
case 5: kf_bfly5(Fout,fstride,st,m); break;
default: kf_bfly_generic(Fout,fstride,st,m,p); break;
}
}
/* facbuf is populated by p1,m1,p2,m2, ...
where
p[i] * m[i] = m[i-1]
m0 = n */
static
void kf_factor(int n,int * facbuf)
{
int p=4;
double floor_sqrt;
floor_sqrt = floor( sqrt((double)n) );
/*factor out powers of 4, powers of 2, then any remaining primes */
do {
while (n % p) {
switch (p) {
case 4: p = 2; break;
case 2: p = 3; break;
default: p += 2; break;
}
if (p > floor_sqrt)
p = n; /* no more factors, skip to end */
}
n /= p;
*facbuf++ = p;
*facbuf++ = n;
} while (n > 1);
}
/*
*
* User-callable function to allocate all necessary storage space for the fft.
*
* The return value is a contiguous block of memory, allocated with malloc. As such,
* It can be freed with free(), rather than a kiss_fft-specific function.
* */
kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem )
{
kiss_fft_cfg st=NULL;
size_t memneeded = sizeof(struct kiss_fft_state)
+ sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/
if ( lenmem==NULL ) {
st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded );
}else{
if (mem != NULL && *lenmem >= memneeded)
st = (kiss_fft_cfg)mem;
*lenmem = memneeded;
}
if (st) {
int i;
st->nfft=nfft;
st->inverse = inverse_fft;
for (i=0;i<nfft;++i) {
const double pi=3.141592653589793238462643383279502884197169399375105820974944;
double phase = -2*pi*i / nfft;
if (st->inverse)
phase *= -1;
kf_cexp(st->twiddles+i, phase );
}
kf_factor(nfft,st->factors);
}
return st;
}
void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride)
{
if (fin == fout) {
//NOTE: this is not really an in-place FFT algorithm.
//It just performs an out-of-place FFT into a temp buffer
kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft);
kf_work(tmpbuf,fin,1,in_stride, st->factors,st);
memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft);
KISS_FFT_TMP_FREE(tmpbuf);
}else{
kf_work( fout, fin, 1,in_stride, st->factors,st );
}
}
void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
{
kiss_fft_stride(cfg,fin,fout,1);
}
void kiss_fft_cleanup(void)
{
// nothing needed any more
}
int kiss_fft_next_fast_size(int n)
{
while(1) {
int m=n;
while ( (m%2) == 0 ) m/=2;
while ( (m%3) == 0 ) m/=3;
while ( (m%5) == 0 ) m/=5;
if (m<=1)
break; /* n is completely factorable by twos, threes, and fives */
n++;
}
return n;
}

Wyświetl plik

@ -0,0 +1,132 @@
/*
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
*
* SPDX-License-Identifier: BSD-3-Clause
* See COPYING file for more information.
*/
#ifndef KISS_FFT_H
#define KISS_FFT_H
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
ATTENTION!
If you would like a :
-- a utility that will handle the caching of fft objects
-- real-only (no imaginary time component ) FFT
-- a multi-dimensional FFT
-- a command-line utility to perform ffts
-- a command-line utility to perform fast-convolution filtering
Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c
in the tools/ directory.
*/
#ifdef USE_SIMD
# include <xmmintrin.h>
# define kiss_fft_scalar __m128
#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16)
#define KISS_FFT_FREE _mm_free
#else
#define KISS_FFT_MALLOC malloc
#define KISS_FFT_FREE free
#endif
#ifdef FIXED_POINT
#include <sys/types.h>
# if (FIXED_POINT == 32)
# define kiss_fft_scalar int32_t
# else
# define kiss_fft_scalar int16_t
# endif
#else
# ifndef kiss_fft_scalar
/* default is float */
# define kiss_fft_scalar float
# endif
#endif
typedef struct {
kiss_fft_scalar r;
kiss_fft_scalar i;
}kiss_fft_cpx;
typedef struct kiss_fft_state* kiss_fft_cfg;
/*
* kiss_fft_alloc
*
* Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
*
* typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL);
*
* The return value from fft_alloc is a cfg buffer used internally
* by the fft routine or NULL.
*
* If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc.
* The returned value should be free()d when done to avoid memory leaks.
*
* The state can be placed in a user supplied buffer 'mem':
* If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
* then the function places the cfg in mem and the size used in *lenmem
* and returns mem.
*
* If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
* then the function returns NULL and places the minimum cfg
* buffer size in *lenmem.
* */
kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem);
/*
* kiss_fft(cfg,in_out_buf)
*
* Perform an FFT on a complex input buffer.
* for a forward FFT,
* fin should be f[0] , f[1] , ... ,f[nfft-1]
* fout will be F[0] , F[1] , ... ,F[nfft-1]
* Note that each element is complex and can be accessed like
f[k].r and f[k].i
* */
void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
/*
A more generic version of the above function. It reads its input from every Nth sample.
* */
void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride);
/* If kiss_fft_alloc allocated a buffer, it is one contiguous
buffer and can be simply free()d when no longer needed*/
#define kiss_fft_free KISS_FFT_FREE
/*
Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up
your compiler output to call this before you exit.
*/
void kiss_fft_cleanup(void);
/*
* Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5)
*/
int kiss_fft_next_fast_size(int n);
/* for real ffts, we need an even size */
#define kiss_fftr_next_fast_size_real(n) \
(kiss_fft_next_fast_size( ((n)+1)>>1)<<1)
#ifdef __cplusplus
}
#endif
#endif

Wyświetl plik

@ -0,0 +1,153 @@
/*
* Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
*
* SPDX-License-Identifier: BSD-3-Clause
* See COPYING file for more information.
*/
#include "kiss_fftr.h"
#include "_kiss_fft_guts.h"
struct kiss_fftr_state{
kiss_fft_cfg substate;
kiss_fft_cpx * tmpbuf;
kiss_fft_cpx * super_twiddles;
#ifdef USE_SIMD
void * pad;
#endif
};
kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem)
{
int i;
kiss_fftr_cfg st = NULL;
size_t subsize = 0, memneeded;
if (nfft & 1) {
fprintf(stderr,"Real FFT optimization must be even.\n");
return NULL;
}
nfft >>= 1;
kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize);
memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2);
if (lenmem == NULL) {
st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded);
} else {
if (*lenmem >= memneeded)
st = (kiss_fftr_cfg) mem;
*lenmem = memneeded;
}
if (!st)
return NULL;
st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */
st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize);
st->super_twiddles = st->tmpbuf + nfft;
kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize);
for (i = 0; i < nfft/2; ++i) {
double phase =
-3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5);
if (inverse_fft)
phase *= -1;
kf_cexp (st->super_twiddles+i,phase);
}
return st;
}
void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata)
{
/* input buffer timedata is stored row-wise */
int k,ncfft;
kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc;
if ( st->substate->inverse) {
fprintf(stderr,"kiss fft usage error: improper alloc\n");
exit(1);
}
ncfft = st->substate->nfft;
/*perform the parallel fft of two real signals packed in real,imag*/
kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf );
/* The real part of the DC element of the frequency spectrum in st->tmpbuf
* contains the sum of the even-numbered elements of the input time sequence
* The imag part is the sum of the odd-numbered elements
*
* The sum of tdc.r and tdc.i is the sum of the input time sequence.
* yielding DC of input time sequence
* The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1...
* yielding Nyquist bin of input time sequence
*/
tdc.r = st->tmpbuf[0].r;
tdc.i = st->tmpbuf[0].i;
C_FIXDIV(tdc,2);
CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i);
CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i);
freqdata[0].r = tdc.r + tdc.i;
freqdata[ncfft].r = tdc.r - tdc.i;
#ifdef USE_SIMD
freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0);
#else
freqdata[ncfft].i = freqdata[0].i = 0;
#endif
for ( k=1;k <= ncfft/2 ; ++k ) {
fpk = st->tmpbuf[k];
fpnk.r = st->tmpbuf[ncfft-k].r;
fpnk.i = - st->tmpbuf[ncfft-k].i;
C_FIXDIV(fpk,2);
C_FIXDIV(fpnk,2);
C_ADD( f1k, fpk , fpnk );
C_SUB( f2k, fpk , fpnk );
C_MUL( tw , f2k , st->super_twiddles[k-1]);
freqdata[k].r = HALF_OF(f1k.r + tw.r);
freqdata[k].i = HALF_OF(f1k.i + tw.i);
freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r);
freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i);
}
}
void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata)
{
/* input buffer timedata is stored row-wise */
int k, ncfft;
if (st->substate->inverse == 0) {
fprintf (stderr, "kiss fft usage error: improper alloc\n");
exit (1);
}
ncfft = st->substate->nfft;
st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r;
st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r;
C_FIXDIV(st->tmpbuf[0],2);
for (k = 1; k <= ncfft / 2; ++k) {
kiss_fft_cpx fk, fnkc, fek, fok, tmp;
fk = freqdata[k];
fnkc.r = freqdata[ncfft - k].r;
fnkc.i = -freqdata[ncfft - k].i;
C_FIXDIV( fk , 2 );
C_FIXDIV( fnkc , 2 );
C_ADD (fek, fk, fnkc);
C_SUB (tmp, fk, fnkc);
C_MUL (fok, tmp, st->super_twiddles[k-1]);
C_ADD (st->tmpbuf[k], fek, fok);
C_SUB (st->tmpbuf[ncfft - k], fek, fok);
#ifdef USE_SIMD
st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0);
#else
st->tmpbuf[ncfft - k].i *= -1;
#endif
}
kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata);
}

Wyświetl plik

@ -0,0 +1,54 @@
/*
* Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
*
* SPDX-License-Identifier: BSD-3-Clause
* See COPYING file for more information.
*/
#ifndef KISS_FTR_H
#define KISS_FTR_H
#include "kiss_fft.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
Real optimized version can save about 45% cpu time vs. complex fft of a real seq.
*/
typedef struct kiss_fftr_state *kiss_fftr_cfg;
kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem);
/*
nfft must be even
If you don't care to allocate space, use mem = lenmem = NULL
*/
void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata);
/*
input timedata has nfft scalar points
output freqdata has nfft/2+1 complex points
*/
void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata);
/*
input freqdata has nfft/2+1 complex points
output timedata has nfft scalar points
*/
#define kiss_fftr_free KISS_FFT_FREE
#ifdef __cplusplus
}
#endif
#endif

Wyświetl plik

@ -0,0 +1,394 @@
#include "constants.h"
//科斯塔阵列
const uint8_t kFT8CostasPattern[7] = { 3, 1, 4, 0, 6, 5, 2 };
const uint8_t kFT4CostasPattern[4][4] = {
{ 0, 1, 3, 2 },
{ 1, 0, 2, 3 },
{ 2, 3, 1, 0 },
{ 3, 2, 0, 1 }
};
// Gray code map (FTx bits -> channel symbols)
//格雷码
const uint8_t kFT8GrayMap[8] = { 0, 1, 3, 2, 5, 6, 4, 7 };
const uint8_t kFT4GrayMap[4] = { 0, 1, 3, 2 };
const uint8_t kFT4XORSequence[10] = {
0x4Au, // 01001010
0x5Eu, // 01011110
0x89u, // 10001001
0xB4u, // 10110100
0xB0u, // 10110000
0x8Au, // 10001010
0x79u, // 01111001
0x55u, // 01010101
0xBEu, // 10111110
0x28u, // 00101 [000]
};
// Parity generator matrix for (174,91) LDPC code, stored in bitpacked format (MSB first)
const uint8_t kFTXLDPCGenerator[FTX_LDPC_M][FTX_LDPC_K_BYTES] = {
{ 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 }
};
// Each row describes one LDPC parity check.
// Each number is an index into the codeword (1-origin).
// The codeword bits mentioned in each row must XOR to zero.
const uint8_t kFTX_LDPC_Nm[FTX_LDPC_M][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 }
};
// Each row corresponds to a codeword bit.
// The numbers indicate which three LDPC parity checks (rows in Nm) refer to the codeword bit.
// 1-origin.
const uint8_t kFTX_LDPC_Mn[FTX_LDPC_N][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 }
};
const uint8_t kFTX_LDPCNumRows[FTX_LDPC_M] = {
7, 6, 6, 6, 7, 6, 7, 6, 6, 7, 6, 6, 7, 7, 6, 6,
6, 7, 6, 7, 6, 7, 6, 6, 6, 7, 6, 6, 6, 7, 6, 6,
6, 6, 7, 6, 6, 6, 7, 7, 6, 6, 6, 6, 7, 7, 6, 6,
6, 6, 7, 6, 6, 6, 7, 6, 6, 6, 6, 7, 6, 6, 6, 7,
6, 6, 6, 7, 7, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 7,
6, 6, 6
};

Wyświetl plik

@ -0,0 +1,89 @@
#ifndef _INCLUDE_CONSTANTS_H_
#define _INCLUDE_CONSTANTS_H_
#include <stdint.h>
#include <time.h>
typedef enum
{
PROTO_FT4,
PROTO_FT8
} ftx_protocol_t;
#define kMin_score (10) /// 候选人的最低同步分数阈值。Minimum sync score threshold for candidates
#define FT8_SAMPLE_RATE (12000)//采样率
#define FT8_SYMBOL_PERIOD (0.160f) ///< FT8 symbol duration, defines tone deviation in Hz and symbol rate
#define FT8_SLOT_TIME (15.0f) ///< FT8 slot period
#define FT4_SYMBOL_PERIOD (0.048f) ///< FT4 symbol duration, defines tone deviation in Hz and symbol rate
#define FT4_SLOT_TIME (7.5f) ///< FT4 slot period
// Define FT8 symbol counts
// FT8 message structure:
// S D1 S D2 S
// S - sync block (7 symbols of Costas pattern)
// D1 - first data block (29 symbols each encoding 3 bits)
#define FT8_ND (58) ///< Data symbols
#define FT8_NN (79) ///< Total channel symbols (FT8_NS + FT8_ND)
#define FT8_LENGTH_SYNC (7) ///< Length of each sync group
#define FT8_NUM_SYNC (3) ///< Number of sync groups
#define FT8_SYNC_OFFSET (36) ///< Offset between sync groups
#define FT8_SYMBOL_BT (2.0f)
// Define FT4 symbol counts
// FT4 message structure:
// R Sa D1 Sb D2 Sc D3 Sd R
// R - ramping symbol (no payload information conveyed)
// Sx - one of four _different_ sync blocks (4 symbols of Costas pattern)
// Dy - data block (29 symbols each encoding 2 bits)
#define FT4_ND (87) ///< Data symbols
#define FT4_NR (2) ///< Ramp symbols (beginning + end)
#define FT4_NN (105) ///< Total channel symbols (FT4_NS + FT4_ND + FT4_NR)
#define FT4_LENGTH_SYNC (4) ///< Length of each sync group
#define FT4_NUM_SYNC (4) ///< Number of sync groups
#define FT4_SYNC_OFFSET (33) ///< Offset between sync groups
// Define LDPC parameters
#define FTX_LDPC_N (174) ///编码消息中的位数LDPC校验和位的有效负载< Number of bits in the encoded message (payload with LDPC checksum bits)
#define FTX_LDPC_K (91) ///< 有效负载位数包括CRCNumber of payload bits (including CRC)
#define FTX_LDPC_M (83) ///< Number of LDPC checksum bits (FTX_LDPC_N - FTX_LDPC_K)
#define FTX_LDPC_N_BYTES ((FTX_LDPC_N + 7) / 8) ///< Number of whole bytes needed to store 174 bits (full message)
#define FTX_LDPC_K_BYTES ((FTX_LDPC_K + 7) / 8) ///< 存储91位所需的整字节数仅限有效负载+CRCNumber of whole bytes needed to store 91 bits (payload + CRC only)
// Define CRC parameters
#define FT8_CRC_POLYNOMIAL ((uint16_t)0x2757u) ///< CRC-14 polynomial without the leading (MSB) 1
#define FT8_CRC_WIDTH (14)
#define DECODE_TIME_OUT (1) ///解码迭代时间超时(秒)
/// Costas 7x7 tone pattern for synchronization
extern const uint8_t kFT8CostasPattern[7];
extern const uint8_t kFT4CostasPattern[4][4];
/// Gray code map to encode 8 symbols (tones)
extern const uint8_t kFT8GrayMap[8];
extern const uint8_t kFT4GrayMap[4];
extern const uint8_t kFT4XORSequence[10];
/// Parity generator matrix for (174,91) LDPC code, stored in bitpacked format (MSB first)
extern const uint8_t kFTXLDPCGenerator[FTX_LDPC_M][FTX_LDPC_K_BYTES];
/// LDPC(174,91) parity check matrix, containing 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 kFTX_LDPC_Nm[FTX_LDPC_M][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.
/// The numbers use 1 as the origin (first entry).
extern const uint8_t kFTX_LDPC_Mn[FTX_LDPC_N][3];
/// Number of rows (columns in C/C++) in the array Nm.
extern const uint8_t kFTX_LDPCNumRows[FTX_LDPC_M];
#endif // _INCLUDE_CONSTANTS_H_

Wyświetl plik

@ -0,0 +1,63 @@
#include "crc.h"
#include "constants.h"
#define TOPBIT (1u << (FT8_CRC_WIDTH - 1))
// Compute 14-bit CRC for a sequence of given number of bits
// Adapted from https://barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code
// [IN] message - byte sequence (MSB first)
// [IN] num_bits - number of bits in the sequence
uint16_t ftx_compute_crc(const uint8_t message[], int num_bits)
{
uint16_t remainder = 0;
int idx_byte = 0;
// Perform modulo-2 division, a bit at a time.
for (int idx_bit = 0; idx_bit < num_bits; ++idx_bit)
{
if (idx_bit % 8 == 0)
{
// Bring the next byte into the remainder.
remainder ^= (message[idx_byte] << (FT8_CRC_WIDTH - 8));
++idx_byte;
}
// Try to divide the current data bit.
if (remainder & TOPBIT)
{
remainder = (remainder << 1) ^ FT8_CRC_POLYNOMIAL;
}
else
{
remainder = (remainder << 1);
}
}
return remainder & ((TOPBIT << 1) - 1u);
}
uint16_t ftx_extract_crc(const uint8_t a91[])
{
uint16_t chksum = ((a91[9] & 0x07) << 11) | (a91[10] << 3) | (a91[11] >> 5);
return chksum;
}
void ftx_add_crc(const uint8_t payload[], uint8_t a91[])
{
// Copy 77 bits of payload data
for (int i = 0; i < 10; i++)
a91[i] = payload[i];
// Clear 3 bits after the payload to make 82 bits
a91[9] &= 0xF8u;
a91[10] = 0;
// Calculate CRC of 82 bits (77 + 5 zeros)
// 'The CRC is calculated on the source-encoded message, zero-extended from 77 to 82 bits'
uint16_t checksum = ftx_compute_crc(a91, 96 - 14);
// Store the CRC at the end of 77 bit message
a91[9] |= (uint8_t)(checksum >> 11);
a91[10] = (uint8_t)(checksum >> 3);
a91[11] = (uint8_t)(checksum << 5);
}

Wyświetl plik

@ -0,0 +1,23 @@
#ifndef _INCLUDE_CRC_H_
#define _INCLUDE_CRC_H_
#include <stdint.h>
#include <stdbool.h>
// Compute 14-bit CRC for a sequence of given number of bits using FT8/FT4 CRC polynomial
// [IN] message - byte sequence (MSB first)
// [IN] num_bits - number of bits in the sequence
uint16_t ftx_compute_crc(const uint8_t message[], int num_bits);
/// Extract the FT8/FT4 CRC of a packed message (during decoding)
/// 提取压缩消息的FT8/FT4 CRC解码期间
/// @param[in] a91 77 bits of payload data + CRC
/// @return Extracted CRC
uint16_t ftx_extract_crc(const uint8_t a91[]);
/// Add FT8/FT4 CRC to a packed message (during encoding)
/// @param[in] payload 77 bits of payload data
/// @param[out] a91 91 bits of payload data + CRC
void ftx_add_crc(const uint8_t payload[], uint8_t a91[]);
#endif // _INCLUDE_CRC_H_

Wyświetl plik

@ -0,0 +1,628 @@
#include "decode.h"
#include "constants.h"
#include "crc.h"
#include "ldpc.h"
#include "unpack.h"
#include <stdbool.h>
#include <math.h>
#include "../common/debug.h"
#include "hash22.h"
/// Compute log likelihood log(p(1) / p(0)) of 174 message bits for later use in soft-decision LDPC decoding
/// @param[in] wf Waterfall data collected during message slot
/// @param[in] cand Candidate to extract the message from
/// @param[in] code_map Symbol encoding map
/// @param[out] log174 Output of decoded log likelihoods for each of the 174 message bits
static void ft4_extract_likelihood(const waterfall_t *wf, const candidate_t *cand, float *log174);
static void ft8_extract_likelihood(const waterfall_t *wf, candidate_t *cand, float *log174);
/// Packs a string of bits each represented as a zero/non-zero byte in bit_array[],
/// as a string of packed bits starting from the MSB of the first byte of packed[]
/// @param[in] plain Array of bits (0 and nonzero values) with num_bits entires
/// @param[in] num_bits Number of bits (entries) passed in bit_array
/// @param[out] packed Byte-packed bits representing the data in bit_array
static void pack_bits(const uint8_t bit_array[], int num_bits, uint8_t packed[]);
static float max2(float a, float b);
static float max4(float a, float b, float c, float d);
static void heapify_down(candidate_t heap[], int heap_size);
static void heapify_up(candidate_t heap[], int heap_size);
static void ftx_normalize_logl(float *log174);
static void ft4_extract_symbol(const uint8_t *wf, float *logl);
static void ft8_extract_symbol(const uint8_t *wf, float *logl);
static void
ft8_decode_multi_symbols(const uint8_t *wf, int num_bins, int n_syms, int bit_idx, float *log174);
static int get_index(const waterfall_t *wf, const candidate_t *candidate) {
int offset = candidate->time_offset;//time_offset:-12 ~ 23,(costas阵列7个符号+29个数据符号=36)
offset = (offset * wf->time_osr) + candidate->time_sub;//time_sub:0~1
offset = (offset * wf->freq_osr) + candidate->freq_sub;//freq_sub:0~1
offset = (offset * wf->num_bins) +
candidate->freq_offset;//num_bins:960freq_offset:0~ 960(-1) -7
return offset;
}
static int ft8_sync_score(const waterfall_t *wf, candidate_t *candidate) {
/*
* ft8580-736-4372-7979
*42*2*-12~24=36*num_bins:960-7
*/
int score = 0;
int num_average = 0;
float signal = 0;
float noise = 0;
// Get the pointer to symbol 0 of the candidate
//获取指向候选符号0的指针在mag数组中取candidate对应的mag数据。
const uint8_t *mag_cand = wf->mag + get_index(wf, candidate);
//用于信号量和噪音的计算,暂时注释掉
// const float *mag_signal = wf->mag2 + get_index(wf, candidate);
// Compute average score over sync symbols (m+k = 0-7, 36-43, 72-79)
//计算同步符号的平均分数m+k=0-7、36-43、72-79
//m=0~2(3组),k=0~6(7个符号)
for (int m = 0; m < FT8_NUM_SYNC; ++m) {
for (int k = 0; k < FT8_LENGTH_SYNC; ++k) {
//FT8_SYNC_OFFSET=36block=0..636~4372~79这是costas阵列在符号序列中的索引
int block = (FT8_SYNC_OFFSET * m) + k; // 相对于消息relative to the message
int block_abs =
//time_offset=-12.。2336个
candidate->time_offset + block; // 相对于捕获的信号relative to the captured signal
// Check for time boundaries
//检查时间界限
if (block_abs < 0)
continue;
if (block_abs >= wf->num_blocks)
break;
// Get the pointer to symbol 'block' of the candidate
//获取指向候选人符号“block”的指针
const uint8_t *p8 = mag_cand + (block * wf->block_stride);
// Weighted difference between the expected and all other symbols
//预期符号和所有其他符号之间的加权差
// Does not work as well as the alternative score below
//效果不如下面的备选分数
// score += 8 * p8[kFT8CostasPattern[k]] -
// p8[0] - p8[1] - p8[2] - p8[3] -
// p8[4] - p8[5] - p8[6] - p8[7];
// ++num_average;
// Check only the neighbors of the expected symbol frequency- and time-wise
//仅检查预期符号频率和时间的相邻项k=0..6
int sm = kFT8CostasPattern[k]; //预期数据的索引 Index of the expected bin
//此处计算信号量和噪音,可能不正确,暂时注释掉
// const float *p8Signal = mag_signal + (block * wf->block_stride);
//通过sm判断相邻频率的信号量是否小于本位置的信号量小于就加分
if (sm > 0) {
// look at one frequency bin lower
//信号量的差值。
score += p8[sm] - p8[sm - 1];
++num_average;
}
if (sm < 7) {
// look at one frequency bin higher
score += p8[sm] - p8[sm + 1];
++num_average;
}
//判断前后符号时间频率信号量是否小于本位置的信号量,小于就加分
if ((k > 0) && (block_abs > 0)) {
// look one symbol back in time
score += p8[sm] - p8[sm - wf->block_stride];
++num_average;
}
if (((k + 1) < FT8_LENGTH_SYNC) && ((block_abs + 1) < wf->num_blocks)) {
// look one symbol forward in time
score += p8[sm] - p8[sm + wf->block_stride];
++num_average;
}
}
}
if (num_average > 0) {
score /= num_average;
}
return score;
}
static int ft4_sync_score(const waterfall_t *wf, const candidate_t *candidate) {
int score = 0;
int num_average = 0;
// Get the pointer to symbol 0 of the candidate
const uint8_t *mag_cand = wf->mag + get_index(wf, candidate);
// Compute average score over sync symbols (block = 1-4, 34-37, 67-70, 100-103)
for (int m = 0; m < FT4_NUM_SYNC; ++m) {
for (int k = 0; k < FT4_LENGTH_SYNC; ++k) {
int block = 1 + (FT4_SYNC_OFFSET * m) + k;
int block_abs = candidate->time_offset + block;
// Check for time boundaries
if (block_abs < 0)
continue;
if (block_abs >= wf->num_blocks)
break;
// Get the pointer to symbol 'block' of the candidate
const uint8_t *p4 = mag_cand + (block * wf->block_stride);
int sm = kFT4CostasPattern[m][k]; // Index of the expected bin
// score += (4 * p4[sm]) - p4[0] - p4[1] - p4[2] - p4[3];
// num_average += 4;
// Check only the neighbors of the expected symbol frequency- and time-wise
if (sm > 0) {
// look at one frequency bin lower
score += p4[sm] - p4[sm - 1];
++num_average;
}
if (sm < 3) {
// look at one frequency bin higher
score += p4[sm] - p4[sm + 1];
++num_average;
}
if ((k > 0) && (block_abs > 0)) {
// look one symbol back in time
score += p4[sm] - p4[sm - wf->block_stride];
++num_average;
}
if (((k + 1) < FT4_LENGTH_SYNC) && ((block_abs + 1) < wf->num_blocks)) {
// look one symbol forward in time
score += p4[sm] - p4[sm + wf->block_stride];
++num_average;
}
}
}
if (num_average > 0)
score /= num_average;
return score;
}
//检测ft8信号num_candidates最大候选人数量=120heap[]候选人列表size=120kMin_score候选人的最低同步分数阈值=10
int ft8_find_sync(const waterfall_t *wf, int num_candidates, candidate_t heap[], int min_score) {
int heap_size = 0;
candidate_t candidate;//候选人
// Here we allow time offsets that exceed signal boundaries, as long as we still have all data bits.
// I.e. we can afford to skip the first 7 or the last 7 Costas symbols, as long as we track how many
// sync symbols we included in the score, so the score is averaged.
//在这里,我们允许超过信号边界的时间偏移,只要我们仍然拥有所有数据位。
//也就是说我们可以跳过前7个或最后7个Costas符号只要我们跟踪有多少个
//我们在分数中包含了同步符号,所以分数是平均值。
//循环:时间过采样*频率过采样*前36个符号7同步+29信息*fft频率偏移=2*2*36*960=3840*36=138240
for (candidate.time_sub = 0; candidate.time_sub < wf->time_osr; ++candidate.time_sub) {
for (candidate.freq_sub = 0; candidate.freq_sub < wf->freq_osr; ++candidate.freq_sub) {
for (candidate.time_offset = -12; candidate.time_offset < 24; ++candidate.time_offset) {
for (candidate.freq_offset = 0;
(candidate.freq_offset + 7) < wf->num_bins; ++candidate.freq_offset) {
if (wf->protocol == PROTO_FT4) {
candidate.score = ft4_sync_score(wf, &candidate);
} else {
candidate.score = ft8_sync_score(wf, &candidate);
}
if (candidate.score < min_score)
continue;
// If the heap is full AND the current candidate is better than
// the worst in the heap, we remove the worst and make space
//如果堆已满且当前候选堆优于在堆中最坏的,我们移除最坏的,并创造空间
if (heap_size == num_candidates && candidate.score > heap[0].score) {
heap[0] = heap[heap_size - 1];
--heap_size;
//降序?
heapify_down(heap, heap_size);
}
// If there's free space in the heap, we add the current candidate
//如果堆中有可用空间,我们将添加当前候选堆
if (heap_size < num_candidates) {
heap[heap_size] = candidate;
++heap_size;
//升序?
heapify_up(heap, heap_size);
}
}
}
}
}
// Sort the candidates by sync strength - here we benefit from the heap structure
int len_unsorted = heap_size;
while (len_unsorted > 1) {
candidate_t tmp = heap[len_unsorted - 1];
heap[len_unsorted - 1] = heap[0];
heap[0] = tmp;
len_unsorted--;
heapify_down(heap, len_unsorted);
}
return heap_size;
}
static void ft4_extract_likelihood(const waterfall_t *wf, const candidate_t *cand, float *log174) {
const uint8_t *mag_cand = wf->mag + get_index(wf, cand);
// Go over FSK tones and skip Costas sync symbols
for (int k = 0; k < FT4_ND; ++k) {
// Skip either 5, 9 or 13 sync symbols
// TODO: replace magic numbers with constants
int sym_idx = k + ((k < 29) ? 5 : ((k < 58) ? 9 : 13));
int bit_idx = 2 * k;
// Check for time boundaries
int block = cand->time_offset + sym_idx;
if ((block < 0) || (block >= wf->num_blocks)) {
log174[bit_idx + 0] = 0;
log174[bit_idx + 1] = 0;
} else {
// Pointer to 4 bins of the current symbol
const uint8_t *ps = mag_cand + (sym_idx * wf->block_stride);
ft4_extract_symbol(ps, log174 + bit_idx);
}
}
}
//解开可能的FT8信号
static void ft8_extract_likelihood(const waterfall_t *wf, candidate_t *cand, float *log174) {
const uint8_t *mag_cand = wf->mag + get_index(wf, cand);
////FT8总消息的为174位符号是174/3=58个加上同步costas阵列的7*3=21个符号共计58+21=79个符号。
//log174数组的大小是174
// Go over FSK tones and skip Costas sync symbols
//浏览FSK音调并跳过Costas同步符号所以log174
//FT8_ND=58k=0..57
for (int k = 0; k < FT8_ND; ++k) {
// Skip either 7 or 14 sync symbols
// TODO: replace magic numbers with constants
//sym_idx=7..35,43..71
int sym_idx = k + ((k < 29) ? 7 : 14);
//bit_idx符号位的索引
int bit_idx = 3 * k;
// Check for time boundaries
//检测时间边界
int block = cand->time_offset + sym_idx;
if ((block < 0) || (block >= wf->num_blocks)) {
log174[bit_idx + 0] = 0;
log174[bit_idx + 1] = 0;
log174[bit_idx + 2] = 0;
} else {
// Pointer to 8 bins of the current symbol
//指向当前符号信号量的8个箱子的指针
//block_stride=960*2*2=3840
const uint8_t *ps = mag_cand + (sym_idx * wf->block_stride);
//每个符号bit_idx是符号的3倍
ft8_extract_symbol(ps, log174 + bit_idx);
}
}
}
static void ftx_normalize_logl(float *log174) {
// Compute the variance of log174
//计算log174的方差
float sum = 0;
float sum2 = 0;
//FTX_LDPC_N=174
for (int i = 0; i < FTX_LDPC_N; ++i) {
sum += log174[i];//取和
sum2 += log174[i] * log174[i];//取平方和
}
float inv_n = 1.0f / FTX_LDPC_N;
//variance方差
float variance = (sum2 - (sum * sum * inv_n)) * inv_n;
// Normalize log174 distribution and scale it with experimentally found coefficient
////规范化log174分布并用实验发现的系数对其进行缩放
float norm_factor = sqrtf(24.0f / variance);
for (int i = 0; i < FTX_LDPC_N; ++i) {
log174[i] *= norm_factor;
}
}
//推算snr
static void ft8_guess_snr(const waterfall_t *wf, candidate_t *cand) {
const float *mag_signal = wf->mag2 + get_index(wf, cand);
float signal = 0, noise = 0;
for (int i = 0; i < 7; ++i) {
if ((cand->time_offset + i >= 0) && (cand->time_offset + i < wf->num_blocks + 8)) {
//LOG_PRINTF("End guess SNR 0...");
signal += mag_signal[(i) * wf->block_stride + kFT8CostasPattern[i]];
noise += mag_signal[(i) * wf->block_stride + ((kFT8CostasPattern[i] + 4) % 8)];
//LOG_PRINTF("End guess SNR 0... done");
}
if ((cand->time_offset + i + 36 >= 0) && (cand->time_offset + i < wf->num_blocks + 8)) {
//LOG_PRINTF("End guess SNR 36...");
signal += mag_signal[(i + 36) * wf->block_stride + kFT8CostasPattern[i]];
noise += mag_signal[(i + 36) * wf->block_stride + ((kFT8CostasPattern[i] + 4) % 8)];
//LOG_PRINTF("End guess SNR 36... done");
}
//此处容易产生数组下标越界的问题
// if ((cand->time_offset+i+72>=0)&&(cand->time_offset+i<wf->num_blocks+8)) {
// LOG_PRINTF("End guess SNR 72...");
// signal += mag_signal[(i + 72) * wf->block_stride + kFT8CostasPattern[i]];
// noise += mag_signal[(i + 72) * wf->block_stride + ((kFT8CostasPattern[i] + 4) % 8)];
// LOG_PRINTF("End guess SNR 72... done");
// }
}
//LOG(LOG_INFO, "Max magnitude:ft8_guess_snr 002\n");
if (noise != 0) {
float raw = signal / noise;
cand->snr = floor(10 * log10f(1E-12f + raw) - 24 + 0.5);
if (cand->snr < -30) {//-30是最小值了。
cand->snr = -30;
}
} else {
cand->snr = -100;
}
}
//max_iterations=20 LDPC的迭代次数。
bool
ft8_decode(waterfall_t *wf, candidate_t *cand, message_t *message, int max_iterations,
decode_status_t *status) {
//FT8总消息的为174位符号是174/3=58个加上同步costas阵列的7*3=21个符号共计58+21=79个符号。
//FTX_LDPC_N=174是把7*3个符号的位去掉后的数组
float log174[FTX_LDPC_N]; //编码为似然的消息位 message bits encoded as likelihood
if (wf->protocol == PROTO_FT4) {
ft4_extract_likelihood(wf, cand, log174);
} else {
//检测可能的FT8信号,结果在log174中,每3个为一组与8个格雷码为索引的信号量的平方差的值
ft8_extract_likelihood(wf, cand, log174);
}
//规范化
ftx_normalize_logl(log174);
//FTX_LDPC_N=174
uint8_t plain174[FTX_LDPC_N]; // message bits (0/1)
//bp_decode是原作者采用的ldpc_decode经测试也是可以用的。
//结果在plain174中以0和1为值。包括77位信息+14位冗余校验+83位前向纠错=174位。
//max_iterations是最大迭代次数越大速度越慢精度越高
bp_decode(log174, max_iterations, plain174, &status->ldpc_errors);
//ldpc_decode(log174, max_iterations, plain174, &status->ldpc_errors);
if (status->ldpc_errors > 0) {
return false;
}
// Extract payload + CRC (first FTX_LDPC_K bits) packed into a byte array
////提取压缩到字节数组中的有效负载+CRC第一个FTX\U LDPC\U K位
////FTX_LDPC_K_BYTES存储91位所需的整字节数仅限有效负载+CRC
////FTX_LDPC_K有效负载位数包括CRC
uint8_t a91[FTX_LDPC_K_BYTES];
//提取出91个位77位信息+14位冗余校验
pack_bits(plain174, FTX_LDPC_K, a91);
// Extract CRC and check it
////提取CRC并进行检查后面crc_extracted又作为hash值保存下来
status->crc_extracted = ftx_extract_crc(a91);
// [1]: 'The CRC is calculated on the source-encoded message, zero-extended from 77 to 82 bits.'
a91[9] &= 0xF8;
a91[10] &= 0x00;
status->crc_calculated = ftx_compute_crc(a91, 96 - 14);
if (status->crc_extracted != status->crc_calculated) {
return false;
}
if (wf->protocol == PROTO_FT4) {
// '[..] for FT4 only, in order to avoid transmitting a long string of zeros when sending CQ messages,
// the assembled 77-bit message is bitwise exclusive-ORed with [a] pseudorandom sequence before computing the CRC and FEC parity bits'
for (int i = 0; i < 10; ++i) {
a91[i] ^= kFT4XORSequence[i];
}
}
//从91位解包77位信息然后返回消息的文本内容。
//status->unpack_status = unpack77(a91, message->text);
message->call_to[0] = message->call_de[0] = message->maidenGrid[0] = message->extra[0] = '\0';
message->call_de_hash.hash10 = message->call_de_hash.hash12 = message->call_de_hash.hash22 = 0;
message->call_to_hash.hash10 = message->call_to_hash.hash12 = message->call_to_hash.hash22 = 0;
memcpy(message->a91, a91, FTX_LDPC_K_BYTES);//把数据包保存下来,用于音频相减
//LOG_PRINTF("hex:%0x %0x %0x %0x %0x %0x %0x %0x %0x %0x"
// ,a91[0],a91[1],a91[2],a91[3],a91[4],a91[5],a91[6],a91[7],a91[8],a91[9]);
status->unpack_status = unpackToMessage_t(a91, message);
//message->call_de_hash.hash12=hashcall(message->call_de,HASH_12) ;
if (status->unpack_status < 0) {
return false;
}
// Reuse binary message CRC as hash value for the message
//重用二进制消息CRC作为消息的哈希值
message->hash = status->crc_extracted;
//2022-05-13增加解析i3,n3
//解出i3和n3
// Extract i3 (bits 74..76)
//message->i3 = (a91[9] >> 3) & 0x07;
// Extract n3 (bits 71..73)
//message->n3 = ((a91[8] << 2) & 0x04) | ((a91[9] >> 6) & 0x03);
//推算信噪比
ft8_guess_snr(wf, cand);
return true;
}
static float max2(float a, float b) {
return (a >= b) ? a : b;
}
static float max4(float a, float b, float c, float d) {
return max2(max2(a, b), max2(c, d));
}
static void heapify_down(candidate_t heap[], int heap_size) {
// heapify from the root down
int current = 0;
while (true) {
int largest = current;
int left = 2 * current + 1;
int right = left + 1;
if (left < heap_size && heap[left].score < heap[largest].score) {
largest = left;
}
if (right < heap_size && heap[right].score < heap[largest].score) {
largest = right;
}
if (largest == current) {
break;
}
candidate_t tmp = heap[largest];
heap[largest] = heap[current];
heap[current] = tmp;
current = largest;
}
}
static void heapify_up(candidate_t heap[], int heap_size) {
// heapify from the last node up
int current = heap_size - 1;
while (current > 0) {
int parent = (current - 1) / 2;
if (heap[current].score >= heap[parent].score) {
break;
}
candidate_t tmp = heap[parent];
heap[parent] = heap[current];
heap[current] = tmp;
current = parent;
}
}
// Compute unnormalized log likelihood log(p(1) / p(0)) of 2 message bits (1 FSK symbol)
static void ft4_extract_symbol(const uint8_t *wf, float *logl) {
// Cleaned up code for the simple case of n_syms==1
float s2[4];
for (int j = 0; j < 4; ++j) {
s2[j] = (float) wf[kFT4GrayMap[j]];
}
logl[0] = max2(s2[2], s2[3]) - max2(s2[0], s2[1]);
logl[1] = max2(s2[1], s2[3]) - max2(s2[0], s2[2]);
}
// Compute unnormalized log likelihood log(p(1) / p(0)) of 3 message bits (1 FSK symbol)
//计算3个消息位1个FSK符号的非规范化对数似然对数log(p1/p0)
//wf当前符号的信号量的地址logl当前符号的位数组的地址。
static void ft8_extract_symbol(const uint8_t *wf, float *logl) {
// Cleaned up code for the simple case of n_syms==1
//清理了n_syms==1简单案例的代码
float s2[8];//信号强度数组,格雷码数组内容做偏移索引:{ 0, 1, 3, 2, 5, 6, 4, 7 }
for (int j = 0; j < 8; ++j) {
s2[j] = (float) wf[kFT8GrayMap[j]];//格雷码值作索引,对应信号的强度保存到
}
//信号量的值在之前已经是平方过的了相减实际上是log(p(1)/p(0))。
logl[0] = max4(s2[4], s2[5], s2[6], s2[7]) - max4(s2[0], s2[1], s2[2], s2[3]);
logl[1] = max4(s2[2], s2[3], s2[6], s2[7]) - max4(s2[0], s2[1], s2[4], s2[5]);
logl[2] = max4(s2[1], s2[3], s2[5], s2[7]) - max4(s2[0], s2[2], s2[4], s2[6]);
}
// Compute unnormalized log likelihood log(p(1) / p(0)) of bits corresponding to several FSK symbols at once
static void
ft8_decode_multi_symbols(const uint8_t *wf, int num_bins, int n_syms, int bit_idx, float *log174) {
const int n_bits = 3 * n_syms;
const int n_tones = (1 << n_bits);
float s2[n_tones];
for (int j = 0; j < n_tones; ++j) {
int j1 = j & 0x07;
if (n_syms == 1) {
s2[j] = (float) wf[kFT8GrayMap[j1]];
continue;
}
int j2 = (j >> 3) & 0x07;
if (n_syms == 2) {
s2[j] = (float) wf[kFT8GrayMap[j2]];
s2[j] += (float) wf[kFT8GrayMap[j1] + 4 * num_bins];
continue;
}
int j3 = (j >> 6) & 0x07;
s2[j] = (float) wf[kFT8GrayMap[j3]];
s2[j] += (float) wf[kFT8GrayMap[j2] + 4 * num_bins];
s2[j] += (float) wf[kFT8GrayMap[j1] + 8 * num_bins];
}
// Extract bit significance (and convert them to float)
// 8 FSK tones = 3 bits
for (int i = 0; i < n_bits; ++i) {
if (bit_idx + i >= FTX_LDPC_N) {
// Respect array size
break;
}
uint16_t mask = (n_tones >> (i + 1));
float max_zero = -1000, max_one = -1000;
for (int n = 0; n < n_tones; ++n) {
if (n & mask) {
max_one = max2(max_one, s2[n]);
} else {
max_zero = max2(max_zero, s2[n]);
}
}
log174[bit_idx + i] = max_one - max_zero;
}
}
// Packs a string of bits each represented as a zero/non-zero byte in plain[],
// as a string of packed bits starting from the MSB of the first byte of packed[]
static void pack_bits(const uint8_t bit_array[], int num_bits, uint8_t packed[]) {
int num_bytes = (num_bits + 7) / 8;
for (int i = 0; i < num_bytes; ++i) {
packed[i] = 0;
}
uint8_t mask = 0x80;
int byte_idx = 0;
for (int i = 0; i < num_bits; ++i) {
if (bit_array[i]) {
packed[byte_idx] |= mask;
}
mask >>= 1;
if (!mask) {
mask = 0x80;
++byte_idx;
}
}
}

Wyświetl plik

@ -0,0 +1,121 @@
#ifndef _INCLUDE_DECODE_H_
#define _INCLUDE_DECODE_H_
#include <stdint.h>
#include <stdbool.h>
#include "constants.h"
#include "../fft/kiss_fft.h"
#include "../common/debug.h"
/// Input structure to ft8_find_sync() function. This structure describes stored waterfall data over the whole message slot.
/// Fields time_osr and freq_osr specify additional oversampling rate for time and frequency resolution.
/// If time_osr=1, FFT magnitude data is collected once for every symbol transmitted, i.e. every 1/6.25 = 0.16 seconds.
/// Values time_osr > 1 mean each symbol is further subdivided in time.
/// If freq_osr=1, each bin in the FFT magnitude data corresponds to 6.25 Hz, which is the tone spacing.
/// Values freq_osr > 1 mean the tone spacing is further subdivided by FFT analysis.
/// ft8_find_sync函数的输入结构。这种结构描述了整个消息槽中存储的瀑布数据。
/// time_osr和freq_osr字段指定时间和频率分辨率的额外过采样率。
/// 如果time_osr=1则针对每个传输的符号收集一次FFT幅度数据即每1/6.25=0.16秒收集一次。FSK符号的时间长度是0.16秒。
/// 如果time_osr>1表示每个符号在时间上进一步过采样。
/// 如果freq_osr=1FFT幅度数据中的每个单元对应于6.25 Hz这是音调间隔FSK的符号时长
/// 如果freq_osr>1意味着通过FFT分析进一步对音调间隔过采样。
typedef struct
{
int max_blocks; ///< mag阵列中分配的块符号数。number of blocks (symbols) allocated in the mag array
int num_blocks; ///< mag阵列中存储的块符号编号时域序列号。number of blocks (symbols) stored in the mag array
//num_bins = 12000 * 0.16 / 2 = 960
int num_bins; ///< 以6.25 Hz为单位的FFT箱数量960。number of FFT bins in terms of 6.25 Hz
int time_osr; ///< 时间过采样率时间细分数。number of time subdivisions
int freq_osr; ///< 频率过采样率频率细分数。number of frequency subdivisions
uint8_t* mag; ///< FFT的magnitudes量级存储。FFT magnitudes stored as uint8_t[blocks][time_osr][freq_osr][num_bins]
int block_stride; ///< 块的步态没搞懂。Helper value = time_osr * freq_osr * num_bins
ftx_protocol_t protocol; ///< 协议。Indicate if using FT4 or FT8
float* mag2;///用于存储准确信号量的数组,这是个平方值,真正的值需要开方,为了速度,就不开方了,等后续再计算。
} waterfall_t;
/// Output structure of ft8_find_sync() and input structure of ft8_decode().
/// Holds the position of potential start of a message in time and frequency.
// 此结构是ft8_find_sync()的输出结构ft8_decode()的输入
// 在时间和频率上,保持消息的潜在起始位置。
typedef struct
{
int16_t score; ///< score 候选分数非负数分数越高表示可能性越大。Candidate score (non-negative number; higher score means higher likelihood)
int16_t time_offset; ///< 时间段索引。Index of the time block
int16_t freq_offset; ///< 频率段索引。Index of the frequency bin
uint8_t time_sub; ///< 所用时间细分的索引。Index of the time subdivision used
uint8_t freq_sub; ///< 所用频率细分的索引。Index of the frequency subdivision used
int snr;//信噪比
} candidate_t;
/// Structure that holds the decoded message
typedef struct {
uint32_t hash22;
uint32_t hash12;
uint32_t hash10;
} hashCode;
// 保存已解码消息的结构
typedef struct
{
//2022-05-13增加i3和n3
uint8_t i3;
uint8_t n3;
// TODO: check again that this size is enough
//char text[25]; ///< 纯文本Plain text原文是25
char text[48]; ///<但在在unpack.c中unpack77函数的最大可能是14+14+19=
uint16_t hash; ///用于对消息hash防止消息重复< Hash value to be used in hash table and quick checking for duplicates
//2022-05-26新增以下内容
char call_to[14];//被呼叫的呼号
char call_de[14];//发起的呼号
char extra[19];//扩展内容
//---TODO-------------
char maidenGrid[5];//梅登海德
int report;//信号报告
hashCode call_to_hash;//22位长度的哈希码
hashCode call_de_hash;//22位长度的哈希码
//TO HASH , FROM HASH
uint8_t a91[FTX_LDPC_K_BYTES];//用于生成减法代码的数据
} message_t;
/// Structure that contains the status of various steps during decoding of a message
/// 包含消息解码过程中各个步骤的状态的结构
typedef struct
{
int ldpc_errors; ///< 解码期间的LDPC稀疏校验矩阵错误数。Number of LDPC errors during decoding
uint16_t crc_extracted; ///< 从消息中恢复的CRC值。CRC value recovered from the message
uint16_t crc_calculated; ///< 在有效负载上计算的CRC值。CRC value calculated over the payload
int unpack_status; ///< 解包例程的返回值。Return value of the unpack routine
} decode_status_t;
/// Localize top N candidates in frequency and time according to their sync strength (looking at Costas symbols)
/// 根据同步强度查看Costas符号在频率和时间上对前N名候选人进行本地化。
/// We treat and organize the candidate list as a min-heap (empty initially).
/// 我们将候选列表视为一个最小堆(最初为空)。
/// @param[in] power 在消息槽期间收集的瀑布数据。Waterfall data collected during message slot
/// @param[in] sync_pattern 同步模式。Synchronization pattern
/// @param[in] num_candidates 最大候选数量堆数组大小。Number of maximum candidates (size of heap array)
/// @param[in,out] heap 候选项类型的数组分配了num个候选项。Array of candidate_t type entries (with num_candidates allocated entries)
/// @param[in] min_score 删减不太可能的候选项所允许的最低分数可以为零没有效果。Minimal score allowed for pruning unlikely candidates (can be zero for no effect)
/// @return 堆中填写的候选人数。Number of candidates filled in the heap
int ft8_find_sync(const waterfall_t* power, int num_candidates, candidate_t heap[], int min_score);
/// Attempt to decode a message candidate. Extracts the bit probabilities, runs LDPC decoder, checks CRC and unpacks the message in plain text.
/// 尝试解码候选消息。提取比特概率运行LDPC解码器检查CRC并将消息解压为纯文本。
/// @param[in] power 在消息槽期间收集的瀑布数据。Waterfall data collected during message slot
/// @param[in] cand 要解码的候选人。Candidate to decode
/// @param[out] message 将接收解码消息的message_t结构。message_t structure that will receive the decoded message
/// @param[in] max_iterations 允许的最大LDPC迭代次数数字越小解码速度越快但精度越低。Maximum allowed LDPC iterations (lower number means faster decode, but less precise)
/// @param[out] status decode_status_t结构该结构将填充各种解码步骤的状态。decode_status_t structure that will be filled with the status of various decoding steps
/// @return 如果解码成功则为True否则为false查看状态了解详细信息。True if the decoding was successful, false otherwise (check status for details)
bool ft8_decode(waterfall_t* power, candidate_t* cand, message_t* message, int max_iterations, decode_status_t* status);
#endif // _INCLUDE_DECODE_H_

Wyświetl plik

@ -0,0 +1,197 @@
#include "encode.h"
#include "constants.h"
#include "crc.h"
#include <stdio.h>
#include "../common/debug.h"
// Returns 1 if an odd number of bits are set in x, zero otherwise
static uint8_t parity8(uint8_t x)
{
x ^= x >> 4; // a b c d ae bf cg dh
x ^= x >> 2; // a b ac bd cae dbf aecg bfdh
x ^= x >> 1; // a ab bac acbd bdcae caedbf aecgbfdh
return x % 2; // modulo 2
}
// Encode via LDPC a 91-bit message and return a 174-bit codeword.
// The generator matrix has dimensions (87,87).
// The code is a (174,91) regular LDPC code with column weight 3.
// Arguments:
// [IN] message - array of 91 bits stored as 12 bytes (MSB first)
// [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first)
static void encode174(const uint8_t* message, uint8_t* codeword)
{
// This implementation accesses the generator bits straight from the packed binary representation in kFTXLDPCGenerator
// Fill the codeword with message and zeros, as we will only update binary ones later
for (int j = 0; j < FTX_LDPC_N_BYTES; ++j)
{
codeword[j] = (j < FTX_LDPC_K_BYTES) ? message[j] : 0;
}
// Compute the byte index and bit mask for the first checksum bit
uint8_t col_mask = (0x80u >> (FTX_LDPC_K % 8u)); // bitmask of current byte
uint8_t col_idx = FTX_LDPC_K_BYTES - 1; // index into byte array
// Compute the LDPC checksum bits and store them in codeword
for (int i = 0; i < FTX_LDPC_M; ++i)
{
// Fast implementation of bitwise multiplication and parity checking
// Normally nsum would contain the result of dot product between message and kFTXLDPCGenerator[i],
// but we only compute the sum modulo 2.
uint8_t nsum = 0;
for (int j = 0; j < FTX_LDPC_K_BYTES; ++j)
{
uint8_t bits = message[j] & kFTXLDPCGenerator[i][j]; // bitwise AND (bitwise multiplication)
nsum ^= parity8(bits); // bitwise XOR (addition modulo 2)
}
// Set the current checksum bit in codeword if nsum is odd
if (nsum % 2)
{
codeword[col_idx] |= col_mask;
}
// Update the byte index and bit mask for the next checksum bit
col_mask >>= 1;
if (col_mask == 0)
{
col_mask = 0x80u;
++col_idx;
}
}
}
void ft8_encode(const uint8_t* payload, uint8_t* tones)
{
uint8_t a91[FTX_LDPC_K_BYTES]; // Store 77 bits of payload + 14 bits CRC
// Compute and add CRC at the end of the message
// a91 contains 77 bits of payload + 14 bits of CRC
ftx_add_crc(payload, a91);
uint8_t codeword[FTX_LDPC_N_BYTES];
encode174(a91, codeword);
// Message structure: S7 D29 S7 D29 S7
// Total symbols: 79 (FT8_NN)
uint8_t mask = 0x80u; // Mask to extract 1 bit from codeword
int i_byte = 0; // Index of the current byte of the codeword
for (int i_tone = 0; i_tone < FT8_NN; ++i_tone)
{
if ((i_tone >= 0) && (i_tone < 7))
{
tones[i_tone] = kFT8CostasPattern[i_tone];
}
else if ((i_tone >= 36) && (i_tone < 43))
{
tones[i_tone] = kFT8CostasPattern[i_tone - 36];
}
else if ((i_tone >= 72) && (i_tone < 79))
{
tones[i_tone] = kFT8CostasPattern[i_tone - 72];
}
else
{
// Extract 3 bits from codeword at i-th position
uint8_t bits3 = 0;
if (codeword[i_byte] & mask)
bits3 |= 4;
if (0 == (mask >>= 1))
{
mask = 0x80u;
i_byte++;
}
if (codeword[i_byte] & mask)
bits3 |= 2;
if (0 == (mask >>= 1))
{
mask = 0x80u;
i_byte++;
}
if (codeword[i_byte] & mask)
bits3 |= 1;
if (0 == (mask >>= 1))
{
mask = 0x80u;
i_byte++;
}
tones[i_tone] = kFT8GrayMap[bits3];
}
}
}
void ft4_encode(const uint8_t* payload, uint8_t* tones)
{
uint8_t a91[FTX_LDPC_K_BYTES]; // Store 77 bits of payload + 14 bits CRC
uint8_t payload_xor[10]; // Encoded payload data
// '[..] for FT4 only, in order to avoid transmitting a long string of zeros when sending CQ messages,
// the assembled 77-bit message is bitwise exclusive-ORed with [a] pseudorandom sequence before computing the CRC and FEC parity bits'
for (int i = 0; i < 10; ++i)
{
payload_xor[i] = payload[i] ^ kFT4XORSequence[i];
}
// Compute and add CRC at the end of the message
// a91 contains 77 bits of payload + 14 bits of CRC
ftx_add_crc(payload_xor, a91);
uint8_t codeword[FTX_LDPC_N_BYTES];
encode174(a91, codeword); // 91 bits -> 174 bits
// Message structure: R S4_1 D29 S4_2 D29 S4_3 D29 S4_4 R
// Total symbols: 105 (FT4_NN)
uint8_t mask = 0x80u; // Mask to extract 1 bit from codeword
int i_byte = 0; // Index of the current byte of the codeword
for (int i_tone = 0; i_tone < FT4_NN; ++i_tone)
{
if ((i_tone == 0) || (i_tone == 104))
{
tones[i_tone] = 0; // R (ramp) symbol
}
else if ((i_tone >= 1) && (i_tone < 5))
{
tones[i_tone] = kFT4CostasPattern[0][i_tone - 1];
}
else if ((i_tone >= 34) && (i_tone < 38))
{
tones[i_tone] = kFT4CostasPattern[1][i_tone - 34];
}
else if ((i_tone >= 67) && (i_tone < 71))
{
tones[i_tone] = kFT4CostasPattern[2][i_tone - 67];
}
else if ((i_tone >= 100) && (i_tone < 104))
{
tones[i_tone] = kFT4CostasPattern[3][i_tone - 100];
}
else
{
// Extract 2 bits from codeword at i-th position
uint8_t bits2 = 0;
if (codeword[i_byte] & mask)
bits2 |= 2;
if (0 == (mask >>= 1))
{
mask = 0x80u;
i_byte++;
}
if (codeword[i_byte] & mask)
bits2 |= 1;
if (0 == (mask >>= 1))
{
mask = 0x80u;
i_byte++;
}
tones[i_tone] = kFT4GrayMap[bits2];
}
}
}

Wyświetl plik

@ -0,0 +1,32 @@
#ifndef _INCLUDE_ENCODE_H_
#define _INCLUDE_ENCODE_H_
#include <stdint.h>
// typedef struct
// {
// uint8_t tones[FT8_NN];
// // for waveform readout:
// int n_spsym; // Number of waveform samples per symbol
// float *pulse; // [3 * n_spsym]
// int idx_symbol; // Index of the current symbol
// float f0; // Base frequency, Hertz
// float signal_rate; // Waveform sample rate, Hertz
// } encoder_t;
// void encoder_init(float signal_rate, float *pulse_buffer);
// void encoder_set_f0(float f0);
// void encoder_process(const message_t *message); // in: message
// void encoder_generate(float *block); // out: block of waveforms
/// Generate FT8 tone sequence from payload data
/// @param[in] payload - 10 byte array consisting of 77 bit payload
/// @param[out] tones - array of FT8_NN (79) bytes to store the generated tones (encoded as 0..7)
void ft8_encode(const uint8_t* payload, uint8_t* tones);
/// Generate FT4 tone sequence from payload data
/// @param[in] payload - 10 byte array consisting of 77 bit payload
/// @param[out] tones - array of FT4_NN (105) bytes to store the generated tones (encoded as 0..3)
void ft4_encode(const uint8_t* payload, uint8_t* tones);
#endif // _INCLUDE_ENCODE_H_

Wyświetl plik

@ -0,0 +1,65 @@
#include "hash22.h"
#include "stdlib.h"
#include "string.h"
//m为hash的长度1222
//call的长度是12包括'\0'
uint32_t hashcall(char* call, int m)
{
const char *chars = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/";
char callsign[11]=" ";
char *temp=call;
int j=0;
while(temp[0] == ' '){
++temp;
if (temp[0]!=' ')
{
break;
}
j++;
}
for (int i = 0; i < 11-j; i++)
{
if (temp[i]=='\0')
{
break;
}else{
callsign[i]=temp[i];
}
}
uint64_t x = 0;
for(int i = 0; i < 11; i++){
int c = (int)callsign[i];
const char *p = strchr(chars, c);
if (p==NULL)
{
return 0;
}
int j = p - chars;
x = 38*x + j;
}
x = x * 47055833459LL;
x = x >> (64 - m);
return x;
}
uint32_t hashcall_10(char* call){
return hashcall(call,HASH_10);
}
uint32_t hashcall_12(char* call){
return hashcall(call,HASH_12);
}
uint32_t hashcall_22(char* call){
return hashcall(call,HASH_22);
}

Wyświetl plik

@ -0,0 +1,11 @@
#include <stdint.h>
#define HASH_10 (10) ///哈希码长度为10
#define HASH_12 (12) ///哈希码长度为12
#define HASH_22 (22) ///哈希码长度为12
uint32_t hashcall(char* call, int m);
uint32_t hashcall_10(char* call);//返回长度是10的哈希码
uint32_t hashcall_12(char* call);//返回长度是12的哈希码
uint32_t hashcall_22(char* call);//返回长度是22的哈希码

Wyświetl plik

@ -0,0 +1,268 @@
//
// LDPC decoder for FT8.
//
// given a 174-bit codeword as an array of log-likelihood of zero,
// return a 174-bit corrected codeword, or zero-length array.
// last 87 bits are the (systematic) plain-text.
// this is an implementation of the sum-product algorithm
// from Sarah Johnson's Iterative Error Correction book.
// codeword[i] = log ( P(x=0) / P(x=1) )
//
////将174位码字作为对数似然为零的数组
////返回一个174位已更正的码字或零长度数组。
////最后87位是系统纯文本。
////这是和积算法的一个实现
////来自Sarah Johnson的迭代纠错手册。
////码字[i]=对数Px=0/Px=1
#include "ldpc.h"
#include "constants.h"
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stdbool.h>
static int ldpc_check(uint8_t codeword[]);
static float fast_tanh(float x);
static float fast_atanh(float x);
// codeword is 174 log-likelihoods.
// plain is a return value, 174 ints, to be 0 or 1.
// max_iters is how hard to try.
// ok == 87 means success.
void ldpc_decode(float codeword[], int max_iters, uint8_t plain[], int* ok)
{
float m[FTX_LDPC_M][FTX_LDPC_N]; // ~60 kB
float e[FTX_LDPC_M][FTX_LDPC_N]; // ~60 kB
int min_errors = FTX_LDPC_M;
for (int j = 0; j < FTX_LDPC_M; j++)
{
for (int i = 0; i < FTX_LDPC_N; i++)
{
m[j][i] = codeword[i];
e[j][i] = 0.0f;
}
}
for (int iter = 0; iter < max_iters; iter++)
{
for (int j = 0; j < FTX_LDPC_M; j++)
{
for (int ii1 = 0; ii1 < kFTX_LDPCNumRows[j]; ii1++)
{
int i1 = kFTX_LDPC_Nm[j][ii1] - 1;
float a = 1.0f;
for (int ii2 = 0; ii2 < kFTX_LDPCNumRows[j]; ii2++)
{
int i2 = kFTX_LDPC_Nm[j][ii2] - 1;
if (i2 != i1)
{
a *= fast_tanh(-m[j][i2] / 2.0f);
}
}
e[j][i1] = -2.0f * fast_atanh(a);
}
}
for (int i = 0; i < FTX_LDPC_N; i++)
{
float l = codeword[i];
for (int j = 0; j < 3; j++)
l += e[kFTX_LDPC_Mn[i][j] - 1][i];
plain[i] = (l > 0) ? 1 : 0;
}
int errors = ldpc_check(plain);
if (errors < min_errors)
{
// Update the current best result
min_errors = errors;
if (errors == 0)
{
break; // Found a perfect answer
}
}
for (int i = 0; i < FTX_LDPC_N; i++)
{
for (int ji1 = 0; ji1 < 3; ji1++)
{
int j1 = kFTX_LDPC_Mn[i][ji1] - 1;
float l = codeword[i];
for (int ji2 = 0; ji2 < 3; ji2++)
{
if (ji1 != ji2)
{
int j2 = kFTX_LDPC_Mn[i][ji2] - 1;
l += e[j2][i];
}
}
m[j1][i] = l;
}
}
}
*ok = min_errors;
}
//
// does a 174-bit codeword pass the FT8's LDPC parity checks?
// returns the number of parity errors.
// 0 means total success.
//
static int ldpc_check(uint8_t codeword[])
{
int errors = 0;
for (int m = 0; m < FTX_LDPC_M; ++m)
{
uint8_t x = 0;
for (int i = 0; i < kFTX_LDPCNumRows[m]; ++i)
{
x ^= codeword[kFTX_LDPC_Nm[m][i] - 1];
}
if (x != 0)
{
++errors;
}
}
return errors;
}
//// 码字是174个对数可能性。
//// plain是一个返回值174 整数为0或1。
//// max_iters是迭代次数。
//// ok==87表示成功。好像不是哦==0才是
void bp_decode(float codeword[], int max_iters, uint8_t plain[], int* ok)
{
float tov[FTX_LDPC_N][3];
float toc[FTX_LDPC_M][7];
//FTX_LDPC_M=83
int min_errors = FTX_LDPC_M;
// initialize message data
//FTX_LDPC_N=174
for (int n = 0; n < FTX_LDPC_N; ++n)
{
tov[n][0] = tov[n][1] = tov[n][2] = 0;
}
for (int iter = 0; iter < max_iters; ++iter)
{
// Do a hard decision guess (tov=0 in iter 0)
int plain_sum = 0;
for (int n = 0; n < FTX_LDPC_N; ++n)
{//转换成0和1
plain[n] = ((codeword[n] + tov[n][0] + tov[n][1] + tov[n][2]) > 0) ? 1 : 0;
plain_sum += plain[n];
}
if (plain_sum == 0)
{
// message converged to all-zeros, which is prohibited
//消息聚合到所有零,这是禁止的
break;
}
// Check to see if we have a codeword (check before we do any iter)
//向LDPC(稀疏校验矩阵)检测LDPC矩阵是预定义的83行91列的矩阵
int errors = ldpc_check(plain);
if (errors < min_errors)
{
// we have a better guess - update the result
min_errors = errors;
if (errors == 0)
{
break; // Found a perfect answer
}
}
// Send messages from bits to check nodes
for (int m = 0; m < FTX_LDPC_M; ++m)
{
for (int n_idx = 0; n_idx < kFTX_LDPCNumRows[m]; ++n_idx)
{
int n = kFTX_LDPC_Nm[m][n_idx] - 1;
// for each (n, m)
float Tnm = codeword[n];
for (int m_idx = 0; m_idx < 3; ++m_idx)
{
if ((kFTX_LDPC_Mn[n][m_idx] - 1) != m)
{
Tnm += tov[n][m_idx];
}
}
toc[m][n_idx] = fast_tanh(-Tnm / 2);
}
}
// send messages from check nodes to variable nodes
for (int n = 0; n < FTX_LDPC_N; ++n)
{
for (int m_idx = 0; m_idx < 3; ++m_idx)
{
int m = kFTX_LDPC_Mn[n][m_idx] - 1;
// for each (n, m)
float Tmn = 1.0f;
for (int n_idx = 0; n_idx < kFTX_LDPCNumRows[m]; ++n_idx)
{
if ((kFTX_LDPC_Nm[m][n_idx] - 1) != n)
{
Tmn *= toc[m][n_idx];
}
}
tov[n][m_idx] = -2 * fast_atanh(Tmn);
}
}
}
*ok = min_errors;
}
// Ideas for approximating tanh/atanh:
// * https://varietyofsound.wordpress.com/2011/02/14/efficient-tanh-computation-using-lamberts-continued-fraction/
// * http://functions.wolfram.com/ElementaryFunctions/ArcTanh/10/0001/
// * https://mathr.co.uk/blog/2017-09-06_approximating_hyperbolic_tangent.html
// * https://math.stackexchange.com/a/446411
static float fast_tanh(float x)
{
if (x < -4.97f)
{
return -1.0f;
}
if (x > 4.97f)
{
return 1.0f;
}
float x2 = x * x;
// float a = x * (135135.0f + x2 * (17325.0f + x2 * (378.0f + x2)));
// float b = 135135.0f + x2 * (62370.0f + x2 * (3150.0f + x2 * 28.0f));
// float a = x * (10395.0f + x2 * (1260.0f + x2 * 21.0f));
// float b = 10395.0f + x2 * (4725.0f + x2 * (210.0f + x2));
float a = x * (945.0f + x2 * (105.0f + x2));
float b = 945.0f + x2 * (420.0f + x2 * 15.0f);
return a / b;
}
static float fast_atanh(float x)
{
float x2 = x * x;
// float a = x * (-15015.0f + x2 * (19250.0f + x2 * (-5943.0f + x2 * 256.0f)));
// float b = (-15015.0f + x2 * (24255.0f + x2 * (-11025.0f + x2 * 1225.0f)));
// float a = x * (-1155.0f + x2 * (1190.0f + x2 * -231.0f));
// float b = (-1155.0f + x2 * (1575.0f + x2 * (-525.0f + x2 * 25.0f)));
float a = x * (945.0f + x2 * (-735.0f + x2 * 64.0f));
float b = (945.0f + x2 * (-1050.0f + x2 * 225.0f));
return a / b;
}

Wyświetl plik

@ -0,0 +1,20 @@
#ifndef _INCLUDE_LDPC_H_
#define _INCLUDE_LDPC_H_
#include <stdint.h>
#include "../common/debug.h"
//// codeword is 174 log-likelihoods.
//// plain is a return value, 174 ints, to be 0 or 1.
//// iters is how hard to try.
//// ok == 87 means success.
//// 码字是174个对数可能性。
//// plain是一个返回值174 整数为0或1。
//// max_iters是迭代次数。
//// ok==87表示成功。??好像不是哦,==0才是
void ldpc_decode(float codeword[], int max_iters, uint8_t plain[], int* ok);
void bp_decode(float codeword[], int max_iters, uint8_t plain[], int* ok);
#endif // _INCLUDE_LDPC_H_

Wyświetl plik

@ -0,0 +1,373 @@
#include "pack.h"
#include "text.h"
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "../common/debug.h"
#define NTOKENS ((uint32_t)2063592L)
#define MAX22 ((uint32_t)4194304L)
#define MAXGRID4 ((uint16_t)32400)
const char A0[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?";
const char A1[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const char A2[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const char A3[] = "0123456789";
const char A4[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Pack a special token, a 22-bit hash code, or a valid base call
// into a 28-bit integer.
int32_t pack28(const char* callsign)
{
// Check for special tokens first
if (starts_with(callsign, "DE "))
return 0;
if (starts_with(callsign, "QRZ "))
return 1;
if (starts_with(callsign, "CQ "))
return 2;
if (starts_with(callsign, "CQ_"))
{
int nnum = 0, nlet = 0;
// TODO:
}
// TODO: Check for <...> callsign
char c6[6] = { ' ', ' ', ' ', ' ', ' ', ' ' };
int length = 0; // strlen(callsign); // We will need it later
while (callsign[length] != ' ' && callsign[length] != 0)
{
length++;
}
// Copy callsign to 6 character buffer
if (starts_with(callsign, "3DA0") && length <= 7)
{
// Work-around for Swaziland prefix: 3DA0XYZ -> 3D0XYZ
memcpy(c6, "3D0", 3);
memcpy(c6 + 3, callsign + 4, length - 4);
}
else if (starts_with(callsign, "3X") && is_letter(callsign[2]) && length <= 7)
{
// Work-around for Guinea prefixes: 3XA0XYZ -> QA0XYZ
memcpy(c6, "Q", 1);
memcpy(c6 + 1, callsign + 2, length - 2);
}
else
{
if (is_digit(callsign[2]) && length <= 6)
{
// AB0XYZ
memcpy(c6, callsign, length);
}
else if (is_digit(callsign[1]) && length <= 5)
{
// A0XYZ -> " A0XYZ"
memcpy(c6 + 1, callsign, length);
}
}
// Check for standard callsign
int i0, i1, i2, i3, i4, i5;
if ((i0 = char_index(A1, c6[0])) >= 0 && (i1 = char_index(A2, c6[1])) >= 0 && (i2 = char_index(A3, c6[2])) >= 0 && (i3 = char_index(A4, c6[3])) >= 0 && (i4 = char_index(A4, c6[4])) >= 0 && (i5 = char_index(A4, c6[5])) >= 0)
{
// This is a standard callsign
int32_t n28 = i0;
n28 = n28 * 36 + i1;
n28 = n28 * 10 + i2;
n28 = n28 * 27 + i3;
n28 = n28 * 27 + i4;
n28 = n28 * 27 + i5;
return NTOKENS + MAX22 + n28;
}
//char text[13];
//if (length > 13) return -1;
// TODO:
// Treat this as a nonstandard callsign: compute its 22-bit hash
return -1;
}
// Check if a string could be a valid standard callsign or a valid
// compound callsign.
// Return base call "bc" and a logical "cok" indicator.
bool chkcall(const char* call, char* bc)
{
int length = strlen(call); // n1=len_trim(w)
if (length > 11)
return false;
if (0 != strchr(call, '.'))
return false;
if (0 != strchr(call, '+'))
return false;
if (0 != strchr(call, '-'))
return false;
if (0 != strchr(call, '?'))
return false;
if (length > 6 && 0 != strchr(call, '/'))
return false;
// TODO: implement suffix parsing (or rework?)
return true;
}
uint16_t packgrid(const char* grid4)
{
if (grid4 == 0)
{
// Two callsigns only, no report/grid
return MAXGRID4 + 1;
}
// Take care of special cases
if (equals(grid4, "RRR"))
return MAXGRID4 + 2;
if (equals(grid4, "RR73"))
return MAXGRID4 + 3;
if (equals(grid4, "73"))
return MAXGRID4 + 4;
// Check for standard 4 letter grid
if (in_range(grid4[0], 'A', 'R') && in_range(grid4[1], 'A', 'R') && is_digit(grid4[2]) && is_digit(grid4[3]))
{
uint16_t igrid4 = (grid4[0] - 'A');
igrid4 = igrid4 * 18 + (grid4[1] - 'A');
igrid4 = igrid4 * 10 + (grid4[2] - '0');
igrid4 = igrid4 * 10 + (grid4[3] - '0');
return igrid4;
}
// Parse report: +dd / -dd / R+dd / R-dd
// TODO: check the range of dd
if (grid4[0] == 'R')
{
int dd = dd_to_int(grid4 + 1, 3);
uint16_t irpt = 35 + dd;
return (MAXGRID4 + irpt) | 0x8000; // ir = 1
}
else
{
int dd = dd_to_int(grid4, 3);
uint16_t irpt = 35 + dd;
return (MAXGRID4 + irpt); // ir = 0
}
return MAXGRID4 + 1;
}
// Pack Type 1 (Standard 77-bit message) and Type 2 (ditto, with a "/P" call)
int pack77_1(const char* msg, uint8_t* b77)
{
// Locate the first delimiter
const char* s1 = strchr(msg, ' ');
if (s1 == 0)
return -1;
const char* call1 = msg; // 1st call
const char* call2 = s1 + 1; // 2nd call
LOG(LOG_DEBUG,"call1 :%s", call1);
LOG(LOG_DEBUG,"call2 :%s", call2);
int32_t n28a = pack28(call1);
int32_t n28b = pack28(call2);
LOG(LOG_DEBUG,"n28a %2X",n28a);
LOG(LOG_DEBUG,"n28b %2X",n28b);
if (n28a < 0 || n28b < 0)
return -1;
uint16_t igrid4;
// Locate the second delimiter
const char* s2 = strchr(s1 + 1, ' ');
if (s2 != 0)
{
LOG(LOG_DEBUG,"GRID: %s",s2);
igrid4 = packgrid(s2 + 1);
}
else
{
// Two callsigns, no grid/report
igrid4 = packgrid(0);
}
LOG(LOG_DEBUG,"G15: %x",igrid4);
uint8_t i3 = 1; // No suffix or /R
// TODO: check for suffixes
// Shift in ipa and ipb bits into n28a and n28b
n28a <<= 1; // ipa = 0
n28b <<= 1; // ipb = 0
// Pack into (28 + 1) + (28 + 1) + (1 + 15) + 3 bits
b77[0] = (n28a >> 21);
b77[1] = (n28a >> 13);
b77[2] = (n28a >> 5);
b77[3] = (uint8_t)(n28a << 3) | (uint8_t)(n28b >> 26);
b77[4] = (n28b >> 18);
b77[5] = (n28b >> 10);
b77[6] = (n28b >> 2);
b77[7] = (uint8_t)(n28b << 6) | (uint8_t)(igrid4 >> 10);
b77[8] = (igrid4 >> 2);
b77[9] = (uint8_t)(igrid4 << 6) | (uint8_t)(i3 << 3);
return 0;
}
void packtext77(const char* text, uint8_t* b77)
{
int length = strlen(text);
// Skip leading and trailing spaces
while (*text == ' ' && *text != 0)
{
++text;
--length;
}
while (length > 0 && text[length - 1] == ' ')
{
--length;
}
// Clear the first 72 bits representing a long number
for (int i = 0; i < 9; ++i)
{
b77[i] = 0;
}
// Now express the text as base-42 number stored
// in the first 72 bits of b77
for (int j = 0; j < 13; ++j)
{
// Multiply the long integer in b77 by 42
uint16_t x = 0;
for (int i = 8; i >= 0; --i)
{
x += b77[i] * (uint16_t)42;
b77[i] = (x & 0xFF);
x >>= 8;
}
// Get the index of the current char
if (j < length)
{
int q = char_index(A0, text[j]);
x = (q > 0) ? q : 0;
}
else
{
x = 0;
}
// Here we double each added number in order to have the result multiplied
// by two as well, so that it's a 71 bit number left-aligned in 72 bits (9 bytes)
x <<= 1;
// Now add the number to our long number
for (int i = 8; i >= 0; --i)
{
if (x == 0)
break;
x += b77[i];
b77[i] = (x & 0xFF);
x >>= 8;
}
}
// Set n3=0 (bits 71..73) and i3=0 (bits 74..76)
b77[8] &= 0xFE;
b77[9] &= 0x00;
}
int pack77(const char* msg, uint8_t* c77)
{
// Check Type 1 (Standard 77-bit message) or Type 2, with optional "/P"
if (0 == pack77_1(msg, c77))
{
return 0;
}
// TODO:
// Check 0.5 (telemetry)
// Check Type 4 (One nonstandard call and one hashed call)
// Default to free text
// i3=0 n3=0
packtext77(msg, c77);
return 0;
}
#ifdef UNIT_TEST
#include <iostream>
bool test1()
{
const char* inputs[] = {
"",
" ",
"ABC",
"A9",
"L9A",
"L7BC",
"L0ABC",
"LL3JG",
"LL3AJG",
"CQ ",
0
};
for (int i = 0; inputs[i]; ++i)
{
int32_t result = ft8_v2::pack28(inputs[i]);
printf("pack28(\"%s\") = %d\n", inputs[i], result);
}
return true;
}
bool test2()
{
const char* inputs[] = {
"CQ LL3JG",
"CQ LL3JG KO26",
"L0UAA LL3JG KO26",
"L0UAA LL3JG +02",
"L0UAA LL3JG RRR",
"L0UAA LL3JG 73",
0
};
for (int i = 0; inputs[i]; ++i)
{
uint8_t result[10];
int rc = ft8_v2::pack77_1(inputs[i], result);
printf("pack77_1(\"%s\") = %d\t[", inputs[i], rc);
for (int j = 0; j < 10; ++j)
{
printf("%02x ", result[j]);
}
printf("]\n");
}
return true;
}
int main()
{
test1();
test2();
return 0;
}
#endif

Wyświetl plik

@ -0,0 +1,12 @@
#ifndef _INCLUDE_PACK_H_
#define _INCLUDE_PACK_H_
#include <stdint.h>
// Pack FT8 text message into 72 bits
// [IN] msg - FT8 message (e.g. "CQ TE5T KN01")
// [OUT] c77 - 10 byte array to store the 77 bit payload (MSB first)
int pack77(const char* msg, uint8_t* c77);
void packtext77(const char* text, uint8_t* b77);
#endif // _INCLUDE_PACK_H_

Wyświetl plik

@ -0,0 +1,253 @@
#include "text.h"
#include <string.h>
const char* trim_front(const char* str)
{
// Skip leading whitespace
while (*str == ' ')
{
str++;
}
return str;
}
void trim_back(char* str)
{
// Skip trailing whitespace by replacing it with '\0' characters
int idx = strlen(str) - 1;
while (idx >= 0 && str[idx] == ' ')
{
str[idx--] = '\0';
}
}
// 1) trims a string from the back by changing whitespaces to '\0'
// 2) trims a string from the front by skipping whitespaces
char* trim(char* str)
{
str = (char*)trim_front(str);
trim_back(str);
// return a pointer to the first non-whitespace character
return str;
}
char to_upper(char c)
{
return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c;
}
bool is_digit(char c)
{
return (c >= '0') && (c <= '9');
}
bool is_letter(char c)
{
return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'));
}
bool is_space(char c)
{
return (c == ' ');
}
bool in_range(char c, char min, char max)
{
return (c >= min) && (c <= max);
}
bool starts_with(const char* string, const char* prefix)
{
return 0 == memcmp(string, prefix, strlen(prefix));
}
bool equals(const char* string1, const char* string2)
{
return 0 == strcmp(string1, string2);
}
int char_index(const char* string, char c)
{
for (int i = 0; *string; ++i, ++string)
{
if (c == *string)
{
return i;
}
}
return -1; // Not found
}
// Text message formatting:
// - replaces lowercase letters with uppercase
// - merges consecutive spaces into single space
void fmtmsg(char* msg_out, const char* msg_in)
{
char c;
char last_out = 0;
while ((c = *msg_in))
{
if (c != ' ' || last_out != ' ')
{
last_out = to_upper(c);
*msg_out = last_out;
++msg_out;
}
++msg_in;
}
*msg_out = 0; // Add zero termination
}
// Parse a 2 digit integer from string
int dd_to_int(const char* str, int length)
{
int result = 0;
bool negative;
int i;
if (str[0] == '-')
{
negative = true;
i = 1; // Consume the - sign
}
else
{
negative = false;
i = (str[0] == '+') ? 1 : 0; // Consume a + sign if found
}
while (i < length)
{
if (str[i] == 0)
break;
if (!is_digit(str[i]))
break;
result *= 10;
result += (str[i] - '0');
++i;
}
return negative ? -result : result;
}
// Convert a 2 digit integer to string
void int_to_dd(char* str, int value, int width, bool full_sign)
{
if (value < 0)
{
*str = '-';
++str;
value = -value;
}
else if (full_sign)
{
*str = '+';
++str;
}
int divisor = 1;
for (int i = 0; i < width - 1; ++i)
{
divisor *= 10;
}
while (divisor >= 1)
{
int digit = value / divisor;
*str = '0' + digit;
++str;
value -= digit * divisor;
divisor /= 10;
}
*str = 0; // Add zero terminator
}
// convert integer index to ASCII character according to one of 6 tables:
// table 0: " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"
// table 1: " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
// table 2: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
// table 3: "0123456789"
// table 4: " ABCDEFGHIJKLMNOPQRSTUVWXYZ"
// table 5: " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/"
char charn(int c, int table_idx)
{
if (table_idx != 2 && table_idx != 3)
{
if (c == 0)
return ' ';
c -= 1;
}
if (table_idx != 4)
{
if (c < 10)
return '0' + c;
c -= 10;
}
if (table_idx != 3)
{
if (c < 26)
return 'A' + c;
c -= 26;
}
if (table_idx == 0)
{
if (c < 5)
return "+-./?"[c];
}
else if (table_idx == 5)
{
if (c == 0)
return '/';
}
return '_'; // unknown character, should never get here
}
// Convert character to its index (charn in reverse) according to a table
int nchar(char c, int table_idx)
{
int n = 0;
if (table_idx != 2 && table_idx != 3)
{
if (c == ' ')
return n + 0;
n += 1;
}
if (table_idx != 4)
{
if (c >= '0' && c <= '9')
return n + (c - '0');
n += 10;
}
if (table_idx != 3)
{
if (c >= 'A' && c <= 'Z')
return n + (c - 'A');
n += 26;
}
if (table_idx == 0)
{
if (c == '+')
return n + 0;
if (c == '-')
return n + 1;
if (c == '.')
return n + 2;
if (c == '/')
return n + 3;
if (c == '?')
return n + 4;
}
else if (table_idx == 5)
{
if (c == '/')
return n + 0;
}
// Character not found
return -1;
}

Wyświetl plik

@ -0,0 +1,37 @@
#ifndef _INCLUDE_TEXT_H_
#define _INCLUDE_TEXT_H_
#include <stdbool.h>
#include <stdint.h>
// Utility functions for characters and strings
const char* trim_front(const char* str);
void trim_back(char* str);
char* trim(char* str);
char to_upper(char c);
bool is_digit(char c);
bool is_letter(char c);
bool is_space(char c);
bool in_range(char c, char min, char max);
bool starts_with(const char* string, const char* prefix);
bool equals(const char* string1, const char* string2);
int char_index(const char* string, char c);
// Text message formatting:
// - replaces lowercase letters with uppercase
// - merges consecutive spaces into single space
void fmtmsg(char* msg_out, const char* msg_in);
// Parse a 2 digit integer from string
int dd_to_int(const char* str, int length);
// Convert a 2 digit integer to string
void int_to_dd(char* str, int value, int width, bool full_sign);
char charn(int c, int table_idx);
int nchar(char c, int table_idx);
#endif // _INCLUDE_TEXT_H_

Wyświetl plik

@ -0,0 +1,626 @@
#ifdef __linux__
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#endif
#include "unpack.h"
#include "text.h"
// #include <stdio.h>//为防止警告
#include <string.h>
#include "hash22.h"
#define MAX22 ((uint32_t)4194304L)
#define NTOKENS ((uint32_t)2063592L)
#define MAXGRID4 ((uint16_t)32400L)
// n28 is a 28-bit integer, e.g. n28a or n28b, containing all the
// call sign bits from a packed message.
int unpack_callsign(uint32_t n28, uint8_t ip, uint8_t i3, char *result,hashCode * hash) {
// Check for special tokens DE, QRZ, CQ, CQ_nnn, CQ_aaaa
hash->hash10=0;
hash->hash12=0;
hash->hash22=0;
if (n28 < NTOKENS) {
if (n28 <= 2) {
if (n28 == 0)
strcpy(result, "DE");
if (n28 == 1)
strcpy(result, "QRZ");
if (n28 == 2)
strcpy(result, "CQ");
return 0; // Success
}
if (n28 <= 1002) {
// CQ_nnn with 3 digits
strcpy(result, "CQ ");
int_to_dd(result + 3, n28 - 3, 3, false);
return 0; // Success
}
if (n28 <= 532443L) {
// CQ_aaaa with 4 alphanumeric symbols
uint32_t n = n28 - 1003;
char aaaa[5];
aaaa[4] = '\0';
for (int i = 3; /* */; --i) {
aaaa[i] = charn(n % 27, 4);
if (i == 0)
break;
n /= 27;
}
strcpy(result, "CQ ");
strcat(result, trim_front(aaaa));
return 0; // Success
}
// ? TODO: unspecified in the WSJT-X code
return -1;
}
n28 = n28 - NTOKENS;
if (n28 < MAX22) {
hash->hash10=n28;//把哈希值保存下来
hash->hash12=n28;//把哈希值保存下来
hash->hash22=n28;//把哈希值保存下来
LOG_PRINTF("N28 HASH: %0x",n28);
// This is a 22-bit hash of a result
// TODO: implement
strcpy(result, "<...>");
// result[0] = '<';
// int_to_dd(result + 1, n28, 7, false);
// result[8] = '>';
// result[9] = '\0';
return 0;
}
// Standard callsign
uint32_t n = n28 - MAX22;
char callsign[7];
callsign[6] = '\0';
callsign[5] = charn(n % 27, 4);
n /= 27;
callsign[4] = charn(n % 27, 4);
n /= 27;
callsign[3] = charn(n % 27, 4);
n /= 27;
callsign[2] = charn(n % 10, 3);
n /= 10;
callsign[1] = charn(n % 36, 2);
n /= 36;
callsign[0] = charn(n % 37, 1);
// Skip trailing and leading whitespace in case of a short callsign
//短呼号时跳过尾随和前导空格
strcpy(result, trim(callsign));
if (strlen(result) == 0)
return -1;
hash->hash10=hashcall_10(result);//对呼号进行22位的哈希
hash->hash12=hashcall_12(result);//对呼号进行22位的哈希
hash->hash22=hashcall_22(result);//对呼号进行22位的哈希
// Check if we should append /R or /P suffix
//检查是否应附加/R或/P后缀
if (ip) {
if (i3 == 1) {
strcat(result, "/R");
} else if (i3 == 2) {
strcat(result, "/P");
}
}
return 0; // Success
}
//int unpack_type1(const uint8_t *a77, uint8_t i3, char *call_to, char *call_de, char *extra) {
// uint32_t n28a, n28b;
// uint16_t igrid4;
// uint8_t ir;
//
// // Extract packed fields
// n28a = (a77[0] << 21);
// n28a |= (a77[1] << 13);
// n28a |= (a77[2] << 5);
// n28a |= (a77[3] >> 3);
// n28b = ((a77[3] & 0x07) << 26);
// n28b |= (a77[4] << 18);
// n28b |= (a77[5] << 10);
// n28b |= (a77[6] << 2);
// n28b |= (a77[7] >> 6);
// ir = ((a77[7] & 0x20) >> 5);
// igrid4 = ((a77[7] & 0x1F) << 10);
// igrid4 |= (a77[8] << 2);
// igrid4 |= (a77[9] >> 6);
//
// // Unpack both callsigns
// if (unpack_callsign(n28a >> 1, n28a & 0x01, i3, call_to) < 0) {
// return -1;
// }
// if (unpack_callsign(n28b >> 1, n28b & 0x01, i3, call_de) < 0) {
// return -2;
// }
// // Fix "CQ_" to "CQ " -> already done in unpack_callsign()
//
// // TODO: add to recent calls
// // if (call_to[0] != '<' && strlen(call_to) >= 4) {
// // save_hash_call(call_to)
// // }
// // if (call_de[0] != '<' && strlen(call_de) >= 4) {
// // save_hash_call(call_de)
// // }
//
// char *dst = extra;
//
// if (igrid4 <= MAXGRID4) {
// // Extract 4 symbol grid locator
// if (ir > 0) {
// // In case of ir=1 add an "R" before grid
// //dst = stpcpy(dst, "R ");//除错
// dst = strcpy(dst, "R ");
// dst += 3;
// }
//
// uint16_t n = igrid4;
// dst[4] = '\0';
// dst[3] = '0' + (n % 10);
// n /= 10;
// dst[2] = '0' + (n % 10);
// n /= 10;
// dst[1] = 'A' + (n % 18);
// n /= 18;
// dst[0] = 'A' + (n % 18);
// // if (ir > 0 && strncmp(call_to, "CQ", 2) == 0) return -1;
// } else {
// // Extract report
// int irpt = igrid4 - MAXGRID4;
//
// // Check special cases first (irpt > 0 always)
// switch (irpt) {
// case 1:
// extra[0] = '\0';
// break;
// case 2:
// strcpy(dst, "RRR");
// break;
// case 3:
// strcpy(dst, "RR73");
// break;
// case 4:
// strcpy(dst, "73");
// break;
// default:
// // Extract signal report as a two digit number with a + or - sign
// if (ir > 0) {
// *dst++ = 'R'; // Add "R" before report
// }
// int_to_dd(dst, irpt - 35, 2, true);
// break;
// }
// // if (irpt >= 2 && strncmp(call_to, "CQ", 2) == 0) return -1;
// }
//
// return 0; // Success
//}
int unpack_type1_(const uint8_t *a77, message_t *message) {
uint32_t n28a, n28b;
uint16_t igrid4;
uint8_t ir;
// Extract packed fields
n28a = (a77[0] << 21);
n28a |= (a77[1] << 13);
n28a |= (a77[2] << 5);
n28a |= (a77[3] >> 3);
n28b = ((a77[3] & 0x07) << 26);
n28b |= (a77[4] << 18);
n28b |= (a77[5] << 10);
n28b |= (a77[6] << 2);
n28b |= (a77[7] >> 6);
ir = ((a77[7] & 0x20) >> 5);
igrid4 = ((a77[7] & 0x1F) << 10);
igrid4 |= (a77[8] << 2);
igrid4 |= (a77[9] >> 6);
// Unpack both callsigns
if (unpack_callsign(n28a >> 1, n28a & 0x01, message->i3, message->call_to,&message->call_to_hash) < 0) {
return -1;
}
if (unpack_callsign(n28b >> 1, n28b & 0x01, message->i3, message->call_de,&message->call_de_hash) < 0) {
return -2;
}
// Fix "CQ_" to "CQ " -> already done in unpack_callsign()
// TODO: add to recent calls
// if (call_to[0] != '<' && strlen(call_to) >= 4) {
// save_hash_call(call_to)
// }
// if (call_de[0] != '<' && strlen(call_de) >= 4) {
// save_hash_call(call_de)
// }
char *dst = message->extra;
message->report=-100;//-100说明没有信号报告
message->maidenGrid[0]='\0';
if (igrid4 <= MAXGRID4) {
// // 解码4字符的梅登海德网格网格数据优先于信号报告
if (ir > 0) {
// In case of ir=1 add an "R" before grid
dst = strcpy(dst, "R ");
dst += 3;
}
uint16_t n = igrid4;
dst[4] = '\0';
dst[3] = '0' + (n % 10);
n /= 10;
dst[2] = '0' + (n % 10);
n /= 10;
dst[1] = 'A' + (n % 18);
n /= 18;
dst[0] = 'A' + (n % 18);
strcpy(message->maidenGrid, dst);//把网格内容复制出来
// if (ir > 0 && strncmp(call_to, "CQ", 2) == 0) return -1;
} else {
// 解码信号报告,数值-35是报告。
// 网格数据优先于信号报告
message->report = igrid4 - MAXGRID4 - 35;
// Check special cases first (irpt > 0 always)
switch (message->report) {
case 1 - 35:
message->extra[0] = '\0';
break;
case 2 - 35:
strcpy(dst, "RRR");
break;
case 3 - 35:
strcpy(dst, "RR73");
break;
case 4 - 35:
strcpy(dst, "73");
break;
default:
// Extract signal report as a two digit number with a + or - sign
if (ir > 0) {
*dst++ = 'R'; // Add "R" before report
}
int_to_dd(dst, message->report, 2, true);
break;
}
// if (irpt >= 2 && strncmp(call_to, "CQ", 2) == 0) return -1;
}
return 0; // Success
}
int unpack_text(const uint8_t *a71, char *text) {
// TODO: test
uint8_t b71[9];
// Shift 71 bits right by 1 bit, so that it's right-aligned in the byte array
uint8_t carry = 0;
for (int i = 0; i < 9; ++i) {
b71[i] = carry | (a71[i] >> 1);
carry = (a71[i] & 1) ? 0x80 : 0;
}
char c14[14];
c14[13] = 0;
for (int idx = 12; idx >= 0; --idx) {
// Divide the long integer in b71 by 42
uint16_t rem = 0;
for (int i = 0; i < 9; ++i) {
rem = (rem << 8) | b71[i];
b71[i] = rem / 42;
rem = rem % 42;
}
c14[idx] = charn(rem, 0);
}
strcpy(text, trim(c14));
return 0; // Success
}
int unpack_telemetry(const uint8_t *a71, char *telemetry) {
uint8_t b71[9];
// Shift bits in a71 right by 1 bit
uint8_t carry = 0;
for (int i = 0; i < 9; ++i) {
b71[i] = (carry << 7) | (a71[i] >> 1);
carry = (a71[i] & 0x01);
}
// Convert b71 to hexadecimal string
for (int i = 0; i < 9; ++i) {
uint8_t nibble1 = (b71[i] >> 4);
uint8_t nibble2 = (b71[i] & 0x0F);
char c1 = (nibble1 > 9) ? (nibble1 - 10 + 'A') : nibble1 + '0';
char c2 = (nibble2 > 9) ? (nibble2 - 10 + 'A') : nibble2 + '0';
telemetry[i * 2] = c1;
telemetry[i * 2 + 1] = c2;
}
telemetry[18] = '\0';
return 0;
}
//none standard for wsjt-x 2.0
//by KD8CEC
int unpack_nonstandard(const uint8_t *a77, message_t *message) {
uint32_t n12, iflip, nrpt, icq;
uint64_t n58;
n12 = (a77[0] << 4); //11 ~4 : 8
n12 |= (a77[1] >> 4); //3~0 : 12
uint32_t h12=a77[0];
n58 = ((uint64_t) (a77[1] & 0x0F) << 54); //57 ~ 54 : 4
n58 |= ((uint64_t) a77[2] << 46); //53 ~ 46 : 12
n58 |= ((uint64_t) a77[3] << 38); //45 ~ 38 : 12
n58 |= ((uint64_t) a77[4] << 30); //37 ~ 30 : 12
n58 |= ((uint64_t) a77[5] << 22); //29 ~ 22 : 12
n58 |= ((uint64_t) a77[6] << 14); //21 ~ 14 : 12
n58 |= ((uint64_t) a77[7] << 6); //13 ~ 6 : 12
n58 |= ((uint64_t) a77[8] >> 2); //5 ~ 0 : 765432 10
iflip = (a77[8] >> 1) & 0x01; //76543210
nrpt = ((a77[8] & 0x01) << 1);
nrpt |= (a77[9] >> 7); //76543210
icq = ((a77[9] >> 6) & 0x01);
if (iflip==1){//h1==1
message->call_de_hash.hash12=n12;
} else{
message->call_to_hash.hash12=n12;
}
char c11[12];
c11[11] = '\0';
for (int i = 10; /* no condition */; --i) {
c11[i] = charn(n58 % 38, 5);
if (i == 0)
break;
n58 /= 38;
}
char call_3[15];
// should replace with hash12(n12, call_3);
strcpy(call_3, "<...>");
// call_3[0] = '<';
// int_to_dd(call_3 + 1, n12, 4, false);
// call_3[5] = '>';
// call_3[6] = '\0';
char *call_1 = (iflip) ? c11 : call_3;
char *call_2 = (iflip) ? call_3 : c11;
//save_hash_call(c11_trimmed);
if (icq == 0) {
strcpy(message->call_to, trim(call_1));
if (nrpt == 1)
strcpy(message->extra, "RRR");
else if (nrpt == 2)
strcpy(message->extra, "RR73");
else if (nrpt == 3)
strcpy(message->extra, "73");
else {
message->extra[0] = '\0';
}
} else {
strcpy(message->call_to, "CQ");
message->extra[0] = '\0';
}
strcpy(message->call_de, trim(call_2));
return 0;
}
//int unpack77_fields(const uint8_t *a77, char *call_to, char *call_de, char *extra) {
// call_to[0] = call_de[0] = extra[0] = '\0';
//
// // Extract i3 (bits 74..76)
// uint8_t i3 = (a77[9] >> 3) & 0x07;
//
// if (i3 == 0) {
// // Extract n3 (bits 71..73)
// uint8_t n3 = ((a77[8] << 2) & 0x04) | ((a77[9] >> 6) & 0x03);
//
// if (n3 == 0) {
// // 0.0 Free text
// return unpack_text(a77, extra);
// }
// // else if (i3 == 0 && n3 == 1) {
// // // 0.1 K1ABC RR73; W9XYZ <KH1/KH7Z> -11 28 28 10 5 71 DXpedition Mode
// // }
// // else if (i3 == 0 && n3 == 2) {
// // // 0.2 PA3XYZ/P R 590003 IO91NP 28 1 1 3 12 25 70 EU VHF contest
// // }
// // else if (i3 == 0 && (n3 == 3 || n3 == 4)) {
// // // 0.3 WA9XYZ KA1ABC R 16A EMA 28 28 1 4 3 7 71 ARRL Field Day
// // // 0.4 WA9XYZ KA1ABC R 32A EMA 28 28 1 4 3 7 71 ARRL Field Day
// // }
// else if (n3 == 5) {
// // 0.5 0123456789abcdef01 71 71 Telemetry (18 hex)
// return unpack_telemetry(a77, extra);
// }
// } else if (i3 == 1 || i3 == 2) {
// // Type 1 (standard message) or Type 2 ("/P" form for EU VHF contest)
// return unpack_type1(a77, i3, call_to, call_de, extra);
// }
// // else if (i3 == 3) {
// // // Type 3: ARRL RTTY Contest
// // }
// else if (i3 == 4) {
// // // Type 4: Nonstandard calls, e.g. <WA9XYZ> PJ4/KA1ABC RR73
// // // One hashed call or "CQ"; one compound or nonstandard call with up
// // // to 11 characters; and (if not "CQ") an optional RRR, RR73, or 73.
// return unpack_nonstandard(a77, call_to, call_de, extra);
// }
// // else if (i3 == 5) {
// // // Type 5: TU; W9XYZ K1ABC R-09 FN 1 28 28 1 7 9 74 WWROF contest
// // }
//
// // unknown type, should never get here
// return -1;
//}
//
//int unpack77(const uint8_t* a77, char* message)
//{
// char call_to[14];
// char call_de[14];
// char extra[19];
//
// //// TO-DO:在此处修改。把Message的格式修改成i3,n3,from,to,extra方便消息显示。
// int rc = unpack77_fields(a77, call_to, call_de, extra);
// if (rc < 0)
// return rc;
//
// // int msg_sz = strlen(call_to) + strlen(call_de) + strlen(extra) + 2;
// char* dst = message;
//
// dst[0] = '\0';
//
// if (call_to[0] != '\0')
// {
// // dst = stpcpy(dst, call_to); //除错
// dst = strcpy(dst, call_to);
// dst +=strlen(call_to);
// *dst++ = ' ';
// }
//
// if (call_de[0] != '\0')
// {
// // dst = stpcpy(dst, call_de);//除错
// dst = strcpy(dst, call_de);
// dst +=strlen(call_de);
// *dst++ = ' ';
// }
//
// // dst = stpcpy(dst, extra);//除错
// dst = strcpy(dst, extra);
// dst += strlen(extra);
// *dst = '\0';
//
// return 0;
//}
int unpack77_fields_(const uint8_t *a77, message_t *message) {
//获取指令类型i3.n3
message->i3 = (a77[9] >> 3) & 0x07;
message->n3 = 0;
if (message->i3 == 0) {
//n3指令
message->n3 = ((a77[8] << 2) & 0x04) | ((a77[9] >> 6) & 0x03);
if (message->n3 == 0) {
// 0.0 Free text
return unpack_text(a77, message->extra);
}
// else if (i3 == 0 && n3 == 1) {
// // 0.1 K1ABC RR73; W9XYZ <KH1/KH7Z> -11 28 28 10 5 71 DXpedition Mode
// }
// else if (i3 == 0 && n3 == 2) {
// // 0.2 PA3XYZ/P R 590003 IO91NP 28 1 1 3 12 25 70 EU VHF contest
// }
// else if (i3 == 0 && (n3 == 3 || n3 == 4)) {
// // 0.3 WA9XYZ KA1ABC R 16A EMA 28 28 1 4 3 7 71 ARRL Field Day
// // 0.4 WA9XYZ KA1ABC R 32A EMA 28 28 1 4 3 7 71 ARRL Field Day
// }
else if (message->n3 == 5) {
// 0.5 0123456789abcdef01 71 71 Telemetry (18 hex)
return unpack_telemetry(a77, message->extra);
}
} else if (message->i3 == 1 || message->i3 == 2) {
// Type 1 (standard message) or Type 2 ("/P" form for EU VHF contest)
return unpack_type1_(a77, message);
}
// else if (i3 == 3) {
// // Type 3: ARRL RTTY Contest
// }
else if (message->i3 == 4) {
// // Type 4: Nonstandard calls, e.g. <WA9XYZ> PJ4/KA1ABC RR73
// // One hashed call or "CQ"; one compound or nonstandard call with up
// // to 11 characters; and (if not "CQ") an optional RRR, RR73, or 73.
//return unpack_nonstandard(a77, message->call_to, message->call_de, message->extra);
return unpack_nonstandard(a77, message);
}
// else if (i3 == 5) {
// // Type 5: TU; W9XYZ K1ABC R-09 FN 1 28 28 1 7 9 74 WWROF contest
// }
// unknown type, should never get here
return -1;
}
//把77位消息解码到message_t
int unpackToMessage_t(const uint8_t *a77, message_t *message) {
//char call_to[14];
//char call_de[14];
//char extra[19];
//// TO-DO:在此处修改。把Message的格式修改成i3,n3,from,to,extra方便消息显示。
int rc = unpack77_fields_(a77, message);
if (rc < 0)
return rc;
// int msg_sz = strlen(call_to) + strlen(call_de) + strlen(extra) + 2;
char *dst = message->text;
message->text[0] = '\0';
if (message->call_to[0] != '\0') {
// dst = stpcpy(dst, call_to); //除错
dst = strcpy(dst, message->call_to);
dst += strlen(message->call_to);
*dst++ = ' ';
}
if (message->call_de[0] != '\0') {
// dst = stpcpy(dst, call_de);//除错
dst = strcpy(dst, message->call_de);
dst += strlen(message->call_de);
*dst++ = ' ';
}
// dst = stpcpy(dst, extra);//除错
dst = strcpy(dst, message->extra);
dst += strlen(message->extra);
*dst = '\0';
return 0;
}

Wyświetl plik

@ -0,0 +1,19 @@
#ifndef _INCLUDE_UNPACK_H_
#define _INCLUDE_UNPACK_H_
#include <stdint.h>
#include "decode.h"
// field1 - at least 14 bytes
// field2 - at least 14 bytes
// field3 - at least 7 bytes
//int unpack77_fields(const uint8_t* a77, char* field1, char* field2, char* field3);
// message should have at least 35 bytes allocated (34 characters + zero terminator)
/// 消息应至少分配35个字节34个字符+零终止符)
int unpack77(const uint8_t* a77, char* message);
//新增的函数把消息解码成message_t
int unpackToMessage_t(const uint8_t* a77, message_t* message);
#endif // _INCLUDE_UNPACK_H_

Wyświetl plik

@ -0,0 +1,292 @@
//
// Created by jmsmf on 2022/4/24.
//
#include "ft8Decoder.h"
#define LOG_LEVEL LOG_INFO
//decoder_t decoder;
// Hanning窗汉宁窗适用于95%的情况。
static float hann_i(int i, int N) {
float x = sinf((float) M_PI * i / N);
return x * x;
}
//把信号FFT,在解码decoder中减去信号
void signalToFFT(decoder_t *decoder, float signal[], int sample_rate) {
int nfft = kFreq_osr * (int) (sample_rate * FT8_SYMBOL_PERIOD);//nfft=一个FSK符号的样本数*频率过采样率
float fft_norm = 2.0f / nfft;//< FFT归一化因子。FFT normalization factor
float *window = (float *) malloc(
nfft * sizeof(window[0]));// 申请窗空间大小是fft块大小*sizeof(me->windows[0])
for (int i = 0; i < nfft; ++i) {
window[i] = hann_i(i, nfft);// 使用Hanning窗
// window[i] = blackman_i(i, me->nfft);// Blackman-Harris窗
// window[i] = hamming_i(i, me->nfft);// Hamming窗
// window[i] = (i < len_window) ? hann_i(i, len_window) : 0;
}
// 申请当前STFT(短时傅氏变换)分析框架nfft样本的空间。
//last_frame:申请傅里叶变换分析框架用的nfft样本
// 空间大小=时域数据量*数据类型占用的空间=一个FSK符号占用的采样数据量*频率过采样率=12000*0.16*2*SizeOf(float)
float *last_frame = (float *) malloc(nfft * sizeof(last_frame[0]));
size_t fft_work_size;
// 第一步获取可以用的FFT工作区域的大小到fft_work_size
//nfft=一个FSK符号的样本数*频率过采样率=0.16*12000*2=3840
kiss_fftr_alloc(nfft, 0, 0, &fft_work_size);
// 申请FFT工作区域的内存38676个
void *fft_work = malloc(fft_work_size);
//第二步返回fft的设置信息
kiss_fftr_cfg fft_cfg = kiss_fftr_alloc(nfft, 0, fft_work, &fft_work_size);
free(fft_work);
free(window);
free(last_frame);
}
void *init_decoder(int64_t utcTime, int sample_rate, int num_samples, bool is_ft8) {
//此处,改为一个变量,不是以指针,申请新内存的方式处理了。
//其实这种方式要注意一个问题,在一个周期之内,必须解码完毕,否则新的解码又要开始了
//此处不用申请内存的方式解决
// decoder.utcTime = utcTime;
// decoder.num_samples = num_samples;
// decoder.mon_cfg = (monitor_config_t) {
// .f_min = 100,//分析的最低频率边界
// .f_max = 3000,//分析的最大频率边界
// .sample_rate = sample_rate,//采样率12000Hz
// .time_osr = kTime_osr,//时间过采样率=2
// .freq_osr = kFreq_osr,//频率过采样率=2
// .protocol = is_ft8 ? PROTO_FT8 : PROTO_FT4
// };
// //LOGD("Init decoder . address : %lld", decoder);
// monitor_init(&decoder.mon, &decoder.mon_cfg);
//
//
// return &decoder;
//此部分,是老的解决方式,是动态申请内存。
decoder_t *decoder;
decoder = malloc(sizeof(decoder_t));
decoder->utcTime = utcTime;
decoder->num_samples = num_samples;
decoder->mon_cfg = (monitor_config_t) {
.f_min = 100,//分析的最低频率边界
.f_max = 3000,//分析的最大频率边界
.sample_rate = sample_rate,//采样率12000Hz
.time_osr = kTime_osr,//时间过采样率=2
.freq_osr = kFreq_osr,//频率过采样率=2
.protocol = is_ft8 ? PROTO_FT8 : PROTO_FT4
};
decoder->kLDPC_iterations = fast_kLDPC_iterations;
//LOGD("Init decoder . address : %lld", decoder);
monitor_init(&decoder->mon, &decoder->mon_cfg);
return decoder;
}
void delete_decoder(decoder_t *decoder) {
//LOGD("Free decoder , address:%lld", decoder);
monitor_free(&decoder->mon);
free(decoder);
}
void decoder_monitor_press(float signal[], decoder_t *decoder) {
// 以每一个FSK符号占用的数据量为单位循环。
//block_size每个符号的样本数12000*0.16=1920
for (int frame_pos = 0;
frame_pos + decoder->mon.block_size <=
decoder->num_samples; frame_pos += decoder->mon.block_size) {
// Process the waveform data frame by frame - you could have a live loop here with data from an audio device
// 逐帧处理波形数据,这个位置可以使用音频设备的数据环。
//以每一个符号时间长度0.16内的数据做瀑布数据最后会形成一个信号量mag数组。
// mag数组的总长度是最大符号块数93*时间过采样率2*频率过采样率2*分析块960也就是Waterfall size = 357120
//一次调用monitor_process处理的是一个符号长度的信号量数据生成2*2*960=3840个mag数据。
//一共有93个符号的循环mag数组的大小2*2*960*93=357120
//mag数据保存在monitor.wf.mag中。
monitor_process(&decoder->mon, signal + frame_pos);
}
// /data/user/0/com.bg7yoz.ft8cn/cache/
//把fft数据保存下来
//FILE * fp2 = fopen("/data/user/0/com.bg7yoz.ft8cn/cache/fft001.txt", "w");//打开输出文件
//for (int i = 0; i < 3840; ++i) {
// for (int j = 0; j < 93; ++j) {
// fprintf (fp2,"%d\n", decoder->mon.wf.mag[i*j]);//把数组a逆序写入到输出文件当中
// }
//}
//fclose(fp2);//关闭输出文件,相当于保存
LOG(LOG_DEBUG, "Waterfall accumulated %d symbols\n", decoder->mon.wf.num_blocks);//积累的信号
LOG(LOG_INFO, "Max magnitude: %.1f dB\n", decoder->mon.max_mag);//最大信号值dB
}
int decoder_ft8_find_sync(decoder_t *decoder) {
//检测ft8信号kMax_candidates最大候选人数量=120candidate_list候选人列表(size=120)kMin_score候选人的最低同步分数阈值=10
decoder->num_candidates = ft8_find_sync(&decoder->mon.wf, kMax_candidates,
decoder->candidate_list, kMin_score);
LOG(LOG_DEBUG, "ft8_find_sync finished. %d candidates\n", decoder->num_candidates);
// Hash table for decoded messages (to check for duplicates)
// 解码消息的哈希表(用于检查重复项)
//int num_decoded = 0;
//message_t decoded[kMax_decoded_messages];//kMax_decoded_messages=50
// 哈希表指针列表(指针数组)
//message_t *decoded_hashtable[kMax_decoded_messages];
// Initialize hash table pointers
// 初始化哈希表指针列表
for (int i = 0; i < kMax_decoded_messages; ++i) {
decoder->decoded_hashtable[i] = NULL;
}
return decoder->num_candidates;
}
ft8_message decoder_ft8_analysis(int idx, decoder_t *decoder) {
ft8_message ft8Message;
ft8Message.isValid = false;
ft8Message.utcTime = decoder->utcTime;
// 候选列表candidate_list已经从ft8_fing_sync获得。
ft8Message.candidate = decoder->candidate_list[idx];
if (ft8Message.candidate.score < kMin_score) {
//ft8Message.isValid = false;
return ft8Message;
}
ft8Message.freq_hz =
(ft8Message.candidate.freq_offset +
(float) ft8Message.candidate.freq_sub / decoder->mon.wf.freq_osr) /
decoder->mon.symbol_period;
ft8Message.time_sec =
((ft8Message.candidate.time_offset + (float) ft8Message.candidate.time_sub)
* decoder->mon.symbol_period) / decoder->mon.wf.time_osr;
//ft8Message.snr=ft8Message.candidate.snr;
//这是原来代码的时间偏移同样的数据与JTDX的时间差异很大改用上面的代码稍微接近一些
//(ft8Message.candidate.time_offset +
//(float) ft8Message.candidate.time_sub / decoder->mon.wf.time_osr) *
//decoder->mon.symbol_period;
// 如果解码失败,跳到下一次循环 kLDPC_iterations=20 LDPC低密度奇偶校验的迭代次数。
if (!ft8_decode(&decoder->mon.wf, &ft8Message.candidate
//, &ft8Message.message, kLDPC_iterations,
, &ft8Message.message, decoder->kLDPC_iterations,
&ft8Message.status)) {
// printf("000000 %3d %+4.2f %4.0f ~ ---\n", cand->score, time_sec, freq_hz);
if (ft8Message.status.ldpc_errors > 0) {
// LDPC:低密度奇偶校验
LOG(LOG_DEBUG, "LDPC decode: %d errors\n", ft8Message.status.ldpc_errors);
} else if (ft8Message.status.crc_calculated != ft8Message.status.crc_extracted) {
LOG(LOG_DEBUG, "CRC mismatch!\n");
} else if (ft8Message.status.unpack_status != 0) {
LOG(LOG_DEBUG, "Error while unpacking!\n");
}
//ft8Message.isValid = false;
return ft8Message;
}
ft8Message.snr = ft8Message.candidate.snr;
LOG(LOG_DEBUG, "Checking hash table for %4.1fs / %4.1fHz [%d]...\n", ft8Message.time_sec,
ft8Message.freq_hz,
ft8Message.candidate.score);
int idx_hash =
ft8Message.message.hash % kMax_decoded_messages;//为啥是取模稍后研究kMax_decoded_messages=50
bool found_empty_slot = false;
bool found_duplicate = false;
//检查哈希表,只有空插槽,或重复插槽(哈希值相同,并且消息相同)
do {
if (decoder->decoded_hashtable[idx_hash] == NULL) {
LOG(LOG_DEBUG, "Found an empty slot\n");
found_empty_slot = true;
} else if ((decoder->decoded_hashtable[idx_hash]->hash == ft8Message.message.hash) &&
(0 ==
strcmp(decoder->decoded_hashtable[idx_hash]->text, ft8Message.message.text))) {
LOG(LOG_DEBUG, "Found a duplicate [%s]\n", ft8Message.message.text);
found_duplicate = true;
} else {
LOG(LOG_DEBUG, "Hash table clash!\n");
// Move on to check the next entry in hash table
idx_hash = (idx_hash + 1) % kMax_decoded_messages;
}
} while (!found_empty_slot && !found_duplicate);
if (found_empty_slot) {
// Fill the empty hashtable slot
memcpy(&decoder->decoded[idx_hash], &ft8Message.message, sizeof(ft8Message.message));
decoder->decoded_hashtable[idx_hash] = &decoder->decoded[idx_hash];
++decoder->num_decoded;
ft8Message.isValid = true;
LOG_PRINTF("%3d %+4.2f %4.0f ~ %s report:%d grid:%s,toHash:%x,fromHash:%x",
ft8Message.snr,
ft8Message.time_sec, ft8Message.freq_hz, ft8Message.message.text,
ft8Message.message.report, ft8Message.message.maidenGrid,
ft8Message.message.call_to_hash.hash12, ft8Message.message.call_de_hash.hash12);
}
memcpy(decoder->a91, ft8Message.message.a91, FTX_LDPC_K_BYTES);
return ft8Message;
}
void decoder_ft8_reset(decoder_t *decoder, long utcTime, int num_samples) {
LOG(LOG_DEBUG, "Monitor is resetting...");
decoder->mon.wf.num_blocks = 0;
//decoder->mon.max_mag = 0;
decoder->mon.max_mag = -120.0f;
decoder->utcTime = utcTime;
decoder->num_samples = num_samples;
}
/**
* 17479
* @param a174 174int
* @param a79 79int
*/
void recode(int a174[], int a79[]) {
int i174 = 0;
//int costas[] = { 3, 1, 4, 0, 6, 5, 2 };
//std::vector<int> out79;
for (int i79 = 0; i79 < 79; i79++) {
if (i79 < 7) {
//out79.push_back(costas[i79]);
a79[i79] = kFT8CostasPattern[i79];
} else if (i79 >= 36 && i79 < 36 + 7) {
//out79.push_back(costas[i79-36]);
a79[i79] = kFT8CostasPattern[i79 - 36];
} else if (i79 >= 72) {
//out79.push_back(costas[i79-72]);
a79[i79] = kFT8CostasPattern[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);
a79[i79] = sym;
}
}
};

Wyświetl plik

@ -0,0 +1,69 @@
//
// Created by jmsmf on 2022/4/24.
//
//
#include "ft8/decode.h"
#include "monitor_opr.h"
#include "ft8/constants.h"
#include <time.h>
//const int kMin_score = 10; // 候选人的最低同步分数阈值。Minimum sync score threshold for candidates
const int kMax_candidates = 120;//最大候选人数量
const int kMax_decoded_messages = 100;
//const int kMax_decoded_messages = 50;
const int kLDPC_iterations = 20;//LDPC低密度奇偶校验的迭代次数数值越大精度越高速度越慢
const int deep_kLDPC_iterations = 200;//LDPC低密度奇偶校验的迭代次数
const int fast_kLDPC_iterations = 20;//LDPC低密度奇偶校验的迭代次数
typedef struct {
long long utcTime;//UTC时间
int num_samples;//采样率
int num_candidates;
int num_decoded;
message_t decoded[kMax_decoded_messages];//kMax_decoded_messages=50
// 哈希表指针列表(指针数组)
message_t *decoded_hashtable[kMax_decoded_messages];
// Find top candidates by Costas sync score and localize them in time and frequency
// 从科斯塔斯阵列Costas寻找最佳候选并在时间和频率上对其进行本地化。候选数组最大120个
// candidate_t定义在decode.h
candidate_t candidate_list[kMax_candidates];//kMax_candidates=120
monitor_t mon;
monitor_config_t mon_cfg;
uint8_t a91[FTX_LDPC_K_BYTES];//用于生成减法代码的数据
int kLDPC_iterations;//ldpc 迭代次数数值越大精度越高速度越慢20或100
} decoder_t;
typedef struct {
int64_t utcTime;//消息的UTC时间
bool isValid;//是否为有效消息
int snr;//信噪比
candidate_t candidate;//消息的原始信号数据
float time_sec;//时间偏移值
float freq_hz;//频率偏移值
message_t message;//解码后的消息
decode_status_t status;
} ft8_message;
static const int kFreq_osr = 2; // 频率过采样率。Frequency oversampling rate (bin subdivision)
static const int kTime_osr = 2; // 时间过采样率。Time oversampling rate (symbol subdivision)
//把信号FFT,在解码decoder中减去信号
void signalToFFT(decoder_t *decoder,float signal[], int sample_rate);
//初始化解码器所需要的参数最后通过指针的方式传递给java
void *init_decoder(int64_t utcTime, int sample_rate, int num_samples, bool is_ft8);
void delete_decoder(decoder_t *decoder);
void decoder_monitor_press(float signal[], decoder_t *decoder);
int decoder_ft8_find_sync(decoder_t *decoder);
ft8_message decoder_ft8_analysis(int idx, decoder_t *decoder);
void decoder_ft8_reset(decoder_t *decoder,long utcTime,int num_samples);
void recode(int a174[],int a79[]);

Wyświetl plik

@ -0,0 +1,150 @@
//
// Created by jmsmf on 2022/6/1.
//
#include "ft8Encoder.h"
//#define LOG_LEVEL LOG_INFO
#define FT8_SYMBOL_BT 2.0f /// 符号平滑滤波器带宽因子BT
#define FT4_SYMBOL_BT 1.0f /// 符号平滑滤波器带宽因子BT
#define GFSK_CONST_K 5.336446f ///< == pi * sqrt(2 / log(2))
/// 生成高斯平滑脉冲
/// 脉冲理论上是无限长的然而这里它被截断为符号长度的3倍。
/// 这意味着脉冲阵列必须有空间容纳3*n_spsym元素。
/// @param[in] n_spsym 每个符号的样本数 Number of samples per symbol
/// @param[in] b 形状参数为FT8/FT4定义的值
/// @param[out] pulse 脉冲采样输出阵列
///
void gfsk_pulse(int n_spsym, float symbol_bt, float *pulse) {
for (int i = 0; i < 3 * n_spsym; ++i) {
float t = i / (float) n_spsym - 1.5f;
float arg1 = GFSK_CONST_K * symbol_bt * (t + 0.5f);
float arg2 = GFSK_CONST_K * symbol_bt * (t - 0.5f);
pulse[i] = (erff(arg1) - erff(arg2)) / 2;
}
}
/// 使用GFSK相位整形合成波形数据。
/// 输出波形将包含n_sym个符号。
/// @param[in] symbols 符号(音调)数组 (0-7 for FT8)
/// @param[in] n_sym 符号数组中的符号数
/// @param[in] f0 符号0的音频频率赫兹 (载波频率)
/// @param[in] symbol_bt 符号平滑滤波器带宽 (2 for FT8, 1 for FT4)
/// @param[in] symbol_period 符号周期(持续时间),秒
/// @param[in] signal_rate 合成信号的采样率,赫兹
/// @param[out] signal 信号波形样本的输出阵列应为n_sym*n_spsym样本留出空间
///
void synth_gfsk(const uint8_t *symbols, int n_sym, float f0, float symbol_bt, float symbol_period,
int signal_rate, float *signal) {
int n_spsym = (int) (0.5f + (float)signal_rate * symbol_period); // 每个符号的样本数12000*0.16=1920
int n_wave = n_sym * n_spsym; // 输出样本数79*1920=151680
float hmod = 1.0f;
// 计算平滑的频率波形。
// Length = (nsym+2)*n_spsym samples, 首个和最后一个扩展符号
float dphi_peak = 2 * M_PI * hmod / n_spsym;
//此处是与采样率有关,采样率提高后,可能会有闪退的问题
float *dphi;//此处使用内存申请的方式,而不是原来数组的方式,因为是采样率过高时,会报内存出错。
dphi = malloc(sizeof(float) * (n_wave + 2 * n_spsym));
if (dphi==0) return;//内存申请失败
//float dphi[n_wave + 2 * n_spsym];//原来的方式
// 频率上移f0
for (int i = 0; i < n_wave + 2 * n_spsym; ++i) {
dphi[i] = 2 * M_PI * f0 / signal_rate;
}
//float pulse[3 * n_spsym];
float *pulse=(float *) malloc(sizeof(float)*3 * n_spsym);
gfsk_pulse(n_spsym, symbol_bt, pulse);
for (int i = 0; i < n_sym; ++i) {
int ib = i * n_spsym;
for (int j = 0; j < 3 * n_spsym; ++j) {
dphi[j + ib] += dphi_peak * symbols[i] * pulse[j];
}
}
// 在开头和结尾添加伪符号,音调值分别等于第一个符号和最后一个符号
for (int j = 0; j < 2 * n_spsym; ++j) {
dphi[j] += dphi_peak * pulse[j + n_spsym] * symbols[0];
dphi[j + n_sym * n_spsym] += dphi_peak * pulse[j] * symbols[n_sym - 1];
}
// 计算并插入音频波形
float phi = 0;
for (int k = 0; k < n_wave; ++k) { // 不包括虚拟符号
signal[k] = sinf(phi);
phi = fmodf(phi + dphi[k + n_spsym], 2 * M_PI);
}
// 对第一个和最后一个符号应用封套成形,此处是前后增加斜坡函数,
int n_ramp = n_spsym / 8;//240个样本20毫秒T/8
for (int i = 0; i < n_ramp; ++i) {
float env = (1 - cosf(2 * M_PI * i / (2 * n_ramp))) / 2;
signal[i] *= env;
signal[n_wave - 1 - i] *= env;
}
free(pulse);
free(dphi);//要释放掉内存
}
//此代码已经弃用
void generateFt8ToBuffer(char *message, float frequency, short *buffer) {
// 首先,将文本数据打包为二进制消息
uint8_t packed[FTX_LDPC_K_BYTES];//91位包括CRC。
int rc = pack77(message, packed);//生成数据
if (rc < 0) {
//LOGE("Cannot parse message!\n");
//LOGE("RC = %d\n", rc);
return;
}
//int num_tones = FT8_NN;//符号数量FT8是79个FT4是105个。
//float symbol_period = FT8_SYMBOL_PERIOD;//FT8_SYMBOL_PERIOD=0.160f
float symbol_bt = FT8_SYMBOL_BT;//FT8_SYMBOL_BT=2.0f
float slot_time = FT8_SLOT_TIME;//FT8_SLOT_TIME=15f
// 其次将二进制消息编码为FSK音调序列
uint8_t tones[FT8_NN]; // 79音调符号数组
ft8_encode(packed, tones);
// 第三将FSK音调转换为音频信号b
//int sample_rate = FT8_SAMPLE_RATE;//采样率
int num_samples = (int) (0.5f + FT8_NN * FT8_SYMBOL_PERIOD *
FT8_SAMPLE_RATE); // 数据信号中的采样数0.5+79*0.16*12000
//int num_silence = (slot_time * sample_rate - num_samples) / 2; // 两端填充静音到15秒15*12000-num_samples/21.18秒的样本数)
int num_silence = 20;//把前面的静音时长缩短为20毫秒留出时间给解码
//int num_total_samples = num_silence + num_samples + num_silence; // 填充信号中的样本数2.36秒+12.64秒=15秒的样本数
float signal[Ft8num_samples];
//Ft8num_sampleFT8声音的总采样数不是字节数。15*12000
for (int i = 0; i < Ft8num_samples; i++)//把数据全部静音。
{
signal[i] = 0;
//buffer[i + num_samples + num_silence] = 0;
}
// 合成波形数据信号并将其保存为WAV文件
synth_gfsk(tones, FT8_NN, frequency, symbol_bt, FT8_SYMBOL_PERIOD, FT8_SAMPLE_RATE,
signal + num_silence);
for (int i = 0; i < Ft8num_samples; i++) {
float x = signal[i];
if (x > 1.0)
x = 1.0;
else if (x < -1.0)
x = -1.0;
buffer[i] = (short) (0.5 + (x * 32767.0));
}
//save_wav(signal, num_total_samples, sample_rate, wav_path);
}

Wyświetl plik

@ -0,0 +1,25 @@
//
// Created by jmsmf on 2022/6/1.
//
#ifndef FT8CN_FT8ENCODER_H
#define FT8CN_FT8ENCODER_H
#endif //FT8CN_FT8ENCODER_H
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
#include "common/common.h"
//#include "common/wave.h"
#include "common/debug.h"
#include "ft8/pack.h"
#include "ft8/encode.h"
#include "ft8/constants.h"
const int Ft8num_samples = 15*12000;//FT8采样数不是字节数16bit,字节数要乘以2
//void generateFt8ToFile(char* message,float frequency,char* wav_path,bool is_ft4);
void generateFt8ToBuffer(char* message,float frequency,short * buffer);
void synth_gfsk(const uint8_t* symbols, int n_sym, float f0, float symbol_bt, float symbol_period, int signal_rate, float* signal);

Some files were not shown because too many files have changed in this diff Show More