kopia lustrzana https://github.com/N0BOY/FT8CN
commit
0b78e47b4b
|
@ -1,69 +0,0 @@
|
|||
|
||||
# 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} )
|
|
@ -1,11 +0,0 @@
|
|||
//
|
||||
// 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!!!";
|
|
@ -1,3 +0,0 @@
|
|||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
|
@ -1,24 +0,0 @@
|
|||
#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类型
|
|
@ -1,131 +0,0 @@
|
|||
#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;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
#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_
|
|
@ -1,158 +0,0 @@
|
|||
/*
|
||||
* 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
|
|
@ -1,402 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
/*
|
||||
* 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
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* 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
|
|
@ -1,394 +0,0 @@
|
|||
#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
|
||||
};
|
|
@ -1,89 +0,0 @@
|
|||
#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) ///< 有效负载位数(包括CRC)Number 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位所需的整字节数(仅限有效负载+CRC)Number 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_
|
||||
|
||||
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
#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);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
#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_
|
|
@ -1,628 +0,0 @@
|
|||
#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:960,freq_offset:0~ 960(-1) -7
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
static int ft8_sync_score(const waterfall_t *wf, candidate_t *candidate) {
|
||||
/*
|
||||
* ft8本应有58个符号,但在开始(0-7)、中间(36-43)、结尾(72-79)加了科斯塔阵列,所以共有79个符号,
|
||||
*此函数在4层循环中执行。时间采样率2*频率采样率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=36,block=0..6,36~43,72~79,这是costas阵列在符号序列中的索引
|
||||
int block = (FT8_SYNC_OFFSET * m) + k; // 相对于消息relative to the message
|
||||
int block_abs =
|
||||
//time_offset=-12.。23(36个)
|
||||
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最大候选人数量=120,heap[]候选人列表(size=120),kMin_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=58,k=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-OR’ed 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((p(1)/p(0))
|
||||
//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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
#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=1,FFT幅度数据中的每个单元对应于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_
|
|
@ -1,197 +0,0 @@
|
|||
#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-OR’ed 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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
#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_
|
|
@ -1,65 +0,0 @@
|
|||
|
||||
|
||||
#include "hash22.h"
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
//m为hash的长度,12,22
|
||||
//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);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#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的哈希码
|
|
@ -1,268 +0,0 @@
|
|||
//
|
||||
// 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]=对数(P(x=0)/P(x=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;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
#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_
|
|
@ -1,373 +0,0 @@
|
|||
#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
|
|
@ -1,12 +0,0 @@
|
|||
#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_
|
|
@ -1,253 +0,0 @@
|
|||
#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;
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
#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_
|
|
@ -1,626 +0,0 @@
|
|||
#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;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#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_
|
|
@ -1,292 +0,0 @@
|
|||
//
|
||||
// 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最大候选人数量=120,candidate_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对174码,重新编生成79码
|
||||
* @param a174 174个int
|
||||
* @param a79 79个int
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,69 +0,0 @@
|
|||
//
|
||||
// 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[]);
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
//
|
||||
// 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)/2(1.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);
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
//
|
||||
// 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);
|
|
@ -1,333 +0,0 @@
|
|||
//
|
||||
// Created by jmsmf on 2022/6/2.
|
||||
//
|
||||
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
|
||||
|
||||
extern "C" {
|
||||
#include "common/debug.h"
|
||||
#include "ft8Decoder.h"
|
||||
#include "ft8Encoder.h"
|
||||
}
|
||||
|
||||
//
|
||||
////将char类型转换成jstring类型
|
||||
//jstring CStr2Jstring(JNIEnv *env, const char *pat) {
|
||||
// // 定义java String类 strClass
|
||||
// jclass strClass = (env)->FindClass("java/lang/String");
|
||||
// // 获取java String类方法String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
|
||||
// jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
|
||||
// // 建立byte数组
|
||||
// jbyteArray bytes = (env)->NewByteArray((jsize) strlen(pat));
|
||||
// // 将char* 转换为byte数组
|
||||
// (env)->SetByteArrayRegion(bytes, 0, (jsize) strlen(pat), (jbyte *) pat);
|
||||
// //设置String, 保存语言类型,用于byte数组转换至String时的参数
|
||||
// jstring encoding = (env)->NewStringUTF("GB2312");
|
||||
// //将byte数组转换为java String,并输出
|
||||
// return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding);
|
||||
//
|
||||
//}
|
||||
//
|
||||
//char *Jstring2CStr(JNIEnv *env, jstring jstr) {
|
||||
// char *rtn = NULL;
|
||||
// jclass clsstring = env->FindClass("java/lang/String");
|
||||
// jstring strencode = env->NewStringUTF("GB2312");
|
||||
// jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
|
||||
// jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
|
||||
// jsize alen = env->GetArrayLength(barr);
|
||||
// jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
|
||||
// if (alen > 0) {
|
||||
// rtn = (char *) malloc(alen + 1); //new char[alen+1];
|
||||
// memcpy(rtn, ba, alen);
|
||||
// rtn[alen] = 0;
|
||||
// }
|
||||
// env->ReleaseByteArrayElements(barr, ba, 0);
|
||||
//
|
||||
// return rtn;
|
||||
//}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DecoderFt8Reset(JNIEnv *env, jobject thiz,
|
||||
jlong decoder, jlong utcTime,
|
||||
jint num_samples) {
|
||||
decoder_t *dd;
|
||||
dd = (decoder_t *) decoder;
|
||||
decoder_ft8_reset(dd, utcTime, num_samples);
|
||||
//dd->utcTime=utcTime;
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DeleteDecoder(JNIEnv *env, jobject,
|
||||
jlong decoder) {
|
||||
decoder_t *dd;
|
||||
dd = (decoder_t *) decoder;
|
||||
delete_decoder(dd);
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DecoderFt8Analysis(JNIEnv *env, jobject,
|
||||
jint idx,
|
||||
jlong decoder,
|
||||
jobject ft8Message) {
|
||||
decoder_t *dd;
|
||||
dd = (decoder_t *) decoder;
|
||||
|
||||
|
||||
ft8_message message = decoder_ft8_analysis(idx, dd);
|
||||
|
||||
|
||||
jclass objectClass = env->FindClass("com/bg7yoz/ft8cn/Ft8Message");
|
||||
|
||||
jfieldID utcTime = env->GetFieldID(objectClass, "utcTime", "J");
|
||||
jfieldID isValid = env->GetFieldID(objectClass, "isValid", "Z");
|
||||
jfieldID time_sec = env->GetFieldID(objectClass, "time_sec", "F");
|
||||
jfieldID freq_hz = env->GetFieldID(objectClass, "freq_hz", "F");
|
||||
jfieldID score = env->GetFieldID(objectClass, "score", "I");
|
||||
jfieldID snr = env->GetFieldID(objectClass, "snr", "I");
|
||||
jfieldID messageHash = env->GetFieldID(objectClass, "messageHash", "I");
|
||||
env->SetBooleanField(ft8Message, isValid, message.isValid);
|
||||
|
||||
jfieldID i3 = env->GetFieldID(objectClass, "i3", "I");
|
||||
jfieldID n3 = env->GetFieldID(objectClass, "n3", "I");
|
||||
jfieldID callsignFrom = env->GetFieldID(objectClass, "callsignFrom", "Ljava/lang/String;");
|
||||
jfieldID callsignTo = env->GetFieldID(objectClass, "callsignTo", "Ljava/lang/String;");
|
||||
jfieldID extraInfo = env->GetFieldID(objectClass, "extraInfo", "Ljava/lang/String;");
|
||||
jfieldID maidenGrid = env->GetFieldID(objectClass, "maidenGrid", "Ljava/lang/String;");
|
||||
jfieldID report = env->GetFieldID(objectClass, "report", "I");
|
||||
jfieldID callFromHash10 = env->GetFieldID(objectClass, "callFromHash10", "J");
|
||||
jfieldID callFromHash12 = env->GetFieldID(objectClass, "callFromHash12", "J");
|
||||
jfieldID callFromHash22 = env->GetFieldID(objectClass, "callFromHash22", "J");
|
||||
jfieldID callToHash10 = env->GetFieldID(objectClass, "callToHash10", "J");
|
||||
jfieldID callToHash12 = env->GetFieldID(objectClass, "callToHash12", "J");
|
||||
jfieldID callToHash22 = env->GetFieldID(objectClass, "callToHash22", "J");
|
||||
|
||||
|
||||
if (message.isValid) {
|
||||
|
||||
env->SetLongField(ft8Message, utcTime, message.utcTime);
|
||||
env->SetFloatField(ft8Message, time_sec, message.time_sec);
|
||||
env->SetFloatField(ft8Message, freq_hz, message.freq_hz);
|
||||
env->SetIntField(ft8Message, score, message.candidate.score);
|
||||
env->SetIntField(ft8Message, snr, message.snr);
|
||||
//env->SetObjectField(ft8Message,messageText,env->NewStringUTF(message.message.text));
|
||||
env->SetIntField(ft8Message, messageHash, message.message.hash);
|
||||
|
||||
env->SetIntField(ft8Message, i3, message.message.i3);
|
||||
env->SetIntField(ft8Message, n3, message.message.n3);
|
||||
env->SetObjectField(ft8Message, callsignFrom, env->NewStringUTF(message.message.call_de));
|
||||
env->SetObjectField(ft8Message, callsignTo, env->NewStringUTF(message.message.call_to));
|
||||
env->SetObjectField(ft8Message, extraInfo, env->NewStringUTF(message.message.extra));
|
||||
env->SetObjectField(ft8Message, maidenGrid, env->NewStringUTF(message.message.maidenGrid));
|
||||
env->SetIntField(ft8Message, report, message.message.report);
|
||||
env->SetLongField(ft8Message, callFromHash10,
|
||||
(long long) message.message.call_de_hash.hash10);
|
||||
env->SetLongField(ft8Message, callFromHash12,
|
||||
(long long) message.message.call_de_hash.hash12);
|
||||
env->SetLongField(ft8Message, callFromHash22,
|
||||
(long long) message.message.call_de_hash.hash22);
|
||||
env->SetLongField(ft8Message, callToHash10,
|
||||
(long long) message.message.call_to_hash.hash10);
|
||||
env->SetLongField(ft8Message, callToHash12,
|
||||
(long long) message.message.call_to_hash.hash12);
|
||||
env->SetLongField(ft8Message, callToHash22,
|
||||
(long long) message.message.call_to_hash.hash22);
|
||||
|
||||
}
|
||||
return message.isValid;
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DecoderFt8FindSync(JNIEnv *env, jobject,
|
||||
jlong decoder) {
|
||||
decoder_t *dd;
|
||||
dd = (decoder_t *) decoder;
|
||||
return decoder_ft8_find_sync(dd);
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DecoderMonitorPress(JNIEnv *env, jobject,
|
||||
jintArray buffer,
|
||||
jlong decoder) {
|
||||
decoder_t *dd;
|
||||
dd = (decoder_t *) decoder;
|
||||
|
||||
|
||||
// jfloat *arr;
|
||||
// jint length;
|
||||
// arr = (*env).GetFloatArrayElements(buffer, NULL);
|
||||
// decoder_monitor_press(arr, dd);
|
||||
int arr_len = env->GetArrayLength(buffer);
|
||||
//将java数组复制到c数组中
|
||||
auto *c_array = (jint *) malloc(arr_len * sizeof(arr_len));
|
||||
|
||||
//env->GetFloatArrayRegion(buffer,0,arr_len,c_array);
|
||||
(*env).GetIntArrayRegion(buffer, 0, arr_len, c_array);
|
||||
auto *raw_data = (float_t *) malloc(sizeof(float_t) * arr_len);
|
||||
|
||||
for (int i = 0; i < arr_len; i++) {
|
||||
raw_data[i] = c_array[i] / 32768.0f;
|
||||
}
|
||||
|
||||
decoder_monitor_press(raw_data, dd);
|
||||
free(raw_data);
|
||||
free(c_array);
|
||||
|
||||
|
||||
|
||||
// jint *arr;
|
||||
// jint length;
|
||||
// arr = (*env).GetIntArrayElements(buffer, nullptr);
|
||||
// length = (*env).GetArrayLength(buffer);
|
||||
// auto *raw_data = (float_t *) malloc(sizeof(float_t) * length);
|
||||
//
|
||||
// for (int i = 0; i < length; i++) {
|
||||
// raw_data[i] = arr[i] / 32768.0f;
|
||||
// }
|
||||
//
|
||||
// decoder_monitor_press(raw_data, dd);
|
||||
//
|
||||
// free(raw_data);
|
||||
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_InitDecoder(JNIEnv *env, jobject, jlong utcTime,
|
||||
jint sampleRate, jint num_samples,
|
||||
jboolean isFt8) {
|
||||
return (long) init_decoder(utcTime, sampleRate, num_samples, isFt8);
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DecoderMonitorPressFloat(JNIEnv *env,
|
||||
jobject thiz,
|
||||
jfloatArray buffer,
|
||||
jlong decoder) {
|
||||
decoder_t *dd;
|
||||
dd = (decoder_t *) decoder;
|
||||
|
||||
|
||||
// jfloat *arr;
|
||||
// jint length;
|
||||
// arr = (*env).GetFloatArrayElements(buffer, NULL);
|
||||
// decoder_monitor_press(arr, dd);
|
||||
int arr_len = env->GetArrayLength(buffer);
|
||||
//将java数组复制到c数组中
|
||||
auto *c_array = (jfloat *) malloc(arr_len * sizeof(arr_len));
|
||||
|
||||
//env->GetFloatArrayRegion(buffer,0,arr_len,c_array);
|
||||
(*env).GetFloatArrayRegion(buffer, 0, arr_len, c_array);
|
||||
decoder_monitor_press(c_array, dd);
|
||||
free(c_array);
|
||||
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_DecoderGetA91(JNIEnv *env, jobject thiz,
|
||||
jlong decoder) {
|
||||
decoder_t *dd;
|
||||
dd = (decoder_t *) decoder;
|
||||
|
||||
jbyteArray array;
|
||||
array = env->NewByteArray(FTX_LDPC_K_BYTES);
|
||||
|
||||
jbyte buf[FTX_LDPC_K_BYTES];
|
||||
memcpy(buf, dd->a91, FTX_LDPC_K_BYTES);
|
||||
|
||||
// 使用 setIntArrayRegion 来赋值
|
||||
env->SetByteArrayRegion(array, 0, FTX_LDPC_K_BYTES, buf);
|
||||
return array;
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8listener_FT8SignalListener_setDecodeMode(JNIEnv *env, jobject thiz,
|
||||
jlong decoder, jboolean is_deep) {
|
||||
decoder_t *dd;
|
||||
dd = (decoder_t *) decoder;
|
||||
if (is_deep) {
|
||||
dd->kLDPC_iterations = deep_kLDPC_iterations;
|
||||
} else {
|
||||
dd->kLDPC_iterations = fast_kLDPC_iterations;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 把频率减去
|
||||
* @param dd
|
||||
* @param index
|
||||
* @param max_block_size
|
||||
*/
|
||||
void setMagToZero(decoder_t * dd ,int index,int max_block_size){
|
||||
if (index>0 && index<max_block_size){
|
||||
dd->mon.wf.mag[index]=0;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8listener_ReBuildSignal_doSubtractSignal(JNIEnv *env, jclass clazz,
|
||||
jlong decoder,
|
||||
jbyteArray payload,
|
||||
jint sample_rate,
|
||||
jfloat frequency,
|
||||
jfloat time_sec) {
|
||||
decoder_t *dd;
|
||||
dd = (decoder_t *) decoder;
|
||||
|
||||
int arr_len = env->GetArrayLength(payload);
|
||||
//将java数组复制到c数组中
|
||||
auto *c_array = (jbyte *) malloc(arr_len * sizeof(arr_len));
|
||||
|
||||
|
||||
//env->GetFloatArrayRegion(buffer,0,arr_len,c_array);
|
||||
(*env).GetByteArrayRegion(payload, 0, arr_len, c_array);
|
||||
|
||||
uint8_t tones[FT8_NN];// 79音调(符号)数组,
|
||||
//此处是12个字节(91+7)/8,可以使用a91生成音频
|
||||
ft8_encode((uint8_t *) c_array, tones);
|
||||
|
||||
//相当于二维数组,freq优先
|
||||
int max_block_size=(int) (FT8_SLOT_TIME / FT8_SYMBOL_PERIOD) * kTime_osr * kFreq_osr
|
||||
* (int) (sample_rate * FT8_SYMBOL_PERIOD / 2);
|
||||
LOG_PRINTF("max_block_size:%d",max_block_size);
|
||||
int block_size = FT8_SYMBOL_PERIOD * dd->mon_cfg.sample_rate;//1920,一个0.08秒的数据块大小,x轴的总长度
|
||||
LOG_PRINTF("block_size +++:%d", block_size);
|
||||
int freq_offset = (int) (frequency * FT8_SYMBOL_PERIOD) * kFreq_osr;//频率的偏移量,x轴
|
||||
int time_offset = (int) ((time_sec / FT8_SYMBOL_PERIOD) * kTime_osr+0.5f);// + 0.5);,y轴
|
||||
LOG_PRINTF("freq_offset +++:%f,%d", (frequency * FT8_SYMBOL_PERIOD) * kFreq_osr, freq_offset);
|
||||
LOG_PRINTF("time_offset +++:%f ,%d,time_offset:%d, time_sec:%f, freq_offset:%d, freq:%f",
|
||||
(time_sec / 0.08),
|
||||
(int) (time_sec / 0.08 + 0.5), time_offset, time_sec, freq_offset, frequency);
|
||||
for (int i = 0; i < FT8_NN; ++i) {//y轴自增循环
|
||||
int index = (i + time_offset) * 2;
|
||||
int index1 = index * block_size + freq_offset+tones[i];
|
||||
int index2 = (index + 1) * block_size + freq_offset+tones[i];
|
||||
int index3 =index1+1;
|
||||
int index4 =index2+1;
|
||||
int index5 =index1-1;
|
||||
int index6 =index2-1;
|
||||
int index7 =index1-2;
|
||||
int index8 =index2-2;
|
||||
int index9 =index1+2;
|
||||
int index10 =index2+2;
|
||||
|
||||
setMagToZero(dd,index1,max_block_size);
|
||||
setMagToZero(dd,index2,max_block_size);
|
||||
setMagToZero(dd,index3,max_block_size);
|
||||
setMagToZero(dd,index4,max_block_size);
|
||||
setMagToZero(dd,index5,max_block_size);
|
||||
setMagToZero(dd,index6,max_block_size);
|
||||
setMagToZero(dd,index7,max_block_size);
|
||||
setMagToZero(dd,index8,max_block_size);
|
||||
setMagToZero(dd,index9,max_block_size);
|
||||
setMagToZero(dd,index10,max_block_size);
|
||||
|
||||
}
|
||||
free(c_array);
|
||||
|
||||
}
|
|
@ -1,293 +0,0 @@
|
|||
#include <jni.h>
|
||||
|
||||
//
|
||||
// Created by jmsmf on 2022/6/11.
|
||||
//
|
||||
|
||||
extern "C" {
|
||||
#include "common/debug.h"
|
||||
#include "spectrum_data.h"
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ui_SpectrumFragment_getFFTData(JNIEnv *env, jobject thiz, jintArray data,
|
||||
jintArray fft_data) {
|
||||
|
||||
int arr_len=env->GetArrayLength(data);
|
||||
//将java数组复制到c数组中
|
||||
auto c_array=(jint *) malloc(arr_len * sizeof(arr_len));
|
||||
|
||||
env->GetIntArrayRegion(data,0,arr_len,c_array);
|
||||
auto *raw_data = (float *) malloc(sizeof(float) * arr_len);
|
||||
for (int i = 0; i < arr_len; i++) {
|
||||
raw_data[i] = c_array[i] / 32768.0f;
|
||||
}
|
||||
|
||||
|
||||
jint temp[arr_len/2];
|
||||
|
||||
do_fftr(raw_data,arr_len,temp);
|
||||
(*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
|
||||
|
||||
free(c_array);
|
||||
free(raw_data);
|
||||
|
||||
//
|
||||
// jint *arr;
|
||||
// jint length;
|
||||
// arr = (*env).GetIntArrayElements(data, NULL);
|
||||
// length = (*env).GetArrayLength(data);
|
||||
// float *raw_data = (float *) malloc(sizeof(float) * length);
|
||||
// for (int i = 0; i < length; i++) {
|
||||
// raw_data[i] = arr[i] / 32768.0f;
|
||||
// }
|
||||
//
|
||||
// jint temp[length/2];
|
||||
//
|
||||
// do_fftr(raw_data,length,temp);
|
||||
//
|
||||
// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
|
||||
// free(raw_data);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ui_SpectrumFragment_getFFTDataRaw(JNIEnv *env, jobject thiz, jintArray data,
|
||||
jintArray fft_data) {
|
||||
|
||||
int arr_len=env->GetArrayLength(data);
|
||||
//将java数组复制到c数组中
|
||||
auto c_array=(jint *) malloc(arr_len * sizeof(arr_len));
|
||||
|
||||
env->GetIntArrayRegion(data,0,arr_len,c_array);
|
||||
auto *raw_data = (float *) malloc(sizeof(float) * arr_len);
|
||||
for (int i = 0; i < arr_len; i++) {
|
||||
raw_data[i] = c_array[i] / 32768.0f;
|
||||
}
|
||||
|
||||
|
||||
jint temp[arr_len/2];
|
||||
|
||||
do_fftr_raw(raw_data,arr_len,temp);
|
||||
(*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
|
||||
|
||||
free(c_array);
|
||||
free(raw_data);
|
||||
|
||||
|
||||
//
|
||||
// jint *arr;
|
||||
// jint length;
|
||||
// arr = (*env).GetIntArrayElements(data, NULL);
|
||||
// length = (*env).GetArrayLength(data);
|
||||
// float *raw_data = (float *) malloc(sizeof(float) * length);
|
||||
// for (int i = 0; i < length; i++) {
|
||||
// raw_data[i] = arr[i] / 32768.0f;
|
||||
// }
|
||||
//
|
||||
// jint temp[length/2];
|
||||
//
|
||||
// do_fftr_raw(raw_data,length,temp);
|
||||
//
|
||||
// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
|
||||
// free(raw_data);
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ui_SpectrumView_getFFTData(JNIEnv *env, jobject thiz, jintArray data,
|
||||
jintArray fft_data) {
|
||||
int arr_len=env->GetArrayLength(data);
|
||||
//将java数组复制到c数组中
|
||||
auto c_array=(jint *) malloc(arr_len * sizeof(arr_len));
|
||||
|
||||
env->GetIntArrayRegion(data,0,arr_len,c_array);
|
||||
auto *raw_data = (float *) malloc(sizeof(float) * arr_len);
|
||||
for (int i = 0; i < arr_len; i++) {
|
||||
raw_data[i] = c_array[i] / 32768.0f;
|
||||
}
|
||||
|
||||
|
||||
jint temp[arr_len/2];
|
||||
|
||||
do_fftr(raw_data,arr_len,temp);
|
||||
(*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
|
||||
|
||||
free(c_array);
|
||||
free(raw_data);
|
||||
|
||||
|
||||
// jint *arr;
|
||||
// jint length;
|
||||
// arr = (*env).GetIntArrayElements(data, NULL);
|
||||
// length = (*env).GetArrayLength(data);
|
||||
// float *raw_data = (float *) malloc(sizeof(float) * length);
|
||||
// for (int i = 0; i < length; i++) {
|
||||
// raw_data[i] = arr[i] / 32768.0f;
|
||||
// }
|
||||
//
|
||||
// jint temp[length/2];
|
||||
//
|
||||
// do_fftr(raw_data,length,temp);
|
||||
//
|
||||
// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
|
||||
// free(raw_data);
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ui_SpectrumView_getFFTDataRaw(JNIEnv *env, jobject thiz, jintArray data,
|
||||
jintArray fft_data) {
|
||||
int arr_len=env->GetArrayLength(data);
|
||||
//将java数组复制到c数组中
|
||||
auto c_array=(jint *) malloc(arr_len * sizeof(arr_len));
|
||||
|
||||
env->GetIntArrayRegion(data,0,arr_len,c_array);
|
||||
auto *raw_data = (float *) malloc(sizeof(float) * arr_len);
|
||||
for (int i = 0; i < arr_len; i++) {
|
||||
raw_data[i] = c_array[i] / 32768.0f;
|
||||
}
|
||||
|
||||
|
||||
jint temp[arr_len/2];
|
||||
|
||||
do_fftr_raw(raw_data,arr_len,temp);
|
||||
(*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
|
||||
|
||||
free(c_array);
|
||||
free(raw_data);
|
||||
|
||||
// jint *arr;
|
||||
// jint length;
|
||||
// arr = (*env).GetIntArrayElements(data, NULL);
|
||||
// length = (*env).GetArrayLength(data);
|
||||
// float *raw_data = (float *) malloc(sizeof(float) * length);
|
||||
// for (int i = 0; i < length; i++) {
|
||||
// raw_data[i] = arr[i] / 32768.0f;
|
||||
// }
|
||||
//
|
||||
// jint temp[length/2];
|
||||
// //jint *fftdata;
|
||||
// //fftdata=(*env).GetIntArrayElements(fft_data, NULL);
|
||||
//
|
||||
// do_fftr_raw(raw_data,length,temp);
|
||||
//
|
||||
// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
|
||||
// free(raw_data);
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ui_SpectrumFragment_getFFTDataFloat(JNIEnv *env, jobject thiz,
|
||||
jfloatArray data, jintArray fft_data) {
|
||||
|
||||
|
||||
int arr_len=env->GetArrayLength(data);
|
||||
//将java数组复制到c数组中
|
||||
auto *c_array=(jfloat *) malloc(arr_len * sizeof(arr_len));
|
||||
|
||||
env->GetFloatArrayRegion(data,0,arr_len,c_array);
|
||||
jint temp[arr_len/2];
|
||||
|
||||
do_fftr(c_array,arr_len,temp);
|
||||
(*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
|
||||
|
||||
free(c_array);
|
||||
|
||||
|
||||
|
||||
// jfloat *arr;
|
||||
// jint length;
|
||||
// arr = (*env).GetFloatArrayElements(data, NULL);
|
||||
// length = (*env).GetArrayLength(data);
|
||||
//
|
||||
// jint temp[length/2];
|
||||
//
|
||||
// do_fftr(arr,length,temp);
|
||||
//
|
||||
// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ui_SpectrumFragment_getFFTDataRawFloat(JNIEnv *env, jobject thiz,
|
||||
jfloatArray data, jintArray fft_data) {
|
||||
|
||||
int arr_len=env->GetArrayLength(data);
|
||||
//将java数组复制到c数组中
|
||||
auto *c_array=(jfloat *) malloc(arr_len * sizeof(arr_len));
|
||||
|
||||
env->GetFloatArrayRegion(data,0,arr_len,c_array);
|
||||
jint temp[arr_len/2];
|
||||
|
||||
do_fftr_raw(c_array,arr_len,temp);
|
||||
(*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
|
||||
|
||||
free(c_array);
|
||||
|
||||
|
||||
|
||||
// jfloat *arr;
|
||||
// jint length;
|
||||
// arr = (*env).GetFloatArrayElements(data, NULL);
|
||||
// length = (*env).GetArrayLength(data);
|
||||
//
|
||||
// jint temp[length/2];
|
||||
// do_fftr_raw(arr,length,temp);
|
||||
|
||||
// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ui_SpectrumView_getFFTDataFloat(JNIEnv *env, jobject thiz, jfloatArray data,
|
||||
jintArray fft_data) {
|
||||
|
||||
int arr_len=env->GetArrayLength(data);
|
||||
//将java数组复制到c数组中
|
||||
auto *c_array=(jfloat *) malloc(arr_len * sizeof(arr_len));
|
||||
|
||||
//env->GetFloatArrayRegion(data,0,arr_len,c_array);
|
||||
(*env).GetFloatArrayRegion(data,0,arr_len,c_array);
|
||||
jint temp[arr_len/2];
|
||||
|
||||
do_fftr(c_array,arr_len,temp);
|
||||
(*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
|
||||
free(c_array);
|
||||
|
||||
// jfloat *arr;
|
||||
// jint length;
|
||||
// arr = (*env).GetFloatArrayElements(data, NULL);
|
||||
// length = (*env).GetArrayLength(data);
|
||||
//
|
||||
// jint temp[length/2];
|
||||
//
|
||||
// do_fftr(arr,length,temp);
|
||||
//
|
||||
// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ui_SpectrumView_getFFTDataRawFloat(JNIEnv *env, jobject thiz,
|
||||
jfloatArray data, jintArray fft_data) {
|
||||
|
||||
|
||||
int arr_len=env->GetArrayLength(data);
|
||||
//将java数组复制到c数组中
|
||||
auto *c_array=(jfloat *) malloc(arr_len * sizeof(arr_len));
|
||||
|
||||
//env->GetFloatArrayRegion(data,0,arr_len,c_array);
|
||||
(*env).GetFloatArrayRegion(data,0,arr_len,c_array);
|
||||
jint temp[arr_len/2];
|
||||
|
||||
do_fftr_raw(c_array,arr_len,temp);
|
||||
(*env).SetIntArrayRegion(fft_data,0,arr_len/2,temp);
|
||||
|
||||
free(c_array);
|
||||
|
||||
// jfloat *arr;
|
||||
// jint length;
|
||||
// arr = (*env).GetFloatArrayElements(data, NULL);
|
||||
// length = (*env).GetArrayLength(data);
|
||||
//
|
||||
// jint temp[length/2];
|
||||
//
|
||||
// do_fftr_raw(arr,length,temp);
|
||||
//
|
||||
// (*env).SetIntArrayRegion(fft_data,0,length/2,temp);
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
//
|
||||
// Created by jmsmf on 2022/6/1.
|
||||
//
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
#include "common/debug.h"
|
||||
#include "ft8Encoder.h"
|
||||
#include "ft8/pack.h"
|
||||
#include "ft8/encode.h"
|
||||
#include "ft8/hash22.h"
|
||||
}
|
||||
#define GFSK_CONST_K 5.336446f ///< == pi * sqrt(2 / log(2))
|
||||
|
||||
char *Jstring2CStr(JNIEnv *env, jstring jstr) {
|
||||
char *rtn = nullptr;
|
||||
jclass clsstring = env->FindClass("java/lang/String");
|
||||
jstring strencode = env->NewStringUTF("GB2312");
|
||||
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
|
||||
auto barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
|
||||
int alen = env->GetArrayLength(barr);
|
||||
jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
|
||||
if (alen > 0) {
|
||||
rtn = (char *) malloc(alen + 1); //new char[alen+1];
|
||||
memcpy(rtn, ba, alen);
|
||||
rtn[alen] = 0;
|
||||
}
|
||||
env->ReleaseByteArrayElements(barr, ba, 0);
|
||||
|
||||
return rtn;
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8transmit_FT8TransmitSignal_GenerateFt8(JNIEnv *env, jobject,
|
||||
jstring message,
|
||||
jfloat frequency,
|
||||
jshortArray buffer) {
|
||||
jshort *_buffer;
|
||||
_buffer = (*env).GetShortArrayElements(buffer, nullptr);
|
||||
char *str=Jstring2CStr(env, message);
|
||||
generateFt8ToBuffer(str, frequency, _buffer);
|
||||
(*env).ReleaseShortArrayElements(buffer,_buffer,JNI_COMMIT);
|
||||
free(str);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8transmit_GenerateFT8_pack77(JNIEnv *env, jclass, jstring msg,
|
||||
jbyteArray c77) {
|
||||
jbyte *_buffer;
|
||||
_buffer = (*env).GetByteArrayElements(c77, nullptr);
|
||||
char *str=Jstring2CStr(env, msg);
|
||||
int result=pack77(str,(uint8_t *)_buffer);
|
||||
(*env).ReleaseByteArrayElements(c77,_buffer,JNI_COMMIT);
|
||||
free(str);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8transmit_GenerateFT8_ft8_1encode(JNIEnv *env, jclass clazz,
|
||||
jbyteArray payload, jbyteArray tones) {
|
||||
jbyte *_payload;
|
||||
jbyte *_tones;
|
||||
_payload = (*env).GetByteArrayElements(payload, nullptr);
|
||||
_tones = (*env).GetByteArrayElements(tones, nullptr);
|
||||
ft8_encode((uint8_t *)_payload,(uint8_t *)_tones);
|
||||
(*env).ReleaseByteArrayElements(payload,_payload,JNI_COMMIT);
|
||||
(*env).ReleaseByteArrayElements(tones,_tones,JNI_COMMIT);
|
||||
|
||||
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8transmit_GenerateFT8_gfsk_1pulse(JNIEnv *env, jclass clazz, jint n_spsym,
|
||||
jfloat symbol_bt, jfloatArray pulse) {
|
||||
jfloat *_pulse;
|
||||
_pulse=(*env).GetFloatArrayElements(pulse, nullptr);
|
||||
|
||||
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;
|
||||
}
|
||||
(*env).ReleaseFloatArrayElements(pulse,_pulse,JNI_COMMIT);
|
||||
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8transmit_GenerateFT8_synth_1gfsk(JNIEnv *env, jclass clazz,
|
||||
jbyteArray symbols, jint n_sym, jfloat f0,
|
||||
jfloat symbol_bt, jfloat symbol_period,
|
||||
jint signal_rate, jfloatArray signal,
|
||||
jint offset) {
|
||||
jbyte *_symbols;
|
||||
jfloat *_signal;
|
||||
_symbols = (*env).GetByteArrayElements(symbols, nullptr);
|
||||
_signal = (*env).GetFloatArrayElements(signal, nullptr);
|
||||
synth_gfsk((uint8_t *)_symbols,n_sym,f0,symbol_bt,symbol_period,signal_rate,_signal+offset);
|
||||
|
||||
(*env).ReleaseByteArrayElements(symbols,_symbols,JNI_COMMIT);
|
||||
(*env).ReleaseFloatArrayElements(signal,_signal,JNI_COMMIT);
|
||||
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8signal_FT8Package_getHash12(JNIEnv *env, jclass clazz, jstring callsign) {
|
||||
char *str=Jstring2CStr(env, callsign);
|
||||
uint32_t hash=hashcall_12(str);
|
||||
free(str);
|
||||
return hash;
|
||||
}
|
||||
extern "C"
|
||||
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8transmit_GenerateFT8_packFreeTextTo77(JNIEnv *env, jclass clazz,
|
||||
jstring msg, jbyteArray c77) {
|
||||
|
||||
jbyte *_buffer;
|
||||
_buffer = (*env).GetByteArrayElements(c77, nullptr);
|
||||
char *str=Jstring2CStr(env, msg);
|
||||
packtext77(str,(uint8_t *)_buffer);
|
||||
(*env).ReleaseByteArrayElements(c77,_buffer,JNI_COMMIT);
|
||||
free(str);
|
||||
return 0;
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8signal_FT8Package_getHash10(JNIEnv *env, jclass clazz, jstring callsign) {
|
||||
char *str=Jstring2CStr(env, callsign);
|
||||
uint32_t hash=(hashcall_10(str));
|
||||
free(str);
|
||||
return hash;
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_bg7yoz_ft8cn_ft8signal_FT8Package_getHash22(JNIEnv *env, jclass clazz, jstring callsign) {
|
||||
char *str=Jstring2CStr(env, callsign);
|
||||
u_int32_t hash=hashcall_22(str);
|
||||
free(str);
|
||||
return hash;
|
||||
}
|
|
@ -1,257 +0,0 @@
|
|||
//
|
||||
// Created by jmsmf on 2022/4/22.
|
||||
//
|
||||
|
||||
#include "monitor_opr.h"
|
||||
#include "ft8/constants.h"
|
||||
//#define LOG_LEVEL LOG_DEBUG
|
||||
#define LOG_LEVEL LOG_FATAL
|
||||
|
||||
|
||||
// FFT转换,采用加窗函数,减少频谱泄露的问题。
|
||||
// Hanning窗(汉宁窗)适用于95%的情况。
|
||||
static float hann_i(int i, int N) {
|
||||
float x = sinf((float) M_PI * i / N);
|
||||
return x * x;
|
||||
}
|
||||
|
||||
static float hamming_i(int i, int N) {
|
||||
const float a0 = (float) 25 / 46;
|
||||
const float a1 = 1 - a0;
|
||||
|
||||
float x1 = cosf(2 * (float) M_PI * i / N);
|
||||
return a0 - a1 * x1;
|
||||
}
|
||||
|
||||
static float blackman_i(int i, int N) {
|
||||
const float alpha = 0.16f; // or 2860/18608
|
||||
const float a0 = (1 - alpha) / 2;
|
||||
const float a1 = 1.0f / 2;
|
||||
const float a2 = alpha / 2;
|
||||
|
||||
float x1 = cosf(2 * (float) M_PI * i / N);
|
||||
float x2 = 2 * x1 * x1 - 1; // Use double angle formula
|
||||
|
||||
return a0 - a1 * x1 + a2 * x2;
|
||||
}
|
||||
|
||||
static void
|
||||
waterfall_init(waterfall_t *me, int max_blocks, int num_bins, int time_osr, int freq_osr) {
|
||||
//mag_size,信号量数组的大小。最大块数93*时间过采样率2*频率过采样率2*分析块960*sizeOf(U_int8)
|
||||
size_t mag_size = max_blocks * time_osr * freq_osr * num_bins * sizeof(me->mag[0]);
|
||||
me->max_blocks = max_blocks;
|
||||
me->num_blocks = 0;
|
||||
me->num_bins = num_bins;//num_bins = 12000 * 0.16 / 2 = 960
|
||||
me->time_osr = time_osr;
|
||||
me->freq_osr = freq_osr;
|
||||
me->block_stride = (time_osr * freq_osr * num_bins);
|
||||
me->mag = (uint8_t *) malloc(mag_size);//申请信号量数组,用于计算得分,357120个
|
||||
me->mag2 = (float *) malloc(mag_size * sizeof(float));//申请信号量数组,用于计算信噪比
|
||||
LOG(LOG_DEBUG, "Waterfall size = %zu\n", mag_size);
|
||||
}
|
||||
|
||||
static void waterfall_free(waterfall_t *me) {
|
||||
free(me->mag);
|
||||
free(me->mag2);
|
||||
}
|
||||
|
||||
|
||||
void monitor_init(monitor_t *me, const monitor_config_t *cfg) {
|
||||
LOG(LOG_DEBUG, "Monitor is initializing...");
|
||||
//协议的时长,FT8_SLOT_TIME=15.0f,FT4_SLOT_TIME=7.5f
|
||||
float slot_time = (cfg->protocol == PROTO_FT4) ? FT4_SLOT_TIME : FT8_SLOT_TIME;
|
||||
//协议每个符号的时长,FT8_SYMBOL_PERIOD=0.160f,FT4_SYMBOL_PERIOD=0.048f
|
||||
float symbol_period = (cfg->protocol == PROTO_FT4) ? FT4_SYMBOL_PERIOD : FT8_SYMBOL_PERIOD;
|
||||
|
||||
//**************************************************
|
||||
// Compute DSP parameters that depend on the sample rate
|
||||
// 根据采样率计算DSP参数
|
||||
// block_size:每一个FSK符号占用的样本数,FT8:12000*0.16=1920个
|
||||
// subblock_size:分析移动的大小(样本数),每符号样本数/时间过采样率。FT8:1920/2=960
|
||||
// nfft:fft size。fft大小=每个FSK符号占用的样本数*频率过采样率=1920*2
|
||||
// fft_norm:FFT归一化因子。2/fft size。
|
||||
me->block_size = (int) (cfg->sample_rate *
|
||||
symbol_period); //12000*0.16 对应于一个FSK符号的样本=1920
|
||||
me->subblock_size = me->block_size / cfg->time_osr;//移动的样本数。一个FSK符号的样本数/过采样率
|
||||
//nfft是傅里叶变换前,时域的实数序列的数量。目前是一个FSK符号的样本数*频率过采样率
|
||||
me->nfft = me->block_size * cfg->freq_osr;//nfft=一个FSK符号的样本数*频率过采样率
|
||||
me->fft_norm = 2.0f / me->nfft;//< FFT归一化因子。FFT normalization factor
|
||||
// const int len_window = 1.8f * me->block_size; // hand-picked and optimized
|
||||
//**************************************************
|
||||
|
||||
// 申请窗空间,大小是fft块大小*me->windows[0]的大小
|
||||
// 采集周期如果是实际信号的非整数时,端点是不连续的。
|
||||
// 这些不连续片段在FFT中显示为高频成分。这些高频成分不存在于原信号中。
|
||||
// 这些频率可能远高于奈奎斯特频率,在0~ 采样率的一半的频率区间内产生混叠。
|
||||
// 使用FFT获得的频率,不是原信号的实际频率,而是一个改变过的频率。
|
||||
// 类似于某个频率的能量泄漏至其他频率。 这种现象叫做频谱泄漏。
|
||||
// 频率泄漏使好的频谱线扩散到更宽的信号范围中。这些不连续片段在FFT中显示为高频成分。
|
||||
// 通过加窗来尽可能减少在非整数个周期上进行FFT产生的误差。
|
||||
// 加窗可减少这些不连续部分的幅值。
|
||||
// 加窗包括将时间记录乘以有限长度的窗,窗的幅值逐渐变小,在边沿处为0。
|
||||
// 加窗的结果是尽可能呈现出一个连续的波形,减少剧烈的变化。 这种方法也叫应用一个加窗。
|
||||
// 窗函数分很多种,常见的的如:Hamming窗、Hanning窗、Blackman-Harris窗、Kaiser-Bessel窗、Flat top窗。
|
||||
// Hanning窗适用于95%的情况。
|
||||
me->window = (float *) malloc(
|
||||
me->nfft * sizeof(me->window[0]));// 申请窗空间,大小是fft块大小*sizeof(me->windows[0])
|
||||
//此处是窗函数的设置,使用常用的hanning窗
|
||||
for (int i = 0; i < me->nfft; ++i) {
|
||||
// window[i] = 1;
|
||||
me->window[i] = hann_i(i, me->nfft);// 使用Hanning窗
|
||||
// me->window[i] = blackman_i(i, me->nfft);// Blackman-Harris窗
|
||||
// me->window[i] = hamming_i(i, me->nfft);// Hamming窗
|
||||
// me->window[i] = (i < len_window) ? hann_i(i, len_window) : 0;
|
||||
}
|
||||
|
||||
|
||||
// 申请当前STFT(短时傅氏变换)分析框架(nfft样本)的空间。
|
||||
//last_frame:申请傅里叶变换分析框架用的(nfft样本)。
|
||||
// 空间大小=时域数据量*数据类型占用的空间=一个FSK符号占用的采样数据量*频率过采样率=12000*0.16*2*SizeOf(float)
|
||||
me->last_frame = (float *) malloc(me->nfft * sizeof(me->last_frame[0]));
|
||||
|
||||
// size_t 类型定义在cstddef头文件中,该文件是C标准库的头文件stddef.h的C++版。
|
||||
// 它是一个与机器相关的unsigned类型,其大小足以保证存储内存中对象的大小。
|
||||
size_t fft_work_size;
|
||||
// 第一步,获取可以用的FFT工作区域的大小到fft_work_size
|
||||
//nfft=一个FSK符号的样本数*频率过采样率=0.16*12000*2=3840
|
||||
kiss_fftr_alloc(me->nfft, 0, 0, &fft_work_size);
|
||||
//fft_work_size
|
||||
|
||||
|
||||
// 申请FFT工作区域的内存,38676个
|
||||
me->fft_work = malloc(fft_work_size);
|
||||
//第二步,返回fft的设置信息
|
||||
me->fft_cfg = kiss_fftr_alloc(me->nfft, 0, me->fft_work, &fft_work_size);
|
||||
|
||||
// 最大块数,FT8的周期时长/每个符号时长,FT8:15/0.16 =93
|
||||
const int max_blocks = (int) (slot_time / symbol_period);
|
||||
|
||||
// num_bins:如果以6.25 Hz为单位的FFT箱数量。6.25:每秒6.25个FSK符号,0.16*6.25=1。
|
||||
// num_bins = 12000 * 0.16 / 2 = 960。2是啥?是生成的频域数据量是采集数据量的一半
|
||||
const int num_bins = (int) (cfg->sample_rate * symbol_period / 2);
|
||||
|
||||
//初始化瀑布图。
|
||||
waterfall_init(&me->wf, max_blocks, num_bins, cfg->time_osr, cfg->freq_osr);
|
||||
me->wf.protocol = cfg->protocol;
|
||||
me->symbol_period = symbol_period;
|
||||
|
||||
me->max_mag = -120.0f;
|
||||
|
||||
|
||||
LOG(LOG_INFO, "Block size = %d\n", me->block_size);
|
||||
LOG(LOG_INFO, "Subblock size = %d\n", me->subblock_size);
|
||||
LOG(LOG_INFO, "N_FFT = %d\n", me->nfft);
|
||||
LOG(LOG_DEBUG, "FFT work area = %zu\n", fft_work_size);
|
||||
// 瀑布图中最多能申请max_blocks个块数
|
||||
LOG(LOG_DEBUG, "Waterfall allocated %d symbols\n", me->wf.max_blocks);
|
||||
}
|
||||
|
||||
void monitor_free(monitor_t *me) {
|
||||
|
||||
waterfall_free(&me->wf);
|
||||
free(me->fft_work);
|
||||
free(me->last_frame);
|
||||
free(me->window);
|
||||
LOG(LOG_DEBUG, "Monitor is free .");
|
||||
}
|
||||
|
||||
|
||||
// Compute FFT magnitudes (log wf) for a frame in the signal and update waterfall data
|
||||
// 计算信号中一帧的FFT幅度(log wf),并更新瀑布数据
|
||||
void monitor_process(monitor_t *me, const float *frame) {
|
||||
// Check if we can still store more waterfall data
|
||||
//防止溢出
|
||||
//mag阵列中存储的块(符号)编号
|
||||
if (me->wf.num_blocks >= me->wf.max_blocks)
|
||||
return;
|
||||
|
||||
//num_bins 的值是 12000 * 0.16 / 2 = 960
|
||||
//wf.block_strid= (time_osr * freq_osr * num_bins)=2*2*960
|
||||
//offset是在mag数组中的偏移量。wf.num_blocks是当前符号的块编号,以num_bins(数据片段)*时间过采样*频率过采样为单位。
|
||||
//mag的数组大小实际上是时间过采样*频率过采样*符号的最大量(93)*每符号真实采样数据(过采样的数据,960).
|
||||
int offset = me->wf.num_blocks * me->wf.block_stride;
|
||||
int frame_pos = 0;
|
||||
|
||||
// Loop over block subdivisions
|
||||
//循环块细分,wf.time_osr=2 时间过采样率
|
||||
for (int time_sub = 0; time_sub < me->wf.time_osr; ++time_sub) {
|
||||
kiss_fft_scalar timedata[me->nfft];
|
||||
kiss_fft_cpx freqdata[me->nfft / 2 + 1];
|
||||
|
||||
// Shift the new data into analysis frame
|
||||
//将新数据转移到分析框架中。
|
||||
// subblock_size:分析移动的大小(样本数)blocksize/2 每秒块数/时间过采样率=FT8:12000*0.16/2=1920/2=960个
|
||||
//last_frame的空间已经申请好了。空间大小=时域数据量*数据类型占用的空间=一个FSK符号占用的采样数据量*频率过采样率=12000*0.16*2*SizeOf(float)=3840
|
||||
//nfft=一个FSK符号的样本数*频率过采样率=1920*2=3840
|
||||
//subblock_size。移动的样本数。一个FSK符号的样本数/过采样率,1920/2=960
|
||||
//第一个循环,把过采样的后半段数据向前移960个数据,
|
||||
//第二个循环,把新的声音数据导入到last_frame的后半部分,新的声音数据960个。外面的时间过采样率循环2遍,正好960*2=1920,一个符号
|
||||
//这样就可以对一个符号周期的时域数据做傅里叶变换了
|
||||
for (int pos = 0; pos < me->nfft - me->subblock_size; ++pos) {
|
||||
me->last_frame[pos] = me->last_frame[pos + me->subblock_size];
|
||||
}
|
||||
for (int pos = me->nfft - me->subblock_size; pos < me->nfft; ++pos) {
|
||||
me->last_frame[pos] = frame[frame_pos];
|
||||
++frame_pos;
|
||||
}
|
||||
|
||||
|
||||
// Compute windowed analysis frame
|
||||
//用窗函数做一次转换,汉宁窗。
|
||||
for (int pos = 0; pos < me->nfft; ++pos) {
|
||||
//把last_frame中的数据赋值到timedata中来,timedata是时域数据,要做一次归一化、窗函数处理
|
||||
timedata[pos] = me->fft_norm * me->window[pos] * me->last_frame[pos];
|
||||
//timedata[pos] =me->window[pos] * me->last_frame[pos];
|
||||
}
|
||||
|
||||
//傅里叶变换把timedata的时域数据(长度是nfft)转换到频域数据上来。频域数据是复数数组,数组长度是nfft/2+1
|
||||
//nfft=一个FSK符号的样本数*频率过采样率=12000*0.16*2=3840
|
||||
kiss_fftr(me->fft_cfg, timedata, freqdata);
|
||||
|
||||
// Loop over two possible frequency bin offsets (for averaging)
|
||||
//在两个可能的频率单元偏移上循环(用于平均)
|
||||
//两个循环的意义是:在一个符号采样数据的范围内(12000*0.16)对freqdata的能量做计算
|
||||
for (int freq_sub = 0; freq_sub < me->wf.freq_osr; ++freq_sub) {
|
||||
for (int bin = 0; bin < me->wf.num_bins; ++bin) { //num_bins 的值是 12000 * 0.16 / 2 = 960
|
||||
|
||||
//循环次数=2*960=1920
|
||||
//信号量位置src_bin,
|
||||
int src_bin = (bin * me->wf.freq_osr) + freq_sub;
|
||||
|
||||
////此位置可能是计算信噪比的位置
|
||||
//求各频率点上的信号强度是傅里叶之后的平方?少了开方,mag2应当是信号量的平方
|
||||
float mag2 = (freqdata[src_bin].i * freqdata[src_bin].i) +
|
||||
(freqdata[src_bin].r * freqdata[src_bin].r);
|
||||
float db = 10.0f * log10f(1E-12f + mag2);
|
||||
|
||||
//把信号量保存下来
|
||||
//offset=me->wf.num_blocks * me->wf.block_stride;
|
||||
//wf.block_strid= (time_osr * freq_osr * num_bins)=2*2*960
|
||||
//偏移量就是当前块编号*每个符号ftt数据量
|
||||
|
||||
me->wf.mag2[offset] = mag2;
|
||||
|
||||
//每循环一次,偏移量移一位。共移动time_osr*freq_osr*num_bins=2*2*960=3840
|
||||
|
||||
// Scale decibels to unsigned 8-bit range and clamp the value
|
||||
//将分贝缩放到无符号8位范围,并钳制该值
|
||||
// Range 0-240 covers -120..0 dB in 0.5 dB steps
|
||||
int scaled = (int) (2 * db + 240);
|
||||
|
||||
//0~255之间
|
||||
me->wf.mag[offset] = (scaled < 0) ? 0 : ((scaled > 255) ? 255 : scaled);
|
||||
++offset;
|
||||
|
||||
if (db > me->max_mag)
|
||||
me->max_mag = db;
|
||||
}
|
||||
}
|
||||
}
|
||||
++me->wf.num_blocks;//mag阵列中存储的块(符号)编号,块大小:2*2*960
|
||||
}
|
||||
|
||||
void monitor_reset(monitor_t *me) {
|
||||
LOG(LOG_DEBUG, "Monitor is resetting...");
|
||||
me->wf.num_blocks = 0;
|
||||
me->max_mag = -120.0f;
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include "ft8/decode.h"
|
||||
#include "fft/kiss_fftr.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#define LOG_LEVEL LOG_INFO
|
||||
|
||||
|
||||
|
||||
/// Configuration options for FT4/FT8 monitor
|
||||
typedef struct {
|
||||
float f_min; ///< 最低频率界限,Lower frequency bound for analysis
|
||||
float f_max; ///< 最高频率界限,Upper frequency bound for analysis
|
||||
int sample_rate; ///< 采样率,Sample rate in Hertz
|
||||
int time_osr; ///< 时间过采样率,Number of time subdivisions
|
||||
int freq_osr; ///< 频率过采样率,Number of frequency subdivisions
|
||||
ftx_protocol_t protocol; ///< Protocol: FT4 or FT8
|
||||
} monitor_config_t;
|
||||
|
||||
/// FT4/FT8 monitor object that manages DSP processing of incoming audio data
|
||||
/// and prepares a waterfall object
|
||||
typedef struct {
|
||||
float symbol_period; ///< FT4/FT8符号周期(秒)。FT4/FT8 symbol period in seconds
|
||||
int block_size; ///< 每个符号(FSK)的样本数。Number of samples per symbol (block)
|
||||
int subblock_size; ///< 分析移动的大小(样本数)。Analysis shift size (number of samples)
|
||||
int nfft; ///< FFT size
|
||||
float fft_norm; ///< FFT归一化因子。FFT normalization factor
|
||||
float *window; ///< STFT分析的窗口函数(nfft样本)。Window function for STFT analysis (nfft samples)
|
||||
float *last_frame; ///< 当前STFT分析框架(nfft样本)。Current STFT analysis frame (nfft samples)
|
||||
waterfall_t wf; ///< 瀑布对象。Waterfall object
|
||||
float max_mag; ///< 最大检测量(调试统计)。Maximum detected magnitude (debug stats)
|
||||
|
||||
// KISS FFT housekeeping variables
|
||||
void *fft_work; ///< FFT需要的工作区域。Work area required by Kiss FFT
|
||||
kiss_fftr_cfg fft_cfg; ///< Kiss FFT需要的设置信息。Kiss FFT housekeeping object
|
||||
} monitor_t;
|
||||
|
||||
|
||||
void monitor_init(monitor_t *me, const monitor_config_t *cfg);
|
||||
void monitor_free(monitor_t* me);
|
||||
void monitor_process(monitor_t *me, const float *frame);
|
||||
void monitor_reset(monitor_t *me);
|
|
@ -1,125 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include "spectrum_data.h"
|
||||
|
||||
static float hann_i(int i, int N) {
|
||||
float x = sinf((float) M_PI * i / N);
|
||||
return x * x;
|
||||
}
|
||||
|
||||
void do_fftr(float *voiceData, int dataSize, int *fftData) {
|
||||
// int block_size = FT8_SYMBOL_PERIOD * 12000; //=1920
|
||||
int fftSize = FT8_SYMBOL_PERIOD * 12000; //=1920
|
||||
float *window = (float *) malloc(
|
||||
fftSize * sizeof(window[0])); // 申请窗空间,大小是fft块大小*sizeof(windows[0])
|
||||
for (int i = 0; i < fftSize; ++i) //汉宁窗
|
||||
{
|
||||
window[i] = hann_i(i, fftSize);
|
||||
}
|
||||
// 申请当前STFT分析框架(nfft样本)的空间。
|
||||
//float *last_frame = (float *) malloc(fftSize * sizeof(last_frame[0]));
|
||||
|
||||
size_t fft_work_size;
|
||||
|
||||
// 获取FFT工作区域的大小到fft_work_size
|
||||
kiss_fftr_alloc(fftSize, 0, 0, &fft_work_size);
|
||||
|
||||
// 申请FFT工作区域的内存
|
||||
void *fft_work = malloc(fft_work_size);
|
||||
kiss_fftr_cfg fft_cfg = kiss_fftr_alloc(fftSize, 0, fft_work, &fft_work_size);
|
||||
|
||||
// 最大块数,FT8的周期时长/每个符号时长,FT8:15/0.16 =93
|
||||
const int max_blocks = (int) (FT8_SLOT_TIME / FT8_SYMBOL_PERIOD);
|
||||
const int num_bins = (int) (12000 * FT8_SYMBOL_PERIOD / 2);
|
||||
int fftOffset = 0;
|
||||
int offset = 0;
|
||||
float maxMag = 0;
|
||||
float minMag = 65535.0f;
|
||||
float mags[dataSize / 2];
|
||||
for (int pos = 0; pos < dataSize / fftSize; pos++) {
|
||||
kiss_fft_scalar timedata[fftSize];
|
||||
kiss_fft_cpx freqdata[fftSize / 2 + 1];
|
||||
for (int i = 0; i < fftSize; i++) {//fftSize=3840
|
||||
timedata[i] = window[i] * voiceData[offset];//window[i] *
|
||||
offset++;
|
||||
}
|
||||
kiss_fftr(fft_cfg, timedata, freqdata);
|
||||
|
||||
for (int i = 1; i < fftSize / 2 + 1; i++) {
|
||||
float mag2 = sqrtf(freqdata[i].i * freqdata[i].i + freqdata[i].r * freqdata[i].r);
|
||||
mags[fftOffset] = mag2;
|
||||
if (maxMag < mag2) {
|
||||
maxMag = mag2;
|
||||
}
|
||||
if (minMag > mag2) {
|
||||
minMag = mag2;
|
||||
}
|
||||
fftOffset++;
|
||||
}
|
||||
|
||||
float normal = (maxMag - minMag) / 256;
|
||||
for (int i = 0; i < dataSize / 2; ++i) {
|
||||
fftData[i] = roundf((mags[i] - minMag) / normal);
|
||||
}
|
||||
}
|
||||
free(fft_work);
|
||||
free(window);
|
||||
//free(last_frame);
|
||||
//free(fft_cfg);
|
||||
}
|
||||
|
||||
void do_fftr_raw(float *voiceData, int dataSize, int *fftData) {
|
||||
//int block_size = FT8_SYMBOL_PERIOD * 12000; //=1920
|
||||
int fftSize = FT8_SYMBOL_PERIOD * 12000; //=1920
|
||||
float *window = (float *) malloc(
|
||||
fftSize * sizeof(window[0])); // 申请窗空间,大小是fft块大小*sizeof(windows[0])
|
||||
for (int i = 0; i < fftSize; ++i) //汉宁窗
|
||||
{
|
||||
window[i] = hann_i(i, fftSize);
|
||||
}
|
||||
// 申请当前STFT分析框架(nfft样本)的空间。
|
||||
//float *last_frame = (float *) malloc(fftSize * sizeof(last_frame[0]));
|
||||
|
||||
size_t fft_work_size;
|
||||
|
||||
// 获取FFT工作区域的大小到fft_work_size
|
||||
kiss_fftr_alloc(fftSize, 0, 0, &fft_work_size);
|
||||
|
||||
// 申请FFT工作区域的内存
|
||||
void *fft_work = malloc(fft_work_size);
|
||||
kiss_fftr_cfg fft_cfg = kiss_fftr_alloc(fftSize, 0, fft_work, &fft_work_size);
|
||||
|
||||
// 最大块数,FT8的周期时长/每个符号时长,FT8:15/0.16 =93
|
||||
const int max_blocks = (int) (FT8_SLOT_TIME / FT8_SYMBOL_PERIOD);
|
||||
const int num_bins = (int) (12000 * FT8_SYMBOL_PERIOD / 2);
|
||||
int fftOffset = 0;
|
||||
int offset = 0;
|
||||
|
||||
//float mags[dataSize / 2];
|
||||
for (int pos = 0; pos < dataSize / fftSize; pos++) {
|
||||
kiss_fft_scalar timedata[fftSize];
|
||||
kiss_fft_cpx freqdata[fftSize / 2 + 1];
|
||||
for (int i = 0; i < fftSize; i++) {//fftSize=3840
|
||||
timedata[i] = window[i] * voiceData[offset];//window[i] *
|
||||
offset++;
|
||||
}
|
||||
kiss_fftr(fft_cfg, timedata, freqdata);
|
||||
|
||||
for (int i = 1; i < fftSize / 2 + 1; i++) {
|
||||
float mag2 =(freqdata[i].i * freqdata[i].i + freqdata[i].r * freqdata[i].r);
|
||||
mag2= 10.0f * log10f(1E-12f + mag2);
|
||||
int scaled = (int) (mag2 +20)*4;
|
||||
|
||||
//0~255之间
|
||||
fftData[fftOffset] = (scaled < 0) ? 0 : ((scaled > 255) ? 255 : scaled);
|
||||
|
||||
fftOffset++;
|
||||
}
|
||||
|
||||
// float normal = (maxMag - minMag) / 256;
|
||||
// for (int i = 0; i < dataSize / 2; ++i) {
|
||||
// fftData[i] = roundf((mags[i] - minMag) / normal);
|
||||
// }
|
||||
}
|
||||
free(fft_work);
|
||||
free(window);
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "ft8/constants.h"
|
||||
#include "common/debug.h"
|
||||
#include "fft/kiss_fftr.h"
|
||||
|
||||
/**
|
||||
* 对符号进行快速傅里叶变换,以1920块数据变换,生成960块。0~3000Hz
|
||||
* normalization=1对数据归一化处理,方便显示FT8信号的频率
|
||||
* @param voiceData 声音数据
|
||||
* @param dataSize 声音数据的大小
|
||||
* @param fftData fft数据
|
||||
* @param normalization 是否是归一化处理
|
||||
*/
|
||||
void do_fftr(float* voiceData, int dataSize, int* fftData);
|
||||
void do_fftr_raw(float *voiceData, int dataSize, int *fftData);
|
Ładowanie…
Reference in New Issue