Merge branch 'main' of cau-git.rz.uni-kiel.de:nt/mitarbeiter/silas/imdd_simulation

This commit is contained in:
Silas Oettinghaus
2026-03-24 18:01:13 +01:00
159 changed files with 2650 additions and 1451 deletions

View File

@@ -0,0 +1,54 @@
classdef TestRand_examplecode < matlab.unittest.TestCase
properties (ClassSetupParameter)
generator = {'twister','combRecursive','multFibonacci'};
end
properties (MethodSetupParameter)
seed = {0,123,4294967295};
end
properties (TestParameter)
dim1 = struct('small',1,'medium',2,'large',3);
dim2 = struct('small',2,'medium',3,'large',4);
dim3 = struct('small',3,'medium',4,'large',5);
type = {'single','double'};
end
methods (TestClassSetup)
function classSetup(testCase,generator)
orig = rng;
testCase.addTeardown(@rng,orig)
rng(0,generator)
end
end
methods (TestMethodSetup)
function methodSetup(testCase,seed)
orig = rng;
testCase.addTeardown(@rng,orig)
rng(seed)
end
end
methods (Test, ParameterCombination = 'sequential')
function testSize(testCase,dim1,dim2,dim3)
testCase.verifySize(rand(dim1,dim2,dim3),[dim1 dim2 dim3])
end
end
methods (Test, ParameterCombination = 'pairwise')
function testRepeatable(testCase,dim1,dim2,dim3)
state = rng;
firstRun = rand(dim1,dim2,dim3);
rng(state)
secondRun = rand(dim1,dim2,dim3);
testCase.verifyEqual(firstRun,secondRun)
end
end
methods (Test)
function testClass(testCase,dim1,dim2,type)
testCase.verifyClass(rand(dim1,dim2,type),type)
end
end
end

View File

@@ -0,0 +1,15 @@
if 0
AWG = AwgKeysight("model","M8196A","fdac",92e9,"scaletodac",[1,1,1,1],"skews",[0,0,0,0],"voltages",[0,0,0,0.6]);
tx_sig = Electricalsignal(clip_out,"fs",92e9);
tic
AWG.upload("signal4",clip_out);
toc
end
SCP = ScopeKeysight("model","DSAZ634A",'autoscale',1,"fadc",'GSa_160',"channel",[1],"recordLen",2000000);
signals = SCP.read();

View File

@@ -0,0 +1,219 @@
%% Bayesian Optimization for FFE Parameter Tuning
% This script uses bayesopt to find optimal mu_dd and mu_tr values
% that minimize BER for the FFE equalizer.
clear; clc;
%% Setup - Same as gpu_processing_dpfiber.m
s.wavelengthplan = calcWavelengthPlan(4, 400e9, 1310);
link_length = 10;
s.pmd = 0.1;
s.gamma = 0.0023;
s.M = 4;
fsym = 112e9;
fdac = 2*fsym;
fadc = 120000000000;
s.random_key = 1;
% Laser / Modulator
vbias_rel = 0.5;
u_pi = 4.6;
vbias = -vbias_rel*u_pi;
laser_linewidth = 0e6;
duob_mode = db_mode.no_db;
rcalpha = 0.05;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
s.chirpalpha = 0;
s.p_launch = 3;
s.p = "co";
N = numel(s.wavelengthplan);
switch s.p
case "co"
pol_rot = 100.*ones(1,N);
d_local = 0;
end
f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9);
margin = 25e12;
f_span = (max(f_plan)+margin)-(min(f_plan)-margin);
f_nyq = f_span/2;
kover = 4;
upsample_required = f_nyq./(fdac*kover/2);
upsample_pow = 2^nextpow2(upsample_required);
s.f_opt = fdac*kover*upsample_pow;
s.f_opt_nyq = s.f_opt/2;
s.rop = -8; % Fixed ROP for optimization
%% Generate TX signals (run once)
fprintf('Generating TX signals...\n');
for l = 1:N
[Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ...
"fsym",fsym,"M",s.M,"order",15,"useprbs",0, ...
"fs_out",fdac, ...
"applyclipping",0,"clipfactor",1.5, ...
"applypulseform",1,"pulseformer",Pform, ...
"randkey",s.random_key+l, ...
"mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode ...
).process();
Lp_awg = Filter('filtdegree',3,"f_cutoff",56e9,"fs",fdac*kover, ...
"filterType",filtertypes.gaussian,"active",true);
El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover, ...
"bit_resolution",6,"upsampling_method","samplehold","precomp_sinc_rolloff",0, ...
"H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig);
El_sig = El_sig.normalize("mode","oneone");
scaling = 0.6*(u_pi/2-abs(vbias-u_pi/2));
El_sig = El_sig .* scaling;
Eml_out = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs, ...
"lambda",s.wavelengthplan(l),"bias",vbias,"u_pi",u_pi, ...
"linewidth",laser_linewidth,"randomkey",s.random_key+l,"alpha",s.chirpalpha).process(El_sig);
signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",pol_rot(l)).process(Eml_out);
end
%% WDM mux + launch
Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover, ...
"lambda_center",1310,"random_key",0,"filtype",1,"B",120e9).process(signal_cell);
Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
"amplification_db",s.p_launch+10*log10(N)).process(Opt_sig_wdm);
%% Fiber propagation
segment_length = 1;
nSegments = link_length/segment_length;
nSegments = round(nSegments);
zdw = 1310;
randomize_D = true;
Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key);
Opt_sig_wdm_fib = Opt_sig_wdm;
fprintf('Running fiber propagation...\n');
for seg = 1:nSegments
fprintf('Segment %d/%d\n', seg, nSegments);
Opt_sig_wdm_fib = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
"beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
"gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
"SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",true,"useSingle",true).process(Opt_sig_wdm_fib);
end
%% Pre-process to get Rx_sig (do demux once)
fprintf('Pre-processing receiver chain...\n');
l = 1; % Use channel 1 for optimization
Opt_sig_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1, ...
"fs_out",fdac*kover,"fs_in",fdac*kover*upsample_pow,"lambda_center",1310).process(Opt_sig_wdm_fib);
Opt_sig_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
"amplification_db",s.rop).process(Opt_sig_demux{l});
PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20, ...
"nep",1.8e-11,"randomkey",s.random_key+l).process(Opt_sig_rx);
rx_bwl = 100e9;
PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover, ...
"filterType",filtertypes.butterworth,"active",true).process(PD_sig);
Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc, ...
"delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth, ...
"samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0, ...
"adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',0,'H_lpf',Lp_scpe).process(PD_sig);
Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym);
[~, Scpe_cell, ~, ~] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 0);
Rx_sig = Scpe_cell{1};
Rx_sig = Rx_sig.normalize("mode","rms");
fprintf('Receiver pre-processing complete. Ready for optimization.\n\n');
%% Define the objective function for bayesopt
function ber = ffe_objective(params, Rx_sig, Symbols_l, Tx_bits_l, M, duob_mode)
mu_dd = params.mu_dd;
mu_tr = params.mu_tr;
try
eq_ffe = FFE("epochs_tr", 5, "epochs_dd", 2, "len_tr", 2^13, ...
"mu_dd", mu_dd, "mu_tr", mu_tr, ...
"order", 50, "sps", 2, "decide", 0, ...
"adaption", adaption_method.nlms, "dd_mode", 1);
ffe_results = ffe(eq_ffe, M, Rx_sig, Symbols_l, Tx_bits_l, ...
"precode_mode", duob_mode, ...
'showAnalysis', 0, ...
"postFFE", [], ...
"eth_style_symbol_mapping", 0);
ber = ffe_results.metrics.BER;
if ber == 0
ber = 1e-10;
end
if ~isfinite(ber)
ber = 0.5;
end
fprintf(' mu_dd=%.4e, mu_tr=%.4e -> BER=%.4e\n', mu_dd, mu_tr, ber);
catch ME
fprintf(' mu_dd=%.4e, mu_tr=%.4e -> FAILED (%s)\n', mu_dd, mu_tr, ME.message);
ber = 0.5;
end
end
%% Define optimizable variables
mu_dd_var = optimizableVariable('mu_dd', [1e-5, 0.1], 'Transform', 'log');
mu_tr_var = optimizableVariable('mu_tr', [1e-5, 0.1], 'Transform', 'log');
%% Run Bayesian Optimization
fprintf('========== Starting Bayesian Optimization ==========\n');
fprintf('Optimizing mu_dd and mu_tr to minimize BER\n');
fprintf('Search range: mu_dd=[1e-5, 0.1], mu_tr=[1e-5, 0.1]\n\n');
objective_fn = @(params) ffe_objective(params, Rx_sig, Symbols{l}, Tx_bits{l}, s.M, duob_mode);
results = bayesopt(objective_fn, [mu_dd_var, mu_tr_var], ...
'MaxObjectiveEvaluations', 30, ...
'AcquisitionFunctionName', 'expected-improvement-plus', ...
'IsObjectiveDeterministic', false, ...
'ExplorationRatio', 0.5, ...
'Verbose', 1, ...
'PlotFcn', []);
%% Display Results
fprintf('\n========== FFE Optimization Complete ==========\n');
fprintf('Best FFE parameters found:\n');
fprintf(' mu_dd = %.6e\n', results.XAtMinObjective.mu_dd);
fprintf(' mu_tr = %.6e\n', results.XAtMinObjective.mu_tr);
fprintf(' BER = %.6e\n', results.MinObjective);
%% Verify with optimal parameters
fprintf('\nVerifying optimal FFE parameters...\n');
best_mu_dd = results.XAtMinObjective.mu_dd;
best_mu_tr = results.XAtMinObjective.mu_tr;
eq_ffe_best = FFE("epochs_tr", 5, "epochs_dd", 2, "len_tr", 2^13, ...
"mu_dd", best_mu_dd, "mu_tr", best_mu_tr, ...
"order", 50, "sps", 2, "decide", 0, ...
"adaption", adaption_method.nlms, "dd_mode", 1);
ffe_results_best = ffe(eq_ffe_best, s.M, Rx_sig, Symbols{l}, Tx_bits{l}, ...
"precode_mode", duob_mode, ...
'showAnalysis', 1, ...
"postFFE", [], ...
"eth_style_symbol_mapping", 0);
fprintf('\nFinal FFE BER with optimal parameters: %.6e\n', ffe_results_best.metrics.BER);

View File

