+++ Changes +++

+ duobinary_target now supports memoryless decoding (FFE targets DB response without MLSE)
+ PAMmapper.quantize now supports custom constellations for quantization
+ Added a new folder 'Documentations' for pdfs, slides, etc.
+ Added new FSO evaluation scripts in projects/FSO transmission/Evaluation Scripts
+ Added ffe_db (rudimentary module, not important anymore)
This commit is contained in:
magf
2026-03-05 10:41:21 +01:00
parent 2a724b833f
commit 3676d92b30
32 changed files with 2307 additions and 232 deletions

View File

@@ -0,0 +1,195 @@
%==========================================================================
% High-Speed Link Modell (Dispersion + Reflexionen + Crosstalk)
% Eigenständiges MATLAB-Skript mit Testsequenz (PRBS), Kanalmodell im f-Bereich
% und Auswertung (Eye + Spektren).
%
% Idee:
% - Hauptkanal: H11(f) = exp(-gamma(f)*l)
% - Crosstalk: H21(f) = k_xt * j*(f/fc) / (1 + j*(f/fc)) * exp(-gamma_xt(f)*l)
% (Highpass-artig: typisches FEXT-ähnliches Verhalten; simpel aber nützlich)
% - Reflexionen: optionaler 2-Tap Echo-Term (vereinfachtes Stub/Mismatch-Modell)
%
% Keine Toolboxes zwingend nötig (außer eye diagram -> optional).
%==========================================================================
clear; close all; clc;
%% ----------------------- Simulationseinstellungen -----------------------
Rb = 10e9; % Bitrate [b/s]
UI = 1/Rb; % Unit Interval [s]
os = 32; % Oversampling
Fs = Rb*os; % Abtastrate [Hz]
Ts = 1/Fs;
Nbits = 4096; % Anzahl Bits pro Lane
A = 1.0; % NRZ-Amplitude (0/1 -> +/-A)
% PRBS-Seed
rng(1);
%% ----------------------- Testsequenzen (Aggressor + Victim) -------------
% Victim-Lane Daten
b_v = randi([0 1], Nbits, 1);
x_v = A*(2*b_v - 1); % NRZ: -A/+A
% Aggressor-Lane Daten (unabhängig)
b_a = randi([0 1], Nbits, 1);
x_a = A*(2*b_a - 1);
% Upsampling (NRZ-Rechteck)
x_v_up = upsample(x_v, os);
x_a_up = upsample(x_a, os);
% Simple Zero-Order Hold (rechteckig halten)
x_v_up = filter(ones(os,1), 1, x_v_up);
x_a_up = filter(ones(os,1), 1, x_a_up);
% Am Anfang Filter-Transient entfernen / zentrieren
x_v_up = x_v_up(os:end);
x_a_up = x_a_up(os:end);
% Signal-Länge
N = length(x_v_up);
% Optional: leichte TX-RiseTime-Abbildung (1st order LP)
% (hilft, unrealistische unendliche Bandbreite zu vermeiden)
f_tx_3dB = 0.35/(35e-12); % grob: 35ps 10-90% -> f3dB ~ 0.35/tr
[x_v_up, x_a_up] = apply_1pole_lp(x_v_up, x_a_up, Fs, f_tx_3dB);
%% ----------------------- Frequenzachse (FFT) ----------------------------
% FFT-Länge als Power-of-two (schneller, weniger Circular-Artefakte)
Nfft = 2^nextpow2(N*2);
f = (0:Nfft-1).'*Fs/Nfft; % 0 ... Fs*(1-1/Nfft)
% Für Transferfunktionen brauchen wir auch negative Frequenzen implizit:
% MATLAB-FFT nutzt diese Darstellung automatisch, solange H hermitesch ist.
% Wir definieren H für alle FFT-Bins konsistent mit reellen Signalen.
%% ----------------------- RLGC-Modell (Dispersion & Loss) -----------------
% Leitungslänge
l = 0.40; % [m]
% Per-unit-length Parameter (Beispielwerte, grob PCB-ähnlich)
L = 3.2e-7; % [H/m]
C = 1.6e-10; % [F/m]
G0 = 0; % [S/m] (DC)
tanD = 0.012; % Verlustfaktor (vereinfachtes Dielektrikum-Modell)
% G(f) ~ omega*C*tanD
omega = 2*pi*f;
G = G0 + omega.*C*tanD;
% Skin-Effekt / Rauheit: R(f) ~ Rdc + k*sqrt(f)
Rdc = 0.05; % [Ohm/m]
kR = 0.20; % [Ohm/m/sqrt(Hz)] * sqrt(Hz) -> Ohm/m
R = Rdc + kR*sqrt(max(f,1)); % max(..,1) vermeidet sqrt(0)
% gamma(f) und Z0(f)
gamma = sqrt( (R + 1j*omega*L) .* (G + 1j*omega*C) ); % [1/m]
Z0 = sqrt( (R + 1j*omega*L) ./ (G + 1j*omega*C) ); % [Ohm]
% Hauptpfad-Transferfunktion (perfekt terminiert)
H11 = exp(-gamma*l);
%% ----------------------- Crosstalk-Modell (Aggressor -> Victim) ----------
% Sehr vereinfachtes FEXT-ähnliches Modell: wächst mit f, sättigt
k_xt = 0.04; % Crosstalk-Stärke (skalieren)
fc = 8e9; % Eckfrequenz der Koppelcharakteristik
% optional etwas andere Verluste auf dem Koppelpfad
gamma_xt = gamma .* (1.05 + 0.00j);
Hxt_shape = (1j*(f/fc)) ./ (1 + 1j*(f/fc)); % ~ Highpass
H21 = k_xt .* Hxt_shape .* exp(-gamma_xt*l);
%% ----------------------- Reflexions/Echo-Modell (optional) ---------------
% Einfache 2-Tap-Approximation: Hecho = 1 + a*exp(-j*2*pi*f*tau)
% (z.B. Stub/Via-Resonanz als Echo)
useEcho = true;
a_echo = 0.18; % Echo-Amplitude (|a|<1)
tau = 120e-12; % Echo-Delay [s]
if useEcho
Hecho = 1 + a_echo*exp(-1j*2*pi*f*tau);
H11 = H11 .* Hecho;
end
%% ----------------------- Kanal-Anwendung per FFT -------------------------
% Zero-padding auf Nfft
Xv = fft(x_v_up, Nfft);
Xa = fft(x_a_up, Nfft);
% Victim-Ausgang = Hauptpfad * Victim + Crosstalkpfad * Aggressor
Yv = H11 .* Xv + H21 .* Xa;
y_v = real(ifft(Yv, Nfft));
y_v = y_v(1:N); % zurück auf Originallänge
%% ----------------------- Auswertung: Eye Diagramm ------------------------
% Eye über viele UIs (ohne Toolbox: eigene Darstellung)
plot_eye(y_v, os, 2); % 2 UI Breite
%% ----------------------- Auswertung: Frequenzgänge -----------------------
figure('Name','Transferfunktionen');
subplot(2,1,1);
plot(f/1e9, 20*log10(abs(H11)+1e-15)); grid on;
xlabel('f [GHz]'); ylabel('|H_{11}| [dB]');
title('Hauptkanal (inkl. Dispersion/Loss + optional Echo)');
subplot(2,1,2);
plot(f/1e9, 20*log10(abs(H21)+1e-15)); grid on;
xlabel('f [GHz]'); ylabel('|H_{21}| [dB]');
title('Crosstalk-Pfad (Aggressor -> Victim)');
%% ----------------------- Optional: Bitfehlersicht / Sampling -------------
% Einfaches Sampling in der UI-Mitte (ohne Taktwiedergewinnung)
% (nur grobe Demo, kein echter CDR)
sampleOffset = round(os/2);
idx = sampleOffset:os:(sampleOffset + (Nbits-2)*os);
samples = y_v(idx);
% Hard Decision
bh = samples > 0;
ber = mean(bh(:) ~= b_v(1:length(bh)));
fprintf('--- Ergebnis ---\n');
fprintf('Bitrate: %.2f Gb/s, Oversampling: %d, Fs: %.2f GHz\n', Rb/1e9, os, Fs/1e9);
fprintf('Echo: %d (a=%.3f, tau=%.1f ps)\n', useEcho, a_echo, tau*1e12);
fprintf('Crosstalk k_xt=%.3f, fc=%.2f GHz\n', k_xt, fc/1e9);
fprintf('Grobe BER (ohne CDR/EQ): %.3e\n', ber);
%==========================================================================
% Lokale Hilfsfunktionen
%==========================================================================
function [x1o, x2o] = apply_1pole_lp(x1, x2, Fs, f3dB)
% 1. Ordnung Tiefpass als TX-Bandbegrenzung (optional)
if isempty(f3dB) || f3dB<=0
x1o = x1; x2o = x2; return;
end
w = 2*pi*f3dB;
a = exp(-w/Fs);
b = 1-a;
x1o = filter(b, [1 -a], x1);
x2o = filter(b, [1 -a], x2);
end
function plot_eye(y, os, nUI)
% Eye-Diagramm ohne Toolbox: Segmente von nUI*os sampeln und überlagern
Lseg = nUI*os;
nSeg = floor(length(y)/os) - nUI - 2;
if nSeg < 10
warning('Zu wenig Daten für Eye.');
return;
end
figure('Name','Eye Diagramm');
hold on; grid on;
t = (0:Lseg-1)/os; % in UI
for k = 1:nSeg
i0 = (k-1)*os + 1;
seg = y(i0:i0+Lseg-1);
plot(t, seg);
end
xlabel('Zeit [UI]');
ylabel('Amplitude [V (rel.)]');
title(sprintf('Eye Diagramm (Breite = %d UI, Oversampling = %d)', nUI, os));
end

