199 lines
10 KiB
C
199 lines
10 KiB
C
/**
|
||
* transmitter.c — OFDM + RS(255,223) FEC + PlutoSDR
|
||
* СИНХРОНИЗИРОВАННАЯ ВЕРСИЯ
|
||
*
|
||
* ОПТИМАЛЬНАЯ КОМАНДА ЗАПУСКА:
|
||
* cat /dev/urandom | ./transmitter -f 1265000000 -r 3840000 -b 2000000 -g -20 -a 0.3 -p 0 -c rs8
|
||
*/
|
||
|
||
#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>
|
||
|
||
// Параметры передачи по умолчанию
|
||
#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 // Максимальный размер полезной нагрузки в байтах
|
||
|
||
// Параметры OFDM-модуляции
|
||
#define OFDM_M 64 // Количество поднесущих
|
||
#define OFDM_CP 16 // Длина циклического префикса
|
||
#define OFDM_TAPER 4 // Длина сглаживающего окна
|
||
|
||
// Параметры помехоустойчивого кодирования Рида-Соломона
|
||
#define RS_DATA 223 // Размер блока данных
|
||
#define RS_ENC 255 // Размер кодированного блока
|
||
|
||
// Глобальный флаг для корректного завершения программы
|
||
volatile sig_atomic_t stop = 0;
|
||
void sigint_handler(int s) { (void)s; stop = 1; }
|
||
|
||
int main(int argc, char *argv[]) {
|
||
// Инициализация параметров значениями по умолчанию
|
||
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:b:g:a:p: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 '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;
|
||
}
|
||
}
|
||
|
||
// Установка обработчиков сигналов для корректного завершения
|
||
signal(SIGINT, sigint_handler);
|
||
signal(SIGTERM, sigint_handler);
|
||
signal(SIGPIPE, SIG_IGN);
|
||
|
||
// Вывод параметров конфигурации передатчика
|
||
fprintf(stderr, "ПЕРЕДАТЧИК: %.1f МГц | скорость=%.2f МГц | полоса=%.2f МГц | усиление=%.0fдБ | амплитуда=%.2f | FEC=%s\n",
|
||
freq/1e6, rate/1e6, bw/1e6, gain, amp, use_fec ? "RS(255,223)" : "ОТКЛ");
|
||
|
||
// Инициализация контекста PlutoSDR
|
||
struct iio_context *ctx = iio_create_context_from_uri(uri);
|
||
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, "Радиоустройства не найдены\n"); return 1; }
|
||
|
||
// Настройка всех параметров радиотракта
|
||
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);
|
||
|
||
// Включение каналов передатчика
|
||
iio_channel_enable(iio_device_find_channel(tx, "voltage0", true));
|
||
iio_channel_enable(iio_device_find_channel(tx, "voltage1", true));
|
||
|
||
// Создание буфера передачи
|
||
struct iio_buffer *buf = iio_device_create_buffer(tx, BUF_SAMPLES, false);
|
||
if (!buf) { fprintf(stderr, "Ошибка создания буфера передачи\n"); return 1; }
|
||
|
||
// Инициализация помехоустойчивого кодирования и OFDM-модулятора
|
||
fec enc = use_fec ? fec_create(LIQUID_FEC_RS_M8, NULL) : NULL;
|
||
|
||
ofdmflexframegenprops_s fgp;
|
||
ofdmflexframegenprops_init_default(&fgp);
|
||
fgp.check = LIQUID_CRC_32;
|
||
ofdmflexframegen fg = ofdmflexframegen_create(OFDM_M, OFDM_CP, OFDM_TAPER, NULL, &fgp);
|
||
|
||
// Счётчики статистики передачи
|
||
uint64_t frames = 0, bytes_tx = 0;
|
||
uint32_t seq = 0;
|
||
time_t last = time(NULL);
|
||
|
||
// Основной цикл передачи
|
||
while (!stop) {
|
||
uint8_t raw[MAX_PAYLOAD];
|
||
size_t n = fread(raw, 1, MAX_PAYLOAD, stdin);
|
||
if (!n) {
|
||
if (feof(stdin) || stop) break;
|
||
usleep(10000);
|
||
continue;
|
||
}
|
||
|
||
uint8_t *tx_payload = NULL;
|
||
size_t tx_len = 0;
|
||
uint8_t hdr[12] = {0};
|
||
|
||
// Кодирование данных с использованием 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; // Размер исходных данных
|
||
}
|
||
|
||
// Сборка 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); // Пауза между кадрами
|
||
}
|
||
|
||
// Завершение работы и освобождение ресурсов
|
||
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;
|
||
} |