@@ -0,0 +1,555 @@
classdef bcjr_pam < handle
%MLSE calculates the most probable sequence for an input signal with given/ known channel impulse response of any length
properties(Access=public)
M %PAM-M
DIR
trellis_states
duobinary_output
end
methods (Access=public)
function obj = bcjr_pam(options)
%NAME Construct an instance of this class
% Detailed explanation goes here
arguments
options.M double = 4;
options.DIR double = [1];
options.trellis_states double = [-3 -1 1 3];
options.duobinary_output logical = false;
end
%
fn = fieldnames(options);
for n = 1:numel(fn)
try
obj.(fn{n}) = options.(fn{n});
end
end
end
function [VITERBI_ESTIMATION_SYMBOLS,LLR_exact,GMI] = process(obj,data_in,data_ref,tx_bits,bit_mapping)
debug = 0;
% States should match the target states of the prev. EQ (EQ's job was to reduce the error between signal and the target)
trellis_state_mode = 2;
% 0 = use provided states (MUST provide the correct states);
% 1 = normalize to = 1 rms;
% 2 = use target symbols;
% 3 = use statistical levels
% 3 analyzes avg of rx signal levels - can help with nonlinear impairments
trellis_exclusion = 1; % PAM-6 only (only if data is NOT precoded!)
% Additional scaling between states, expected output (noiseless_received) and the noisy, filtered input signal
scale_mode = 2; % scale_mode:
% 0 = no scaling,
% 1 = use RMS to scale MODEL,
% 2 = use MMSE/time-corr to scale MODEL, -> This best to get the GMI right -> sometimes the LLP's are not centered around zero...
% 3 = use RMS to scale DATA,
% 4 = use MMSE/time-corr to scale DATA
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%% PREPARATIONS %%%%%%%%
% remove unnecessary zeros at start of impulse response to keep
% number of trellis states minimal
DIR_nonzero = find(obj.DIR ~= 0);
if DIR_nonzero(1) > 1
obj.DIR(1:DIR_nonzero(1)-1) = [];
end
if isscalar(obj.DIR)
obj.DIR = [0 obj.DIR];
end
% impulse respnse to remove from signal
obj.DIR = flip(obj.DIR); %i.e. -0.2676 -0.0478 1.0000
% Trellis States
obj.trellis_states = reshape(obj.trellis_states,1,[]);
if trellis_state_mode == 1 % Normalize the Trellis states to =1 RMS
obj.trellis_states = obj.trellis_states ./ rms(obj.trellis_states);
elseif trellis_state_mode == 2 %simply use the states from the ref signal (should be a robust option)
obj.trellis_states = reshape(unique(data_ref),size(obj.trellis_states));
elseif trellis_state_mode == 3 %use_statistical_levels
%%%% Separate the equalized signal into the respective levels based on the actually transmitted level
constellation = unique(data_ref);
% find actual levels from rx signal
symbols_for_lvl = NaN(numel(constellation),length(data_ref));
for l = 1:numel(constellation)
level_amplitude = constellation(l);
symbols_for_lvl(l,data_ref==level_amplitude) = data_in(data_ref==level_amplitude);
end
%replace the trellis states
avg_levels = mean(symbols_for_lvl,2,'omitnan');
obj.trellis_states = sort(avg_levels)';
%also replace the whole ref signal (PAM-M) levels
[~, idx] = ismember(data_ref, unique(data_ref));
data_ref = avg_levels(idx);
end
% seems to be the only way to use combvec for a flexible amount
% of vectors. 'combs' contains all trellis states
pre_comb_mat = repmat(obj.trellis_states,length(obj.DIR)-1,1);
pre_comb_cell = mat2cell(pre_comb_mat,ones(1,size(pre_comb_mat,1)),size(pre_comb_mat,2));
combs = fliplr(combvec(pre_comb_cell{:}).');
first_sym = combs(:,1); % das ist das älteste/ trailing Symbol aus der sequenz
last_sym = combs(:,end); %hiermit wird entschieden/ das ist das cursor symbol am ende der sequenz
nStates = length(last_sym);
% % Calculate all possible input symbols for the desired impulse
% % response. Row number is the index of the previous state,
% % column number is the index of the next state
% % noise free received == branch metrics
% assumes: last_sym = combs(:,end); % already defined earlier
levels = sort(unique(obj.trellis_states(:)).');
edges = [levels(1) levels(end)]; % edge levels (0 and 5 in PAM6)
noise_free_received = inf(nStates,nStates); % rows: to, cols: from
edge_edge_mask = false(nStates,nStates); % rows: to, cols: from
for from = 1:nStates
for to = 1:nStates
% valid transition if shift-register overlap holds
if all(combs(to,2:end) == combs(from,1:end-1))
% noiseless sample for the 'to' state reached from 'from'
noise_free_received(to,from) = ...
dot(combs(to,:), obj.DIR(end:-1:2)) + last_sym(from)*obj.DIR(1);
% mark edgeedge candidate (to be excluded only on evenodd steps)
edge_edge_mask(to,from) = ...
(last_sym(from)==edges(1) || last_sym(from)==edges(2)) && ...
(last_sym(to) ==edges(1) || last_sym(to) ==edges(2));
end
end
end
h = flip(obj.DIR(:)).';
data_in = data_in(:);
y_ideal = conv(data_ref(:), h, "same");
switch scale_mode
case 0
g = 1; b = 0;
case 1 % RMS: scale model to data
g = rms(data_in)/rms(y_ideal); b = mean(data_in) - g*mean(y_ideal);
case 2 % MMSE/time-corr: scale states to data
[c,lags] = xcorr(data_in(:), y_ideal, 64);
[~,ix] = max(abs(c));
lag = lags(ix);
y_ideal = circshift(y_ideal, lag);
mu_y = mean(data_in(:));
mu_i = mean(y_ideal);
y_c = data_in(:)-mu_y;
yi_c = y_ideal-mu_i;
g = (yi_c'*y_c)/(yi_c'*yi_c);
b = mu_y - g*mu_i;
case 3 % RMS flipped: scale data to model
gd = rms(y_ideal)/rms(data_in); bd = mean(y_ideal) - gd*mean(data_in);
data_in = gd*data_in + bd;
g = 1; b = 0;
case 4 % MMSE/time-corr flipped: scale data to states
[c,lags] = xcorr(data_in(:), y_ideal(:), 64);
[~,ix] = max(abs(c));
lag = lags(ix);
y_ideal = circshift(y_ideal(:), lag);
mu_y = mean(data_in(:));
mu_i = mean(y_ideal);
y_c = data_in(:) - mu_y; % data_in centered
yi_c = y_ideal - mu_i; % ideal centered
g = (y_c' * yi_c) / (y_c' * y_c);
b = mu_i - g * mu_y;
data_in = g * data_in(:) + b;
g = 1; b = 0;
end
% apply (g,b) to states/ expected values
noise_free_received = g*noise_free_received + b;
last_sym = g*last_sym + b;
% calculate noise power
sigma2 = mean(abs(data_in - (g*y_ideal + b)).^2); %noise = mean(abs((RX Signal - IDEAL Signal)))^2
inv2s2 = 1/(2*sigma2);
if debug
figure(100); clf; hold on
obj.showLevelScatter_(data_in, data_ref);
yline(noise_free_received(:), 'DisplayName','Transition States','Color','red','HandleVisibility','off');
yline(obj.trellis_states(:), 'DisplayName','Transition States','Color','green','LineWidth',2,'HandleVisibility','off')
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% FORWARD PASS (VITERBI -Alpha's) %%%%%
% Initialize the output vector
pm = zeros(nStates,nStates);
bm_fw = zeros(nStates,nStates,length(data_in));
% first start is evaluated without ISI/ wihout the full Impulse response
% so simply use the constellation here
bm = -(data_in(1) - last_sym).^2 * inv2s2;
pm = pm + bm;
[alpha(:,1),pm_survivor_fw_idx(:,1)] = max(pm,[],2);
pm = repmat(alpha(:,1).',nStates,1);
bm_fw(:,:,1) = pm;
% Forward Recursion (FSM Computation)
for n = 2:length(data_in)
bm = -(data_in(n) - noise_free_received).^2 * inv2s2;
% exclude edge to edge transitions only for even->odd steps && PAM-6
if mod(n,2) == 0 && obj.M == 6 && trellis_exclusion
bm(edge_edge_mask) = -Inf;
end
pm = pm + bm;
[alpha(:,n),pm_survivor_fw_idx(:,n)] = max(pm,[],2); % choose lowest path metric as new state (get min distance for all state transitions towards a new state)
pm = repmat(alpha(:,n).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state)
bm_fw(:,:,n) = bm;
end
% we can now get the best path as min
viterbi_path = NaN(1,length(data_in));
% find ideal trellis path by going through the trellis backwards
[~,viterbi_path(length(data_in))] = max(alpha(:,length(data_in)));
for n = length(data_in):-1:2
viterbi_path(n-1) = pm_survivor_fw_idx(viterbi_path(n),n);
end
if debug
alpha_ = alpha - min(alpha) + eps;
figure();hold on;
n = 10;
scatter(1:n,obj.trellis_states(repmat([1:numel(obj.trellis_states)]',1,n)),abs(alpha_(:,end-n+1:end)),'Marker','o','LineWidth',1);
scatter(1:n,obj.trellis_states(viterbi_path(end-n+1:end)),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','green');
% scatter(1:n,data_ref(end-n+1:end),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','red');
yticks(obj.trellis_states);
ylim([min(obj.trellis_states)-1 max(obj.trellis_states)+1]);
end
VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = first_sym(viterbi_path);
VITERBI_ESTIMATION_SYMBOLS = reshape(VITERBI_ESTIMATION_SYMBOLS,size(data_in));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% BACKWARD (Beta's) %%%%%
% Initialize the output vector
pm = zeros(nStates,nStates);
beta = zeros(nStates,length(data_in));
pm_survivor_bw_idx = zeros(nStates,length(data_in));
bm_bw = zeros(nStates,nStates,length(data_in));
% starting with the state that has the lowest sum path
% metric, follow the stored information about the
% predecessor
for h = length(data_in)-1:-1:1
bm = -(data_in(h+1) - noise_free_received).^2 * inv2s2;
% exclude edge to edge transitions for even->odd steps && PAM-6
if mod(h+1, 2) == 0 && obj.M == 6 && trellis_exclusion
bm(edge_edge_mask) = -Inf;
end
pm = pm + bm.';
[beta(:,h),pm_survivor_bw_idx(:,h)] = max(pm,[],2); % choose lowest path metric as new state
pm = repmat(beta(:,h).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state)
bm_bw(:,:,h) = bm;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% FORWARD (Combine Alpha and Beta to yield LLP's) %%%%%
%calc the log probabilities (llp's)
for k = 1:length(data_in)
if k == 1
alpha_ = repmat(alpha(:,k)',[nStates,1])';
beta_ = beta(:,k);
LLP(:,k) = max(alpha_ + beta_,[],2);
else
alpha_ = repmat(alpha(:,k-1)',[nStates,1])';
gamma_ = bm_fw(:,:,k)';
beta_ = beta(:,k);
LLP(:,k) = max(alpha_ + gamma_,[],1) + beta_';
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% Calc LLR's %%%%%
% These are interchangeable...
nml_LLP = LLP - max(LLP); %subtract highest value for better numerical stability, LLP's are not always close to zero
expLLP = exp(nml_LLP);
state_prob = expLLP ./ sum(expLLP); % sums to one (or numerically close to one)
% compute symbolposteriors from LLP in the logdomain:
amax = max(LLP,[],1);
logZ = amax + log(sum(exp(LLP - amax), 1));
logPstate = LLP - logZ; % still in logdomain
state_prob = exp(logPstate); % exact, sums to 1
if obj.M == 6
num_bits = 5;
% all possible transitions (for now 36, including the "edges"
% of the QAM 32 constellation)
states = [-5 -3 -1 1 3 5];
pam6transitions = combvec(states,states)'; % pam6transitions =
% [-5 -5;
% -3 -5;
% -1 -5; ...
[~, idx_sym_1] = ismember(pam6transitions(:,1), states);
[~, idx_sym_2] = ismember(pam6transitions(:,2), states);
pam6ind = [idx_sym_1, idx_sym_2];
numPairs = floor(size(LLP,2)/2);
LLR_exact = zeros(numPairs,5);
LLR_maxlogmap = zeros(numPairs,5);
for k = 1:numPairs
symbol1 = 2*k-1;
symbol2 = 2*k;
LLP1 = LLP(:,symbol1);
LLP2 = LLP(:,symbol2);
prob1 = state_prob(:,symbol1);
prob2 = state_prob(:,symbol2);
% All 36 Combinations: M = LLP Symbol 1 + LLP Symbol 2
Mij = LLP1(pam6ind(:,1)) + LLP2(pam6ind(:,2));
pij = prob1(pam6ind(:,1)) .* prob2(pam6ind(:,2));
% for each of the 5 bits sum exact-probs or max-log
for b = 1:num_bits
idx_sym_1 = bit_mapping(:,b)==1;
idx_bit_1 = bit_mapping(:,b)==0;
% exact LLR from probabilities
P1 = sum(pij(idx_sym_1)); %prob that bit == 1
P0 = sum(pij(idx_bit_1));
LLR_exact(k,b) = log(P1./P0); %ratio by multiplication
% max-log:
LLR_maxlogmap(k,b) = max( Mij(idx_sym_1) ) - max( Mij(idx_bit_1) ); % ratio by subtraction
end
end
% GMI calc includes the Tx-bitstream
tx_bits_pam6_reshaped = reshape(tx_bits',5,[])'; % N x 5
MI = zeros(1, num_bits);
for k = 1:num_bits
idx_bit_1 = (tx_bits_pam6_reshaped(:,k) == 0); %wo sind die 1en
idx_sym_1 = (tx_bits_pam6_reshaped(:,k) == 1); %wo sind die 0en
%LLR's for all actually transmitted ones or zeros
llr0 = LLR_exact(idx_bit_1,k);
llr1 = LLR_exact(idx_sym_1,k);
% Calculate mutual information for bit position k
I0 = mean(log2(1 + exp(llr0))); % exp(--LLR) = exp(positive) > 1
I1 = mean(log2(1 + exp(-llr1))); % exp(-+LLR) = exp(negative) < 1
MI(k) = 1 - 0.5 * (I0 + I1);
end
GMI = sum(MI); % Total mutual information per symbol
GMI = GMI/2; % GMI per single symbol not per two symbols
else
% Number of symbols and bits per symbol
num_bits = log2(length(obj.trellis_states)); % 2 bits per symbol
% bit_mapping = PAMmapper(length(obj.trellis_states),0,"eth_style",0).showBitMapping;
% Initialize LLR storage
LLR_maxlogmap = zeros(length(data_in),num_bits);
LLR_exact = zeros(length(data_in),num_bits);
% Compute bit-wise LLRs
for bit_idx = 1:num_bits
% Find indices where bit is 0 and where it is 1
idx_bit_0 = bit_mapping(:,bit_idx) == 0;
idx_bit_1 = bit_mapping(:,bit_idx) == 1;
% Sum over log-probabilities
% Max-Log approximation uses the single max LLP value
% instead of sum over all LLP's
LLR_maxlogmap(:,bit_idx) = max(LLP(idx_bit_1,:), [], 1) - max(LLP(idx_bit_0,:), [], 1);
% Sum probabilities over states for which the bit is 1 and 0, respectively.
P0 = sum(state_prob(idx_bit_0, :),1);
P1 = sum(state_prob(idx_bit_1, :),1);
LLR_exact(:,bit_idx) = log(P1./P0); % N x num_bits
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% CALC NGMI %%%%%
MI = zeros(1, num_bits);
for k = 1:num_bits
idx_bit_0 = (tx_bits(:,k) == 0); %wo sind die 1en
idx_bit_1 = (tx_bits(:,k) == 1); %wo sind die 0en
%LLR's for all actually transmitted ones or zeros
llr0 = LLR_exact(idx_bit_0,k);
llr1 = LLR_exact(idx_bit_1,k);
% mutual information for bit position k
I0 = mean(log2(1 + exp(llr0))); % exp(--LLR) = exp(positive) > 1
I1 = mean(log2(1 + exp(-llr1))); % exp(-+LLR) = exp(negative) < 1
MI(k) = 1 - 0.5 * (I0 + I1); % assumes equally distributed ones and zeros
end
GMI = sum(MI); % Total bitwise mutual information
end
if debug
%%% DEBUG PLOT LIKELIHOOD RATIOS %%%
figure(115);clf
subplot(2,1,1)
for bit = 1:num_bits
hold on;
histogram(LLR_exact(:,bit),1000,"DisplayName",sprintf('Actual LLR of Bit Pos %d',bit),'LineStyle','none','FaceAlpha',0.4);
end
legend
subplot(2,1,2)
for bit = 1:num_bits
hold on;
histogram(LLR_maxlogmap(:,bit),1000,"DisplayName",sprintf('Max Log LLR of Bit Pos %d',bit),'LineStyle','none','FaceAlpha',0.4);
end
legend
if obj.M == 6
pairs = reshape(VITERBI_ESTIMATION_SYMBOLS,2,[]).';
levels = sort(unique(VITERBI_ESTIMATION_SYMBOLS));
isedge = ismember(pairs, [levels(1) levels(end)]);
isforbidden = sum(isedge,2)==2;
fprintf('Found %d forbidden transitions (even -> odd ; edge -> edge).\n', nnz(isforbidden));
end
end
end
function [symbols_for_lvl,avg_for_lvl] = showLevelScatter_(~,eq_signal,ref_symbols)
figure()
rx_symbols = eq_signal; %./ rms(eq_signal);
correct_symbols = ref_symbols;
% col = cbrewer2('Paired',numel(unique(correct_symbols))*2);
col = ...
[0.6510 0.8078 0.8902; ...
0.1216 0.4706 0.7059; ...
0.6980 0.8745 0.5412; ...
0.2000 0.6275 0.1725; ...
0.9843 0.6039 0.6000; ...
0.8902 0.1020 0.1098; ...
0.9922 0.7490 0.4353; ...
1.0000 0.4980 0; ...
0.7922 0.6980 0.8392; ...
0.4157 0.2392 0.6039; ...
1.0000 1.0000 0.6000; ...
0.6941 0.3490 0.1569; ...
0.6510 0.8078 0.8902; ...
0.1216 0.4706 0.7059; ...
0.6980 0.8745 0.5412; ...
0.2000 0.6275 0.1725];
ccnt = -1;
levels = unique(correct_symbols);
symbols_for_lvl = NaN(numel(levels),length(correct_symbols));
start = 1;
ende = length(correct_symbols);
for l = 1:numel(levels)
ccnt = ccnt+2;
level_amplitude = levels(l);
symbols_for_lvl(l,correct_symbols==level_amplitude) = rx_symbols(correct_symbols==level_amplitude);
std_lvl(l) = std(symbols_for_lvl(l,:),'omitnan');
xax = 1:length(correct_symbols);
scatter(xax(start:ende),symbols_for_lvl(l,start:ende),10,'.','MarkerFaceAlpha',0.5,'MarkerEdgeAlpha',0.5,'MarkerEdgeColor',col(ccnt,:));
hold on;
end
std_lvl = round(std_lvl,2);
ccnt = 0;
avg_for_lvl = NaN(numel(levels),length(correct_symbols));
% Add the windowed/ smoothed curves
for l = 1:numel(levels)
ccnt = ccnt+2;
level_amplitude = levels(l);
L = 500;
movmean = 1/L .* movsum(rx_symbols(correct_symbols==level_amplitude),[L/2,L/2], 'Endpoints', 'fill');
avg_for_lvl(l,correct_symbols==level_amplitude) = movmean;
nanx = isnan(avg_for_lvl(l,:));
t = 1:numel(avg_for_lvl(l,:));
avg_for_lvl(l,nanx) = interp1(t(~nanx), avg_for_lvl(l,~nanx), t(nanx));
plot(xax(start:ende),avg_for_lvl(l,start:ende),'Color',col(ccnt,:));
hold on
end
% yline(levels);
xlabel('Samples');
ylabel('Amplitude');
ylim([-3 3]);
end
end
end

View File

@@ -0,0 +1,43 @@
%%%%% SETTINGS %%%%%%
useprbs = 1;
M = 8;
randkey = 1;
fsym = 112e9;
viewresults = 0;
%%%%% Mapping %%%%%
M = 6;
data = 0:M-1;
bitpersymbol = log2(M);
s = RandStream('twister','Seed',1);
bitpattern = randi(s,[0 1], 2^18, 1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
bits_tx = Informationsignal(bitpattern);
symbols = PAMmapper(M,0).map(bits);
pam6transitions = combvec(PAMmapper(M,0).levels,PAMmapper(M,0).levels)';
pam6bits = PAMmapper(6,0,"eth_style",0).demap(reshape(pam6transitions',[],1)./sqrt(10));
symbols_rx = PAMmapper(M,0).map(pam6bits).*sqrt(10);
pam6bits = reshape(pam6bits',5,[])';
figure; hold on
scatter(pam6transitions(:,1), pam6transitions(:,2), 'x', 'LineWidth', 1);
n = size(pam6transitions,1);
labels = cellstr(char(pam6bits + '0')); % -> N x 1 cell array of char rows
text(pam6transitions(:,1), pam6transitions(:,2), labels, ...
'HorizontalAlignment','left', 'VerticalAlignment','bottom');
bits_rx = PAMmapper(M,0).demap(symbols);
[~,error_num,ber,error_pos] = calc_ber(bits_tx.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
PAMmapper(8,0).showBitMapping

View File

@@ -0,0 +1,17 @@
db = DBHandler("type","mysql");
db.tableNames
currentTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss');
newRun = db.tables.Runs;
newRun.run_id = NaN;
newRun.loop_id = 82;
newRun.date_of_run = datetime(currentTime, 'InputFormat', 'yyyyMMdd_HHmmss');
newRun.tx_bits_path = 'pathtohell';
newRun.tx_symbols_path = 'pathtohell2';
newRun.rx_sync_path = ""; % Leave empty for now
newRun.rx_raw_path = 'pathtohell4';
newRun.filename = 'filenametohell';
newRun.tx_signal_path = 'pathtohell';
run_id = db.appendToTable('Runs', newRun);

View File

@@ -0,0 +1,68 @@
M = 4;
apply_precode = 1;
bitpattern = [];
s = RandStream('twister','Seed',1);
for i = 1:log2(M)
N = 2^(17-1); %length of prbs
bitpattern(:,i) = randi(s,[0 1], N, 1);
end
if M == 6
bitpattern = reshape(bitpattern',[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
bits = Informationsignal(bitpattern);
symbols = PAMmapper(M,0).map(bits);
bits_rx = PAMmapper(M,0).demap(symbols);
[~,~,ber_direct,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
if apply_precode
symbols_tx = Duobinary().precode(symbols);
else
symbols_tx = symbols;
end
disp(['Tx Sequenz: -- RMS:',sprintf('%.1f',rms(symbols_rx.signal)),' - - Levels -',num2str(numel(unique(symbols_rx.signal)))]);
unique(symbols_tx.signal)
disp('- - - - - - - - - -');
show2Dconstellation(symbols_tx,symbols_tx,"displayname",'VNLE Out','fignum',2241);
if apply_precode
% Entschiedene Symbole codieren: d_DB(n) = d(n) + d(n-1) (im Fall von PAM4 7 level [0 1 2 3 4 5 6])
symbols_db = Duobinary().encode(symbols_tx);
disp(['DB encoded -- RMS:',sprintf('%.1f',rms(symbols_db.signal)),' - - Levels -',num2str(numel(unique(symbols_db.signal)))]);
unique(symbols_db.signal)
disp('- - - - - - - - - -');
% Entschiedene codierte Symbole decodieren: d_dec(n) = d_DB(n) mod4
symbols_rx = Duobinary().decode(symbols_db);
else
symbols_rx = symbols_tx;
end
% Vergleichen von b(n) und d_dec(n)
bits_rx = PAMmapper(M,0).demap(symbols_rx);
disp(['Wieder normal -- RMS:',sprintf('%.1f',rms(symbols_rx.signal)),' - - Levels -',num2str(numel(unique(symbols_rx.signal)))]);
unique(symbols_rx.signal)
disp('- - - - - - - - - -');
[~,~,ber,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",10,"skip_end",10,"returnErrorLocation",1);
disp(['BER: ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]);
figure()
subplot(1,2,1)
histogram(symbols_tx.signal,100,'Normalization','count')
subplot(1,2,2)
histogram(symbols_db.signal,100,'Normalization','count')

View File

@@ -0,0 +1,139 @@
useprbs = 1;
M = 2;
randkey = 1;
datarate = 448e9;
fsym = round(datarate / log2(M)) ;
%%%%% PRBS Generation in correct shape for Modulation Format %%%%%%
O = 16; %O of prbs
N = 2^(O); %length of prbs
[~,seed] = prbs(O,1); %initialize first seed of prbs
bitpattern=[];
state = struct();
para = struct();
if M == 6
para.bl = 2^(O-2);
para.dimension = 5;
else
para.bl = 2^(O-1);
para.dimension = log2(M); %2.5bits/sym -> 2 bit/sym
end
para.rand = 0;
para.order = floor(O / log2(M));
para.skip =0;
para.bruijn = 0;
para.reset_prms = 0;
para.method = 1;
data_in = [];
global loop;
loop = 0;
[data_out,state_] = prms(data_in, state, para);
loop = 1;
[data_out,state_out] = prms(data_in, state_, para);
bitpattern = data_out';
if M == 6
bitpattern = reshape(bitpattern',[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
Tx_bits = Informationsignal(bitpattern);
%%%%% Duobinary %%%%%%
close all
Symbols_tx = PAMmapper(M,0,"eth_style",0).map(Tx_bits);
Symbols_tx.fs = fsym;
precode = db_mode.db_precoded;
%%% precode
switch precode
case db_mode.db_precoded
Symbols_tx = Duobinary().precode(Symbols_tx);
case db_mode.db_encoded
Symbols_tx = Duobinary().precode(Symbols_tx);
Symbols_tx = Duobinary().encode(Symbols_tx);
case db_mode.no_db
end
for n = 10
Symbols_rx = Symbols_tx;
pos = 1;
if n~=0
for pos = 1:n
po = randi(100);
a = Symbols_rx.signal(100+pos) == Symbols_tx.signal(100+po);
while a == 1
po = po+1;
po = randi(100);
a = Symbols_rx.signal(100+pos) == Symbols_tx.signal(100+po);
end
Symbols_rx.signal(100+pos) = Symbols_tx.signal(100+po);
end
end
error_positions = ~(Symbols_rx.signal == Symbols_tx.signal);
error_positions = find(error_positions==1);
switch precode
case db_mode.db_precoded
Symbols_rx = Duobinary().encode(Symbols_rx);
Symbols_rx = Duobinary().decode(Symbols_rx);
case db_mode.db_encoded
Symbols_rx = Duobinary().decode(Symbols_rx);
end
Rx_bits = PAMmapper(M,0).demap(Symbols_rx);
%%%%% Check BER of Bit Sequence %%%%%%
[~,error_num(n+1),ber,error_pos] = calc_ber(Tx_bits.signal,Rx_bits.signal,"skip_front",10,"skip_end",10,"returnErrorLocation",1);
% disp(['BER: ',sprintf('%.1E',ber),sprintf(' - Num. Err: %.1d',error_num(n+1)-2),' - - PAM-',num2str(M)]);
fprintf('n: %d - Num. Err: %.1d \n',n,error_num(n+1));
end
figure(3);
clf
%sgtitle(['BER: ',num2str(ber),' // Error is at position: ',num2str(error_pos),''])
subplot(2,2,1)
hold on
title('First Bits')
stairs(Tx_bits.signal(100:150,1),'LineStyle','-','LineWidth',2,'DisplayName','Tx Bits');
stairs(Rx_bits.signal(100:150,1),'LineWidth',2,'DisplayName','Rx Bits','LineStyle',':')
legend
subplot(2,2,2)
title('Last Bits')
hold on
stairs(Tx_bits.signal(end-50:end,1),'LineStyle','-','LineWidth',2,'DisplayName','Tx Bits');
stairs(Rx_bits.signal(end-50:end,1),'LineWidth',2,'DisplayName','Rx Bits','LineStyle',':')
legend
subplot(2,2,3)
hold on
title('First Symbols Compare')
stairs(Symbols_tx.signal(1:100,1),'LineWidth',2,'DisplayName','Tx Symbols','LineStyle','-')
stairs(Symbols_rx.signal(1:100,1),'LineStyle',':','LineWidth',2,'DisplayName','Rx Symbols');
legend
subplot(2,2,4)
hold on
title('Last Symbols Compare')
stairs(Symbols_tx.signal(end-50:end,1),'LineWidth',2,'DisplayName','Tx Symbols','LineStyle','-')
stairs(Symbols_rx.signal(end-50:end,1),'LineStyle',':','LineWidth',2,'DisplayName','Rx Symbols');
legend

View File

@@ -0,0 +1,32 @@
function output = exampleFunction(varargin)
% Default values for optional variables
var_1 = 1;
var_2 = 2;
var_4 = 10; % Default value for var4
var_5 = 20; % Default value for var5
var_6 = 30; % Default value for var6
var_7 = 40; % Default value for var7
var_8 = 50; % Default value for var8
var_9 = 60; % Default value for var9
var_10 = 70; % Default value for var10
% Parse optional input arguments
if ~isempty(varargin)
var_s = varargin{1};
if isstruct(var_s)
fields = fieldnames(var_s);
for i = 1:numel(fields)
eval([fields{i}, ' = ', num2str( var_s.(fields{i}) ), ';']);
fprintf("%s <-- %.2f \n",fields{i},var_s.(fields{i}))
end
else
error('Optional variables should be passed as a struct.');
end
end
output = var_4+var_10+var_9+var_8+var_1+var_2;
end

View File

@@ -0,0 +1,349 @@
%% GPU vs CPU Comparison Test for DP_Fiber
% This script runs the fiber simulation with and without GPU acceleration
% and compares the numerical results.
% clear; clc;
%% Setup (same as gpu_processing_dpfiber.m but simplified)
s.wavelengthplan = calcWavelengthPlan(4, 400e9, 1310);
link_length = 10;
s.pmd = 0.1;
s.gamma = 0.0023;
s.M = 4;
fsym = 112e9;
fdac = 2*fsym;
fadc = 120000000000;
s.random_key = 1;
% Laser / Modulator
vbias_rel = 0.5;
u_pi = 4.6;
vbias = -vbias_rel*u_pi;
laser_linewidth = 0e6;
% DB Stuff
duob_mode = db_mode.no_db;
rcalpha = 0.05;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
s.chirpalpha = 0;
s.p_launch = 3;
s.p = "co";
N = numel(s.wavelengthplan);
switch s.p
case "co"
pol_rot = 100.*ones(1,N);
d_local = 0;
case "pair"
pol_rot = repmat([100,100,0,0],1,N/4);
d_local = 0;
case "alt"
pol_rot = repmat([100,0,100,0],1,N/4);
d_local = 0;
case "seg"
pol_rot = 100.*ones(1,N);
d_local = 3;
otherwise
error('Unknown fwm_mitigation_technique: %s', string(s.p));
end
f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9);
margin = 5e12;
f_span = (max(f_plan)+margin)-(min(f_plan)-margin);
f_nyq = f_span/2;
kover = 4;
upsample_required = f_nyq./(fdac*kover/2);
upsample_pow = 2^nextpow2(upsample_required);
s.f_opt = fdac*kover*upsample_pow;
s.f_opt_nyq = s.f_opt/2;
%% TX per channel
for l = 1:N
[Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ...
"fsym",fsym,"M",s.M,"order",15,"useprbs",0, ...
"fs_out",fdac, ...
"applyclipping",0,"clipfactor",1.5, ...
"applypulseform",1,"pulseformer",Pform, ...
"randkey",s.random_key+l, ...
"mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode ...
).process();
Lp_awg = Filter('filtdegree',3,"f_cutoff",56e9,"fs",fdac*kover, ...
"filterType",filtertypes.gaussian,"active",true);
El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover, ...
"bit_resolution",6,"upsampling_method","samplehold","precomp_sinc_rolloff",0, ...
"H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig);
clear Digi_sig
El_sig = El_sig.normalize("mode","oneone");
scaling = 0.6*(u_pi/2-abs(vbias-u_pi/2));
El_sig = El_sig .* scaling;
Eml_out = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs, ...
"lambda",s.wavelengthplan(l),"bias",vbias,"u_pi",u_pi, ...
"linewidth",laser_linewidth,"randomkey",s.random_key+l,"alpha",s.chirpalpha).process(El_sig);
clear El_sig
signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",pol_rot(l)).process(Eml_out);
clear Eml_out Lp_awg
end
disp('Signal generated for all channels.');
%% WDM mux + launch
Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover, ...
"lambda_center",1310,"random_key",0,"filtype",1,"B",120e9).process(signal_cell);
Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
"amplification_db",s.p_launch+10*log10(N)).process(Opt_sig_wdm);
% Save input for both runs
Opt_sig_input = Opt_sig_wdm;
segment_length = 1;
nSegments = link_length/segment_length;
if abs(nSegments - round(nSegments)) > 1e-12
error('fiber_length_km=%g must be an integer multiple of segment_length=%g km.', link_length, segment_length);
end
nSegments = round(nSegments);
zdw = 1310;
randomize_D = true;
if nSegments > 0
Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key);
else
Dvec = [];
end
%% Run WITHOUT GPU
fprintf('\n========== Running WITHOUT GPU (CPU) ==========\n');
Opt_sig_cpu = Opt_sig_input;
tic;
for seg = 1:nSegments
fprintf('CPU Segment %d/%d \n',seg, nSegments);
Opt_sig_cpu = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
"beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
"gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
"SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",false).process(Opt_sig_cpu);
end
time_cpu = toc;
fprintf('CPU Time: %.3f seconds\n', time_cpu);
%% Run WITH GPU (Double Precision)
fprintf('\n========== Running WITH GPU (Double Precision) ==========\n');
Opt_sig_gpu_double = Opt_sig_input;
tic;
for seg = 1:nSegments
fprintf('GPU-Double Segment %d/%d \n',seg, nSegments);
Opt_sig_gpu_double = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
"beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
"gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
"SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",true,"useSingle",false).process(Opt_sig_gpu_double);
end
time_gpu_double = toc;
fprintf('GPU Double Time: %.3f seconds\n', time_gpu_double);
%% Run WITH GPU (Single Precision)
fprintf('\n========== Running WITH GPU (Single Precision) ==========\n');
Opt_sig_gpu_single = Opt_sig_input;
tic;
for seg = 1:nSegments
fprintf('GPU-Single Segment %d/%d \n',seg, nSegments);
Opt_sig_gpu_single = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
"beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
"gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
"SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",true,"useSingle",true).process(Opt_sig_gpu_single);
end
time_gpu_single = toc;
fprintf('GPU Single Time: %.3f seconds\n', time_gpu_single);
%% Compare Results
fprintf('\n========== Numerical Comparison ==========\n');
sig_cpu = Opt_sig_cpu.signal;
sig_gpu_double = Opt_sig_gpu_double.signal;
sig_gpu_single = Opt_sig_gpu_single.signal;
% Check dimensions
fprintf('CPU signal size: [%d x %d]\n', size(sig_cpu,1), size(sig_cpu,2));
fprintf('GPU Double signal size: [%d x %d]\n', size(sig_gpu_double,1), size(sig_gpu_double,2));
fprintf('GPU Single signal size: [%d x %d]\n', size(sig_gpu_single,1), size(sig_gpu_single,2));
% CPU vs GPU Double
fprintf('\n--- CPU vs GPU Double ---\n');
diff_cpu_double = abs(sig_cpu - sig_gpu_double);
max_diff_cpu_double = max(diff_cpu_double(:));
mean_diff_cpu_double = mean(diff_cpu_double(:));
rel_diff_cpu_double = max_diff_cpu_double / max(abs(sig_cpu(:)));
fprintf('Max absolute difference: %.6e\n', max_diff_cpu_double);
fprintf('Mean absolute difference: %.6e\n', mean_diff_cpu_double);
fprintf('Max relative difference: %.6e\n', rel_diff_cpu_double);
% CPU vs GPU Single
fprintf('\n--- CPU vs GPU Single ---\n');
diff_cpu_single = abs(sig_cpu - sig_gpu_single);
max_diff_cpu_single = max(diff_cpu_single(:));
mean_diff_cpu_single = mean(diff_cpu_single(:));
rel_diff_cpu_single = max_diff_cpu_single / max(abs(sig_cpu(:)));
fprintf('Max absolute difference: %.6e\n', max_diff_cpu_single);
fprintf('Mean absolute difference: %.6e\n', mean_diff_cpu_single);
fprintf('Max relative difference: %.6e\n', rel_diff_cpu_single);
% GPU Double vs GPU Single
fprintf('\n--- GPU Double vs GPU Single ---\n');
diff_double_single = abs(sig_gpu_double - sig_gpu_single);
max_diff_double_single = max(diff_double_single(:));
mean_diff_double_single = mean(diff_double_single(:));
rel_diff_double_single = max_diff_double_single / max(abs(sig_gpu_double(:)));
fprintf('Max absolute difference: %.6e\n', max_diff_double_single);
fprintf('Mean absolute difference: %.6e\n', mean_diff_double_single);
fprintf('Max relative difference: %.6e\n', rel_diff_double_single);
% Check tolerances
fprintf('\n--- Tolerance Check ---\n');
tol_double = 1e-10;
tol_single = 1e-5; % Single precision has ~7 significant digits
if max_diff_cpu_double < tol_double
fprintf(' CPU vs GPU Double: EQUIVALENT (diff < %.0e)\n', tol_double);
else
fprintf(' CPU vs GPU Double: DIFFER beyond tolerance (%.0e)\n', tol_double);
end
if max_diff_cpu_single < tol_single
fprintf(' CPU vs GPU Single: ACCEPTABLE (diff < %.0e)\n', tol_single);
else
fprintf(' CPU vs GPU Single: Precision loss detected (diff = %.2e, tol = %.0e)\n', max_diff_cpu_single, tol_single);
end
% Performance comparison
fprintf('\n========== Performance Summary ==========\n');
fprintf('CPU Time: %.3f s\n', time_cpu);
fprintf('GPU Double Time: %.3f s\n', time_gpu_double);
fprintf('GPU Single Time: %.3f s\n', time_gpu_single);
fprintf('\n');
fprintf('Speedup (GPU Double vs CPU): %.2fx\n', time_cpu/time_gpu_double);
fprintf('Speedup (GPU Single vs CPU): %.2fx\n', time_cpu/time_gpu_single);
fprintf('Speedup (GPU Single vs GPU Double): %.2fx\n', time_gpu_double/time_gpu_single);
%% ========== BER Comparison ==========
% Process each fiber output through simplified receiver to check if
% single-precision affects actual BER performance
fprintf('\n========== BER Comparison ==========\n');
fprintf('Processing signals through receiver chain...\n');
% Receiver parameters
rop = -7; % Received optical power [dBm]
len_tr = 4096; % Training length
mu_dc = 0.005;
mu_ffe = [0.0001 0.0008 0.001];
mu_dfe = 0.0004;
% Helper function to process through receiver and get BER
function ber = process_receiver(Opt_sig_fib, l, Symbols, Tx_bits, ...
fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode)
% Demux single channel
Opt_sig_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1, ...
"fs_out",fdac*kover,"fs_in",fdac*kover*upsample_pow,"lambda_center",1310).process(Opt_sig_fib);
% ROP amplifier
Opt_sig_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
"amplification_db",rop).process(Opt_sig_demux{l});
% Photodiode
PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20, ...
"nep",1.8e-11,"randomkey",s.random_key+l).process(Opt_sig_rx);
% Low-pass filter
rx_bwl = 100e9;
PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover, ...
"filterType",filtertypes.butterworth,"active",true).process(PD_sig);
% Scope
Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc, ...
"delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth, ...
"samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0, ...
"adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',0,'H_lpf',Lp_scpe).process(PD_sig);
% Resample to 2 sps
Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym);
% Time sync
[~, Scpe_cell, ~, ~] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 0);
Rx_sig = Scpe_cell{1};
Rx_sig = Rx_sig.normalize("mode","rms");
% FFE Equalizer
ffe_order = [50, 0, 0];
eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
ffe_results = ffe(eq_ffe,s.M,Rx_sig,Symbols{l},Tx_bits{l}, ...
"precode_mode",duob_mode, ...
'showAnalysis',0, ...
"postFFE",[], ...
"eth_style_symbol_mapping",0);
ber = ffe_results.metrics.BER;
end
% Process each mode for channel 1
l = 4; % Use first channel for comparison
fprintf('Processing CPU result...\n');
ber_cpu = process_receiver(Opt_sig_cpu, l, Symbols, Tx_bits, ...
fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode);
fprintf('Processing GPU Double result...\n');
ber_gpu_double = process_receiver(Opt_sig_gpu_double, l, Symbols, Tx_bits, ...
fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode);
fprintf('Processing GPU Single result...\n');
ber_gpu_single = process_receiver(Opt_sig_gpu_single, l, Symbols, Tx_bits, ...
fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode);
% Display BER results
fprintf('\n========== BER Results (Channel %d, ROP = %d dBm) ==========\n', l, rop);
fprintf('CPU: BER = %.4e\n', ber_cpu);
fprintf('GPU Double: BER = %.4e\n', ber_gpu_double);
fprintf('GPU Single: BER = %.4e\n', ber_gpu_single);
fprintf('\n--- BER Comparison ---\n');
if ber_cpu == 0 && ber_gpu_double == 0 && ber_gpu_single == 0
fprintf(' All BERs are zero (no errors detected)\n');
else
ber_diff_double = abs(ber_cpu - ber_gpu_double);
ber_diff_single = abs(ber_cpu - ber_gpu_single);
fprintf('|BER_cpu - BER_gpu_double| = %.4e\n', ber_diff_double);
fprintf('|BER_cpu - BER_gpu_single| = %.4e\n', ber_diff_single);
if ber_diff_double < 1e-6 && ber_diff_single < 1e-6
fprintf(' BER differences are negligible\n');
elseif ber_diff_single > ber_diff_double * 10
fprintf(' Single precision shows measurable BER impact\n');
else
fprintf(' BER differences within acceptable range\n');
end
end
fprintf('\n========== Test Complete ==========\n');

View File

@@ -0,0 +1,265 @@
s.wavelengthplan = calcWavelengthPlan(16, 400e9, 1310);
N = numel(s.wavelengthplan);
link_length = 10;
s.pmd = 0.1;%0.1;
s.gamma = 0.0023;
s.M = 4;
fsym = 112e9;
fdac = 2*fsym;
fadc = 120000000000;
s.random_key = 1;
% Laser / s.Modulator
vbias_rel = 0.5;
u_pi = 4.6;
vbias = -vbias_rel*u_pi;
laser_linewidth = 0e6;
% DB Stuff
duob_mode = db_mode.no_db;
rcalpha = 0.05;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
s.chirpalpha = 0;
s.p_launch = 3;
s.p = "co";
switch s.p
case "co"
pol_rot = 100.*ones(1,N);
d_local = 0;
case "pair"
pol_rot = repmat([100,100,0,0],1,N/4);
d_local = 0;
case "alt"
pol_rot = repmat([100,0,100,0],1,N/4);
d_local = 0;
case "seg"
pol_rot = 100.*ones(1,N);
d_local = 3;
otherwise
error('Unknown fwm_mitigation_technique: %s', string(s.p));
end
f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9);
margin = 25e12; % some THz left and right
f_span = (max(f_plan)+margin)-(min(f_plan)-margin);
f_nyq = f_span/2;
kover = 4;
upsample_required = f_nyq./(fdac*kover/2);
upsample_pow = 2^nextpow2(upsample_required);
s.f_opt = fdac*kover*upsample_pow;
s.f_opt_nyq = s.f_opt/2;
s.rop = -10:1:0;
profile on
%% ---------- TX per channel ----------
for l = 1:N
[Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ...
"fsym",fsym,"M",s.M,"order",17,"useprbs",0, ...
"fs_out",fdac, ...
"applyclipping",0,"clipfactor",1.5, ...
"applypulseform",1,"pulseformer",Pform, ...
"randkey",s.random_key+l, ...
"mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode ...
).process();
Lp_awg = Filter('filtdegree',3,"f_cutoff",56e9,"fs",fdac*kover, ...
"filterType",filtertypes.gaussian,"active",true);
El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover, ...
"bit_resolution",6,"upsampling_method","samplehold","precomp_sinc_rolloff",0, ...
"H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig);
% Digi_sig not needed after AWG
clear Digi_sig
% Electrical Driver Amplifier
El_sig = El_sig.normalize("mode","oneone");
scaling = 0.6*(u_pi/2-abs(vbias-u_pi/2));
El_sig = El_sig .* scaling;
% E/O Conversion
Eml_out = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs, ...
"lambda",s.wavelengthplan(l),"bias",vbias,"u_pi",u_pi, ...
"linewidth",laser_linewidth,"randomkey",s.random_key+l,"alpha",s.chirpalpha).process(El_sig);
% El_sig not needed after EML
clear El_sig
signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",pol_rot(l)).process(Eml_out);
% Eml_out not needed after pol controller
clear Eml_out Lp_awg
end
disp('Signal generated for all channels.');
%% ---------- WDM mux + launch ----------
Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover, ...
"lambda_center",1310,"random_key",0,"filtype",1,"B",120e9).process(signal_cell);
Opt_sig_wdm.spectrum();
Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
"amplification_db",s.p_launch+10*log10(N)).process(Opt_sig_wdm);
Opt_sig_wdm_fib = Opt_sig_wdm;
segment_length = 1;
nSegments = link_length/segment_length;
if abs(nSegments - round(nSegments)) > 1e-12
error('fiber_length_km=%g must be an integer multiple of segment_length=%g km.', link_length, segment_length);
end
nSegments = round(nSegments);
zdw = 1310;
randomize_D = true;
% Guard for 0 km: avoid calling getDispersionVector(0,...) if it doesn't support it
if nSegments > 0
Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key);
else
Dvec = [];
end
for seg = 1:nSegments
fprintf('Segment %d/%d \n',seg, nSegments);
Opt_sig_wdm_fib = DP_Fiber("L",segment_length,"D",Dvec(seg),"Dpmd",s.pmd,"Ds",0.07, ...
"beat_len",10,"corr_len",100,"dz",1,"manakov",0, ...
"gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01, ...
"SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1,"useGPU",true,"useSingle",1).process(Opt_sig_wdm_fib);
end
profile off
profile viewer
Opt_sig_wdm_fib.spectrum();
%% ========== BER Evaluation ==========
fprintf('\n========== BER Evaluation ==========\n');
fprintf('Processing signals through receiver chain...\n');
% Receiver parameters
len_tr = 4096; % Training length
mu_dc = 0.005;
mu_ffe = [0.0001 0.0008 0.001];
mu_dfe = 0.0004;
% Helper function to process through receiver and get BER
function ber = process_receiver(Opt_sig_fib, l, Symbols, Tx_bits, ...
fdac, kover, upsample_pow, fsym, fadc, rop, s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode)
% Demux single channel
Opt_sig_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1, ...
"fs_out",fdac*kover,"fs_in",fdac*kover*upsample_pow,"lambda_center",1310).process(Opt_sig_fib);
% ROP amplifier
Opt_sig_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ...
"amplification_db",rop).process(Opt_sig_demux{l});
% Photodiode
PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20, ...
"nep",1.8e-11,"randomkey",s.random_key+l).process(Opt_sig_rx);
% Low-pass filter
rx_bwl = 100e9;
PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover, ...
"filterType",filtertypes.butterworth,"active",true).process(PD_sig);
% Scope
Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc, ...
"delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth, ...
"samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0, ...
"adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',0,'H_lpf',Lp_scpe).process(PD_sig);
% Resample to 2 sps
Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym);
% Time sync
[~, Scpe_cell, ~, ~] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 0);
Rx_sig = Scpe_cell{1};
Rx_sig = Rx_sig.normalize("mode","rms");
% FFE Equalizer
ffe_order = [50, 0, 0];
eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
eq_ffe = FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",6.624e-05,"mu_tr",0.058136,"order",50,"sps",2,"decide",0, "adaption",adaption_method.nlms,"dd_mode",1);
ffe_results = ffe(eq_ffe,s.M,Rx_sig,Symbols{l},Tx_bits{l}, ...
"precode_mode",duob_mode, ...
'showAnalysis',0, ...
"postFFE",[], ...
"eth_style_symbol_mapping",0);
ber = ffe_results.metrics.BER;
end
% Process each ROP value for selected channels using parfor
ber_results = zeros(length(s.rop), N);
% Flatten loop for parfor: iterate over all (ROP, channel) combinations
num_rop = length(s.rop);
rop_vals = s.rop;
ber_flat = zeros(num_rop * N, 1);
parfor idx = 1:(num_rop * N)
% Convert linear index to (ri, l) subscripts
ri = ceil(idx / N);
l = mod(idx - 1, N) + 1;
fprintf('ROP %d dBm, Channel %d/%d\n', rop_vals(ri), l, N);
ber_flat(idx) = process_receiver(Opt_sig_wdm_fib, l, Symbols, Tx_bits, ...
fdac, kover, upsample_pow, fsym, fadc, rop_vals(ri), s, len_tr, mu_dc, mu_ffe, mu_dfe, duob_mode);
end
% Reshape back to [num_rop × N] matrix
ber_results = reshape(ber_flat, [N, num_rop]).';
%% Display BER Results
fprintf('\n========== BER Results ==========\n');
fprintf('ROP [dBm] | ');
for l = 1:N
fprintf('Ch%d | ', l);
end
fprintf('\n');
for ri = 1:length(s.rop)
fprintf('%8d | ', s.rop(ri));
for l = 1:N
fprintf('%.2e | ', ber_results(ri, l));
end
fprintf('\n');
end
% Plot BER vs ROP
figure;
semilogy(s.rop, mean(ber_results, 2), '-o', 'LineWidth', 2);
hold on;
for l = 1:N
semilogy(s.rop, ber_results(:, l), '--', 'LineWidth', 1);
end
hold off;
xlabel('ROP [dBm]');
ylabel('BER');
title('BER vs Received Optical Power (GPU Single Precision)');
legend(['Mean', arrayfun(@(x) sprintf('Ch%d', x), 1:N, 'UniformOutput', false)]);
grid on;
fprintf('\n========== Test Complete ==========\n');

View File

@@ -0,0 +1,70 @@
% 1. Setup Data OUTSIDE the timer
d = gpuDevice;
N = 10000;
fprintf('Preparing data...\n');
A_cpu_double = rand(N, N); % Create double on CPU
A_cpu_single = single(A_cpu_double); % Create single on CPU
% Warmup run (wakes up the GPU from idle state)
A_warm = gpuArray.rand(1000, 1000, 'single');
B_warm = A_warm * A_warm;
wait(d);
fprintf('------------------------------------------------\n');
% TEST 1: Double Precision (The "Slow" way)
% We move data to GPU first so we only measure calculation time
A_gpu_double = gpuArray(A_cpu_double);
wait(d); % Ensure transfer is done before starting timer
fprintf('Running DOUBLE precision test... ');
tic;
B_gpu = A_gpu_double * A_gpu_double;
wait(d); % FORCE MATLAB TO WAIT FOR GPU
time_double = toc;
fprintf('Done.\n');
fprintf('Double Precision Time: %.4f seconds\n', time_double);
% TEST 2: Single Precision (The "Fast" way)
A_gpu_single = gpuArray(A_cpu_single);
wait(d); % Ensure transfer is done
fprintf('Running SINGLE precision test... ');
tic;
B_gpu = A_gpu_single * A_gpu_single;
wait(d); % FORCE MATLAB TO WAIT FOR GPU
time_single = toc;
fprintf('Done.\n');
fprintf('Single Precision Time: %.4f seconds\n', time_single);
% Calculate Speedup
fprintf('------------------------------------------------\n');
fprintf('Speedup Factor using single precision: %.2fx\n', time_double / time_single);
%
% Create 10,000 small matrices (10x10) stacked in a 3D array
A_stack = gpuArray.rand(10, 10, 10000, 'single');
B_stack = gpuArray.rand(10, 10, 10000, 'single');
% BAD: Looping (GPU overhead kills you)
tic;
for i=1:10000
C(:,:,i) = A_stack(:,:,i) * B_stack(:,:,i);
end
wait(d);
loop_time = toc;
% GOOD: Pagefun (Executes all 10,000 mults simultaneously)
tic;
C_stack = pagefun(@mtimes, A_stack, B_stack);
wait(d);
pagefun_time = toc;
fprintf('------------------------------------------------\n');
fprintf('Speedup Factor using pagefun: %.2fx\n', loop_time / pagefun_time);
fprintf('Total VRAM: %.2f GB\n', d.TotalMemory / 1e9);
fprintf('Available VRAM: %.2f GB\n', d.AvailableMemory / 1e9);
fprintf('Usage: %.1f%%\n', 100 * (1 - d.AvailableMemory / d.TotalMemory));

View File

@@ -0,0 +1,47 @@
x = -10:2:25; % Input power [dBm]
y1 = 1e-5 * 10.^(0.12*x); % Dispersion-only
y2 = 1e0 ./ (1 + exp(-0.4*(x-12))); % NLPN
y3 = 1e-6 * 10.^(0.45*x); % RP on gamma
y4 = 1e-2 * 10.^(0.18*(x-8)); % RP on beta2
cmap = WesPalette.AsteroidCity1.rgb(4);
cmap = linspecer(4);
figure1=figure(202998);clf;hold on
lw = 0.8; ms = 3;
plot(x,y1,'LineWidth',lw,'Color',cmap(1,:),'Marker','o','MarkerEdgeColor',cmap(1,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms);
plot(x,y2,'LineWidth',lw,'Color',cmap(2,:),'Marker','square','MarkerEdgeColor',cmap(2,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms);
plot(x,y3,'LineWidth',lw,'Color',cmap(3,:),'Marker','o','MarkerEdgeColor',cmap(3,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms);
plot(x,y4,'LineWidth',lw,'Color',cmap(4,:),'Marker','o','MarkerEdgeColor',cmap(4,:),'MarkerFaceColor',[1,1,1],'MarkerSize',ms);
yline(3.8e-3,'HandleVisibility','off')
grid on
xlabel('Input power [dBm]')
ylabel('NSD ($\%$)')
legend({'Dispersion','NLPN','RP','RP on $\beta_2$'}, ...
'Location','best')
grid off
set(gca,'MinorGridLineWidth',0.5);
set(gca,'GridLineWidth',0.5,'GridLineStyle','--','GridColor',[0.9,0.9,0.9]);
set(gca,'FontSize',12,'YScale','log');
ylim([1e-6 1e3])
xlim([-10 23])
fig_path = 'C:\Users\Silas\Documents\Dissertation\00_Examples\tikz\textfig.tikz';
matlab2tikz(fig_path, ...
'width','\fwidth', ...
'height','\fheight', ...
'showInfo',false, ...
'extraAxisOptions',{ ...
'legend style={font=\footnotesize}', ...
'xlabel style={font=\color{white!15!black},font=\small},',...
'ylabel style={font=\color{white!15!black},font=\small},',...
'legend columns=1', ...
'every axis/.append style={font=\scriptsize}',...
'legend columns=2',...
'legend style={at={(0.02,0.98)},font=\footnotesize,draw=black!60,rounded corners=2pt,inner sep=1pt,fill=white,column sep=6pt,anchor= north west}',...
});

View File

@@ -0,0 +1,118 @@
%%%%% SETTINGS %%%%%%
useprbs = 1;
M = 8;
randkey = 1;
fsym = 112e9;
viewresults = 0;
%%%%% Mapping %%%%%
M = 8;
data = 0:M-1;
bitpersymbol = log2(M);
%Bits
bits = int2bit(data,bitpersymbol, true);
%Integers
ints = bit2int(bits,bitpersymbol, true);
Tx_bits = Informationsignal(bits');
Digi_Mod = PAMmapper(M,0);
Symbols = Digi_Mod.map(Tx_bits);
moveitgray = Symbols.signal;
%Gray Symbol Mapping
matlabgray = pammod(ints,M,0,'gray');
scaling_factor = rms(unique(matlabgray));
matlabgray = matlabgray ./ scaling_factor;
%Demod
moveitgray = moveitgray.* scaling_factor;
matlabgray = matlabgray .* scaling_factor;
ints_demap = pamdemod(matlabgray,M,0,'gray');
bits_demap = int2bit(ints_demap,bitpersymbol, true);
% for i = 1:M
% fprintf('%d , %d , %d --> %d \n',bits(1,i),bits(2,i),bits(3,i),matlabgray(i));
% end
%
% for i = 1:M
% fprintf('%d , %d , %d --> %d \n',bits(1,i),bits(2,i),bits(3,i),moveitgray(i));
% end
scatterplot(matlabgray,1,0,'b*');
for k = 1:M
text(real(matlabgray(k)),imag(matlabgray(k))+0.6,num2str(ints_demap(k)),"Color",[1 1 1]);
text(real(matlabgray(k)),imag(matlabgray(k))-1.6,num2str(bits(:,k)),"Color",'blue');
text(real(moveitgray(k)),imag(moveitgray(k))-3,num2str(bits(:,k)),"Color",'green');
end
axis([-M M -3 2])
symbols = bit2int(bitGroups',bitpersymbol, true);
%%%%% PRBS Generation in correct shape for Modulation Format %%%%%%
O = 10; %order of prbs
N = 2^(O-1); %length of prbs
[~,seed] = prbs(O,1); %initialize first seed of prbs
bitpattern=[];
if useprbs
for i = 1:log2(M)
[bitpattern(:,i),seed] = prbs(O,N,seed);
end
else
s = RandStream('twister','Seed',randkey);
for i = 1:log2(M)
bitpattern(:,i) = randi(s,[0 1], N, 1);
end
end
if M == 6
bitpattern = reshape(bitpattern,[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
Tx_bits = Informationsignal(bitpattern);
%%%%% ACTUAL TEST: Back to Back Mapping: Bits -> Symbols and Symbols -> Bits %%%%%%
Digi_Mod = PAMmapper(M,0);
symbols = bitMapper(bitpattern, M, 'PAM');
Symbols = Digi_Mod.map(Tx_bits);
Rx_bits = PAMmapper(M,0).demap(Symbols);
%%%%% VALIDATION: BER is required to be zero %%%%%%
[~,error_num,ber,error_pos] = calc_ber(Tx_bits.signal,Rx_bits.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
%%%%% For User: Show Debug Info and Results %%%%%%
if viewresults
disp(['BER: ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]);
figure
subplot(1,2,1)
hold on
title('Symbols Out')
stairs(Symbols_tx.signal(1:100,1),'LineWidth',2,'DisplayName','Tx Symbols')
legend
grid
subplot(1,2,2)
u = unique(Symbols_tx.signal);
scatter(0,u,'filled','o','LineWidth',2,'MarkerFaceColor',linspecer(1));
grid
end

View File

@@ -0,0 +1,72 @@
%%% Run parameters
% TX
M = 4;
fsym = 32e9;
apply_pulsef = 1;
fdac = 256e9;
fadc = 256e9;
random_key = 1;
precomp = 0;
db_precode = 0;
db_encode = 0;
kover = 16;
vbias_rel = 0.5;
u_pi = 2.9;
vbias = -vbias_rel*u_pi;
laser_wavelength = 1293;
laser_linewidth = 0;
tx_bw_nyquist = 0.8;
% 1) PRBS Generation
O = 18; %order of prbs
N = 2^(O-1); %length of prbs
%%%%% MOVE-IT PRMS %%%%
Mi_prms = Moveit_wrapper("prms");
if M == 6
Mi_prms.para.bl = 2^(O-2);
Mi_prms.para.dimension = 5;
else
Mi_prms.para.bl = 2^(O-1);
Mi_prms.para.dimension = log2(M); %2.5bits/sym -> 2 bit/sym
end
Mi_prms.para.rand = 0;
Mi_prms.para.order = floor(O / log2(M));
Mi_prms.para.skip =0;
Mi_prms.para.bruijn = 0;
Mi_prms.para.reset_prms = 0;
Mi_prms.para.method = 1;
bitpattern = Mi_prms.process([]);
if M == 6
bitpattern = reshape(bitpattern',[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
bits = Informationsignal(bitpattern.');
symbols = PAMmapper(M,0).map(bits);
symbols.fs = fsym;
symbols.spectrum("displayname",'Symbols','fignum',1);
%% RRC Shaping
for rcalpha = 0.1:0.2:1
% rcalpha = 0.5;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha);
Digi_sig = Pform.process(symbols);
% Digi_sig.spectrum("displayname",'Signal after pluse shaping','fignum',1);
Digi_sig.eye(fsym,M,"fignum",0.1*10,"mode",1);
end
%% RRC Matched Filtering
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha);
Rx_sig = Pform.process(Digi_sig);
Rx_sig.spectrum("displayname",'Signal after matched filter','fignum',1);

View File

@@ -0,0 +1,171 @@
M_format = [2,4,6,8];
for m = 1:length(M_format)
% --- Parameters ---
M = M_format(m); % PAM order (e.g., 2,4,8)
Nsym = 1e5; % number of symbols
h = [1, 0.5]; % Impulse response to remove
b = log2(M);
if M == 6 b = 5; end
rng(1);
bits_tx = logical(randi([0 1], Nsym, b, 'uint8'));
tx_symbols = pammap(bits_tx,M);
if M == 6
states = unique(tx_symbols);
pam6transitions = combvec(states',states')'; % pam6transitions =
bitmapping = pamdemap(reshape(pam6transitions',1,[])',M);
else
bitmapping = pamdemap(unique(tx_symbols),M);
end
scaling = sqrt(sum(unique(tx_symbols).^2)/numel(unique(tx_symbols)));
tx_symbols = tx_symbols ./ scaling;
% apply impulse response to signal
y_filt = filter(h, 1, tx_symbols);
sir = 10:25;
for s = 1:length(sir)
% apply noise
y = awgn(y_filt,sir(s),"measured",1);
% apply bcjr
BCJR = bcjr_pam("DIR",h,"duobinary_output",0,"M",M,"trellis_states",unique(tx_symbols));
[viterbi_estimate,LLR,GMI(m,s)] = BCJR.process(y,tx_symbols,bits_tx,bitmapping);
% decode LLR's
bits_LLR = LLR > 0;
% demap viterbi symbols sequence
rx_symbols = viterbi_estimate .* scaling;
bits_rx = pamdemap(rx_symbols,M);
% BER calc
BER_vit(m,s) = nnz(bits_tx ~= bits_LLR) / numel(bits_tx);
fprintf('BER LLR = %.2e \n', BER_vit);
BER_llr(m,s) = nnz(bits_tx ~= bits_rx) / numel(bits_tx);
fprintf('BER = %.2e \n', BER_llr);
end
end
figure();hold on
for m = 1:length(M_format)
plot(sir,BER_llr(m,:),'DisplayName',sprintf('PAM %d',M_format(m)))
% plot(sir,BER_vit(m,:),'DisplayName',sprintf('PAM %d',M_format(m)),'LineStyle',':','LineWidth',0.1,'HandleVisibility','off');
end
ylabel('BER');
xlabel('SNR')
title('BER vs. SNR');
set(gca, 'XScale', 'linear', ...
'YScale', 'log', ...
'TickLabelInterpreter', 'latex', ...
'FontSize', 11);
figure();hold on
for m = 1:length(M_format)
plot(sir,GMI(m,:),'DisplayName',sprintf('GMI PAM %d',M_format(m)))
end
ylabel('GMI');
xlabel('SNR')
title('GMI vs. SNR');
set(gca, 'XScale', 'linear', ...
'YScale', 'linear', ...
'TickLabelInterpreter', 'latex', ...
'FontSize', 11);
function symbols = pammap(bits,M)
bits = logical(bits);
if M == 2
symbols = bits;
elseif M == 4
symbols= 2*bits(:,1) + (bits(:,1)==bits(:,2));
symbols=2*symbols-3;
elseif M == 6
m = 1;
if size(bits,2)>size(bits,1)
bits = bits'; %vector aufrecht stellen
end
bits = reshape(bits',1,[])';
thres = [-3 5;-1 5;-3 -5;-1 -5;-5 3;-5 1;-5 -3;-5 -1;-1 3;-1 1;-1 -3;-1 -1;-3 3;-3 1;-3 -3;-3 -1;3 5;1 5;3 -5;1 -5;5 3;5 1;5 -3;5 -1;1 3;1 1;1 -3;1 -1;3 3;3 1;3 -3;3 -1];
% LUT based mapping
for k = 1:5:fix(length(bits)/5)*5
symbols(m:m+1,1) = thres(bin2dec(int2str(bits(k:k+4)'))+1,:);
m = m+2;
end
elseif M == 8
x1 = bits(:,1);
x2 = (bits(:,1)==bits(:,3));
x3 = x2~=bits(:,2);
symbols = 4*x1 + 2*x2 + x3;
symbols=2*symbols-7;
end
end
function bits = pamdemap(symbols,M)
if M == 2
thres=0;
elseif M == 4
thres=[-2,0,2];
elseif M == 6
thres = [-3 5;-1 5;-3 -5;-1 -5;-5 3;-5 1;-5 -3;-5 -1;-1 3;-1 1;-1 -3;-1 -1;-3 3;-3 1;-3 -3;-3 -1;3 5;1 5;3 -5;1 -5;5 3;5 1;5 -3;5 -1;1 3;1 1;1 -3;1 -1;3 3;3 1;3 -3;3 -1];
elseif M == 8
thres=-6:2:6;
end
if M ~= 6
symbols = symbols';
a = squeeze(repmat(real(symbols),[1 1 length(thres)])); %Eingangssignal in 3 spalten
b = squeeze(repmat(reshape(thres(:).',[1 1 length(thres)]),[1 length(symbols) 1])); %Threshold in 3 Spalten
comp_real = a > b; %check for each symbol/ sampling if it exeeds the obj.thresholdseshold 1, 2 or 3
comp_real=repmat(real(symbols),[1 1 length(thres)]) > repmat(reshape(thres(:).',[1 1 length(thres)]),[1 length(symbols) 1]);
s1=size(comp_real,1);
s2=size(comp_real,2);
end
if M == 2
data_out=abs(comp_real(:,:,1));
elseif M == 4
data_out=[comp_real(:,:,2); ones(s1,s2) - comp_real(:,:,1) + comp_real(:,:,3)];
elseif M == 6
if size(symbols,2) > 1
symbols = symbols.';
end
if length(symbols)/2 ~= round(length(symbols)/2)
symbols = [symbols;0];
end
m = 1;
for n = 1:2:length(symbols)
dist = sqrt((symbols(n)-thres(:,1)).^2+(symbols(n+1)-thres(:,2)).^2);
[~,dd_idx] = min(dist);
% dec_out(n:n+1) = LUT(dd_idx,:);
data_out(m:m+4) = bitget(dd_idx-1,5:-1:1);
m = m+5;
end
data_out = reshape(data_out',5,[]);
elseif M == 8
data_out=[comp_real(:,:,4);
comp_real(:,:,1)-comp_real(:,:,3)+comp_real(:,:,5)-comp_real(:,:,7);
1-comp_real(:,:,2)+comp_real(:,:,6)];
end
bits = data_out';
end

View File

@@ -0,0 +1,80 @@
useprbs = 0;
M = 4;
randkey = 2;
fsym = 112e9;
%%%%% PRBS Generation in correct shape for Modulation Format %%%%%%
O = 18; %order of prbs
N = 2^(O-1); %length of prbs
[~,seed] = prbs(O,1); %initialize first seed of prbs
bitpattern=[];
if useprbs
for i = 1:log2(M)
[bitpattern(:,i),seed] = prbs(O,N,seed);
end
else
s = RandStream('twister','Seed',randkey);
for i = 1:log2(M)
bitpattern(:,i) = randi(s,[0 1], N, 1);
end
end
if M == 6
bitpattern = reshape(bitpattern,[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
Tx_bits = Informationsignal(bitpattern);
Digi_Mod = PAMmapper(M,0);
Symbols_tx = Digi_Mod.map(Tx_bits);
Symbols_tx.fs = fsym;
if 0
Symbols = Duobinary().precode(Symbols_tx);
Symbols = Duobinary().encode(Symbols);
Symbols = MLSE("DIR",[1 1],"duobinary_output",1,"trellis_states",Digi_Mod.levels,"M",M).process(Symbols);
Symbols = Duobinary().decode(Symbols);
else
cnt = 1;
Symbols = Symbols_tx;
coeff = [1,0.5,0.2,0.1];
Symbols.signal = filter(coeff, 1, Symbols.signal);
Symbols.spectrum("fignum",129,"displayname",['coeff:',num2str(coeff)]);
Symbols = MLSE("DIR",coeff,"duobinary_output",0,"trellis_states",Digi_Mod.levels,"M",M).process(Symbols);
Rx_bits = PAMmapper(M,0).demap(Symbols);
[~,error_num,ber,error_pos] = calc_ber(Tx_bits.signal,Rx_bits.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
disp(['BER: ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]);
end
%
figure(494)
clf
subplot(2,1,1)
title('Bits Compare')
hold on
stairs(Rx_bits.signal(1:100,1),'LineWidth',2,'DisplayName','Rx Bits')
stairs(Tx_bits.signal(1:100,1),'LineStyle',':','LineWidth',2,'DisplayName','Tx Bits');
legend
subplot(2,1,2)
hold on
title('Symbols Compare')
stairs(Symbols.signal(1:100,1),'LineStyle','-','LineWidth',2,'DisplayName','Rx Symbols');
stairs(Symbols_tx.signal(1:100,1),'LineWidth',2,'DisplayName','Tx Symbols','LineStyle',':')
legend

View File

@@ -0,0 +1,116 @@
% datarate = 128e9;
M = 4;
laser_linewidth = 0;
kover = 32;
fsym = 170e9;%round(datarate*1e-9 / log2(M))*1e9;
fdac = 256e9;
% 1) PRBS Generation
O = 18; %order of prbs
N = 2^(O-1); %length of prbs
[~,seed] = prbs(O,1); %initialize first seed of prbs
bitpattern=[];
for i = 1:log2(M)
[bitpattern(:,i),seed] = prbs(O,N,seed);
end
if M == 6
bitpattern = reshape(bitpattern,[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
bits = Informationsignal(bitpattern);
% 2) Digi modulation -> PAM-M signal
digimod_out = PAMmapper(M,0).map(bits);
digimod_out.fs = fsym;
% 3) Pulseform Raised Cosine
X = Pulseformer("fsym",fsym,"fdac",fdac,"pulse","rrc","pulselength",16,"rrcalpha",0.01).process(digimod_out);
% Implememt Precompensation
% Implement Precoding
% 4) AWG (lowpass, quantization, sample and hold)
LP_awg = Filter('filtdegree',4,"f_cutoff",75e9,"fs",fdac*kover,"filterType",filtertypes.gaussian);
AWG_=AWG("fdac",fdac,"dac_min",-1,"dac_max",1,"H_lpf",LP_awg,"kover",kover,"bit_resolution",16,"lpf_active",1,"normalize2dac",1,"upsampling_method","samplehold");
X = AWG_.process(X);
disp(['El. power: ',num2str(X.power),' dBm (into 50 Ohm)']);
disp(['El. RMS voltage: ',num2str(sqrt(mean(X.signal.^2))),' V']);
disp(['max voltage: ',num2str(max(X.signal)),' V']);
% 5) Lowpass behavior before laser
LP_modulator= Filter('filtdegree',4,"f_cutoff",70e9,"fs",fdac*kover,"filterType",filtertypes.butterworth);
X = LP_modulator.process(X);
% 6) Laser; Modulation -> OPTICAL DOMAIN
u_pi = 4;
vbias = 2;
extmodlaser = EML("mode",eml_mode.im_cosinus,"power",0,"fsimu",X.fs,"lambda",1290,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",5);
[Opt,extmodlaser] = extmodlaser.process(X);
if 1
f = figure(120);
f.Name = 'bla';
tiledlayout(2,4);
nexttile
rms_ = rms(X.signal);
max_ = max(X.signal);
min_ = min(X.signal);
hold on
plot(X.signal,'LineWidth',0.1);
yline([max_, min_],'LineWidth',2,'LineStyle','--');
yline([rms_, -rms_],'LineWidth',2,'LineStyle',':');
ylim([-3 3]);
title(['AWG output: ',num2str(X.power), 'dBm']);
% Add text boxes for MIN, MAX, and RMS voltage
text(0.5, min_-0.3, ['MIN: ', num2str(min_),' V'],'FontSize', 10, 'HorizontalAlignment', 'left');
text(0.5, max_+0.3, ['MAX: ', num2str(max_),' V'],'FontSize', 10, 'HorizontalAlignment', 'left');
text(0.5, rms_+0.22, ['RMS: ', num2str(rms_),' V'],'FontSize', 10, 'HorizontalAlignment', 'left');
text(0.5, -rms_-0.22, ['RMS: ', num2str(rms_),' V'],'FontSize', 10, 'HorizontalAlignment', 'left');
nexttile
plot_eye(X.signal,X.fs,fsym);
ylabel('Signal in V')
nexttile
hold on
v_in_curve = [-u_pi*1.5/2:0.1:u_pi*1.5/2];
field=sqrt(10^(extmodlaser.power/10-3));
mzm_curve = ((field.*cos(pi/2*(real(v_in_curve)+vbias)/u_pi)).^2)*1e3;
scatter(v_in_curve+vbias,mzm_curve,10,'o','filled','DisplayName','Modulator TF complete');
scatter(X.signal(1:100000)+vbias,(abs(Opt.signal(1:100000)).^2)*1e3,0.1,'.','DisplayName','Modulator TF')
scatter(min_+vbias,((field.*cos(pi/2*(real(min_)+vbias)/u_pi)).^2)*1e3,50,'x','LineWidth',2);
scatter(max_+vbias,((field.*cos(pi/2*(real(max_)+vbias)/u_pi)).^2)*1e3,50,'x','LineWidth',2);
xlim([-u_pi*1.5/2+vbias, u_pi*1.5/2+vbias]);
ylim([min(mzm_curve),max(mzm_curve)]);
xlabel('Input in V')
ylabel('Output in mW')
title("MZM input (v) to output (w)");
nexttile
plot_eye(abs(Opt.signal.^2).*1e3 ,Opt.fs,fsym);
ylabel('Opt. Signal in mW')
nexttile([1 2])
spectrum_plot( Opt.signal,Opt.fs, 'bla');
end

View File

@@ -0,0 +1,79 @@
M = 6;
data = [1,2,3,4,5,6];
M = 6;
bitpattern = [];
s = RandStream('twister','Seed',1);
for i = 1:log2(M)
N = 2^(12-1); %length of prbs
bitpattern(:,i) = randi(s,[0 1], N, 1);
end
if M == 6
bitpattern = reshape(bitpattern',[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
bits = Informationsignal(bitpattern);
symbols = PAMmapper(M,0).map(bits);
symbols_tx_prec = Duobinary().precode(symbols);
% all possible transitions (for now 36, including the "edges"
% of the QAM 32 constellation)
states = PAMmapper(6,0,"eth_style",0).levels;
pam6transitions = combvec(states,states)'; % pam6transitions =
% [-5 -5;
% -3 -5;
% -1 -5; ...
pam6transitions_serial = reshape(pam6transitions',[],1);
data = pam6transitions_serial;
data = round(data);
b = min(data);
data = data - b;
data = data ./ 2;
% THIS WAS USED!
bk = zeros(size(data));
for k = 2:numel(data)
bk(k) = mod(data(k)-bk(k-1),M);
end
%% State Analysis
x = bk;%symbols_tx_prec.signal;
levels = sort(unique(x)).'; % or provide known 1x6 level values
[~,ix] = min(abs(x - levels),[],2);
x = levels(ix); % snapped/quantized
%% TRANSITION COUNTS & PROBABILITIES
K = numel(levels);
% map to state indices 1..K
[tf, idx] = ismember(x, levels);
idx = idx(:);
from = idx(1:end-1);
to = idx(2:end);
from = idx(1:2:end);
to = idx(2:2:end);
% counts C(from,to)
C = accumarray([from,to], 1, [K K], @sum, 0);
% row-stochastic transition matrix P(to|from)
rowSums = sum(C,2);
P = C ./ max(rowSums,1);
%% 1) HEATMAP (which transitions are more probable?)
figure('Name','Transition Probabilities (to | from)');
h = heatmap(levels, levels, P, 'Colormap', parula, 'ColorbarVisible','on');
colormap(gca,[[1,1,1];flip(cbrewer2('Spectral',100))]);clim([0,ceil(max(P(:))*10)/10]);
h.XLabel = 'From state (level)';
h.YLabel = 'To state (level)';
h.Title = 'P(to | from)';
%% 2) WEIGHTED TRANSITION GRAPH
% Use dtmc if you have Econometrics Toolbox:
mc = dtmc(P, 'StateNames', string(levels));
figure('Name','Markov Graph (dtmc)');
gp = graphplot(mc, 'ColorEdges',true, 'LabelEdges',true);

View File

@@ -0,0 +1,212 @@
M = 6;
bitpattern = [];
s = RandStream('twister','Seed',1);
for i = 1:log2(M)
N = 2^(17-1); %length of prbs
bitpattern(:,i) = randi(s,[0 1], N, 1);
end
if M == 6
bitpattern = reshape(bitpattern',[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
bits = Informationsignal(bitpattern);
symbols = PAMmapper(M,0).map(bits);
bits_rx = PAMmapper(M,0).demap(symbols);
[~,~,ber_direct,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
assert(ber_direct==0,'Mapping is wrong');
nBursts = 0;
% No Precoding %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%SEND DIRECTLY
symbols_tx = symbols;
symbols_rx = introduce_symbol_errors(symbols_tx, 1, 10, nBursts, 42);
%RECEIVE BRANCH (do nothing special)
bits_rx = PAMmapper(M,0).demap(symbols_rx);
[~,~,ber,errpos] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
disp(['BER normal: - ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]);
bursts_normal = count_error_bursts(errpos, 20);
% Precode Emulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%SEND DIRECTLY
symbols_tx = symbols;
symbols_rx = introduce_symbol_errors(symbols_tx, 1, 10, nBursts, 42);
%REFERENCE BRACH
symbols_db = Duobinary().encode(symbols_tx);
symbols_tx_emu = Duobinary().decode(symbols_db);
bits_tx_emu = PAMmapper(M,0).demap(symbols_tx_emu);
% symbols_rx = introduce_symbol_errors(symbols_tx, 1, 10, 200, 42);
%RECEIVE BRANCH
symbols_db = Duobinary().encode(symbols_rx);
symbols_rx_emu = Duobinary().decode(symbols_db);
bits_rx = PAMmapper(M,0).demap(symbols_rx_emu);
[~,~,ber_precode_emulation,errpos_precode_emulation] = calc_ber(bits_tx_emu.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
disp(['BER precode emulation: ',sprintf('%.1E',ber_precode_emulation),' - - PAM-',num2str(M)]);
bursts_precode_emulation = count_error_bursts(errpos_precode_emulation, 20);
% Precode at Tx %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%SEND PRECODED DATA
symbols_tx_prec = Duobinary().precode(symbols);
symbols_rx_prec = introduce_symbol_errors(symbols_tx_prec, 1, 10, nBursts, 42);
%RECEIVE BRANCH
symbols_db = Duobinary().encode(symbols_rx_prec);
symbols_rx_prec = Duobinary().decode(symbols_db);
bits_rx = PAMmapper(M,0).demap(symbols_rx_prec);
[~,~,ber_precoded,errpos_precoded] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
disp(['BER precoded: ',sprintf('%.1E',ber_precoded),' - - PAM-',num2str(M)]);
burst_precoded = count_error_bursts(errpos_precoded, 20);
% Precode at Tx but omit at Rx %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%SEND PRECODED DATA
symbols_tx_prec = Duobinary().precode(symbols);
bits_tx_prec = PAMmapper(M,0).demap(symbols_tx_prec);
symbols_rx_omit = introduce_symbol_errors(symbols_tx_prec, 1, 10, nBursts, 42);
%RECEIVE BRANCH
bits_rx = PAMmapper(M,0).demap(symbols_rx_omit);
[~,~,ber_omit,errpos_omit] = calc_ber(bits_tx_prec.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
disp(['BER (omit precode): ',sprintf('%.1E',ber_omit),' - - PAM-',num2str(M)]);
burst_omit = count_error_bursts(errpos_omit, 20);
if 0
cols = linspecer(8);
figure();hold on;
stem(1:20,bursts_normal,'LineWidth',2,'Color',cols(4,:),'Marker','_','DisplayName','w/o diff. precoder');
stem(1:20,bursts_precode_emulation,'LineWidth',2,'Color',cols(3,:),'Marker','.','LineStyle','-','DisplayName','emulated precoder');
stem(1:20,burst_precoded,'LineWidth',1,'Color',cols(6,:),'Marker','_','DisplayName','w/ diff. precoder');
stem(1:20,burst_omit,'LineWidth',1,'Color',cols(5,:),'Marker','.','LineStyle',':','DisplayName','omit precoder');
xlabel('Bit Error Burst Length')
ylabel('Occurence')
set(gca, 'yscale', 'log');
end
%% State Analysis
signal_to_analyze = symbols_tx_emu;
x = signal_to_analyze.signal(:);
levels = sort(unique(x)).'; % or provide known 1x6 level values
[~,ix] = min(abs(x - levels),[],2);
x = levels(ix); % snapped/quantized
%% TRANSITION COUNTS & PROBABILITIES
K = numel(levels);
% map to state indices 1..K
[tf, idx] = ismember(x, levels);
idx = idx(:);
from = idx(1:end-1);
to = idx(2:end);
from = idx(1:2:end);
to = idx(2:2:end);
% counts C(from,to)
C = accumarray([from,to], 1, [K K], @sum, 0);
% row-stochastic transition matrix P(to|from)
rowSums = sum(C,2);
P = C ./ max(rowSums,1);
%% 1) HEATMAP (which transitions are more probable?)
figure('Name','Transition Probabilities (to | from)');
h = heatmap(levels, levels, P, 'Colormap', parula, 'ColorbarVisible','on');
colormap(gca,[[1,1,1];flip(cbrewer2('Spectral',100))]);clim([0,ceil(max(P(:))*10)/10]);
h.XLabel = 'From state (level)';
h.YLabel = 'To state (level)';
h.Title = 'P(to | from)';
%% 2) WEIGHTED TRANSITION GRAPH
% Use dtmc if you have Econometrics Toolbox:
mc = dtmc(P, 'StateNames', string(levels.*PAMmapper(M,0).get_scaling));
figure('Name','Markov Graph (dtmc)');
gp = graphplot(mc, 'ColorEdges',true, 'LabelEdges',true);
function symbols = introduce_symbol_errors(symbols, j, maxBurstLen, nBursts, seed)
%INTRODUCE_SYMBOL_ERRORS injects bursty level errors into symbols.signal.
% symbols.signal : column/row vector of quantized levels (exactly one of 6 values)
% j : max level step per sample (default 1)
% maxBurstLen : maximum burst length (default 8)
% nBursts : number of bursts to insert (default ~1% of length)
% seed : RNG seed (optional)
if nargin < 2 || isempty(j), j = 1; end
if nargin < 3 || isempty(maxBurstLen), maxBurstLen = 8; end
x = symbols.signal(:);
N = numel(x);
if nargin < 4 || isempty(nBursts), nBursts = max(1, round(0.01*N)); end
if nargin >= 5 && ~isempty(seed), rng(seed); end
% known levels and index mapping
lvls = sort(unique(x)).';
K = numel(lvls);
[~, idx] = ismember(x, lvls); % idx in 1..6
used = false(N,1); % avoid overlapping bursts
burst_ranges = zeros(nBursts,2);
for b = 1:nBursts
% pick start not inside an existing burst
s = randi(N);
while used(s), s = randi(N); end
L = randi(maxBurstLen);
e = min(N, s+L-1);
% mark used range
used(s:e) = true;
burst_ranges(b,:) = [s e];
% choose one direction for the whole burst: -1 (down) or +1 (up)
dir = randi([0 1])*2 - 1;
% apply level errors within the burst
for t = s:e
k = idx(t); % current level index (1..6)
% force inward movement at edges; prevents "flipping" to opposite edge
if k == 1 && dir == -1, dir = +1; end
if k == K && dir == +1, dir = -1; end
step = randi([1 j]); % 1..j steps
kNew = k + dir*step;
% clamp to [1,K], no wrap-around
if kNew < 1, kNew = 1; elseif kNew > K, kNew = K; end
% if clamped to the same edge repeatedly, flip direction to keep changing
if kNew == k
dir = -dir;
kNew = max(1, min(K, k + dir*step));
end
idx(t) = kNew;
end
end
x_err = lvls(idx);
symbols.signal = reshape(x_err, size(symbols.signal)); % preserve original shape
end

View File

@@ -0,0 +1,100 @@
% Define ranges for variables to iterate over
var1_range = [1, 2, 3, 4, 6];
var2_range = [10, 20];
var3_range = [100, 200];
% Prepare the parallel pool
if isempty(gcp('nocreate'))
parpool; % Start a parallel pool if not already running
end
% Array to hold measurement futures
measurements = parallel.FevalFuture.empty();
% Array to hold DSP results
dsp_results = parallel.Future.empty();
% Nested for loops for all parameter combinations
lin_idx = 1;
for v1 = var1_range
for v2 = var2_range
for v3 = var3_range
% Construct the struct of optional variables for this iteration
optionalVars = struct('var_4', v1, 'var_5', v2, 'var_6', v3);
% Submit the measurement function to the parallel pool
measurements(lin_idx) = parfeval(@measurement, 1, optionalVars);
% Link DSP function to run after measurement completes
dsp_results(lin_idx) = afterEach(measurements(lin_idx), @(output) rundsp(output, optionalVars), 1);
lin_idx = lin_idx + 1; % Increment linear index
end
end
end
% Fetch and display DSP results
final_results = cell(numel(dsp_results), 1);
for i = 1:numel(dsp_results)
fprintf('Fetching DSP result for job %d...\n', i);
final_results{i} = fetchOutputs(dsp_results(i)); % Fetch each DSP result individually
end
fprintf('All DSP evaluations completed.\n');
disp('Final Results:');
disp(final_results);
% --- Measurement Function ---
function output = measurement(varargin)
% Default values for optional variables
var_1 = 1;
var_2 = 2;
var_4 = 10; % Default value for var4
var_5 = 20; % Default value for var5
var_6 = 30; % Default value for var6
var_7 = 40; % Default value for var7
var_8 = 50; % Default value for var8
var_9 = 60; % Default value for var9
var_10 = 70; % Default value for var10
% Parse optional input arguments
if ~isempty(varargin)
var_s = varargin{1};
if isstruct(var_s)
fields = fieldnames(var_s);
for i = 1:numel(fields)
eval([fields{i}, ' = ', num2str( var_s.(fields{i}) ), ';']);
fprintf("%s <-- %.2f \n", fields{i}, var_s.(fields{i}));
end
else
error('Optional variables should be passed as a struct.');
end
end
% Simulate output with a random delay
output = randi(5); % Random result
pause(output); % Simulate processing time
end
% --- DSP Function ---
function output = rundsp(measurement_output, varargin)
% Parse optional input arguments
if ~isempty(varargin)
var_s = varargin{1};
if isstruct(var_s)
fields = fieldnames(var_s);
for i = 1:numel(fields)
eval([fields{i}, ' = ', num2str( var_s.(fields{i}) ), ';']);
fprintf("%s <-- %.2f \n", fields{i}, var_s.(fields{i}));
end
else
error('Optional variables should be passed as a struct.');
end
end
% Simulate DSP processing based on measurement output
output = measurement_output + 10; % Add 10 to measurement output
pause(measurement_output); % Simulate DSP processing time
end

View File

@@ -0,0 +1,65 @@
M = 4;
apply_precode_at_tx = 1;
bitpattern = [];
s = RandStream('twister','Seed',1);
for i = 1:log2(M)
N = 2^(17-1); %length of prbs
bitpattern(:,i) = randi(s,[0 1], N, 1);
end
if M == 6
bitpattern = reshape(bitpattern',[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
bits = Informationsignal(bitpattern);
symbols = PAMmapper(M,0).map(bits);
if apply_precode_at_tx
symbols_tx = Duobinary().precode(symbols);
else
symbols_tx = symbols;
end
disp(['Tx Sequenz: -- RMS:',sprintf('%.1f',rms(symbols_tx.signal)),' - - Levels -',num2str(numel(unique(symbols_tx.signal)))]);
unique(symbols_tx.signal)
disp('- - - - - - - - - -');
symbols_tx.signal = awgn(symbols_tx.signal,20,"measured",1);
% show2Dconstellation(symbols_tx,symbols_tx,"displayname",'VNLE Out','fignum',2241);
if apply_precode_at_tx
% Entschiedene Symbole codieren: d_DB(n) = d(n) + d(n-1) (im Fall von PAM4 7 level [0 1 2 3 4 5 6])
symbols_db = Duobinary().encode(symbols_tx);
disp(['DB encoded -- RMS:',sprintf('%.1f',rms(symbols_db.signal)),' - - Levels -',num2str(numel(unique(symbols_db.signal)))]);
unique(symbols_db.signal)
disp('- - - - - - - - - -');
% Entschiedene codierte Symbole decodieren: d_dec(n) = d_DB(n) mod4
symbols_rx = Duobinary().decode(symbols_db);
else
symbols_db = Duobinary().encode(symbols_tx);
symbols_rx = Duobinary().decode(symbols_db);
end
% Vergleichen von b(n) und d_dec(n)
bits_rx = PAMmapper(M,0).demap(symbols_rx);
disp(['Wieder normal -- RMS:',sprintf('%.1f',rms(symbols_rx.signal)),' - - Levels -',num2str(numel(unique(symbols_rx.signal)))]);
unique(symbols_rx.signal)
disp('- - - - - - - - - -');
[~,~,ber,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",10,"skip_end",10,"returnErrorLocation",1);
disp(['BER: ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]);
figure()
subplot(1,2,1)
histogram(symbols_tx.signal,100,'Normalization','count')
subplot(1,2,2)
histogram(symbols_db.signal,100,'Normalization','count')

View File

@@ -0,0 +1,39 @@
% Setup PRBS parameters
O = 6;
M = 6;
N = 2^(O-1); % Length of PRBS
randkey = 1; % Random key for random stream
use_eth_mapping =1;
if M ~= 6
dimension = log2(M);
else
dimension = 5;
end
[~, seed] = prbs(O, 1); % Initialize first seed of PRBS
bitpattern = [];
s = RandStream('twister', 'Seed', randkey);
for i = 1:dimension
bitpattern(:, i) = randi(s, [0 1], N, 1);
end
if M == 6
bitpattern = reshape(bitpattern',[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
Tx_bits = Informationsignal(bitpattern);
Digi_Mod = PAMmapper(M, 0,"eth_style",use_eth_mapping);
% Map bits to symbols
Symbols = Digi_Mod.map(Tx_bits);
% Demap symbols back to bits
Rx_bits = Digi_Mod.demap(Symbols);
[~, error_num, ber, ~] = calc_ber(Tx_bits.signal(1:length(Rx_bits.signal)), Rx_bits.signal,"skip_front", 0, "skip_end", 0, "returnErrorLocation", 1);
fprintf('BER: %.1E \n',ber);

View File

@@ -0,0 +1,74 @@
classdef test_modulation < matlab.unittest.TestCase
properties
Tx_bits
Digi_Mod
end
properties (MethodSetupParameter)
% Define method-level parameters for PRBS and bit pattern
useprbs = struct('false', 0, 'true', 1); % variations: {0, 1}
M = struct('M2', 2, 'M4', 4, 'M6', 6, 'M8', 8); % variations: {2, 4, 6, 8}
O = struct('O10', 10, 'O15', 15, 'O17', 17); % variations: {10, 15, 17}
end
properties (TestParameter)
% Define test-level parameters for M and O
end
methods (TestMethodSetup)
function setupModulation(testCase, useprbs, M, O)
% Setup PRBS and bit pattern for the test case using parameters
% Setup PRBS parameters
N = 2^(O-1); % Length of PRBS
randkey = 1; % Random key for random stream
[~, seed] = prbs(O, 1); % Initialize first seed of PRBS
bitpattern = [];
if useprbs
for i = 1:log2(M)
[bitpattern(:, i), seed] = prbs(O, N, seed);
end
else
s = RandStream('twister', 'Seed', randkey);
for i = 1:log2(M)
bitpattern(:, i) = randi(s, [0 1], N, 1);
end
end
if M == 6
bitpattern = reshape(bitpattern, [], 1);
bitpattern = bitpattern(1:end-mod(length(bitpattern), 5));
end
testCase.Tx_bits = Informationsignal(bitpattern);
testCase.Digi_Mod = PAMmapper(M, 0);
end
end
methods (Test, ParameterCombination = 'sequential')
% Test with sequential combination of parameters
function testBackToBackMapping(testCase, M, useprbs, O)
% Test Bits -> Symbols -> Bits (Back-to-Back Mapping)
% Map bits to symbols
Symbols = testCase.Digi_Mod.map(testCase.Tx_bits);
% Demap symbols back to bits
Rx_bits = testCase.Digi_Mod.demap(Symbols);
% Validate BER is zero
[~, error_num, ber, ~] = calc_ber(testCase.Tx_bits.signal, Rx_bits.signal, ...
"skip_front", 0, "skip_end", 0, "returnErrorLocation", 1);
% Assert that BER is zero
testCase.verifyEqual(ber, 0, 'BER should be zero');
testCase.verifyEqual(error_num, 0, 'No errors should occur in the mapping process');
end
end
end