Files
go-service/SDR/transmitter.c

199 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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;
}