M&M error plot
This commit is contained in:
140
QAM/debug.py
140
QAM/debug.py
@ -5,91 +5,137 @@ import numpy as np
|
||||
import sys, os
|
||||
|
||||
# -------------------- Fichiers de données --------------------
|
||||
REF_FILE = "constellation_ref.dat"
|
||||
RX_FILE = "constellation.dat"
|
||||
ERROR_FILE = "pll_error.dat"
|
||||
REF_FILE = "constellation_ref.dat"
|
||||
RX_FILE = "constellation.dat"
|
||||
PLL_ERROR_FILE = "pll_error.dat"
|
||||
MM_ERROR_FILE = "mm_timing_error.dat" # Nouveau fichier pour l'erreur M&M
|
||||
|
||||
# -------------------- Fonctions de chargement --------------------
|
||||
def load_points(filename):
|
||||
if os.path.exists(filename):
|
||||
return np.loadtxt(filename)
|
||||
# Utiliser usecols=(0, 1) pour être sûr d'avoir 2 colonnes
|
||||
return np.loadtxt(filename, usecols=(0, 1))
|
||||
else:
|
||||
return np.zeros((0,2))
|
||||
|
||||
def load_error(filename):
|
||||
if os.path.exists(filename):
|
||||
return np.loadtxt(filename)
|
||||
# Colonne 0: Indice du symbole, Colonne 1: Valeur d'erreur
|
||||
return np.loadtxt(filename, usecols=(0, 1))
|
||||
else:
|
||||
return np.zeros((0,2))
|
||||
|
||||
# -------------------- Application --------------------
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
win = pg.GraphicsLayoutWidget(show=True, title="Constellation")
|
||||
win.resize(900, 900)
|
||||
win = pg.GraphicsLayoutWidget(show=True, title="Synchronisation QAM (M&M)")
|
||||
win.resize(1200, 800)
|
||||
win.setBackground('#0a0a0a') # fond noir profond
|
||||
|
||||
plot = win.addPlot()
|
||||
plot.setAspectLocked(True) # axes égaux
|
||||
# -------------------- Plot 1: Constellation --------------------
|
||||
# Le premier plot prend la première ligne complète
|
||||
plot_constel = win.addPlot(title="Constellation Corrigée (Après M&M)")
|
||||
plot_constel.setAspectLocked(True) # axes égaux
|
||||
plot_constel.setLabel('left', 'Quadrature (Q)', color='#EEE')
|
||||
plot_constel.setLabel('bottom', 'In-phase (I)', color='#EEE')
|
||||
plot_constel.getAxis('left').setPen(pg.mkPen('#AAA', width=2))
|
||||
plot_constel.getAxis('bottom').setPen(pg.mkPen('#AAA', width=2))
|
||||
|
||||
# -------------------- Axes stylés --------------------
|
||||
plot.getAxis('left').setPen(pg.mkPen('#AAA', width=2))
|
||||
plot.getAxis('bottom').setPen(pg.mkPen('#AAA', width=2))
|
||||
plot.getAxis('left').setTextPen(pg.mkPen('#EEE'))
|
||||
plot.getAxis('bottom').setTextPen(pg.mkPen('#EEE'))
|
||||
plot.setLabel('left', 'Quadrature (Q)', color='#EEE', size='12pt')
|
||||
plot.setLabel('bottom', 'In-phase (I)', color='#EEE', size='12pt')
|
||||
|
||||
# -------------------- Grille subtile --------------------
|
||||
grid = pg.GridItem()
|
||||
grid.setPen(pg.mkPen('#444', width=1, style=QtCore.Qt.DotLine))
|
||||
plot.addItem(grid)
|
||||
grid_constel = pg.GridItem()
|
||||
grid_constel.setPen(pg.mkPen('#444', width=1, style=QtCore.Qt.DotLine))
|
||||
plot_constel.addItem(grid_constel)
|
||||
|
||||
# -------------------- Chargement initial --------------------
|
||||
ref_data = load_points(REF_FILE)
|
||||
rx_data = load_points(RX_FILE)
|
||||
error_data = load_error(ERROR_FILE)
|
||||
|
||||
# Points référence
|
||||
ref_plot = plot.plot(ref_data[:,0], ref_data[:,1],
|
||||
pen=None,
|
||||
symbol='o',
|
||||
symbolSize=10,
|
||||
symbolBrush=pg.mkBrush('#1E90FF'), # bleu vif
|
||||
symbolPen=None,
|
||||
name='Référence')
|
||||
ref_plot = plot_constel.plot(ref_data[:,0], ref_data[:,1],
|
||||
pen=None,
|
||||
symbol='o',
|
||||
symbolSize=10,
|
||||
symbolBrush=pg.mkBrush('#1E90FF'), # bleu vif
|
||||
symbolPen=None,
|
||||
name='Référence')
|
||||
|
||||
# Points reçus
|
||||
rx_plot = plot.plot(rx_data[:,0], rx_data[:,1],
|
||||
pen=None,
|
||||
symbol='x',
|
||||
symbolSize=6,
|
||||
symbolBrush=pg.mkBrush('#FF4500'), # orange vif
|
||||
symbolPen=None,
|
||||
name='Reçu')
|
||||
|
||||
# PLL error en vert
|
||||
error_plot = plot.plot(error_data[:,0], error_data[:,1],
|
||||
pen=pg.mkPen('#00FF00', width=2),
|
||||
symbol=None,
|
||||
name='PLL error')
|
||||
rx_plot = plot_constel.plot(rx_data[:,0], rx_data[:,1],
|
||||
pen=None,
|
||||
symbol='x',
|
||||
symbolSize=6,
|
||||
symbolBrush=pg.mkBrush('#FF4500'), # orange vif
|
||||
symbolPen=None,
|
||||
name='Reçu')
|
||||
|
||||
# -------------------- Légende stylée --------------------
|
||||
legend = plot.addLegend()
|
||||
for item in legend.items:
|
||||
legend_constel = plot_constel.addLegend()
|
||||
for item in legend_constel.items:
|
||||
item[1].setPen(pg.mkPen('#FFF', width=2))
|
||||
|
||||
# -------------------- Plot 2: Erreur de Phase PLL --------------------
|
||||
win.nextRow() # On passe à la deuxième ligne
|
||||
plot_pll = win.addPlot(title="Erreur de Phase PLL (Boucle de Costas)")
|
||||
plot_pll.setLabel('left', 'Erreur de Phase Instant. (%)', color='#EEE')
|
||||
plot_pll.setLabel('bottom', 'Symbole k', color='#EEE')
|
||||
plot_pll.getAxis('left').setPen(pg.mkPen('#AAA', width=2))
|
||||
plot_pll.getAxis('bottom').setPen(pg.mkPen('#AAA', width=2))
|
||||
|
||||
pll_error_data = load_error(PLL_ERROR_FILE)
|
||||
pll_error_plot = plot_pll.plot(pll_error_data[:,0], pll_error_data[:,1],
|
||||
pen=pg.mkPen('#00FF00', width=2), # Vert
|
||||
symbol=None,
|
||||
name='Erreur PLL')
|
||||
|
||||
# -------------------- Plot 3: Correction Temporelle M&M --------------------
|
||||
# Ce plot est ajouté SANS win.nextRow() pour le placer à droite du Plot 2 (horizontalement)
|
||||
plot_mm = win.addPlot(title="Correction Temporelle M&M (Sortie NCO)")
|
||||
plot_mm.setLabel('left', 'Timing Offset (échantillons)', color='#EEE')
|
||||
plot_mm.setLabel('bottom', 'Symbole k', color='#EEE')
|
||||
plot_mm.getAxis('left').setPen(pg.mkPen('#AAA', width=2))
|
||||
plot_mm.getAxis('bottom').setPen(pg.mkPen('#AAA', width=2))
|
||||
|
||||
mm_error_data = load_error(MM_ERROR_FILE)
|
||||
mm_error_plot = plot_mm.plot(mm_error_data[:,0], mm_error_data[:,1],
|
||||
pen=pg.mkPen('#FFFF00', width=2), # Jaune
|
||||
symbol=None,
|
||||
name='Correction M&M')
|
||||
|
||||
# Ligne zéro pour la référence de convergence
|
||||
plot_mm.addLine(y=0, pen=pg.mkPen('#555', width=1, style=QtCore.Qt.DashLine))
|
||||
# Ligne de l'offset initial (N/4) pour référence
|
||||
N_val = 0
|
||||
if 'qam' in dir():
|
||||
N_val = qam.N
|
||||
plot_mm.addLine(y=N_val/4, pen=pg.mkPen('#555', width=1, style=QtCore.Qt.DashLine))
|
||||
|
||||
# -------------------- Timer pour mise à jour dynamique --------------------
|
||||
def update():
|
||||
global N_val
|
||||
ref_data = load_points(REF_FILE)
|
||||
rx_data = load_points(RX_FILE)
|
||||
error_data = load_error(ERROR_FILE)
|
||||
pll_error_data = load_error(PLL_ERROR_FILE)
|
||||
mm_error_data = load_error(MM_ERROR_FILE)
|
||||
|
||||
# Constellation
|
||||
if ref_data.size > 0:
|
||||
ref_plot.setData(ref_data[:,0], ref_data[:,1])
|
||||
if rx_data.size > 0:
|
||||
rx_plot.setData(rx_data[:,0], rx_data[:,1])
|
||||
if error_data.size > 0:
|
||||
error_plot.setData(error_data[:,0], error_data[:,1])
|
||||
|
||||
# PLL Error
|
||||
if pll_error_data.size > 0:
|
||||
pll_error_plot.setData(pll_error_data[:,0], pll_error_data[:,1])
|
||||
|
||||
# M&M Timing Correction
|
||||
if mm_error_data.size > 0:
|
||||
mm_error_plot.setData(mm_error_data[:,0], mm_error_data[:,1])
|
||||
# Mise à jour de la ligne de référence N/4 si N est connu
|
||||
if N_val == 0 and len(mm_error_data) > 0:
|
||||
# N = Fs * Ts = 44100 * 0.01 = 441. Utiliser cette valeur si non récupérable
|
||||
N_val = 441 # Valeur par défaut basée sur main.c
|
||||
plot_mm.clear()
|
||||
plot_mm.addItem(mm_error_plot)
|
||||
plot_mm.addLine(y=0, pen=pg.mkPen('#555', width=1, style=QtCore.Qt.DashLine))
|
||||
plot_mm.addLine(y=N_val/4.0, pen=pg.mkPen('#555', width=1, style=QtCore.Qt.DotLine, dash=[2, 2])) # Ligne de l'offset initial
|
||||
|
||||
timer = QtCore.QTimer()
|
||||
timer.timeout.connect(update)
|
||||
|
||||
21
QAM/qam.c
21
QAM/qam.c
@ -364,7 +364,7 @@ void pll(qam_system* qam, double complex* s_with_preamble, double complex* r_cor
|
||||
}
|
||||
|
||||
// Synchro temporelle Müller et Müller
|
||||
void muller_muller_sync(qam_system* qam, double complex* s_data, int nb_symbols, double Kp_mm, double Ki_mm, double complex* r_mm_out) {
|
||||
void muller_muller_sync(qam_system* qam, double complex* s_data, int nb_symbols, double Kp_mm, double Ki_mm, double complex* r_mm_out, FILE* fp_error) {
|
||||
double integrator = 0.0;
|
||||
double timing_offset = 0.0;
|
||||
double current_idx_double = 0.0;
|
||||
@ -411,6 +411,11 @@ void muller_muller_sync(qam_system* qam, double complex* s_data, int nb_symbols,
|
||||
current_idx_double += qam->N - timing_offset;
|
||||
|
||||
d_k_minus_1 = d_k;
|
||||
|
||||
if (fp_error) {
|
||||
fprintf(fp_error, "%d % .8f\n", k, 5 * timing_offset);
|
||||
fflush(fp_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -474,7 +479,7 @@ int main () {
|
||||
init_constellation(&qam);
|
||||
|
||||
// Conversion du texte en bits
|
||||
char* texte = "Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux";
|
||||
char* texte = "Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux,Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux,Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux,Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux,Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux, Vif juge, trempez ce blond whisky aqueux,FIN";
|
||||
int nb_chars = strlen(texte);
|
||||
int nb_bits = nb_chars * 8;
|
||||
int nb_symbols = (nb_bits + qam.k - 1) / qam.k;
|
||||
@ -530,12 +535,14 @@ int main () {
|
||||
fclose(fp_pll_error);
|
||||
|
||||
double complex* s_bande_base = malloc(sizeof(double complex) * nb_symbols * qam.N);
|
||||
demodulate_carrier(&qam, s_corrected + L * qam.N, s_bande_base, total_samples);
|
||||
demodulate_carrier(&qam, s_corrected + L * qam.N, s_bande_base, nb_symbols * qam.N);
|
||||
|
||||
double complex* r_mm_out = malloc(sizeof(double complex) * nb_bits);
|
||||
double Kp_mm = 0.05;
|
||||
double Ki_mm = 0.001;
|
||||
muller_muller_sync(&qam, s_bande_base, nb_symbols, Kp_mm, Ki_mm, r_mm_out);
|
||||
double complex* r_mm_out = malloc(sizeof(double complex) * nb_symbols);
|
||||
double Kp_mm = 0.5;
|
||||
double Ki_mm = 0.01;
|
||||
FILE *fp_mm_error = fopen("mm_timing_error.dat", "w");
|
||||
muller_muller_sync(&qam, s_bande_base, nb_symbols, Kp_mm, Ki_mm, r_mm_out, fp_mm_error);
|
||||
fclose(fp_mm_error);
|
||||
|
||||
// Démodulation
|
||||
FILE *fp_constel = fopen("constellation.dat", "w");
|
||||
|
||||
Reference in New Issue
Block a user