/** * receiver.c — OFDM-приёмник с RS(255,223) FEC и частотной коррекцией */ #include "common.h" #define BUF_SAMPLES 32768 #define MAX_FRAME_SAMPLES (1 << 16) #define RX_GAIN 50.0 #define CFO_FILTER_ALPHA 0.95f #define CFO_MAX_VALID 0.5f extern volatile sig_atomic_t stop; static uint64_t calls = 0, hdr_ok = 0, crc_raw = 0; static uint64_t rs_saved = 0, crc_final = 0, total_bytes = 0; static float rssi = -100, evm = 0; static float cfo_est = 0.0f; static nco_crcf nco = NULL; static fec decoder = NULL; static int fec_on = 0; static liquid_float_complex frame[MAX_FRAME_SAMPLES]; static int callback(unsigned char *hdr, int hdr_valid, unsigned char *pay, unsigned int pay_len, int pay_valid, framesyncstats_s stats, void *user) { (void)user; calls++; rssi = stats.rssi; evm = stats.evm; // Обновляем CFO только если NCO инициализирован if (nco && fabsf(stats.cfo) < CFO_MAX_VALID) { cfo_est = CFO_FILTER_ALPHA * cfo_est + (1.0f - CFO_FILTER_ALPHA) * stats.cfo; nco_crcf_set_frequency(nco, cfo_est); } if (!hdr_valid) return 0; hdr_ok++; if (pay_valid) crc_raw++; uint16_t original_len = (hdr[6] << 8) | hdr[7]; uint8_t last_block_bytes = hdr[10]; if (fec_on && decoder && pay_len >= RS_ENC) { size_t nblocks = pay_len / RS_ENC; if (nblocks == 0 || nblocks > 100) return 0; uint8_t recovered[4096]; int rs_ok = 1; for (size_t b = 0; b < nblocks; b++) { if (fec_decode(decoder, RS_DATA, pay + b * RS_ENC, recovered + b * RS_DATA) != 0) { rs_ok = 0; break; } } if (rs_ok) { if (!pay_valid) rs_saved++; size_t out_len = (nblocks - 1) * RS_DATA + (last_block_bytes > 0 ? last_block_bytes : RS_DATA); if (out_len > nblocks * RS_DATA) out_len = nblocks * RS_DATA; if (out_len > original_len) out_len = original_len; if (out_len > 0) { fwrite(recovered, 1, out_len, stdout); fflush(stdout); total_bytes += out_len; crc_final++; } } } else if (pay_valid && original_len > 0 && original_len <= pay_len) { fwrite(pay, 1, original_len, stdout); fflush(stdout); total_bytes += original_len; crc_final++; } return 0; } int main(int argc, char *argv[]) { long long freq = DEFAULT_FREQ, rate = DEFAULT_RATE, bw = DEFAULT_BW; const char *uri = RX_URI_DEFAULT; const char *fec_opt = "rs8"; int opt; while ((opt = getopt(argc, argv, "f:r:b:u:c:h")) != -1) { switch (opt) { case 'f': freq = atoll(optarg); break; case 'r': rate = atoll(optarg); break; case 'b': bw = atoll(optarg); break; case 'u': uri = optarg; break; case 'c': fec_opt = optarg; break; case 'h': print_usage(argv[0], "ПРИЁМНИК"); fprintf(stderr, "\nСПЕЦИФИЧНЫЕ ОПЦИИ RX:\n"); fprintf(stderr, " Усиление фиксированное: %.0f дБ\n", RX_GAIN); fprintf(stderr, " Частотная коррекция: адаптивная (α=%.2f)\n", CFO_FILTER_ALPHA); fprintf(stderr, "\nОПТИМАЛЬНЫЙ ЗАПУСК:\n"); fprintf(stderr, " %s -f %lld -r %lld -b %lld -c rs8 > output.bin\n", argv[0], DEFAULT_FREQ, DEFAULT_RATE, DEFAULT_BW); return 0; default: return 1; } } fec_on = !strcmp(fec_opt, "rs8"); setup_signal_handlers(); print_config("ПРИЁМНИК", freq, rate, bw, fec_on); fprintf(stderr, "Усиление: %.0f дБ (ручное)\n", RX_GAIN); fprintf(stderr, "CFO фильтр: α=%.2f\n", CFO_FILTER_ALPHA); fprintf(stderr, "URI: %s\n", uri); fprintf(stderr, "═══════════════════════════════════════════════════\n"); struct iio_context *ctx = NULL; struct iio_device *phy = NULL, *rx = NULL; if (pluto_init(uri, &ctx, &phy, &rx, 0) != 0) return 1; pluto_configure(phy, freq, rate, bw, RX_GAIN, 0); iio_channel_enable(iio_device_find_channel(rx, "voltage0", false)); iio_channel_enable(iio_device_find_channel(rx, "voltage1", false)); struct iio_buffer *buf = iio_device_create_buffer(rx, BUF_SAMPLES, false); if (!buf) { fprintf(stderr, "Ошибка создания буфера приёма\n"); iio_context_destroy(ctx); return 1; } // Инициализация FEC if (fec_on) { decoder = fec_create(LIQUID_FEC_RS_M8, NULL); if (!decoder) { fprintf(stderr, "Ошибка создания FEC-декодера\n"); } } // Инициализация NCO для частотной коррекции nco = nco_crcf_create(LIQUID_NCO); if (!nco) { fprintf(stderr, "Ошибка создания NCO\n"); } else { nco_crcf_set_frequency(nco, 0.0f); } ofdmflexframesync fs = ofdmflexframesync_create( OFDM_M, OFDM_CP, OFDM_TAPER, NULL, callback, NULL); if (!fs) { fprintf(stderr, "Ошибка создания OFDM-синхронизатора\n"); if (decoder) fec_destroy(decoder); if (nco) nco_crcf_destroy(nco); iio_buffer_destroy(buf); iio_context_destroy(ctx); return 1; } fprintf(stderr, "Приёмник готов. Ожидание сигнала...\n\n"); fprintf(stderr, "Кадры | Загол. | CRC | RS испр | Вывод | RSSI | EVM | CFO\n"); fprintf(stderr, "--------+--------+------+---------+--------+-------+-------+----------\n"); time_t last = time(NULL); while (!stop) { ssize_t n = iio_buffer_refill(buf); if (n < 0) { usleep(500); continue; } int16_t *bb = (int16_t *)iio_buffer_start(buf); int ns = (iio_buffer_end(buf) - iio_buffer_start(buf)) / sizeof(int16_t) / 2; if (ns > MAX_FRAME_SAMPLES) ns = MAX_FRAME_SAMPLES; for (int i = 0; i < ns; i++) { frame[i] = ((float)bb[2*i] / 2048.0f) + ((float)bb[2*i+1] / 2048.0f) * _Complex_I; if (nco) { nco_crcf_mix_down(nco, frame[i], &frame[i]); nco_crcf_step(nco); } } if (nco) nco_crcf_set_phase(nco, 0.0f); ofdmflexframesync_execute(fs, frame, ns); if (time(NULL) > last) { fprintf(stderr, "\r%6llu | %5llu | %4llu | %6llu | %6llu | %+5.0f | %+5.1f | %+8.4f", (unsigned long long)calls, (unsigned long long)hdr_ok, (unsigned long long)crc_raw, (unsigned long long)rs_saved, (unsigned long long)crc_final, rssi, evm, cfo_est); fflush(stderr); last = time(NULL); } } fprintf(stderr, "\n\nПРИЁМ ЗАВЕРШЁН\n"); fprintf(stderr, "Кадров: %llu | Заголовков: %llu | CRC: %llu | RS испр: %llu | Вывод: %llu | Байт: %llu\n", (unsigned long long)calls, (unsigned long long)hdr_ok, (unsigned long long)crc_raw, (unsigned long long)rs_saved, (unsigned long long)crc_final, (unsigned long long)total_bytes); if (decoder) fec_destroy(decoder); if (nco) nco_crcf_destroy(nco); ofdmflexframesync_destroy(fs); iio_buffer_destroy(buf); iio_context_destroy(ctx); return 0; }