FT8CN/ft8CN/app/src/main/cpp/ft8Decoder.c

293 wiersze
12 KiB
C
Czysty Wina Historia

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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