+++ 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:
82
Classes/03_electrical/CTLE.m
Normal file
82
Classes/03_electrical/CTLE.m
Normal file
@@ -0,0 +1,82 @@
|
||||
classdef CTLE < handle
|
||||
|
||||
properties(Access=public)
|
||||
Aac_dB
|
||||
Adc_dB
|
||||
f_p1
|
||||
f_p2
|
||||
plot
|
||||
end
|
||||
|
||||
methods(Access=public)
|
||||
function obj = CTLE(options)
|
||||
arguments
|
||||
options.Aac_dB = 0;
|
||||
options.Adc_dB = -6;
|
||||
options.f_p1 = 1.5e9;
|
||||
options.f_p2 = 5e9;
|
||||
options.plot = 0;
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function data_out = process(obj, data_in)
|
||||
|
||||
x = data_in.signal(:);
|
||||
Fs = data_in.fs;
|
||||
N = length(x);
|
||||
|
||||
% CTLE Transfer Function Parameters
|
||||
A_ac = 10^(obj.Aac_dB/20);
|
||||
A_dc = 10^(obj.Adc_dB/20);
|
||||
w1 = 2*pi*obj.f_p1;
|
||||
w2 = 2*pi*obj.f_p2;
|
||||
|
||||
% Frequency Domain Conversion
|
||||
X = fft(x);
|
||||
|
||||
% Generating Frequency Axis In The Range Of [-Fs/2,Fs/2]
|
||||
f_fft = (0:N-1).' * (Fs/N);
|
||||
f_signed = f_fft;
|
||||
idxNeg = f_signed > Fs/2;
|
||||
f_signed(idxNeg) = f_signed(idxNeg) - Fs;
|
||||
f_pos = abs(f_signed);
|
||||
w_pos = 2*pi*f_pos;
|
||||
s_pos = 1j*w_pos;
|
||||
|
||||
% Calculate CTLE Transfer Function
|
||||
H_pos = A_ac*w2 .* (s_pos + (A_dc/A_ac)*w1) ./ ((s_pos + w1).*(s_pos + w2));
|
||||
|
||||
% Enforce H(-f) = conj(H(f)) For Negative Bins
|
||||
H = H_pos;
|
||||
H(idxNeg) = conj(H_pos(idxNeg));
|
||||
|
||||
% Apply CTLE
|
||||
Y = H .* X;
|
||||
|
||||
% Calculate Time Domain Signal
|
||||
y = ifft(Y, 'symmetric');
|
||||
data_out = data_in;
|
||||
data_out.signal = y;
|
||||
data_out.fs = Fs;
|
||||
|
||||
% Plot
|
||||
if obj.plot
|
||||
% plot only positive frequencies up to Fs/2
|
||||
k = 1:floor(N/2)+1;
|
||||
fplot = f_fft(k);
|
||||
Hplot = H(k);
|
||||
|
||||
figure;
|
||||
semilogx(fplot, 20*log10(abs(Hplot)+1e-15));
|
||||
grid on; xlabel('frequency (Hz)'); ylabel('magnitude (dB)');
|
||||
title('CTLE magnitude on FFT grid');
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
130
Classes/03_electrical/Electrical_Hybrid.m
Normal file
130
Classes/03_electrical/Electrical_Hybrid.m
Normal file
@@ -0,0 +1,130 @@
|
||||
classdef Electrical_Hybrid < handle
|
||||
|
||||
properties(Access=public)
|
||||
file_path
|
||||
plot = 0
|
||||
|
||||
% If true: perform digital residual-echo cancellation using known TX
|
||||
cancel_echo = 1
|
||||
|
||||
% If true: in addition return v_hyb and v_echo_est
|
||||
return_intermediates = 1
|
||||
end
|
||||
|
||||
methods(Access=public)
|
||||
|
||||
function obj = Electrical_Hybrid(options)
|
||||
arguments
|
||||
options.file_path = ''
|
||||
options.plot = 0
|
||||
options.cancel_echo = 1
|
||||
options.return_intermediates = 1
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
end
|
||||
|
||||
function [v_fe_rec, v_hyb, v_echo_est] = process(obj, v_ne_tx, v_fe_tx)
|
||||
% v_ne_tx : Near-End TX signal object (Port 1)
|
||||
% v_fe_tx : Far-End TX signal object (Port 2)
|
||||
%
|
||||
% Output:
|
||||
% v_hyb : physical hybrid differential output (Port4 - Port3)
|
||||
% v_echo_est : estimated residual echo due to local TX only
|
||||
% v_fe_rec : v_hyb - v_echo_est (if cancel_echo enabled), else v_hyb
|
||||
|
||||
% --- Load S-Parameters (.s4p) ---
|
||||
net = sparameters(obj.file_path);
|
||||
f_s = net.Frequencies(:);
|
||||
S4 = net.Parameters;
|
||||
|
||||
% Extract needed S-parameters for differential output:
|
||||
% V3 = S31*V1 + S32*V2
|
||||
% V4 = S41*V1 + S42*V2
|
||||
% Vhyb = V4 - V3 = (S41-S31)*V1 + (S42-S32)*V2
|
||||
S31_s = squeeze(S4(3,1,:));
|
||||
S41_s = squeeze(S4(4,1,:));
|
||||
S32_s = squeeze(S4(3,2,:));
|
||||
S42_s = squeeze(S4(4,2,:));
|
||||
|
||||
% --- Time-domain signals ---
|
||||
x1 = v_ne_tx.signal(:);
|
||||
x2 = v_fe_tx.signal(:);
|
||||
|
||||
if length(x2) ~= length(x1)
|
||||
error('Near-end and far-end signals must have the same length.');
|
||||
end
|
||||
|
||||
N = length(x1);
|
||||
Fs = v_ne_tx.fs;
|
||||
|
||||
% --- Use zero padding to avoid circular convolution artifacts ---
|
||||
Nfft = 2^nextpow2(2*N); % robust choice
|
||||
|
||||
% --- FFT ---
|
||||
V1 = fft(x1, Nfft);
|
||||
V2 = fft(x2, Nfft);
|
||||
|
||||
% --- Frequency axis for interpolation (signed then abs) ---
|
||||
f_fft = (0:Nfft-1).' * (Fs/Nfft);
|
||||
f_signed = f_fft;
|
||||
idxNeg = f_signed > Fs/2;
|
||||
f_signed(idxNeg) = f_signed(idxNeg) - Fs; % (-Fs/2, Fs/2]
|
||||
f_pos = abs(f_signed);
|
||||
|
||||
% --- Interpolate S-parameters onto f_pos ---
|
||||
S31 = interp1(f_s, S31_s, f_pos, 'linear', 'extrap');
|
||||
S41 = interp1(f_s, S41_s, f_pos, 'linear', 'extrap');
|
||||
S32 = interp1(f_s, S32_s, f_pos, 'linear', 'extrap');
|
||||
S42 = interp1(f_s, S42_s, f_pos, 'linear', 'extrap');
|
||||
|
||||
% Hermitian symmetry for real time-domain response:
|
||||
% For negative frequencies enforce conj symmetry.
|
||||
S31(idxNeg) = conj(S31(idxNeg));
|
||||
S41(idxNeg) = conj(S41(idxNeg));
|
||||
S32(idxNeg) = conj(S32(idxNeg));
|
||||
S42(idxNeg) = conj(S42(idxNeg));
|
||||
|
||||
% --- Physical hybrid differential output ---
|
||||
% Vhyb = (S41-S31)*V1 + (S42-S32)*V2
|
||||
He = (S41 - S31); % residual echo transfer from local TX
|
||||
Hr = (S42 - S32); % transfer from far-end TX to output
|
||||
|
||||
V_hyb = He .* V1 + Hr .* V2;
|
||||
|
||||
% --- Residual echo estimate (digital canceller model) ---
|
||||
V_echo = He .* V1;
|
||||
|
||||
% --- Back to time-domain (take first N samples after padding) ---
|
||||
v_hyb_full = ifft(V_hyb, 'symmetric');
|
||||
v_echo_full = ifft(V_echo, 'symmetric');
|
||||
|
||||
v_hyb = v_hyb_full(1:N);
|
||||
v_echo_est = v_echo_full(1:N);
|
||||
|
||||
% --- Optional cancellation ---
|
||||
if obj.cancel_echo
|
||||
v_fe_rec = v_hyb - v_echo_est;
|
||||
else
|
||||
v_fe_rec = v_hyb;
|
||||
end
|
||||
|
||||
if ~obj.return_intermediates
|
||||
v_hyb = [];
|
||||
v_echo_est = [];
|
||||
end
|
||||
|
||||
% --- Bring output in the correct form ---
|
||||
v_fe_rec = Informationsignal(v_fe_rec,"fs",v_fe_tx.fs);
|
||||
v_hyb = Informationsignal(v_hyb,"fs",v_fe_tx.fs);
|
||||
v_echo_est = Informationsignal(v_echo_est,"fs",v_fe_tx.fs);
|
||||
|
||||
if obj.plot
|
||||
rfplot(net)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
31
Classes/03_electrical/Electrical_Trace.m
Normal file
31
Classes/03_electrical/Electrical_Trace.m
Normal file
@@ -0,0 +1,31 @@
|
||||
classdef Electrical_Trace < handle
|
||||
|
||||
properties(Access=public)
|
||||
file_path
|
||||
end
|
||||
|
||||
methods(Access=public)
|
||||
function obj = Electrical_Trace(options)
|
||||
arguments(Input)
|
||||
|
||||
options.file_path = 'C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\COM Test\Mellitzz\TA_6002_6003_FX_B6_C6_B7_C7_Terminated.s4p'
|
||||
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
function [data_out,timing_error] = process(obj, data_in)
|
||||
|
||||
S =
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
114
Classes/03_electrical/Electrical_Trace_BiDi.m
Normal file
114
Classes/03_electrical/Electrical_Trace_BiDi.m
Normal file
@@ -0,0 +1,114 @@
|
||||
classdef Electrical_Trace_BiDi < handle
|
||||
|
||||
properties(Access=public)
|
||||
file_path
|
||||
fsym
|
||||
rolloff
|
||||
K_over
|
||||
plot
|
||||
test
|
||||
S_test
|
||||
end
|
||||
|
||||
methods(Access=public)
|
||||
function obj = Electrical_Trace_BiDi(options)
|
||||
arguments(Input)
|
||||
|
||||
options.file_path = 'C:\Users\magf\Desktop\Desktop\MATLAB-Zeugs\COM Test\Mellitzz\TA_6002_6003_FX_B6_C6_B7_C7_Terminated.s4p'
|
||||
options.fsym = 0;
|
||||
options.rolloff = 0;
|
||||
options.K_over = 1;
|
||||
options.plot = 0;
|
||||
options.test = 0;
|
||||
options.S_test = [0,1;1,0];
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
function [b_1,b_2] = process(obj, a_1, a_2)
|
||||
|
||||
% Rx Signal Calculation Using S-Paramters For a 2-Port Network
|
||||
% [B_1(f); B_2(f)] = [S_11(f), S_12(f); S_21(f), S_22(f)] * [A_1(f); A_2(f)]
|
||||
|
||||
% Initialize Rx Time Domain Signals
|
||||
b_1 = a_2;
|
||||
b_2 = a_1;
|
||||
|
||||
% Loading and Extract S-Paramters
|
||||
net = sparameters(obj.file_path);
|
||||
f_s = net.Frequencies(:);
|
||||
|
||||
if ~obj.test
|
||||
S2 = net.Parameters(1:2, 1:2, :);
|
||||
else
|
||||
S2 = repmat(obj.S_test,1,1,size(f_s,1));
|
||||
end
|
||||
|
||||
S11_s = squeeze(S2(1,1,:));
|
||||
S12_s = squeeze(S2(1,2,:));
|
||||
S21_s = squeeze(S2(2,1,:));
|
||||
S22_s = squeeze(S2(2,2,:));
|
||||
|
||||
% Setup Time Domain Signals
|
||||
x1 = a_1.signal(:);
|
||||
x2 = a_2.signal(:);
|
||||
N = length(x1);
|
||||
assert(length(x2)==N, 'a_1 and a_2 must have same length');
|
||||
|
||||
% Extract Time Domain Signal Frequencies
|
||||
if obj.fsym == 0 && obj.rolloff == 0 && obj.K_over == 1
|
||||
Fs = a_1.fs;
|
||||
else
|
||||
Fs = (1+obj.rolloff)*obj.fsym;
|
||||
end
|
||||
|
||||
% Calculate Frequency Domain Signals
|
||||
A1 = fft(x1);
|
||||
A2 = fft(x2);
|
||||
|
||||
% Calculate Frequency Axis [-Fs/2,...,Fs/2]
|
||||
f_fft = (0:N-1).' * (Fs/N);
|
||||
f_signed = f_fft;
|
||||
idxNeg = f_signed > Fs/2;
|
||||
f_signed(idxNeg) = f_signed(idxNeg) - Fs; % Now in (-Fs/2, Fs/2]
|
||||
|
||||
% Absolute Value Used for Interpolation
|
||||
f_pos = abs(f_signed);
|
||||
|
||||
% Interpolate S-Parameters onto f_pos
|
||||
S11_p = interp1(f_s, S11_s, f_pos, 'linear', 'extrap');
|
||||
S12_p = interp1(f_s, S12_s, f_pos, 'linear', 'extrap');
|
||||
S21_p = interp1(f_s, S21_s, f_pos, 'linear', 'extrap');
|
||||
S22_p = interp1(f_s, S22_s, f_pos, 'linear', 'extrap');
|
||||
|
||||
% Apply Hermitian Symmetry: S(-f) = conj(S(f))
|
||||
S11 = S11_p; S12 = S12_p; S21 = S21_p; S22 = S22_p;
|
||||
S11(idxNeg) = conj(S11_p(idxNeg));
|
||||
S12(idxNeg) = conj(S12_p(idxNeg));
|
||||
S21(idxNeg) = conj(S21_p(idxNeg));
|
||||
S22(idxNeg) = conj(S22_p(idxNeg));
|
||||
|
||||
% Compute Rx Frequency Domain Signals
|
||||
B1 = S11 .* A1 + S12 .* A2;
|
||||
B2 = S21 .* A1 + S22 .* A2;
|
||||
|
||||
% Calculate Rx Time Domain Signals
|
||||
b_1.signal = ifft(B1, 'symmetric');
|
||||
b_2.signal = ifft(B2, 'symmetric');
|
||||
|
||||
if obj.plot
|
||||
rfplot(net)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user