Files
go-service/SDR/reciever.c
2026-05-27 14:13:57 +03:00

215 lines
7.9 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.

// qpsk_rx_constellation.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <math.h>
#include <string.h>
#include <signal.h>
#include <iio.h>
#define RX_BUF_SIZE 32768
#define SAMP_RATE 3840000.0
#define CENTER_FREQ 1300000000.0
#define SPS 8
#define PLOT_SIZE 40
#define ACCUM_COUNT 4000
#define DC_ALPHA 0.001
volatile int stop = 0;
void sigint_handler(int s) { stop = 1; }
unsigned int qpsk_demod(float i, float q) {
if (i > 0 && q > 0) return 0;
if (i < 0 && q > 0) return 1;
if (i < 0 && q < 0) return 2;
return 3;
}
int main() {
signal(SIGINT, sigint_handler);
printf("══════════════════════════════════════════\n");
printf(" QPSK RX + Costas loop\n");
printf("══════════════════════════════════════════\n");
struct iio_context *ctx = iio_create_context_from_uri("ip:192.168.2.1");
struct iio_device *phy = iio_context_find_device(ctx, "ad9361-phy");
struct iio_device *rx_dma = iio_context_find_device(ctx, "cf-ad9361-lpc");
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(rx_lo, "frequency", (long long)CENTER_FREQ);
iio_channel_attr_write_longlong(rx_phy, "sampling_frequency", (long long)SAMP_RATE);
iio_channel_attr_write_longlong(rx_phy, "rf_bandwidth", (long long)(SAMP_RATE * 1.5));
iio_channel_attr_write(rx_phy, "gain_control_mode", "slow_attack");
iio_channel_attr_write(rx_phy, "rf_port_select", "A_BALANCED");
struct iio_channel *rx_i = iio_device_find_channel(rx_dma, "voltage0", false);
struct iio_channel *rx_q = iio_device_find_channel(rx_dma, "voltage1", false);
iio_channel_enable(rx_i);
iio_channel_enable(rx_q);
struct iio_buffer *rxbuf = iio_device_create_buffer(rx_dma, RX_BUF_SIZE, false);
if (!rxbuf) { fprintf(stderr, "ОШИБКА: буфер RX\n"); return 1; }
// Costas loop
float costas_phase = 0.0;
float costas_freq = 0.0;
float costas_alpha = 0.05;
float costas_beta = 0.001;
double dc_i = 0.0, dc_q = 0.0;
// Поиск timing offset
double best_energy[8] = {0};
int best_offset = SPS/2;
printf("[RX] Поиск timing offset...\n");
size_t search_samples = 0;
while (!stop && search_samples < 50000) {
ssize_t n = iio_buffer_refill(rxbuf);
if (n < 0) continue;
int16_t *buf = (int16_t *)iio_buffer_start(rxbuf);
size_t samples = (iio_buffer_end(rxbuf) - iio_buffer_start(rxbuf)) / sizeof(int16_t) / 2;
for (size_t i = 0; i < samples; i++) {
double i_val = (double)buf[i * 2] / 2048.0;
double q_val = (double)buf[i * 2 + 1] / 2048.0;
dc_i = dc_i * 0.999 + i_val * 0.001;
dc_q = dc_q * 0.999 + q_val * 0.001;
best_energy[i % SPS] += (i_val - dc_i) * (i_val - dc_i) + (q_val - dc_q) * (q_val - dc_q);
search_samples++;
}
}
double max_e = 0;
for (int p = 0; p < SPS; p++)
if (best_energy[p] > max_e) { max_e = best_energy[p]; best_offset = p; }
printf("[RX] Timing offset: %d\n\n", best_offset);
// Накопление
double *i_acc = malloc(sizeof(double) * ACCUM_COUNT);
double *q_acc = malloc(sizeof(double) * ACCUM_COUNT);
size_t acc_idx = 0;
dc_i = dc_q = 0.0;
printf("[RX] Накопление с Costas loop...\n");
while (!stop && acc_idx < ACCUM_COUNT) {
ssize_t n = iio_buffer_refill(rxbuf);
if (n < 0) { usleep(1000); continue; }
int16_t *buf = (int16_t *)iio_buffer_start(rxbuf);
size_t samples = (iio_buffer_end(rxbuf) - iio_buffer_start(rxbuf)) / sizeof(int16_t) / 2;
for (size_t i = best_offset; i < samples && acc_idx < ACCUM_COUNT; i += SPS) {
double i_val = (double)buf[i * 2] / 2048.0;
double q_val = (double)buf[i * 2 + 1] / 2048.0;
dc_i = dc_i * (1.0 - DC_ALPHA) + i_val * DC_ALPHA;
dc_q = dc_q * (1.0 - DC_ALPHA) + q_val * DC_ALPHA;
i_val -= dc_i;
q_val -= dc_q;
// Costas: вращение
float cos_p = cosf(costas_phase);
float sin_p = sinf(costas_phase);
float i_rot = i_val * cos_p + q_val * sin_p;
float q_rot = -i_val * sin_p + q_val * cos_p;
// Costas phase detector
float phase_error = (i_rot > 0 ? q_rot : -q_rot) - (q_rot > 0 ? i_rot : -i_rot);
costas_freq += costas_beta * phase_error;
costas_phase += costas_freq + costas_alpha * phase_error;
while (costas_phase > M_PI) costas_phase -= 2*M_PI;
while (costas_phase < -M_PI) costas_phase += 2*M_PI;
i_acc[acc_idx] = i_rot;
q_acc[acc_idx] = q_rot;
acc_idx++;
}
if (acc_idx % 200 == 0) {
printf("\r Накоплено: %zu | фаза: %+.2f", acc_idx, costas_phase);
fflush(stdout);
}
}
printf("\n\n[RX] Готово. Фаза: %.2f рад\n", costas_phase);
// Проверка сигнала
double avg_power = 0.0;
for (size_t i = 0; i < acc_idx; i++)
avg_power += i_acc[i] * i_acc[i] + q_acc[i] * q_acc[i];
avg_power /= acc_idx;
double avg_power_db = 10.0 * log10(avg_power + 1e-20);
printf(" Средняя мощность: %.1f dB\n", avg_power_db);
if (avg_power_db < -20.0) {
printf("\n >>> СИГНАЛ ОТСУТСТВУЕТ! <<<\n");
free(i_acc); free(q_acc);
iio_buffer_destroy(rxbuf);
iio_context_destroy(ctx);
return 0;
}
// Созвездие
double min_i = 999, max_i = -999, min_q = 999, max_q = -999;
for (size_t i = 0; i < acc_idx; i++) {
if (i_acc[i] < min_i) min_i = i_acc[i];
if (i_acc[i] > max_i) max_i = i_acc[i];
if (q_acc[i] < min_q) min_q = q_acc[i];
if (q_acc[i] > max_q) max_q = q_acc[i];
}
double center_i = (max_i + min_i) / 2.0;
double center_q = (max_q + min_q) / 2.0;
double range = fmax(max_i - min_i, max_q - min_q) * 0.55;
printf(" I: [%.2f, %.2f] Q: [%.2f, %.2f]\n", min_i, max_i, min_q, max_q);
int density[PLOT_SIZE][PLOT_SIZE];
memset(density, 0, sizeof(density));
for (size_t i = 0; i < acc_idx; i++) {
int x = (int)((i_acc[i] - center_i + range) / (2.0 * range) * (PLOT_SIZE - 1) + 0.5);
int y = (int)((q_acc[i] - center_q + range) / (2.0 * range) * (PLOT_SIZE - 1) + 0.5);
if (x >= 0 && x < PLOT_SIZE && y >= 0 && y < PLOT_SIZE) density[y][x]++;
}
int max_density = 1;
for (int y = 0; y < PLOT_SIZE; y++)
for (int x = 0; x < PLOT_SIZE; x++)
if (density[y][x] > max_density) max_density = density[y][x];
const char *shade = " .:-=+*#@";
printf("\n Q\n ^\n");
for (int y = PLOT_SIZE - 1; y >= 0; y--) {
printf("%s", (y == PLOT_SIZE/2) ? "0 -" : " |");
for (int x = 0; x < PLOT_SIZE; x++) {
int level = density[y][x] * 8 / max_density;
printf("%c", shade[level]);
}
printf("%s\n", (y == PLOT_SIZE/2) ? "-> I" : "");
}
int quad[4] = {0};
for (size_t i = 0; i < acc_idx; i++) {
double i_n = i_acc[i] - center_i;
double q_n = q_acc[i] - center_q;
if (i_n > 0 && q_n > 0) quad[0]++;
if (i_n < 0 && q_n > 0) quad[1]++;
if (i_n < 0 && q_n < 0) quad[2]++;
if (i_n > 0 && q_n < 0) quad[3]++;
}
printf("\n Квадранты: Q1=%.0f%% Q2=%.0f%% Q3=%.0f%% Q4=%.0f%%\n",
100.0*quad[0]/acc_idx, 100.0*quad[1]/acc_idx,
100.0*quad[2]/acc_idx, 100.0*quad[3]/acc_idx);
printf("\n[RX] Готово.\n");
free(i_acc); free(q_acc);
iio_buffer_destroy(rxbuf);
iio_context_destroy(ctx);
return 0;
}