/** * transmitter.c — OFDM-передатчик с RS(255,223) FEC для PlutoSDR * * ОПТИМАЛЬНАЯ КОМАНДА ЗАПУСКА: * cat test.bin | ./transmitter -f 1265000000 -r 3840000 -b 2000000 -g -20 -a 0.3 -p 0 -c rs8 */ #include "common.h" // Параметры передатчика по умолчанию #define DEFAULT_GAIN -30.0 // Усиление передатчика: -30 дБ #define DEFAULT_AMP 0.2f // Амплитуда сигнала #define DEFAULT_PAUSE 2000 // Пауза между кадрами: 2000 мкс #define BUF_SAMPLES 16384 // Размер буфера в отсчётах #define MAX_PAYLOAD 1024 // Максимальный размер полезной нагрузки // Внешняя переменная флага завершения (определена в common.c) extern volatile sig_atomic_t stop; 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 = TX_URI_DEFAULT; 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; case 'c': use_fec = strcmp(optarg, "none") ? 1 : 0; break; case 'h': print_usage(argv[0], "ПЕРЕДАТЧИК"); fprintf(stderr, "\nСПЕЦИФИЧНЫЕ ОПЦИИ TX:\n"); fprintf(stderr, " -g УСИЛЕНИЕ Усиление в дБ (по умолчанию: %.0f)\n", DEFAULT_GAIN); fprintf(stderr, " -a АМПЛИТ. Амплитуда сигнала (по умолчанию: %.2f)\n", DEFAULT_AMP); fprintf(stderr, " -p ПАУЗА Пауза между кадрами в мкс (по умолчанию: %d)\n", DEFAULT_PAUSE); fprintf(stderr, "\nОПТИМАЛЬНЫЙ ЗАПУСК:\n"); fprintf(stderr, " cat test.bin | %s -f %lld -r %lld -b %lld -c rs8 > /dev/null\n", argv[0], DEFAULT_FREQ, DEFAULT_RATE, DEFAULT_BW); return 0; default: return 1; } } // Настройка обработчиков сигналов setup_signal_handlers(); // Вывод конфигурации print_config("ПЕРЕДАТЧИК", freq, rate, bw, use_fec); fprintf(stderr, "Усиление: %.0f дБ\n", gain); fprintf(stderr, "Амплитуда: %.2f\n", amp); fprintf(stderr, "Пауза: %d мкс\n", pause_us); fprintf(stderr, "URI: %s\n", uri); fprintf(stderr, "═══════════════════════════════════════════════════\n"); // Инициализация PlutoSDR struct iio_context *ctx = NULL; struct iio_device *phy = NULL, *tx = NULL; if (pluto_init(uri, &ctx, &phy, &tx, 1) != 0) return 1; // Настройка радиотракта pluto_configure(phy, freq, rate, bw, gain, 1); // Включение каналов передатчика 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) { LOG_ERROR("Ошибка создания буфера передачи"); iio_context_destroy(ctx); return 1; } // Инициализация FEC и 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); fprintf(stderr, " Передатчик готов. Ожидание данных...\n\n"); // Основной цикл передачи 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[HDR_SIZE] = {0}; // Кодирование данных 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] = HDR_SIGNATURE_0; hdr[1] = HDR_SIGNATURE_1; 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; hdr[10] = n % RS_DATA; hdr[11] = 0; } else { tx_len = n; tx_payload = malloc(tx_len); memcpy(tx_payload, raw, n); hdr[0] = HDR_SIGNATURE_0; hdr[1] = HDR_SIGNATURE_1; 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]; 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; }