Доработка синхронизации
This commit is contained in:
117
SDR/common.c
Normal file
117
SDR/common.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* common.c — Реализация общих функций для приёмника и передатчика
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
// Глобальный флаг завершения (используется обоими модулями)
|
||||
volatile sig_atomic_t stop = 0;
|
||||
|
||||
/**
|
||||
* Обработчик сигналов завершения
|
||||
*/
|
||||
static void sigint_handler(int s) {
|
||||
(void)s;
|
||||
stop = 1;
|
||||
}
|
||||
|
||||
void setup_signal_handlers(void) {
|
||||
signal(SIGINT, sigint_handler);
|
||||
signal(SIGTERM, sigint_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
|
||||
void print_usage(const char *prog_name, const char *mode) {
|
||||
fprintf(stderr, "═══════════════════════════════════════════════════\n");
|
||||
fprintf(stderr, "OFDM %s — PlutoSDR Radio Link\n", mode);
|
||||
fprintf(stderr, "═══════════════════════════════════════════════════\n");
|
||||
fprintf(stderr, "Использование: %s [ОПЦИИ]\n\n", prog_name);
|
||||
fprintf(stderr, "ОБЩИЕ ОПЦИИ:\n");
|
||||
fprintf(stderr, " -f ЧАСТОТА Частота несущей в Гц (по умолчанию: %.0f)\n",
|
||||
(double)DEFAULT_FREQ);
|
||||
fprintf(stderr, " -r СКОРОСТЬ Частота дискретизации (по умолчанию: %.0f)\n",
|
||||
(double)DEFAULT_RATE);
|
||||
fprintf(stderr, " -b ПОЛОСА Полоса пропускания (по умолчанию: %.0f)\n",
|
||||
(double)DEFAULT_BW);
|
||||
fprintf(stderr, " -u URI URI PlutoSDR\n");
|
||||
fprintf(stderr, " -c РЕЖИМ FEC: rs8 или none (по умолчанию: rs8)\n");
|
||||
fprintf(stderr, " -h Показать эту справку\n");
|
||||
}
|
||||
|
||||
void print_config(const char *mode, long long freq, long long rate,
|
||||
long long bw, int fec_on) {
|
||||
fprintf(stderr, "═══════════════════════════════════════════════════\n");
|
||||
fprintf(stderr, "КОНФИГУРАЦИЯ %s\n", mode);
|
||||
fprintf(stderr, "Частота: %.1f МГц\n", freq / 1e6);
|
||||
fprintf(stderr, "Дискрет.: %.2f МГц\n", rate / 1e6);
|
||||
fprintf(stderr, "Полоса: %.2f МГц\n", bw / 1e6);
|
||||
fprintf(stderr, "OFDM: M=%d, CP=%d, Taper=%d\n",
|
||||
OFDM_M, OFDM_CP, OFDM_TAPER);
|
||||
fprintf(stderr, "FEC: %s\n", fec_on ? "RS(255,223)" : "ОТКЛЮЧЕН");
|
||||
}
|
||||
|
||||
int pluto_init(const char *uri, struct iio_context **ctx,
|
||||
struct iio_device **phy, struct iio_device **dev, int is_tx) {
|
||||
// Создание контекста
|
||||
*ctx = iio_create_context_from_uri(uri);
|
||||
if (!*ctx) {
|
||||
LOG_ERROR("Не удалось подключиться к PlutoSDR по адресу %s", uri);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Поиск устройств
|
||||
*phy = iio_context_find_device(*ctx, "ad9361-phy");
|
||||
*dev = iio_context_find_device(*ctx,
|
||||
is_tx ? "cf-ad9361-dds-core-lpc" : "cf-ad9361-lpc");
|
||||
|
||||
if (!*phy || !*dev) {
|
||||
LOG_ERROR("Радиоустройства не найдены в контексте");
|
||||
iio_context_destroy(*ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pluto_configure(struct iio_device *phy, long long freq, long long rate,
|
||||
long long bw, double gain, int is_tx) {
|
||||
const char *voltage_ch = is_tx ? "voltage0" : "voltage0";
|
||||
const char *altvoltage_ch = is_tx ? "altvoltage1" : "altvoltage0";
|
||||
bool output = is_tx ? true : false;
|
||||
|
||||
// Установка частоты несущей
|
||||
iio_channel_attr_write_longlong(
|
||||
iio_device_find_channel(phy, altvoltage_ch, output),
|
||||
"frequency", freq);
|
||||
|
||||
// Установка частоты дискретизации
|
||||
iio_channel_attr_write_longlong(
|
||||
iio_device_find_channel(phy, voltage_ch, output),
|
||||
"sampling_frequency", rate);
|
||||
|
||||
// Установка полосы пропускания
|
||||
iio_channel_attr_write_longlong(
|
||||
iio_device_find_channel(phy, voltage_ch, output),
|
||||
"rf_bandwidth", bw);
|
||||
|
||||
if (is_tx) {
|
||||
// Настройки передатчика
|
||||
iio_channel_attr_write(
|
||||
iio_device_find_channel(phy, voltage_ch, output),
|
||||
"rf_port_select", "A");
|
||||
iio_channel_attr_write_double(
|
||||
iio_device_find_channel(phy, voltage_ch, output),
|
||||
"hardwaregain", gain);
|
||||
} else {
|
||||
// Настройки приёмника
|
||||
iio_channel_attr_write(
|
||||
iio_device_find_channel(phy, voltage_ch, output),
|
||||
"gain_control_mode", "manual");
|
||||
iio_channel_attr_write_double(
|
||||
iio_device_find_channel(phy, voltage_ch, output),
|
||||
"hardwaregain", gain);
|
||||
iio_channel_attr_write(
|
||||
iio_device_find_channel(phy, voltage_ch, output),
|
||||
"rf_port_select", "A_BALANCED");
|
||||
}
|
||||
}
|
||||
151
SDR/common.h
Normal file
151
SDR/common.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* common.h — Общие определения и конфигурация для приёмника и передатчика
|
||||
* OFDM + RS(255,223) FEC + PlutoSDR
|
||||
*
|
||||
* Включает:
|
||||
* - Параметры OFDM-модуляции/демодуляции
|
||||
* - Параметры помехоустойчивого кодирования Рида-Соломона
|
||||
* - Настройки радиотракта по умолчанию
|
||||
* - Общие макросы и прототипы функций
|
||||
*/
|
||||
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <signal.h>
|
||||
#include <getopt.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <iio.h>
|
||||
#include <liquid/liquid.h>
|
||||
|
||||
// ============================================================
|
||||
// ПАРАМЕТРЫ OFDM-МОДУЛЯЦИИ (ОБЩИЕ ДЛЯ ПРИЁМНИКА И ПЕРЕДАТЧИКА)
|
||||
// ============================================================
|
||||
#define OFDM_M 64 // Количество поднесущих
|
||||
#define OFDM_CP 16 // Длина циклического префикса
|
||||
#define OFDM_TAPER 4 // Длина сглаживающего окна
|
||||
|
||||
// ============================================================
|
||||
// ПАРАМЕТРЫ ПОМЕХОУСТОЙЧИВОГО КОДИРОВАНИЯ РИДА-СОЛОМОНА
|
||||
// ============================================================
|
||||
#define RS_DATA 223 // Размер блока данных (байт)
|
||||
#define RS_ENC 255 // Размер кодированного блока (байт)
|
||||
|
||||
// ============================================================
|
||||
// ПАРАМЕТРЫ РАДИОТРАКТА ПО УМОЛЧАНИЮ
|
||||
// ============================================================
|
||||
#define DEFAULT_FREQ 1265000000 // Частота несущей: 1265 МГц
|
||||
#define DEFAULT_RATE 3840000 // Частота дискретизации: 3.84 МГц
|
||||
#define DEFAULT_BW 3000000 // Полоса пропускания: 3 МГц
|
||||
|
||||
// ============================================================
|
||||
// АДРЕСА PLUTOSDR ПО УМОЛЧАНИЮ
|
||||
// ============================================================
|
||||
#define TX_URI_DEFAULT "ip:192.168.3.1" // Адрес передатчика
|
||||
#define RX_URI_DEFAULT "ip:192.168.2.1" // Адрес приёмника
|
||||
|
||||
// ============================================================
|
||||
// ОБЩИЕ ВСПОМОГАТЕЛЬНЫЕ МАКРОСЫ
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Безопасное освобождение памяти с проверкой указателя
|
||||
*/
|
||||
#define SAFE_FREE(ptr) do { \
|
||||
if ((ptr) != NULL) { \
|
||||
free(ptr); \
|
||||
(ptr) = NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* Вывод отладочного сообщения с меткой времени
|
||||
*/
|
||||
#define LOG_INFO(fmt, ...) \
|
||||
fprintf(stderr, "[INFO] " fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#define LOG_ERROR(fmt, ...) \
|
||||
fprintf(stderr, "[ОШИБКА] " fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#define LOG_WARN(fmt, ...) \
|
||||
fprintf(stderr, "[ВНИМ.] " fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
// ============================================================
|
||||
// СТРУКТУРА ЗАГОЛОВКА КАДРА (12 байт)
|
||||
// ============================================================
|
||||
/**
|
||||
* Формат заголовка кадра:
|
||||
* Байты 0-1: Сигнатура начала кадра (0xF0, 0xAA)
|
||||
* Байты 2-5: Порядковый номер кадра (32 бита)
|
||||
* Байты 6-7: Размер исходных данных (16 бит)
|
||||
* Байты 8-9: Количество блоков FEC (16 бит)
|
||||
* Байт 10: Размер последнего блока RS
|
||||
* Байт 11: Зарезервирован
|
||||
*/
|
||||
#define HDR_SIGNATURE_0 0xF0 // Первый байт сигнатуры
|
||||
#define HDR_SIGNATURE_1 0xAA // Второй байт сигнатуры
|
||||
#define HDR_SIZE 12 // Размер заголовка в байтах
|
||||
|
||||
// ============================================================
|
||||
// ПРОТОТИПЫ ОБЩИХ ФУНКЦИЙ
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Инициализация общего обработчика сигналов завершения
|
||||
* Устанавливает обработчики для SIGINT и SIGTERM
|
||||
*/
|
||||
void setup_signal_handlers(void);
|
||||
|
||||
/**
|
||||
* Вывод справки по использованию
|
||||
*
|
||||
* @param prog_name Имя исполняемого файла
|
||||
* @param mode Режим работы: "TX" или "RX"
|
||||
*/
|
||||
void print_usage(const char *prog_name, const char *mode);
|
||||
|
||||
/**
|
||||
* Форматированный вывод конфигурации радиотракта
|
||||
*
|
||||
* @param mode Режим: "ПЕРЕДАТЧИК" или "ПРИЁМНИК"
|
||||
* @param freq Частота несущей
|
||||
* @param rate Частота дискретизации
|
||||
* @param bw Полоса пропускания
|
||||
* @param fec_on Флаг активации FEC
|
||||
*/
|
||||
void print_config(const char *mode, long long freq, long long rate,
|
||||
long long bw, int fec_on);
|
||||
|
||||
/**
|
||||
* Инициализация контекста PlutoSDR и базовых устройств
|
||||
*
|
||||
* @param uri URI подключения
|
||||
* @param ctx Указатель на контекст (выход)
|
||||
* @param phy Указатель на физическое устройство (выход)
|
||||
* @param dev Указатель на устройство TX/RX (выход)
|
||||
* @param is_tx Флаг направления (1=TX, 0=RX)
|
||||
* @return 0 при успехе, -1 при ошибке
|
||||
*/
|
||||
int pluto_init(const char *uri, struct iio_context **ctx,
|
||||
struct iio_device **phy, struct iio_device **dev, int is_tx);
|
||||
|
||||
/**
|
||||
* Настройка параметров радиотракта
|
||||
*
|
||||
* @param phy Физическое устройство
|
||||
* @param freq Частота несущей
|
||||
* @param rate Частота дискретизации
|
||||
* @param bw Полоса пропускания
|
||||
* @param gain Усиление (только для TX и manual RX)
|
||||
* @param is_tx Флаг направления
|
||||
*/
|
||||
void pluto_configure(struct iio_device *phy, long long freq, long long rate,
|
||||
long long bw, double gain, int is_tx);
|
||||
|
||||
#endif // COMMON_H
|
||||
538
SDR/reciever.c
538
SDR/reciever.c
@@ -1,23 +1,14 @@
|
||||
/**
|
||||
* rx.c — QPSK приёмник для PlutoSDR (production)
|
||||
* receiver_advanced.c — OFDM + RS(255,223) FEC + PlutoSDR + ЧАСТОТНАЯ КОРРЕКЦИЯ
|
||||
*
|
||||
* Использует framesync64 из liquid-dsp для декодирования кадров.
|
||||
* Архитектура: Pluto ADC → int16 I/Q → float complex → framesync64 → callback
|
||||
* ОПТИМАЛЬНАЯ КОМАНДА ЗАПУСКА:
|
||||
* ./receiver_advanced -f 1265000000 -r 3840000 -b 2000000 -c rs8 > output.bin
|
||||
*
|
||||
* Зависимости:
|
||||
* - libiio (PlutoSDR API)
|
||||
* - libliquid (liquid-dsp, framesync64)
|
||||
* - libm (математика)
|
||||
* - libpthread (сигналы)
|
||||
*
|
||||
* Компиляция:
|
||||
* gcc -o rx rx.c -liio -lliquid -lm -lpthread -Wall -O2
|
||||
*
|
||||
* Примеры запуска:
|
||||
* ./rx # значения по умолчанию
|
||||
* ./rx -f 2400000000 -r 4000000 # 2.4 ГГц, 4 MSPS
|
||||
* ./rx -b 16384 # размер буфера 16384
|
||||
* ./rx -h # справка
|
||||
* ОСОБЕННОСТИ ДАННОЙ ВЕРСИИ:
|
||||
* - Адаптивная компенсация частотного сдвига (CFO) через NCO
|
||||
* - Ручное управление усилением для максимальной стабильности
|
||||
* - Экспоненциальная фильтрация оценки CFO (α=0.95)
|
||||
* - Улучшенная работа в условиях нестабильных генераторов
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -32,286 +23,389 @@
|
||||
#include <iio.h>
|
||||
#include <liquid/liquid.h>
|
||||
|
||||
/* ============================================================
|
||||
* Значения по умолчанию
|
||||
* ============================================================ */
|
||||
// Параметры приёма по умолчанию
|
||||
#define DEFAULT_FREQ 1265000000 // Частота несущей: 1265 МГц
|
||||
#define DEFAULT_RATE 3840000 // Частота дискретизации: 3.84 МГц
|
||||
#define DEFAULT_BW 3000000 // Полоса пропускания: 3 МГц
|
||||
#define BUF_SAMPLES 32768 // Размер аппаратного буфера в отсчётах
|
||||
#define MAX_FRAME_SAMPLES (1 << 16) // Максимальный размер кадра в отсчётах (65536)
|
||||
|
||||
#define DEFAULT_CENTER_FREQ 1300000000 /* 1300 МГц */
|
||||
#define DEFAULT_SAMPLE_RATE 3840000 /* 3.84 MSPS */
|
||||
#define DEFAULT_BUF_SAMPLES 16384 /* размер DMA буфера */
|
||||
#define DEFAULT_URI "ip:192.168.2.1"
|
||||
// Параметры OFDM-демодуляции
|
||||
#define OFDM_M 64 // Количество поднесущих
|
||||
#define OFDM_CP 16 // Длина циклического префикса
|
||||
#define OFDM_TAPER 4 // Длина сглаживающего окна
|
||||
|
||||
// Параметры помехоустойчивого декодирования Рида-Соломона
|
||||
#define RS_DATA 223 // Размер блока данных (байт)
|
||||
#define RS_ENC 255 // Размер кодированного блока (байт)
|
||||
|
||||
/* ============================================================
|
||||
* Глобальные переменные
|
||||
* ============================================================ */
|
||||
// Параметры частотной коррекции
|
||||
#define CFO_FILTER_ALPHA 0.95f // Коэффициент фильтрации CFO (0..1)
|
||||
#define CFO_MAX_VALID 0.5f // Максимальное валидное значение CFO (рад/семпл)
|
||||
|
||||
static volatile int stop = 0; /* флаг остановки (SIGINT) */
|
||||
static uint64_t total_packets = 0; /* всего обнаружено кадров */
|
||||
static uint64_t packets_ok = 0; /* успешно декодировано */
|
||||
static uint64_t total_bytes = 0; /* всего принято байт данных */
|
||||
static time_t last_packet_time = 0; /* время последнего успешного пакета */
|
||||
// Глобальный флаг для корректного завершения программы
|
||||
volatile sig_atomic_t stop = 0;
|
||||
|
||||
/* ============================================================
|
||||
* Обработчик сигналов
|
||||
* ============================================================ */
|
||||
// Глобальные счётчики статистики приёма
|
||||
static uint64_t calls = 0; // Общее количество вызовов callback
|
||||
static uint64_t hdr_ok = 0; // Количество корректных заголовков
|
||||
static uint64_t crc_raw = 0; // Количество кадров с корректной CRC
|
||||
static uint64_t rs_saved = 0; // Количество кадров, исправленных RS-декодером
|
||||
static uint64_t crc_final = 0; // Количество успешно выведенных кадров
|
||||
static uint64_t total_bytes = 0; // Общее количество принятых байт
|
||||
|
||||
// Параметры качества сигнала
|
||||
static float rssi = -100; // Уровень принимаемого сигнала (дБм)
|
||||
static float evm = 0; // Величина вектора ошибки (%)
|
||||
|
||||
// Компоненты системы частотной коррекции
|
||||
static float cfo_est = 0.0f; // Текущая оценка CFO (радиан/семпл)
|
||||
static nco_crcf nco = NULL; // NCO (Numerically Controlled Oscillator) для коррекции
|
||||
|
||||
// Декодер Рида-Соломона и флаг активации FEC
|
||||
static fec decoder = NULL;
|
||||
static int fec_on = 0;
|
||||
|
||||
// Буфер для временного хранения отсчётов после коррекции
|
||||
static liquid_float_complex frame[MAX_FRAME_SAMPLES];
|
||||
|
||||
/**
|
||||
* sigint_handler — обработчик Ctrl+C
|
||||
* Обработчик сигналов завершения (SIGINT, SIGTERM)
|
||||
*/
|
||||
static void sigint_handler(int sig) {
|
||||
(void)sig;
|
||||
void sigint_handler(int s) {
|
||||
(void)s;
|
||||
stop = 1;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Callback framesync64
|
||||
* ============================================================ */
|
||||
|
||||
/**
|
||||
* fs_callback — вызывается framesync64 при обнаружении кадра
|
||||
* Callback-функция обработки принятого OFDM-кадра
|
||||
*
|
||||
* Параметры:
|
||||
* _header — заголовок кадра (8 байт)
|
||||
* _header_valid — флаг валидности заголовка
|
||||
* _payload — данные полезной нагрузки (64 байта)
|
||||
* _payload_len — длина payload
|
||||
* _payload_valid — флаг валидности данных (CRC прошёл)
|
||||
* _stats — статистика кадра (RSSI, CFO, etc.)
|
||||
* _userdata — пользовательские данные (не используется)
|
||||
* Вызывается синхронизатором при успешном обнаружении кадра.
|
||||
* Выполняет:
|
||||
* 1. Обновление метрик качества сигнала (RSSI, EVM)
|
||||
* 2. Адаптивную фильтрацию оценки частотного сдвига (CFO)
|
||||
* 3. Декодирование Рида-Соломона (если включено)
|
||||
* 4. Вывод декодированных данных в stdout
|
||||
*
|
||||
* Возвращает 0 (продолжить обработку)
|
||||
* @param hdr Указатель на заголовок кадра (12 байт)
|
||||
* @param hdr_valid Флаг валидности заголовка
|
||||
* @param pay Указатель на полезную нагрузку
|
||||
* @param pay_len Длина полезной нагрузки в байтах
|
||||
* @param pay_valid Флаг валидности CRC полезной нагрузки
|
||||
* @param stats Статистика кадра (RSSI, EVM, CFO)
|
||||
* @param user Пользовательские данные (не используется)
|
||||
* @return 0 - продолжить обработку, ненулевое значение - остановить
|
||||
*/
|
||||
static int fs_callback(unsigned char *_header, int _header_valid,
|
||||
unsigned char *_payload, unsigned int _payload_len,
|
||||
int _payload_valid, framesyncstats_s _stats,
|
||||
void *_userdata) {
|
||||
(void)_header;
|
||||
(void)_header_valid;
|
||||
(void)_stats;
|
||||
(void)_userdata;
|
||||
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++;
|
||||
|
||||
total_packets++;
|
||||
// Обновление метрик качества сигнала
|
||||
rssi = stats.rssi;
|
||||
evm = stats.evm;
|
||||
|
||||
if (_payload_valid) {
|
||||
packets_ok++;
|
||||
/* первый байт = количество данных */
|
||||
size_t data_len = _payload[0];
|
||||
total_bytes += data_len;
|
||||
last_packet_time = time(NULL);
|
||||
|
||||
/* для отладки: вывод первых 8 байт данных */
|
||||
#ifdef DEBUG
|
||||
printf("\n[RX] Packet #%llu: %zu bytes: ",
|
||||
(unsigned long long)total_packets, data_len);
|
||||
for (size_t i = 1; i <= data_len && i <= 8; i++)
|
||||
printf("%02X ", _payload[i]);
|
||||
if (data_len > 8) printf("...");
|
||||
printf("\n");
|
||||
#endif
|
||||
// Адаптивная фильтрация CFO с защитой от выбросов
|
||||
// Используется экспоненциальное сглаживание для стабильной оценки
|
||||
if (fabsf(stats.cfo) < CFO_MAX_VALID) {
|
||||
// Рекурсивный фильтр 1-го порядка: y[n] = α*y[n-1] + (1-α)*x[n]
|
||||
cfo_est = CFO_FILTER_ALPHA * cfo_est + (1.0f - CFO_FILTER_ALPHA) * stats.cfo;
|
||||
// Применяем скорректированную частоту к NCO
|
||||
nco_crcf_set_frequency(nco, cfo_est);
|
||||
}
|
||||
|
||||
// Проверка валидности заголовка - критически важно для дальнейшей обработки
|
||||
if (!hdr_valid) return 0;
|
||||
hdr_ok++;
|
||||
|
||||
// Подсчёт кадров с корректной CRC (до FEC-коррекции)
|
||||
if (pay_valid) crc_raw++;
|
||||
|
||||
// Извлечение метаданных из заголовка кадра
|
||||
uint16_t original_len = (hdr[6] << 8) | hdr[7]; // Исходная длина данных (байт)
|
||||
uint8_t last_block_bytes = hdr[10]; // Размер последнего RS-блока (байт)
|
||||
|
||||
// Ветвление логики в зависимости от режима FEC
|
||||
if (fec_on && pay_len >= RS_ENC) {
|
||||
// === РЕЖИМ С КОРРЕКЦИЕЙ ОШИБОК (РИД-СОЛОМОН) ===
|
||||
|
||||
size_t nblocks = pay_len / RS_ENC; // Количество RS-блоков
|
||||
// Защита от некорректных/повреждённых данных
|
||||
if (nblocks == 0 || nblocks > 100) return 0;
|
||||
|
||||
uint8_t recovered[4096]; // Буфер для восстановленных данных
|
||||
int rs_ok = 1; // Флаг успешности RS-декодирования
|
||||
|
||||
// Поблочное декодирование Рида-Соломона
|
||||
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) {
|
||||
// RS-декодер успешно исправил ошибки
|
||||
if (!pay_valid) rs_saved++; // CRC была некорректна, но RS исправил
|
||||
|
||||
// Вычисление реальной длины данных (учёт последнего неполного блока)
|
||||
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) {
|
||||
// === РЕЖИМ БЕЗ КОРРЕКЦИИ ОШИБОК ===
|
||||
// Вывод данных только при корректной CRC
|
||||
|
||||
fwrite(pay, 1, original_len, stdout);
|
||||
fflush(stdout); // Немедленный вывод для конвейерной обработки
|
||||
total_bytes += original_len;
|
||||
crc_final++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Вывод справки
|
||||
* ============================================================ */
|
||||
|
||||
/**
|
||||
* print_usage — печать справки по аргументам командной строки
|
||||
* Главная функция приёмника
|
||||
*
|
||||
* Выполняет:
|
||||
* 1. Парсинг аргументов командной строки
|
||||
* 2. Инициализацию PlutoSDR и настройку радиотракта
|
||||
* 3. Создание системы частотной коррекции (NCO)
|
||||
* 4. Инициализацию OFDM-синхронизатора и FEC-декодера
|
||||
* 5. Основной цикл приёма с визуализацией статистики
|
||||
* 6. Корректное освобождение ресурсов при завершении
|
||||
*/
|
||||
static void print_usage(const char *prog) {
|
||||
printf("Usage: %s [options]\n\n", prog);
|
||||
printf("Options:\n");
|
||||
printf(" -f FREQ Center frequency in Hz (default: %d)\n", DEFAULT_CENTER_FREQ);
|
||||
printf(" -r RATE Sample rate in Hz (default: %d)\n", DEFAULT_SAMPLE_RATE);
|
||||
printf(" -b SAMPS Buffer size in samples (default: %d)\n", DEFAULT_BUF_SAMPLES);
|
||||
printf(" -u URI IIO context URI (default: %s)\n", DEFAULT_URI);
|
||||
printf(" -d Debug mode: print packet data\n");
|
||||
printf(" -h This help\n");
|
||||
printf("\nExamples:\n");
|
||||
printf(" %s # defaults\n", prog);
|
||||
printf(" %s -f 2400000000 -r 4000000 # 2.4 GHz, 4 MSPS\n", prog);
|
||||
printf(" %s -d # debug mode\n", prog);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* MAIN
|
||||
* ============================================================ */
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
/* ---- переменные с значениями по умолчанию ---- */
|
||||
long long center_freq = DEFAULT_CENTER_FREQ;
|
||||
long long sample_rate = DEFAULT_SAMPLE_RATE;
|
||||
int buf_samples = DEFAULT_BUF_SAMPLES;
|
||||
const char *uri = DEFAULT_URI;
|
||||
int debug_mode = 0;
|
||||
// Инициализация параметров значениями по умолчанию
|
||||
long long freq = DEFAULT_FREQ, rate = DEFAULT_RATE, bw = DEFAULT_BW;
|
||||
const char *uri = "ip:192.168.2.1";
|
||||
const char *fec_opt = "rs8";
|
||||
|
||||
/* ---- парсинг аргументов ---- */
|
||||
// Обработка аргументов командной строки
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "f:r:b:u:dh")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "f:r:b:u:c:h")) != -1) {
|
||||
switch (opt) {
|
||||
case 'f': center_freq = atoll(optarg); break;
|
||||
case 'r': sample_rate = atoll(optarg); break;
|
||||
case 'b': buf_samples = atoi(optarg); break;
|
||||
case 'u': uri = optarg; break;
|
||||
case 'd': debug_mode = 1; break;
|
||||
case 'h': print_usage(argv[0]); return 0;
|
||||
default: print_usage(argv[0]); return 1;
|
||||
case 'f': freq = atoll(optarg); break; // Частота несущей (Гц)
|
||||
case 'r': rate = atoll(optarg); break; // Частота дискретизации (Гц)
|
||||
case 'b': bw = atoll(optarg); break; // Полоса пропускания (Гц)
|
||||
case 'u': uri = optarg; break; // URI подключения к PlutoSDR
|
||||
case 'c': fec_opt = optarg; break; // Тип FEC: rs8 или none
|
||||
case 'h':
|
||||
fprintf(stderr, "Использование: %s [опции]\n", argv[0]);
|
||||
fprintf(stderr, " -f ЧАСТОТА Частота несущей в Гц (по умолчанию: %.0f)\n", (double)DEFAULT_FREQ);
|
||||
fprintf(stderr, " -r СКОРОСТЬ Частота дискретизации (по умолчанию: %.0f)\n", (double)DEFAULT_RATE);
|
||||
fprintf(stderr, " -b ПОЛОСА Полоса пропускания (по умолчанию: %.0f)\n", (double)DEFAULT_BW);
|
||||
fprintf(stderr, " -u URI URI устройства (по умолчанию: ip:192.168.2.1)\n");
|
||||
fprintf(stderr, " -c РЕЖИМ FEC режим: rs8 или none (по умолчанию: rs8)\n");
|
||||
fprintf(stderr, "\nОПТИМАЛЬНЫЙ ЗАПУСК:\n");
|
||||
fprintf(stderr, " %s -f 1265000000 -r 3840000 -b 3000000 -c rs8 > output.bin\n", argv[0]);
|
||||
return 0;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- инициализация ---- */
|
||||
signal(SIGINT, sigint_handler); /* Ctrl+C → sigint_handler */
|
||||
// Определение режима FEC
|
||||
fec_on = !strcmp(fec_opt, "rs8");
|
||||
|
||||
/* ---- заголовок программы ---- */
|
||||
printf("══════════════════════════════════════════\n");
|
||||
printf(" QPSK RECEIVER (framesync64)\n");
|
||||
printf("══════════════════════════════════════════\n");
|
||||
printf(" Frequency: %.3f MHz\n", center_freq / 1e6);
|
||||
printf(" Sample rate: %.3f MSPS\n", sample_rate / 1e6);
|
||||
printf(" Buffer size: %d samples\n", buf_samples);
|
||||
printf(" URI: %s\n", uri);
|
||||
if (debug_mode) printf(" Debug: ON\n");
|
||||
printf("══════════════════════════════════════════\n");
|
||||
// Установка обработчика сигналов для корректного завершения
|
||||
signal(SIGINT, sigint_handler);
|
||||
signal(SIGTERM, sigint_handler);
|
||||
|
||||
/* ============================================================
|
||||
* Инициализация PlutoSDR
|
||||
* ============================================================ */
|
||||
// Вывод параметров конфигурации приёмника
|
||||
fprintf(stderr, "═══════════════════════════════════════════════════\n");
|
||||
fprintf(stderr, "ПРИЁМНИК OFDM С ЧАСТОТНОЙ КОРРЕКЦИЕЙ\n");
|
||||
fprintf(stderr, "Частота: %.1f МГц\n", freq/1e6);
|
||||
fprintf(stderr, "Дискрет.: %.2f МГц\n", rate/1e6);
|
||||
fprintf(stderr, "Полоса: %.2f МГц\n", bw/1e6);
|
||||
fprintf(stderr, "FEC: %s\n", fec_on ? "RS(255,223)" : "ОТКЛЮЧЕН");
|
||||
fprintf(stderr, "Коррекция: Адаптивная CFO (α=%.2f)\n", CFO_FILTER_ALPHA);
|
||||
fprintf(stderr, "Усиление: Ручное (50 дБ)\n");
|
||||
fprintf(stderr, "═══════════════════════════════════════════════════\n");
|
||||
|
||||
/* создаём контекст IIO */
|
||||
// === ИНИЦИАЛИЗАЦИЯ PLUTOSDR ===
|
||||
struct iio_context *ctx = iio_create_context_from_uri(uri);
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "ERROR: Cannot connect to %s\n", uri);
|
||||
fprintf(stderr, " Check USB cable and try 'iio_info -s'\n");
|
||||
fprintf(stderr, "Ошибка подключения к PlutoSDR по адресу %s\n", uri);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* находим устройства */
|
||||
// Поиск устройств физического уровня и приёмника
|
||||
struct iio_device *phy = iio_context_find_device(ctx, "ad9361-phy");
|
||||
struct iio_device *rx = iio_context_find_device(ctx, "cf-ad9361-lpc");
|
||||
struct iio_device *rx = iio_context_find_device(ctx, "cf-ad9361-lpc");
|
||||
if (!phy || !rx) {
|
||||
fprintf(stderr, "ERROR: Devices not found\n");
|
||||
iio_context_destroy(ctx);
|
||||
fprintf(stderr, "Радиоустройства не найдены в контексте\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* каналы PHY */
|
||||
struct iio_channel *rx_lo = iio_device_find_channel(phy, "altvoltage0", true);
|
||||
struct iio_channel *rx_phy = iio_device_find_channel(phy, "voltage0", false);
|
||||
// === НАСТРОЙКА РАДИОТРАКТА ===
|
||||
// Установка частоты несущей
|
||||
iio_channel_attr_write_longlong(
|
||||
iio_device_find_channel(phy, "altvoltage0", true),
|
||||
"frequency", freq);
|
||||
|
||||
/* настройка частоты несущей */
|
||||
if (iio_channel_attr_write_longlong(rx_lo, "frequency", center_freq) < 0) {
|
||||
fprintf(stderr, "WARNING: Cannot set RX LO frequency\n");
|
||||
}
|
||||
// Установка частоты дискретизации
|
||||
iio_channel_attr_write_longlong(
|
||||
iio_device_find_channel(phy, "voltage0", false),
|
||||
"sampling_frequency", rate);
|
||||
|
||||
/* настройка частоты дискретизации */
|
||||
if (iio_channel_attr_write_longlong(rx_phy, "sampling_frequency", sample_rate) < 0) {
|
||||
fprintf(stderr, "WARNING: Cannot set sample rate\n");
|
||||
}
|
||||
// Установка полосы пропускания
|
||||
iio_channel_attr_write_longlong(
|
||||
iio_device_find_channel(phy, "voltage0", false),
|
||||
"rf_bandwidth", bw);
|
||||
|
||||
/* настройка полосы, порта, АРУ */
|
||||
iio_channel_attr_write_longlong(rx_phy, "rf_bandwidth", sample_rate);
|
||||
iio_channel_attr_write(rx_phy, "gain_control_mode", "slow_attack");
|
||||
iio_channel_attr_write(rx_phy, "rf_port_select", "A_BALANCED");
|
||||
// Настройка ручного управления усилением для максимальной стабильности
|
||||
// Ручной режим предпочтительнее автоматического при работе с OFDM
|
||||
iio_channel_attr_write(
|
||||
iio_device_find_channel(phy, "voltage0", false),
|
||||
"gain_control_mode", "manual");
|
||||
iio_channel_attr_write_double(
|
||||
iio_device_find_channel(phy, "voltage0", false),
|
||||
"hardwaregain", 50.0); // Оптимальное значение для большинства сценариев
|
||||
|
||||
/* включение DMA каналов I и Q */
|
||||
struct iio_channel *rx_i = iio_device_find_channel(rx, "voltage0", false);
|
||||
struct iio_channel *rx_q = iio_device_find_channel(rx, "voltage1", false);
|
||||
iio_channel_enable(rx_i);
|
||||
iio_channel_enable(rx_q);
|
||||
// Выбор сбалансированного антенного входа
|
||||
iio_channel_attr_write(
|
||||
iio_device_find_channel(phy, "voltage0", false),
|
||||
"rf_port_select", "A_BALANCED");
|
||||
|
||||
/* создание DMA буфера */
|
||||
struct iio_buffer *buf = iio_device_create_buffer(rx, buf_samples, false);
|
||||
// Включение I и Q каналов приёмника
|
||||
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, "ERROR: Cannot create RX DMA buffer\n");
|
||||
fprintf(stderr, " Try 'sudo' or check if another process uses Pluto\n");
|
||||
iio_context_destroy(ctx);
|
||||
fprintf(stderr, "Ошибка создания буфера приёма\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Инициализация liquid-dsp
|
||||
* ============================================================ */
|
||||
// === ИНИЦИАЛИЗАЦИЯ FEC-ДЕКОДЕРА ===
|
||||
if (fec_on) {
|
||||
decoder = fec_create(LIQUID_FEC_RS_M8, NULL);
|
||||
if (!decoder) {
|
||||
fprintf(stderr, "Ошибка создания FEC-декодера Рида-Соломона\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* framesync64 — детектор/декодер кадров фиксированной длины
|
||||
*
|
||||
* Что он делает:
|
||||
* 1. Ищет заголовок (header) во входном потоке
|
||||
* 2. Синхронизируется по времени и частоте
|
||||
* 3. Демодулирует QPSK
|
||||
* 4. Проверяет CRC-32
|
||||
* 5. Вызывает callback с результатом
|
||||
*
|
||||
* На передающей стороне используется framegen64
|
||||
*/
|
||||
framesync64 fs = framesync64_create(fs_callback, NULL);
|
||||
// === ИНИЦИАЛИЗАЦИЯ СИСТЕМЫ ЧАСТОТНОЙ КОРРЕКЦИИ ===
|
||||
// Создание NCO (Numerically Controlled Oscillator)
|
||||
// NCO используется для точной компенсации частотного сдвига
|
||||
nco = nco_crcf_create(LIQUID_NCO);
|
||||
nco_crcf_set_frequency(nco, 0.0f); // Начальная частота: 0 (коррекция пока не требуется)
|
||||
|
||||
printf("[RX] Listening... (Ctrl+C to stop)\n\n");
|
||||
// === ИНИЦИАЛИЗАЦИЯ OFDM-СИНХРОНИЗАТОРА ===
|
||||
// AGC встроен в ofdmflexframesync, дополнительная настройка не требуется
|
||||
ofdmflexframesync fs = ofdmflexframesync_create(
|
||||
OFDM_M, OFDM_CP, OFDM_TAPER, NULL, callback, NULL);
|
||||
|
||||
time_t last_report = time(NULL);
|
||||
fprintf(stderr, " Приёмник готов. Прослушивание эфира...\n\n");
|
||||
|
||||
/* ============================================================
|
||||
* Основной цикл приёма
|
||||
* ============================================================ */
|
||||
// Заголовок таблицы статистики
|
||||
fprintf(stderr, "Кадры | Загол. | CRC | RS испр | Вывод | RSSI dBM | EVM dBm | CFO\n");
|
||||
fprintf(stderr, "--------+--------+------+---------+--------+-------+-------+----------\n");
|
||||
|
||||
time_t last = time(NULL);
|
||||
|
||||
// ===========================================
|
||||
// === ОСНОВНОЙ ЦИКЛ ПРИЁМА =================
|
||||
// ===========================================
|
||||
while (!stop) {
|
||||
/* ожидание данных от Pluto */
|
||||
// Заполнение аппаратного буфера данными с SDR
|
||||
ssize_t n = iio_buffer_refill(buf);
|
||||
if (n < 0) {
|
||||
usleep(500); /* небольшая пауза при ошибке */
|
||||
usleep(500); // Кратковременная пауза при ошибке чтения
|
||||
continue;
|
||||
}
|
||||
|
||||
/* указатель на данные */
|
||||
// Получение указателя на сырые данные (16-битные I/Q семплы)
|
||||
int16_t *bb = (int16_t *)iio_buffer_start(buf);
|
||||
int ns = (iio_buffer_end(buf) - iio_buffer_start(buf)) / sizeof(int16_t) / 2;
|
||||
|
||||
/* конвертация int16 → float complex */
|
||||
liquid_float_complex frame[ns];
|
||||
// Вычисление количества комплексных отсчётов
|
||||
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] / 32767.0f) +
|
||||
((float)bb[2 * i + 1] / 32767.0f) * _Complex_I;
|
||||
// Шаг 1: Нормализация для 12-битного АЦП PlutoSDR (4096 уровней → ±2048)
|
||||
// Делим на 2048 для приведения к диапазону ±1.0
|
||||
frame[i] = ((float)bb[2*i] / 2048.0f) +
|
||||
((float)bb[2*i+1] / 2048.0f) * _Complex_I;
|
||||
|
||||
// Шаг 2: Применение частотной коррекции через NCO
|
||||
// mix_down компенсирует частотный сдвиг путём умножения на e^(-j*2π*f_cfo*t)
|
||||
nco_crcf_mix_down(nco, frame[i], &frame[i]);
|
||||
|
||||
// Шаг 3: Продвижение фазы NCO для следующего семпла
|
||||
nco_crcf_step(nco);
|
||||
}
|
||||
|
||||
/* передача всего буфера в framesync64 */
|
||||
framesync64_execute(fs, frame, ns);
|
||||
// Сброс фазы NCO в 0 для каждого нового блока
|
||||
// Это предотвращает накопление фазовой ошибки между блоками
|
||||
nco_crcf_set_phase(nco, 0.0f);
|
||||
|
||||
/* вывод статистики (раз в секунду) */
|
||||
time_t now = time(NULL);
|
||||
if (now > last_report) {
|
||||
double pct = total_packets > 0 ?
|
||||
100.0 * packets_ok / total_packets : 0.0;
|
||||
// Передача скорректированных отсчётов синхронизатору
|
||||
// Синхронизатор выполняет:
|
||||
// - Обнаружение OFDM-кадров
|
||||
// - Оценку и компенсацию оставшегося CFO
|
||||
// - Демодуляцию поднесущих
|
||||
// - Декодирование данных
|
||||
// - Вызов callback-функции для каждого обнаруженного кадра
|
||||
ofdmflexframesync_execute(fs, frame, ns);
|
||||
|
||||
/* время с последнего пакета */
|
||||
int idle_sec = (int)(now - last_packet_time);
|
||||
|
||||
printf("\r[RX] pkts: %5llu | OK: %5llu (%4.1f%%) | "
|
||||
"bytes: %8llu | idle: %d sec ",
|
||||
(unsigned long long)total_packets,
|
||||
(unsigned long long)packets_ok, pct,
|
||||
(unsigned long long)total_bytes,
|
||||
idle_sec);
|
||||
fflush(stdout);
|
||||
last_report = now;
|
||||
// Обновление статистики на экране (раз в секунду)
|
||||
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, // Корректных CRC
|
||||
(unsigned long long)rs_saved, // Исправлено RS-декодером
|
||||
(unsigned long long)crc_final, // Успешно выведено
|
||||
rssi, // Уровень сигнала (дБм)
|
||||
evm, // Вектор ошибки (%)
|
||||
cfo_est); // Текущая оценка CFO
|
||||
fflush(stderr);
|
||||
last = time(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Завершение
|
||||
* ============================================================ */
|
||||
// === ВЫВОД ИТОГОВОЙ СТАТИСТИКИ ===
|
||||
fprintf(stderr, "\n\n═══════════════════════════════════════════════════\n");
|
||||
fprintf(stderr, " ПРИЁМ ЗАВЕРШЁН\n");
|
||||
fprintf(stderr, "═══════════════════════════════════════════════════\n");
|
||||
fprintf(stderr, "Обнаружено кадров: %llu\n", (unsigned long long)calls);
|
||||
fprintf(stderr, "Корректных заголовков: %llu (%.1f%%)\n",
|
||||
(unsigned long long)hdr_ok,
|
||||
calls > 0 ? 100.0 * hdr_ok / calls : 0.0);
|
||||
fprintf(stderr, "Корректных CRC: %llu (%.1f%%)\n",
|
||||
(unsigned long long)crc_raw,
|
||||
hdr_ok > 0 ? 100.0 * crc_raw / hdr_ok : 0.0);
|
||||
fprintf(stderr, "Исправлено RS: %llu\n", (unsigned long long)rs_saved);
|
||||
fprintf(stderr, "Успешно выведено: %llu кадров\n", (unsigned long long)crc_final);
|
||||
fprintf(stderr, "Всего принято данных: %llu байт (%.2f МБ)\n",
|
||||
(unsigned long long)total_bytes, total_bytes / 1e6);
|
||||
fprintf(stderr, "Финальная оценка CFO: %.4f рад/семпл\n", cfo_est);
|
||||
fprintf(stderr, "═══════════════════════════════════════════════════\n");
|
||||
|
||||
printf("\n\n[RX] Stopping...\n");
|
||||
printf(" Total packets: %llu\n", (unsigned long long)total_packets);
|
||||
printf(" OK packets: %llu (%.1f%%)\n",
|
||||
(unsigned long long)packets_ok,
|
||||
total_packets > 0 ? 100.0 * packets_ok / total_packets : 0.0);
|
||||
printf(" Total bytes: %llu\n", (unsigned long long)total_bytes);
|
||||
|
||||
/* освобождение ресурсов */
|
||||
framesync64_destroy(fs);
|
||||
// === ОСВОБОЖДЕНИЕ РЕСУРСОВ ===
|
||||
if (decoder) fec_destroy(decoder);
|
||||
if (nco) nco_crcf_destroy(nco);
|
||||
ofdmflexframesync_destroy(fs);
|
||||
iio_buffer_destroy(buf);
|
||||
iio_context_destroy(ctx);
|
||||
|
||||
|
||||
@@ -1,23 +1,9 @@
|
||||
/**
|
||||
* tx.c — QPSK передатчик для PlutoSDR (production)
|
||||
* transmitter.c — OFDM + RS(255,223) FEC + PlutoSDR
|
||||
* СИНХРОНИЗИРОВАННАЯ ВЕРСИЯ
|
||||
*
|
||||
* Использует framegen64 из liquid-dsp для формирования кадров.
|
||||
* Архитектура: payload → framegen64 → int16 I/Q → Pluto DMA
|
||||
*
|
||||
* Зависимости:
|
||||
* - libiio (PlutoSDR API)
|
||||
* - libliquid (liquid-dsp, framegen64)
|
||||
* - libm (математика)
|
||||
* - libpthread (сигналы)
|
||||
*
|
||||
* Компиляция:
|
||||
* gcc -o tx tx.c -liio -lliquid -lm -lpthread -Wall -O2
|
||||
*
|
||||
* Примеры запуска:
|
||||
* ./tx # значения по умолчанию
|
||||
* ./tx -f 2400000000 -r 4000000 -a 0.5 # 2.4 ГГц, 4 MSPS, половинная амплитуда
|
||||
* ./tx -g -20 -p 1000 # TX gain -20 dB, пауза 1 мс между кадрами
|
||||
* ./tx -h # справка
|
||||
* ОПТИМАЛЬНАЯ КОМАНДА ЗАПУСКА:
|
||||
* cat /dev/urandom | ./transmitter -f 1265000000 -r 3840000 -b 2000000 -g -20 -a 0.3 -p 0 -c rs8
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -32,282 +18,182 @@
|
||||
#include <iio.h>
|
||||
#include <liquid/liquid.h>
|
||||
|
||||
/* ============================================================
|
||||
* Значения по умолчанию
|
||||
* ============================================================ */
|
||||
// Параметры передачи по умолчанию
|
||||
#define DEFAULT_FREQ 1265000000 // Частота несущей: 1265 МГц
|
||||
#define DEFAULT_RATE 3840000 // Частота дискретизации: 3.84 МГц
|
||||
#define DEFAULT_BW 3000000 // Полоса пропускания: 3 МГц
|
||||
#define DEFAULT_GAIN -30.0 // Усиление передатчика: -30 дБ
|
||||
#define DEFAULT_AMP 0.2f // Амплитуда сигнала (коэффициент масштабирования)
|
||||
#define DEFAULT_PAUSE 2000 // Пауза между кадрами: 2000 мкс
|
||||
#define BUF_SAMPLES 16384 // Размер буфера в отсчётах
|
||||
#define MAX_PAYLOAD 1024 // Максимальный размер полезной нагрузки в байтах
|
||||
|
||||
#define DEFAULT_CENTER_FREQ 1300000000 /* 1300 МГц */
|
||||
#define DEFAULT_SAMPLE_RATE 3840000 /* 3.84 MSPS */
|
||||
#define DEFAULT_TX_GAIN -10.0 /* аттенюация TX, dB */
|
||||
#define DEFAULT_AMPLITUDE 0.8f /* амплитуда I/Q (0..1) */
|
||||
#define DEFAULT_BUF_SAMPLES 32768 /* размер DMA буфера */
|
||||
#define DEFAULT_FRAME_PAUS_US 10000 /* пауза между кадрами, мкс */
|
||||
#define DEFAULT_MIN_PAYLOAD 32 /* минимальный размер данных */
|
||||
#define DEFAULT_MAX_PAYLOAD 63 /* максимальный (64-1 байт длины) */
|
||||
#define DEFAULT_URI "ip:192.168.2.1"
|
||||
// Параметры OFDM-модуляции
|
||||
#define OFDM_M 64 // Количество поднесущих
|
||||
#define OFDM_CP 16 // Длина циклического префикса
|
||||
#define OFDM_TAPER 4 // Длина сглаживающего окна
|
||||
|
||||
/* ============================================================
|
||||
* Заголовок framegen64 (8 байт)
|
||||
* Должен совпадать на приёмной стороне
|
||||
* ============================================================ */
|
||||
// Параметры помехоустойчивого кодирования Рида-Соломона
|
||||
#define RS_DATA 223 // Размер блока данных
|
||||
#define RS_ENC 255 // Размер кодированного блока
|
||||
|
||||
static uint8_t header[] = {
|
||||
0xf0, 0xaa, 0x24, 0xdb,
|
||||
0x40, 0x01, 0x55, 0x0f
|
||||
};
|
||||
|
||||
/* ============================================================
|
||||
* Глобальные переменные
|
||||
* ============================================================ */
|
||||
|
||||
static volatile int stop = 0; /* флаг остановки (SIGINT) */
|
||||
static uint64_t total_frames = 0; /* всего отправлено кадров */
|
||||
static uint64_t total_bytes = 0; /* всего отправлено байт */
|
||||
|
||||
/* ============================================================
|
||||
* Обработчик сигналов
|
||||
* ============================================================ */
|
||||
|
||||
/**
|
||||
* sigint_handler — обработчик Ctrl+C
|
||||
* Устанавливает флаг stop, программа завершается на следующей итерации
|
||||
*/
|
||||
static void sigint_handler(int sig) {
|
||||
(void)sig;
|
||||
stop = 1;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Вывод справки
|
||||
* ============================================================ */
|
||||
|
||||
/**
|
||||
* print_usage — печать справки по аргументам командной строки
|
||||
*/
|
||||
static void print_usage(const char *prog) {
|
||||
printf("Usage: %s [options]\n\n", prog);
|
||||
printf("Options:\n");
|
||||
printf(" -f FREQ Center frequency in Hz (default: %d)\n", DEFAULT_CENTER_FREQ);
|
||||
printf(" -r RATE Sample rate in Hz (default: %d)\n", DEFAULT_SAMPLE_RATE);
|
||||
printf(" -g GAIN TX gain in dB, -89..0 (default: %.0f)\n", DEFAULT_TX_GAIN);
|
||||
printf(" -a AMP Amplitude 0.0-1.0 (default: %.1f)\n", DEFAULT_AMPLITUDE);
|
||||
printf(" -p USEC Pause between frames, microseconds (default: %d)\n", DEFAULT_FRAME_PAUS_US);
|
||||
printf(" -m BYTES Min payload size (default: %d)\n", DEFAULT_MIN_PAYLOAD);
|
||||
printf(" -M BYTES Max payload size (default: %d)\n", DEFAULT_MAX_PAYLOAD);
|
||||
printf(" -u URI IIO context URI (default: %s)\n", DEFAULT_URI);
|
||||
printf(" -h This help\n");
|
||||
printf("\nExamples:\n");
|
||||
printf(" %s # defaults\n", prog);
|
||||
printf(" %s -f 2400000000 -r 4000000 # 2.4 GHz, 4 MSPS\n", prog);
|
||||
printf(" %s -g -20 -a 0.5 # lower gain and amplitude\n", prog);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* MAIN
|
||||
* ============================================================ */
|
||||
// Глобальный флаг для корректного завершения программы
|
||||
volatile sig_atomic_t stop = 0;
|
||||
void sigint_handler(int s) { (void)s; stop = 1; }
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
/* ---- переменные с значениями по умолчанию ---- */
|
||||
long long center_freq = DEFAULT_CENTER_FREQ;
|
||||
long long sample_rate = DEFAULT_SAMPLE_RATE;
|
||||
double tx_gain = DEFAULT_TX_GAIN;
|
||||
float amplitude = DEFAULT_AMPLITUDE;
|
||||
int buf_samples = DEFAULT_BUF_SAMPLES;
|
||||
int frame_pause_us = DEFAULT_FRAME_PAUS_US;
|
||||
int min_payload = DEFAULT_MIN_PAYLOAD;
|
||||
int max_payload = DEFAULT_MAX_PAYLOAD;
|
||||
const char *uri = DEFAULT_URI;
|
||||
// Инициализация параметров значениями по умолчанию
|
||||
long long freq = DEFAULT_FREQ, rate = DEFAULT_RATE, bw = DEFAULT_BW;
|
||||
double gain = DEFAULT_GAIN;
|
||||
float amp = DEFAULT_AMP;
|
||||
const char *uri = "ip:192.168.3.1"; // адрес PlutoSDR передатчика
|
||||
int pause_us = DEFAULT_PAUSE, use_fec = 1;
|
||||
|
||||
/* ---- парсинг аргументов ---- */
|
||||
// Обработка аргументов командной строки
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "f:r:g:a:p:m:M:u:h")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "f:r:b:g:a:p:u:c:h")) != -1) {
|
||||
switch (opt) {
|
||||
case 'f': center_freq = atoll(optarg); break;
|
||||
case 'r': sample_rate = atoll(optarg); break;
|
||||
case 'g': tx_gain = atof(optarg); break;
|
||||
case 'a': amplitude = atof(optarg); break;
|
||||
case 'p': frame_pause_us = atoi(optarg); break;
|
||||
case 'm': min_payload = atoi(optarg); break;
|
||||
case 'M': max_payload = atoi(optarg); break;
|
||||
case 'u': uri = optarg; break;
|
||||
case 'h': print_usage(argv[0]); return 0;
|
||||
default: print_usage(argv[0]); return 1;
|
||||
case 'f': freq = atoll(optarg); break; // Частота несущей
|
||||
case 'r': rate = atoll(optarg); break; // Частота дискретизации
|
||||
case 'b': bw = atoll(optarg); break; // Полоса пропускания
|
||||
case 'g': gain = atof(optarg); break; // Усиление передатчика
|
||||
case 'a': amp = atof(optarg); break; // Амплитуда сигнала
|
||||
case 'p': pause_us = atoi(optarg); break; // Пауза между кадрами
|
||||
case 'u': uri = optarg; break; // URI подключения к PlutoSDR
|
||||
case 'c': use_fec = strcmp(optarg, "none") ? 1 : 0; break; // Включение/отключение FEC
|
||||
case 'h':
|
||||
fprintf(stderr, "Использование: %s [-f частота] [-r скорость] [-b полоса] [-g усиление] [-a амплитуда] [-p пауза_мкс] [-c rs8|none]\n", argv[0]);
|
||||
return 0;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- валидация параметров ---- */
|
||||
if (amplitude < 0.0f) amplitude = 0.0f;
|
||||
if (amplitude > 1.0f) amplitude = 1.0f;
|
||||
if (min_payload < 1) min_payload = 1;
|
||||
if (max_payload > 63) max_payload = 63; /* 64 байта payload - 1 байт длины */
|
||||
if (min_payload > max_payload) min_payload = max_payload;
|
||||
// Установка обработчиков сигналов для корректного завершения
|
||||
signal(SIGINT, sigint_handler);
|
||||
signal(SIGTERM, sigint_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
/* ---- инициализация ---- */
|
||||
signal(SIGINT, sigint_handler); /* Ctrl+C → sigint_handler */
|
||||
srand(time(NULL)); /* случайный seed */
|
||||
// Вывод параметров конфигурации передатчика
|
||||
fprintf(stderr, "ПЕРЕДАТЧИК: %.1f МГц | скорость=%.2f МГц | полоса=%.2f МГц | усиление=%.0fдБ | амплитуда=%.2f | FEC=%s\n",
|
||||
freq/1e6, rate/1e6, bw/1e6, gain, amp, use_fec ? "RS(255,223)" : "ОТКЛ");
|
||||
|
||||
/* ---- заголовок программы ---- */
|
||||
printf("══════════════════════════════════════════\n");
|
||||
printf(" QPSK TRANSMITTER (framegen64)\n");
|
||||
printf("══════════════════════════════════════════\n");
|
||||
printf(" Frequency: %.3f MHz\n", center_freq / 1e6);
|
||||
printf(" Sample rate: %.3f MSPS\n", sample_rate / 1e6);
|
||||
printf(" TX gain: %.0f dB\n", tx_gain);
|
||||
printf(" Amplitude: %.2f\n", amplitude);
|
||||
printf(" Payload: %d-%d bytes\n", min_payload, max_payload);
|
||||
printf(" Frame pause: %d us\n", frame_pause_us);
|
||||
printf(" URI: %s\n", uri);
|
||||
printf("══════════════════════════════════════════\n");
|
||||
|
||||
/* ============================================================
|
||||
* Инициализация PlutoSDR
|
||||
* ============================================================ */
|
||||
|
||||
/* создаём контекст IIO */
|
||||
// Инициализация контекста PlutoSDR
|
||||
struct iio_context *ctx = iio_create_context_from_uri(uri);
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "ERROR: Cannot connect to %s\n", uri);
|
||||
fprintf(stderr, " Check USB cable and try 'iio_info -s'\n");
|
||||
return 1;
|
||||
}
|
||||
if (!ctx) { fprintf(stderr, "Ошибка подключения к PlutoSDR\n"); return 1; }
|
||||
|
||||
/* находим устройства */
|
||||
// Поиск устройств в контексте
|
||||
struct iio_device *phy = iio_context_find_device(ctx, "ad9361-phy");
|
||||
struct iio_device *tx = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc");
|
||||
if (!phy || !tx) {
|
||||
fprintf(stderr, "ERROR: Devices not found\n");
|
||||
iio_context_destroy(ctx);
|
||||
return 1;
|
||||
}
|
||||
struct iio_device *tx = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc");
|
||||
if (!phy || !tx) { fprintf(stderr, "Радиоустройства не найдены\n"); return 1; }
|
||||
|
||||
/* каналы PHY */
|
||||
struct iio_channel *tx_lo = iio_device_find_channel(phy, "altvoltage1", true);
|
||||
struct iio_channel *tx_phy = iio_device_find_channel(phy, "voltage0", true);
|
||||
// Настройка всех параметров радиотракта
|
||||
iio_channel_attr_write_longlong(iio_device_find_channel(phy, "altvoltage1", true), "frequency", freq);
|
||||
iio_channel_attr_write_longlong(iio_device_find_channel(phy, "voltage0", true), "sampling_frequency", rate);
|
||||
iio_channel_attr_write_longlong(iio_device_find_channel(phy, "voltage0", true), "rf_bandwidth", bw);
|
||||
iio_channel_attr_write(iio_device_find_channel(phy, "voltage0", true), "rf_port_select", "A");
|
||||
iio_channel_attr_write_double(iio_device_find_channel(phy, "voltage0", true), "hardwaregain", gain);
|
||||
|
||||
/* настройка частоты несущей */
|
||||
if (iio_channel_attr_write_longlong(tx_lo, "frequency", center_freq) < 0) {
|
||||
fprintf(stderr, "WARNING: Cannot set TX LO frequency\n");
|
||||
}
|
||||
// Включение каналов передатчика
|
||||
iio_channel_enable(iio_device_find_channel(tx, "voltage0", true));
|
||||
iio_channel_enable(iio_device_find_channel(tx, "voltage1", true));
|
||||
|
||||
/* настройка частоты дискретизации */
|
||||
if (iio_channel_attr_write_longlong(tx_phy, "sampling_frequency", sample_rate) < 0) {
|
||||
fprintf(stderr, "WARNING: Cannot set sample rate\n");
|
||||
}
|
||||
// Создание буфера передачи
|
||||
struct iio_buffer *buf = iio_device_create_buffer(tx, BUF_SAMPLES, false);
|
||||
if (!buf) { fprintf(stderr, "Ошибка создания буфера передачи\n"); return 1; }
|
||||
|
||||
/* настройка полосы, порта, усиления */
|
||||
iio_channel_attr_write_longlong(tx_phy, "rf_bandwidth", sample_rate);
|
||||
iio_channel_attr_write(tx_phy, "rf_port_select", "A");
|
||||
iio_channel_attr_write_double(tx_phy, "hardwaregain", tx_gain);
|
||||
// Инициализация помехоустойчивого кодирования и OFDM-модулятора
|
||||
fec enc = use_fec ? fec_create(LIQUID_FEC_RS_M8, NULL) : NULL;
|
||||
|
||||
/* включение DMA каналов I и Q */
|
||||
struct iio_channel *tx_i = iio_device_find_channel(tx, "voltage0", true);
|
||||
struct iio_channel *tx_q = iio_device_find_channel(tx, "voltage1", true);
|
||||
iio_channel_enable(tx_i);
|
||||
iio_channel_enable(tx_q);
|
||||
ofdmflexframegenprops_s fgp;
|
||||
ofdmflexframegenprops_init_default(&fgp);
|
||||
fgp.check = LIQUID_CRC_32;
|
||||
ofdmflexframegen fg = ofdmflexframegen_create(OFDM_M, OFDM_CP, OFDM_TAPER, NULL, &fgp);
|
||||
|
||||
/* создание DMA буфера (non-cyclic) */
|
||||
struct iio_buffer *buf = iio_device_create_buffer(tx, buf_samples, false);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "ERROR: Cannot create TX DMA buffer\n");
|
||||
fprintf(stderr, " Try 'sudo' or check if another process uses Pluto\n");
|
||||
iio_context_destroy(ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* указатель на данные буфера */
|
||||
int16_t *out = (int16_t *)iio_buffer_start(buf);
|
||||
|
||||
/* ============================================================
|
||||
* Инициализация liquid-dsp
|
||||
* ============================================================ */
|
||||
|
||||
/**
|
||||
* framegen64 — генератор кадров фиксированной длины (64 байта данных)
|
||||
*
|
||||
* Что он делает:
|
||||
* 1. Добавляет 8-байтовый заголовок (header)
|
||||
* 2. Кодирует 64 байта полезной нагрузки
|
||||
* 3. Добавляет CRC-32
|
||||
* 4. Модулирует QPSK с RRC pulse shaping
|
||||
* 5. Возвращает LIQUID_FRAME64_LEN (обычно 1440) комплексных отсчётов
|
||||
*
|
||||
* На приёмной стороне используется framesync64
|
||||
*/
|
||||
framegen64 fg = framegen64_create();
|
||||
|
||||
/* буфер для payload (64 байта: 1 байт длины + 63 байта данных) */
|
||||
uint8_t payload[64];
|
||||
|
||||
printf("[TX] Running... (Ctrl+C to stop)\n\n");
|
||||
|
||||
time_t last_report = time(NULL);
|
||||
|
||||
/* ============================================================
|
||||
* Основной цикл передачи
|
||||
* ============================================================ */
|
||||
// Счётчики статистики передачи
|
||||
uint64_t frames = 0, bytes_tx = 0;
|
||||
uint32_t seq = 0;
|
||||
time_t last = time(NULL);
|
||||
|
||||
// Основной цикл передачи
|
||||
while (!stop) {
|
||||
/* очистка DMA буфера */
|
||||
memset(out, 0, buf_samples * 2 * sizeof(int16_t));
|
||||
|
||||
/* генерация случайных данных */
|
||||
int payload_len = min_payload + (rand() % (max_payload - min_payload + 1));
|
||||
|
||||
/* первый байт = количество байт данных (как в HackRF версии) */
|
||||
payload[0] = (uint8_t)(payload_len - 1);
|
||||
for (int i = 1; i < payload_len; i++)
|
||||
payload[i] = rand() & 0xFF;
|
||||
|
||||
/* запуск framegen64: payload → модулированный кадр */
|
||||
unsigned int frame_len = LIQUID_FRAME64_LEN;
|
||||
liquid_float_complex frame[frame_len];
|
||||
framegen64_execute(fg, header, payload, frame);
|
||||
|
||||
/* копирование float complex → int16 I/Q в DMA буфер */
|
||||
int sample_idx = 0;
|
||||
for (unsigned int i = 0; i < frame_len && sample_idx < buf_samples; i++) {
|
||||
out[2 * sample_idx] = (int16_t)(crealf(frame[i]) * 32767.0f * amplitude);
|
||||
out[2 * sample_idx + 1] = (int16_t)(cimagf(frame[i]) * 32767.0f * amplitude);
|
||||
sample_idx++;
|
||||
uint8_t raw[MAX_PAYLOAD];
|
||||
size_t n = fread(raw, 1, MAX_PAYLOAD, stdin);
|
||||
if (!n) {
|
||||
if (feof(stdin) || stop) break;
|
||||
usleep(10000);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* отправка буфера в Pluto */
|
||||
iio_buffer_push(buf);
|
||||
uint8_t *tx_payload = NULL;
|
||||
size_t tx_len = 0;
|
||||
uint8_t hdr[12] = {0};
|
||||
|
||||
/* статистика */
|
||||
total_frames++;
|
||||
total_bytes += (payload_len - 1); /* минус байт длины */
|
||||
|
||||
/* вывод статистики (раз в секунду) */
|
||||
time_t now = time(NULL);
|
||||
if (now > last_report) {
|
||||
double kbps = total_bytes * 8.0 / 1000.0;
|
||||
printf("\r[TX] frames: %6llu | bytes: %8llu | ~%.1f kbps | payload: %2d bytes ",
|
||||
(unsigned long long)total_frames,
|
||||
(unsigned long long)total_bytes,
|
||||
kbps,
|
||||
payload_len);
|
||||
fflush(stdout);
|
||||
last_report = now;
|
||||
// Кодирование данных с использованием FEC
|
||||
if (enc) {
|
||||
size_t total_blocks = (n + RS_DATA - 1) / RS_DATA;
|
||||
tx_len = total_blocks * RS_ENC;
|
||||
tx_payload = malloc(tx_len);
|
||||
for (size_t b = 0; b < total_blocks; b++) {
|
||||
uint8_t blk[RS_DATA] = {0};
|
||||
size_t bytes = (b == total_blocks - 1) ? n - b * RS_DATA : RS_DATA;
|
||||
memcpy(blk, raw + b * RS_DATA, bytes);
|
||||
fec_encode(enc, RS_DATA, blk, tx_payload + b * RS_ENC);
|
||||
}
|
||||
// Формирование заголовка кадра
|
||||
hdr[0]=0xF0; hdr[1]=0xAA; // Сигнатура начала кадра
|
||||
hdr[2]=(seq>>24)&0xFF; hdr[3]=(seq>>16)&0xFF;
|
||||
hdr[4]=(seq>>8)&0xFF; hdr[5]=seq&0xFF; // Порядковый номер кадра
|
||||
hdr[6]=(n>>8)&0xFF; hdr[7]=n&0xFF; // Размер исходных данных
|
||||
hdr[8]=(total_blocks>>8)&0xFF; hdr[9]=total_blocks&0xFF; // Количество блоков FEC
|
||||
hdr[10]=n%RS_DATA; hdr[11]=0; // Размер последнего блока
|
||||
} else {
|
||||
tx_len = n;
|
||||
tx_payload = malloc(tx_len);
|
||||
memcpy(tx_payload, raw, n);
|
||||
// Формирование заголовка кадра без FEC
|
||||
hdr[0]=0xF0; hdr[1]=0xAA; // Сигнатура начала кадра
|
||||
hdr[2]=(seq>>24)&0xFF; hdr[3]=(seq>>16)&0xFF;
|
||||
hdr[4]=(seq>>8)&0xFF; hdr[5]=seq&0xFF; // Порядковый номер кадра
|
||||
hdr[6]=(n>>8)&0xFF; hdr[7]=n&0xFF; // Размер исходных данных
|
||||
}
|
||||
|
||||
/* пауза между кадрами (чтобы не забивать буфер Pluto) */
|
||||
if (frame_pause_us > 0)
|
||||
usleep(frame_pause_us);
|
||||
// Сборка OFDM-кадра
|
||||
ofdmflexframegen_assemble(fg, hdr, tx_payload, tx_len);
|
||||
free(tx_payload);
|
||||
|
||||
// Формирование выходного сигнала
|
||||
int16_t *out = (int16_t *)iio_buffer_start(buf);
|
||||
memset(out, 0, BUF_SAMPLES * 2 * sizeof(int16_t));
|
||||
int idx = 0, done = 0;
|
||||
float complex symb[OFDM_M + OFDM_CP];
|
||||
|
||||
// Генерация OFDM-символов и запись в буфер
|
||||
while (!done && (idx + OFDM_M + OFDM_CP) <= BUF_SAMPLES) {
|
||||
done = ofdmflexframegen_write(fg, symb, OFDM_M + OFDM_CP);
|
||||
for (int i = 0; i < OFDM_M + OFDM_CP; i++) {
|
||||
out[2*idx] = (int16_t)(crealf(symb[i]) * 32767 * amp);
|
||||
out[2*idx+1] = (int16_t)(cimagf(symb[i]) * 32767 * amp);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
if (idx > 0) iio_buffer_push(buf); // Отправка буфера в эфир
|
||||
|
||||
// Обновление статистики
|
||||
frames++; bytes_tx += n; seq++;
|
||||
if (time(NULL) > last) {
|
||||
fprintf(stderr, "\rПередано кадров:%llu байт:%llu",
|
||||
(unsigned long long)frames, (unsigned long long)bytes_tx);
|
||||
fflush(stderr); last = time(NULL);
|
||||
}
|
||||
if (pause_us > 0) usleep(pause_us); // Пауза между кадрами
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Завершение
|
||||
* ============================================================ */
|
||||
|
||||
printf("\n\n[TX] Stopping...\n");
|
||||
printf(" Total frames: %llu\n", (unsigned long long)total_frames);
|
||||
printf(" Total bytes: %llu\n", (unsigned long long)total_bytes);
|
||||
|
||||
/* освобождение ресурсов */
|
||||
framegen64_destroy(fg);
|
||||
// Завершение работы и освобождение ресурсов
|
||||
fprintf(stderr, "\nПередача завершена: %llu кадров, %llu байт\n",
|
||||
(unsigned long long)frames, (unsigned long long)bytes_tx);
|
||||
if (enc) fec_destroy(enc);
|
||||
ofdmflexframegen_destroy(fg);
|
||||
iio_buffer_destroy(buf);
|
||||
iio_context_destroy(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user