View File

@@ -0,0 +1,116 @@
%%
clear all;
close all;
% General Parameters
M = 2;
fsym = 20e9;
K_over = 8;
fs = K_over*fsym;
pulselength_mf = 16;
rolloff_mf = 0.1;
N = 18;
Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rc","pulselength",16,"alpha",0.1);
duob_mode = db_mode.no_db;
% FFE Parameters
len_tr = 4096*2;
mu_ffe1 = 0.0001;
mu_ffe2 = 0.0008;
mu_ffe3 = 0.001;
mu_dc = 0.004;
mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
mu_dfe = 0.0004;
ffe_order = [300, 0, 0];
% Toggles
hybrid = 1;
ctle = 1;
timing_recovery = 0;
ffe = 0;
plots = 1;
% Generate Data
[a_1,a_1_Symbols,a_1_Tx_bits] = PAMsource(...
"fsym",fsym,"M",M,"order",N,"useprbs",0,...
"fs_out",fs,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",1,"pulseformer",Pform,...
"randkey",1,...
'duobinary_mode',duob_mode,...
"mrds_code",0,"mrds_blocklength",512).process();
[a_2,a_2_Symbols,a_2_Tx_bits] = PAMsource(...
"fsym",fsym,"M",M,"order",18,"useprbs",0,...
"fs_out",fs,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",1,"pulseformer",Pform,...
"randkey",1,...
'duobinary_mode',duob_mode,...
"mrds_code",0,"mrds_blocklength",512).process();
% S-Parameter Calculation
file_path = "C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\COM Test\Mellitz\host_pkg_top_50mm_max_skew_cable_module_pin_pad_fext1.s4p";
plot_parameters = 1;
test = 0;
S_test = [0,1;1,0];
[b_1, b_2] = Electrical_Trace_BiDi('file_path',file_path,'plot',plot_parameters,'test',test,'S_test',S_test).process(a_1,a_2);
% Hybrid
if hybrid
[~,b_1,~] = Electrical_Hybrid("file_path",file_path).process(a_1,b_1);
[~,b_2,~] = Electrical_Hybrid("file_path",file_path).process(a_2,b_2);
end
% CTLE
if ctle
b_2.normalize("mode","rms").plot("displayname",'Before CTLE','fignum',9);
b_2.normalize("mode","rms").spectrum("displayname",'Before CTLE','normalizeTo0dB',1,'fignum',10);
b_2.normalize("mode","rms").eye(fsym,2,'displayname','Before CTLE','fignum',11);
b_2 = CTLE('Aac_dB',0,'Adc_dB',-5,'f_p1',7.5e9,'f_p2',10e9,'plot',1).process(b_2);
b_2.normalize("mode","rms").plot("displayname",'After CTLE','fignum',9);
b_2.normalize("mode","rms").spectrum("displayname",'After CTLE','normalizeTo0dB',1,'fignum',10);
b_2.normalize("mode","rms").eye(fsym,2,'displayname','After CTLE','fignum',12);
end
% Timing Recovery
if timing_recovery
b_1 = MaxVar_Timing_Recovery('mode',0,'fsym',fsym,'fadc',K_over*fsym,'num_tau',K_over*128,'sps',K_over,'comp_signal',0,'comp_mode',0).process(b_1);
b_2 = MaxVar_Timing_Recovery('mode',0,'fsym',fsym,'fadc',K_over*fsym,'num_tau',K_over*128,'sps',K_over,'comp_signal',0,'comp_mode',0).process(b_2);
end
% FFE
if ffe
eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0], ...
"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
"K",1,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
output.ffe_results = ffe(eq_ffe,M,b_2,a_1_Symbols,a_1_Tx_bits, ...
"precode_mode",duob_mode,'showAnalysis',1,"postFFE",[], ...
"eth_style_symbol_mapping",0);
output.ffe_results.metrics.print
end
% Plots
if plots
% Time Domain Signals
a_1.normalize("mode","rms").plot("displayname",'1st Port Tx','fignum',3);
b_2.normalize("mode","rms").plot("displayname",'2nd Port Rx','fignum',3);
a_2.normalize("mode","rms").plot("displayname",'2nd Port Tx','fignum',4);
b_1.normalize("mode","rms").plot("displayname",'1st Port Rx','fignum',4);
% Spectra
a_1.normalize("mode","rms").spectrum("displayname",'1st Port Tx','normalizeTo0dB',1,'fignum',5);
b_2.normalize("mode","rms").spectrum("displayname",'2nd Port Rx','normalizeTo0dB',1,'fignum',5);
a_2.normalize("mode","rms").spectrum("displayname",'2nd Port Tx','normalizeTo0dB',1,'fignum',6);
b_1.normalize("mode","rms").spectrum("displayname",'1st Port Rx','normalizeTo0dB',1,'fignum',6);
figure;plot(xcorr(a_1.signal,b_2.signal));
figure;plot(xcorr(a_2.signal,b_1.signal));
end