From 0647530f690aaeda28deef85bc0fa11c7681cae4 Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Tue, 23 Dec 2025 08:32:03 +0100 Subject: [PATCH 01/12] file naming for WDM simulation --- projects/WDM/WDM_model.m | 251 +++++++++++------- .../{WDM_model_16x200.m => WDM_model_old.m} | 47 ++-- projects/WDM/WDM_settings.m | 57 ---- projects/WDM/getDispersionVector.m | 32 +++ 4 files changed, 210 insertions(+), 177 deletions(-) rename projects/WDM/{WDM_model_16x200.m => WDM_model_old.m} (88%) create mode 100644 projects/WDM/getDispersionVector.m diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m index cdc8a74..37c0990 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -1,25 +1,96 @@ %%% Run parameters % TX -% --- FIRST LINE: evaluate settings located beside this script --- -run(fullfile(fileparts(mfilename('fullpath')),'WDM_settings.m')); -num_realiz = 50; -s.wavelengthplan = calcWavelengthPlan(16,400e9,1310); +function WDM_model(options) + +arguments + options.num_channels = 16; + options.channel_spacing = 400e9; + options.fiber_length_km = 0; + options.rand_key = 1; + options.num_realiz = 1; + options.fwm_mitigation_technique = "co"; +end + +%% + +% Add the imdd_simulation framework to the path +if ispc + addpath(genpath('C:\Users\Silas\Documents\MATLAB\imdd_simulation')); +else + % Linux path on the cluster + addpath(genpath('/work_beegfs/sutef391/imdd_simulation')); +end + +% Quiet the ambiguous CET warning (best is to set TZ in sbatch; see below) +warning('off','MATLAB:datetime:AmbiguousTimeZone'); + +% How many workers? +cpus = str2double(getenv('SLURM_CPUS_PER_TASK')); +if ~isfinite(cpus) || cpus < 1, cpus = max(1, feature('numcores')); end + +% Use a per-job, node-local JobStorageLocation to avoid stale locks on $HOME +% Prefer $TMPDIR if your cluster provides it, else tempdir(). +tmpbase = getenv('TMPDIR'); +if isempty(tmpbase), tmpbase = tempdir; end +jsl = fullfile(tmpbase, sprintf('matlab_jobstorage_%s_%s', ... + getenv('USER'), getenv('SLURM_JOB_ID'))); +if ~exist(jsl,'dir'); mkdir(jsl); end + +% Configure the local cluster explicitly and start the pool +c = parcluster('local'); +c.NumWorkers = cpus; +c.JobStorageLocation = jsl; + +p = gcp('nocreate'); +if isempty(p) || p.NumWorkers ~= cpus + if ~isempty(p), delete(p); end + p = parpool(c, cpus); % avoids the “queued” state +end +fprintf('parpool up with %d workers; JobStorage=%s\n', p.NumWorkers, c.JobStorageLocation); + +% result filename (timestamp + optional job id) +t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); +jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end +host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end + +% Output directory depends on platform +% create/ use folders foroptions.fiber_length_km, options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique +foldname = sprintf('%dkm_%dch_%dghz_%s', options.fiber_length_km, options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); +if ispc + output_root = fullfile('C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\',foldname,'\'); +else + output_root = fullfile('/work_beegfs/sutef391/results_WDM',foldname,'\'); +end +if ~exist(output_root,'dir'), mkdir(output_root); end + +% Build filename +t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); +jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end +host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end + +fname = sprintf('WDM_%s_%s_%s_%dkm_%dch_%dghz_%s.mat', char(t), host, jobid, options.fiber_length_km, options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); + + +%% +s.num_realiz = options.num_realiz; +% s.wavelengthplan = calcWavelengthPlan(16,400e9,1310); +s.wavelengthplan = calcWavelengthPlan(options.num_channels,options.channel_spacing,1310); % wavelengthplan = [1295,1305,1315,1325]; -link_length = 10; -pmd = 0.1; -gamma = 0.0023; +link_length = options.fiber_length_km; +s.pmd = 0.1; +s.gamma = 0.0023; -M = 4; -m = floor(log2(M)*10)/10; +s.M = 4; +m = floor(log2(s.M)*10)/10; fsym = 112e9; fdac = 2*fsym; -fadc = 2*fsym; -s.random_key = 100; +fadc = 120000000000; +s.random_key = options.rand_key; % Laser / s.Modulator vbias_rel = 0.5; -u_pi = 3.2; +u_pi = 4.6; vbias = -vbias_rel*u_pi; laser_linewidth = 0e6; @@ -38,7 +109,7 @@ mu_dc = 0.005; mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3]; mu_dfe = 0.0004; -%DB Stuff +%DB Stuff db_precode = 0; db_encode = 0; duob_mode = db_mode.no_db; @@ -52,7 +123,7 @@ 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 = 8; +kover = 4; upsample_required = f_nyq./(fdac*kover/2); upsample_pow = 2^nextpow2(upsample_required); upsample_ceil = ceil(upsample_required); @@ -64,18 +135,34 @@ signal_cell = {}; Symbols = {}; Tx_bits = {}; -s.rop = -6:0.75:-0.75; +s.rop = -12:0.75:-0.75; output_ffe = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz); output_vnle = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz); output_mlse = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz); output_dbt = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz); +s.p = options.fwm_mitigation_technique; +switch s.p + case "co" + pol_rot = 100.*ones(1,length(s.wavelengthplan)); + d_local = 0; + case "pair" + pol_rot = repmat([100,100,0,0],1,length(s.wavelengthplan)/4); + d_local = 0; + case "alt" + pol_rot = repmat([100,0,100,0],1,length(s.wavelengthplan)/4); + d_local = 0; + case "seg" + pol_rot = 100.*ones(1,length(s.wavelengthplan)); + d_local = 3; +end + for realiz = 1:s.num_realiz - + parfor l = 1:N - + [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource(... "fsym",fsym,"M",s.M,"order",18,"useprbs",0,... "fs_out",fdac,... @@ -84,93 +171,92 @@ for realiz = 1:s.num_realiz "randkey",s.random_key+l+realiz,... "db_precode",db_precode,"db_encode",db_encode,... "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process(); - + % Digi_sig.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0); - Lp_awg = Filter('filtdegree',3,"f_cutoff",100e9,"fs",fdac*kover,"filterType",filtertypes.gaussian,"active",true); - El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0,"H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig); + 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 = s.M8199B("kover",kover).process(Digi_sig); % El_sig.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0); - + %%%%% Electrical Driver Amplifier %%%%%% El_sig = El_sig.normalize("mode","oneone"); + El_sig = El_sig .* u_pi .* 0.5; % El_sig = El_sig.setPower(1,"dBm"); % figure;histogram(El_sig.signal); - + %%%%% s.MODULATE 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+realiz).process(El_sig); - - signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",100).process(Eml_out); + + signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",pol_rot(l)).process(Eml_out); end - + Opt_sig_wdm = Optical_Multiplex("fs_in",fdac*kover,"fs_out",upsample_pow*fdac*kover,... - "lambda_center",1310,"random_key",0,"filtype",1,"B",200e9).process(signal_cell); - + "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",3+10*log10(N)).process(Opt_sig_wdm); - + % Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0); - + % Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',1,'max_num_lines',2); - + %%%%%% Fiber %%%%%% Opt_sig_wdm_fib=Opt_sig_wdm; - + segment_length = 1; % km nSegments = link_length/segment_length; zdw = 1310; - D_local = 0; %if ~=0, simulation uses "segmented fiber with d+,d-) + d_local = d_local; %if ~=0, simulation uses "segmented fiber with d+,d-) randomize_D = true; - Dvec = getDispersionVector(nSegments, D_local, zdw, randomize_D, s.random_key+realiz); + Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key+realiz); for seg = 1:nSegments - - Opt_sig_wdm_fib = DP_Fiber("L",segment_length,"D",Dvec(s),"Dpmd",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).process(Opt_sig_wdm_fib); - + + 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).process(Opt_sig_wdm_fib); + end - - Opt_sig_wdm_fib.spectrum("fignum",realiz,"displayname",'bla','lambda0_nm',1310,'useWavelengthAxis',0); - + + % Opt_sig_wdm_fib.spectrum("fignum",realiz,"displayname",'bla','lambda0_nm',1310,'useWavelengthAxis',0); + % Opt_sig_wdm_fib.move_it_spectrum("fignum",100212,"displayname",'bla'); - + % Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",s.link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"s.gamma",0,"Dslope",0.07).process(Opt_sig) - + Opt_sig_wdm_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); + for ri = 1:length(s.rop) - - %%%%%% ROP %%%%%% - Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",s.rop(ri)+10*log10(N)).process(Opt_sig_wdm_fib); - - Opt_sig_wdm_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1,"fs_out",Opt_sig_wdm_rx.fs/upsample_pow,"fs_in",Opt_sig_wdm_rx.fs,"lambda_center",1310).process(Opt_sig_wdm_rx); - - PD_cell = {}; - for l = 1:N - + + parfor l = 1:N + + %%%%%% ROP %%%%%% + Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",s.rop(ri)).process(Opt_sig_wdm_demux{l}); % rop+10*log10(N) + %%%%%% PD Square Law %%%%%% - assert(fdac*kover==Opt_sig_wdm_demux{l}.fs,'Sampling Frequencies do not match! Check previous steps'); - PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11,"randomkey",s.random_key+l+realiz).process(Opt_sig_wdm_demux{l}); - + assert(fdac*kover==Opt_sig_wdm_rx.fs,'Sampling Frequencies do not match! Check previous steps'); + PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11,"randomkey",s.random_key+l+realiz).process(Opt_sig_wdm_rx); + % PD_sig.spectrum("fignum",222,"displayname",'bla','normalizeTo0dB',1); - + %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% rx_bwl = 100e9; PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(PD_sig); - + % %%%%%% Low-pass Scope %%%%%% - Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); - + Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); + %%%%%% Scope %%%%%% 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',1,'H_lpf',Lp_scpe).process(PD_sig); - + "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_sig.spectrum("fignum",222,"displayname",'bla','normalizeTo0dB',1); - + [~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 1); Rx_sig = Scpe_cell{1}; Rx_sig = Rx_sig.normalize("mode","rms"); - + % FFE @@ -227,7 +313,7 @@ for realiz = 1:s.num_realiz output_dbt{l,ri,realiz} = dbt_results; end - + end res = struct(); @@ -244,36 +330,7 @@ for realiz = 1:s.num_realiz end - -function dispersion_vector = getDispersionVector(N, D, ref_zdw, randomize_ZDW, randomkey) -% s.MATLAB version of the Python generator shown above. -% Returns an N×1 vector (ps/(nm·km)). -% -% D is the nominal dispersion magnitude. For D>0 the link is segmented with -% alternating sign (+D, -D, +D, …). For D==0 it is flat (0) except for -% ZDW randomization. The ZDW detuning is ~N(0, 2 nm) around 1310 nm and is -% converted to dispersion via 0.09 ps/(nm·km) per nm. - - % constants (matching the Python code) - meanLambda_nm = 1310; % center wavelength - sigma_nm = 2; % ZDW sigma - Dslope = 0.07; % ps/(nm·km) per nm detuning - - % random ZDW-induced dispersion offset - if randomize_ZDW - rng(randomkey, 'twister'); - rand_zdws_nm = meanLambda_nm + sigma_nm .* randn(N,1); - rand_D = (rand_zdws_nm - ref_zdw) .* Dslope; % ps/(nm·km) - else - rand_D = zeros(N,1); - end - - % nominal segmented pattern (match Python intent; keep length N) - if D > 0 - base = (-1) .^ ((0:N-1).'); % +1,-1,+1,-1,... - else % D == 0 (or anything else) - base = ones(N,1); - end - - dispersion_vector = base .* D + rand_D; % ps/(nm·km) end + + + diff --git a/projects/WDM/WDM_model_16x200.m b/projects/WDM/WDM_model_old.m similarity index 88% rename from projects/WDM/WDM_model_16x200.m rename to projects/WDM/WDM_model_old.m index 7000966..cdc8a74 100644 --- a/projects/WDM/WDM_model_16x200.m +++ b/projects/WDM/WDM_model_old.m @@ -3,23 +3,23 @@ % --- FIRST LINE: evaluate settings located beside this script --- run(fullfile(fileparts(mfilename('fullpath')),'WDM_settings.m')); -s.num_realiz = 2; +num_realiz = 50; s.wavelengthplan = calcWavelengthPlan(16,400e9,1310); % wavelengthplan = [1295,1305,1315,1325]; -link_length = 0; +link_length = 10; pmd = 0.1; gamma = 0.0023; -s.M = 4; -m = floor(log2(s.M)*10)/10; +M = 4; +m = floor(log2(M)*10)/10; fsym = 112e9; fdac = 2*fsym; -fadc = 120000000000; +fadc = 2*fsym; s.random_key = 100; % Laser / s.Modulator vbias_rel = 0.5; -u_pi = 4.6; +u_pi = 3.2; vbias = -vbias_rel*u_pi; laser_linewidth = 0e6; @@ -86,14 +86,13 @@ for realiz = 1:s.num_realiz "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process(); % Digi_sig.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0); - 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); + Lp_awg = Filter('filtdegree',3,"f_cutoff",100e9,"fs",fdac*kover,"filterType",filtertypes.gaussian,"active",true); + El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0,"H_lpf",Lp_awg,"dac_max",0.6,"dac_min",-0.6).process(Digi_sig); % El_sig = s.M8199B("kover",kover).process(Digi_sig); % El_sig.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0); %%%%% Electrical Driver Amplifier %%%%%% El_sig = El_sig.normalize("mode","oneone"); - El_sig = El_sig .* u_pi .* 0.5; % El_sig = El_sig.setPower(1,"dBm"); % figure;histogram(El_sig.signal); @@ -104,7 +103,7 @@ for realiz = 1:s.num_realiz end 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); + "lambda_center",1310,"random_key",0,"filtype",1,"B",200e9).process(signal_cell); Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",3+10*log10(N)).process(Opt_sig_wdm); @@ -135,19 +134,21 @@ for realiz = 1:s.num_realiz % Opt_sig_wdm_fib.move_it_spectrum("fignum",100212,"displayname",'bla'); % Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",s.link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"s.gamma",0,"Dslope",0.07).process(Opt_sig) - Opt_sig_wdm_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1,"fs_out",Opt_sig_wdm_rx.fs/upsample_pow,"fs_in",Opt_sig_wdm_rx.fs,"lambda_center",1310).process(Opt_sig_wdm_fib); - + for ri = 1:length(s.rop) - parfor l = 1:N - - %%%%%% ROP %%%%%% - Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",s.rop(ri)).process(Opt_sig_wdm_demux{l}); % rop+10*log10(N) - + %%%%%% ROP %%%%%% + Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",s.rop(ri)+10*log10(N)).process(Opt_sig_wdm_fib); + + Opt_sig_wdm_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1,"fs_out",Opt_sig_wdm_rx.fs/upsample_pow,"fs_in",Opt_sig_wdm_rx.fs,"lambda_center",1310).process(Opt_sig_wdm_rx); + + PD_cell = {}; + for l = 1:N + %%%%%% PD Square Law %%%%%% - assert(fdac*kover==Opt_sig_wdm_rx.fs,'Sampling Frequencies do not match! Check previous steps'); - PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11,"randomkey",s.random_key+l+realiz).process(Opt_sig_wdm_rx); - + assert(fdac*kover==Opt_sig_wdm_demux{l}.fs,'Sampling Frequencies do not match! Check previous steps'); + PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11,"randomkey",s.random_key+l+realiz).process(Opt_sig_wdm_demux{l}); + % PD_sig.spectrum("fignum",222,"displayname",'bla','normalizeTo0dB',1); %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% @@ -155,13 +156,13 @@ for realiz = 1:s.num_realiz PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(PD_sig); % %%%%%% Low-pass Scope %%%%%% - Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); - + Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); + %%%%%% Scope %%%%%% 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); + "adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',1,'H_lpf',Lp_scpe).process(PD_sig); Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym); % Scpe_sig.spectrum("fignum",222,"displayname",'bla','normalizeTo0dB',1); diff --git a/projects/WDM/WDM_settings.m b/projects/WDM/WDM_settings.m index c64071f..fd40910 100644 --- a/projects/WDM/WDM_settings.m +++ b/projects/WDM/WDM_settings.m @@ -2,60 +2,3 @@ -% Add the imdd_simulation framework to the path -if ispc - addpath(genpath('C:\Users\Silas\Documents\MATLAB\imdd_simulation')); -else - % Linux path on the cluster - addpath(genpath('/work_beegfs/sutef391/imdd_simulation')); -end - -% Quiet the ambiguous CET warning (best is to set TZ in sbatch; see below) -warning('off','MATLAB:datetime:AmbiguousTimeZone'); - -% How many workers? -cpus = str2double(getenv('SLURM_CPUS_PER_TASK')); -if ~isfinite(cpus) || cpus < 1, cpus = max(1, feature('numcores')); end - -% Use a per-job, node-local JobStorageLocation to avoid stale locks on $HOME -% Prefer $TMPDIR if your cluster provides it, else tempdir(). -tmpbase = getenv('TMPDIR'); -if isempty(tmpbase), tmpbase = tempdir; end -jsl = fullfile(tmpbase, sprintf('matlab_jobstorage_%s_%s', ... - getenv('USER'), getenv('SLURM_JOB_ID'))); -if ~exist(jsl,'dir'); mkdir(jsl); end - -% Configure the local cluster explicitly and start the pool -c = parcluster('local'); -c.NumWorkers = cpus; -c.JobStorageLocation = jsl; - -p = gcp('nocreate'); -if isempty(p) || p.NumWorkers ~= cpus - if ~isempty(p), delete(p); end - p = parpool(c, cpus); % avoids the “queued” state -end -fprintf('parpool up with %d workers; JobStorage=%s\n', p.NumWorkers, c.JobStorageLocation); - - - - -% result filename (timestamp + optional job id) -t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); -jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end -host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end - -% Output directory depends on platform -if ispc - output_root = fullfile('C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\'); -else - output_root = '/work_beegfs/sutef391/results_WDM'; -end -if ~exist(output_root,'dir'), mkdir(output_root); end - -% Build filename -t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); -jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end -host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end - -fname = sprintf('WDM_%s_%s_%s.mat', char(t), host, jobid); \ No newline at end of file diff --git a/projects/WDM/getDispersionVector.m b/projects/WDM/getDispersionVector.m new file mode 100644 index 0000000..cabd584 --- /dev/null +++ b/projects/WDM/getDispersionVector.m @@ -0,0 +1,32 @@ +function dispersion_vector = getDispersionVector(N, D, ref_zdw, randomize_ZDW, randomkey) +% s.MATLAB version of the Python generator shown above. +% Returns an N×1 vector (ps/(nm·km)). +% +% D is the nominal dispersion magnitude. For D>0 the link is segmented with +% alternating sign (+D, -D, +D, …). For D==0 it is flat (0) except for +% ZDW randomization. The ZDW detuning is ~N(0, 2 nm) around 1310 nm and is +% converted to dispersion via 0.09 ps/(nm·km) per nm. + + % constants (matching the Python code) + meanLambda_nm = 1310; % center wavelength + sigma_nm = 2; % ZDW sigma + Dslope = 0.07; % ps/(nm·km) per nm detuning + + % random ZDW-induced dispersion offset + if randomize_ZDW + rng(randomkey, 'twister'); + rand_zdws_nm = meanLambda_nm + sigma_nm .* randn(N,1); + rand_D = (rand_zdws_nm - ref_zdw) .* Dslope; % ps/(nm·km) + else + rand_D = zeros(N,1); + end + + % nominal segmented pattern (match Python intent; keep length N) + if D > 0 + base = (-1) .^ ((0:N-1).'); % +1,-1,+1,-1,... + else % D == 0 (or anything else) + base = ones(N,1); + end + + dispersion_vector = base .* D + rand_D; % ps/(nm·km) +end \ No newline at end of file From 0bb0dacbada2a5a3ad8a35ce21a386b5a9818db7 Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Tue, 23 Dec 2025 08:59:19 +0100 Subject: [PATCH 02/12] again changes to cluster simulation, added folder structure for saving --- Classes/00_signals/Signal.m | 2 +- projects/WDM/WDM_auswertung.m | 7 ++++--- projects/WDM/WDM_model.m | 10 +++++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index a21e71c..b8113c8 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -143,7 +143,7 @@ classdef Signal arguments obj options.fignum = randi(1000) - options.displayname = []; + options.displayname = ''; options.timeframe = 0; options.clear = 0; options.color = []; diff --git a/projects/WDM/WDM_auswertung.m b/projects/WDM/WDM_auswertung.m index d13c97f..508291d 100644 --- a/projects/WDM/WDM_auswertung.m +++ b/projects/WDM/WDM_auswertung.m @@ -54,9 +54,9 @@ for l = 1:N % plotBandMeanBL(rop, ffe_mat, cols(l,:), sprintf('FFE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--s', showLegend); % scatter(Sffe,fec.*ones(size(Sffe)),20,'v','MarkerFaceColor','black'); - plotBandMeanBL(rop, vnle_mat, cols(l,:), sprintf('VNLE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--x', showLegend); + % plotBandMeanBL(rop, vnle_mat, cols(l,:), sprintf('VNLE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--x', showLegend); - % plotBandMeanBL(rop, mlse_mat, cols(l,:), sprintf('VNLE+PF+MLSE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '-o', showLegend); + plotBandMeanBL(rop, mlse_mat, cols(l,:), sprintf('VNLE+PF+MLSE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '-o', showLegend); % plotBandMeanBL(rop, dbt_mat, cols(l,:), sprintf('DBt.+MLSE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--v', showLegend); @@ -73,6 +73,7 @@ ylim([1e-5 0.3]); grid on; legend show; +%% VIOLIN S_cell = Sdbt; S_cell =Smlse; @@ -106,7 +107,7 @@ for i = 1:length(S_cell) 'Bandwidth', 0.05); ylim([floor(min(S_mat,[],'all')), ceil(max(S_mat,[],'all'))]) - ylim([-8 0]); + % ylim([-8 0]); ylabel('ROP at FEC crossing'); title(sprintf('RROP to cross BER %.2e', fec)); grid on; box on; diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m index 37c0990..75daba0 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -120,7 +120,7 @@ Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",1 N = numel(s.wavelengthplan); f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9); -margin = 25e12; % some THz left and right +margin = 5e12; % some THz left and right f_span = (max(f_plan)+margin)-(min(f_plan)-margin); f_nyq = f_span/2; kover = 4; @@ -142,6 +142,7 @@ output_vnle = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz); output_mlse = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz); output_dbt = cell(length(s.wavelengthplan),length(s.rop),s.num_realiz); +s.p_launch = 3; s.p = options.fwm_mitigation_technique; switch s.p case "co" @@ -180,7 +181,9 @@ for realiz = 1:s.num_realiz %%%%% Electrical Driver Amplifier %%%%%% El_sig = El_sig.normalize("mode","oneone"); - El_sig = El_sig .* u_pi .* 0.5; + scaling = 0.6*(u_pi/2-abs(vbias-u_pi/2)); % scale to 60% of available modulator curve + El_sig = El_sig .* scaling; + % El_sig = El_sig.setPower(1,"dBm"); % figure;histogram(El_sig.signal); @@ -193,7 +196,8 @@ for realiz = 1:s.num_realiz 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",3+10*log10(N)).process(Opt_sig_wdm); + + 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.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0); From afde90fe674bce69c60a1ab5d026876198c20ceb Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Tue, 23 Dec 2025 12:34:47 +0100 Subject: [PATCH 03/12] new function to eval every 2 km --- projects/WDM/WDM_model.m | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m index 75daba0..aba8593 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -56,7 +56,7 @@ host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end % Output directory depends on platform % create/ use folders foroptions.fiber_length_km, options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique -foldname = sprintf('%dkm_%dch_%dghz_%s', options.fiber_length_km, options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); +foldname = sprintf('%dkm_%dch_%dghz_%s', options.fiber_length_km(end), options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); if ispc output_root = fullfile('C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\',foldname,'\'); else @@ -69,7 +69,7 @@ t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end -fname = sprintf('WDM_%s_%s_%s_%dkm_%dch_%dghz_%s.mat', char(t), host, jobid, options.fiber_length_km, options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); +fname = sprintf('WDM_%s_%s_%s_%dkm_%dch_%dghz_%s.mat', char(t), host, jobid, options.fiber_length_km(end), options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); %% @@ -212,20 +212,22 @@ for realiz = 1:s.num_realiz d_local = d_local; %if ~=0, simulation uses "segmented fiber with d+,d-) randomize_D = true; Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key+realiz); + propdist = 0; for seg = 1: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).process(Opt_sig_wdm_fib); + + propdist = segment_length; + + + end - % Opt_sig_wdm_fib.spectrum("fignum",realiz,"displayname",'bla','lambda0_nm',1310,'useWavelengthAxis',0); - - % Opt_sig_wdm_fib.move_it_spectrum("fignum",100212,"displayname",'bla'); - - % Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",s.link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"s.gamma",0,"Dslope",0.07).process(Opt_sig) + %%%%%% Demux after 2 km %%%%%% Opt_sig_wdm_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); for ri = 1:length(s.rop) @@ -327,6 +329,10 @@ for realiz = 1:s.num_realiz res.mlse = output_mlse; res.dbt = output_dbt; + %%%%%% Demux after final (10) km %%%%%% + + + % Save results save(fullfile(output_root, fname), 'res', '-v7.3'); fprintf('Saved results to: %s\n', fullfile(output_root, fname)); From 45c9fc2b1433e4c0925c4763dfbb3249eb0f5445 Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Tue, 23 Dec 2025 12:35:49 +0100 Subject: [PATCH 04/12] noew WITH the new code :_) --- projects/WDM/WDM_model_10km.m | 361 ++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 projects/WDM/WDM_model_10km.m diff --git a/projects/WDM/WDM_model_10km.m b/projects/WDM/WDM_model_10km.m new file mode 100644 index 0000000..85cf2d7 --- /dev/null +++ b/projects/WDM/WDM_model_10km.m @@ -0,0 +1,361 @@ +function WDM_model_10km(options) +%%% Run parameters +% TX + +arguments + options.num_channels = 16; + options.channel_spacing = 400e9; + options.fiber_length_km = 0; + options.rand_key = 1; + options.num_realiz = 1; + options.fwm_mitigation_technique = "co"; +end + +%% Add the imdd_simulation framework to the path +if ispc + addpath(genpath('C:\Users\Silas\Documents\MATLAB\imdd_simulation')); +else + % Linux path on the cluster + addpath(genpath('/work_beegfs/sutef391/imdd_simulation')); +end + +% Quiet the ambiguous CET warning (best is to set TZ in sbatch) +warning('off','MATLAB:datetime:AmbiguousTimeZone'); + +%% How many workers? +cpus = str2double(getenv('SLURM_CPUS_PER_TASK')); +if ~isfinite(cpus) || cpus < 1, cpus = max(1, feature('numcores')); end + +% Use a per-job, node-local JobStorageLocation to avoid stale locks on $HOME +tmpbase = getenv('TMPDIR'); +if isempty(tmpbase), tmpbase = tempdir; end +jsl = fullfile(tmpbase, sprintf('matlab_jobstorage_%s_%s', ... + getenv('USER'), getenv('SLURM_JOB_ID'))); +if ~exist(jsl,'dir'); mkdir(jsl); end + +% Configure the local cluster explicitly and start the pool +c = parcluster('local'); +c.NumWorkers = cpus; +c.JobStorageLocation = jsl; + +p = gcp('nocreate'); +if isempty(p) || p.NumWorkers ~= cpus + if ~isempty(p), delete(p); end + p = parpool(c, cpus); +end +fprintf('parpool up with %d workers; JobStorage=%s\n', p.NumWorkers, c.JobStorageLocation); + +%% result filename (timestamp + optional job id) +t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); +jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end +host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end + +% Output directory depends on platform +foldname = sprintf('%dkm_%dch_%dghz_%s', options.fiber_length_km(end), options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); +if ispc + output_root = fullfile('C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\',foldname,'\'); +else + output_root = fullfile('/work_beegfs/sutef391/results_WDM',foldname,'\'); +end +if ~exist(output_root,'dir'), mkdir(output_root); end + +fname = sprintf('WDM_%s_%s_%s_%dkm_%dch_%dghz_%s.mat', char(t), host, jobid, options.fiber_length_km(end), options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); + +%% Settings +s.num_realiz = options.num_realiz; +s.wavelengthplan = calcWavelengthPlan(options.num_channels, options.channel_spacing, 1310); +link_length = options.fiber_length_km; +s.pmd = 0.1; +s.gamma = 0.0023; + +s.M = 4; +fsym = 112e9; +fdac = 2*fsym; +fadc = 120000000000; +s.random_key = options.rand_key; + +% Laser / s.Modulator +vbias_rel = 0.5; +u_pi = 4.6; +vbias = -vbias_rel*u_pi; +laser_linewidth = 0e6; + +% EQ SETTINGS +vnle_order1 = 50; +vnle_order2 = 3; +vnle_order3 = 3; +vnle_order = [vnle_order1,vnle_order2,vnle_order3]; + +dfe_order = [0 0 0]; +len_tr = 4096*2; +mu_ffe1 = 0.0001; +mu_ffe2 = 0.0008; +mu_ffe3 = 0.001; +mu_dc = 0.005; +mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3]; +mu_dfe = 0.0004; + +% DB Stuff +db_precode = 0; +db_encode = 0; +duob_mode = db_mode.no_db; +apply_pulsef = 0; + +rcalpha = 0.05; +Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); + +N = numel(s.wavelengthplan); +f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9); +margin = 5e12; % 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); +upsample_ceil = ceil(upsample_required); %#ok + +s.f_opt = fdac*kover*upsample_pow; +s.f_opt_nyq = s.f_opt/2; + +signal_cell = {}; +Symbols = {}; +Tx_bits = {}; + +s.rop = -12:0.75:-0.75; + +%% ---------- Intermediate evaluation points (distance dimension) ---------- +% Evaluate BER at these intermediate distances (km), plus always include the final link_length if > 0. +segment_length = 1; % km (must match fiber loop below) + +eval_dist_km = [2 4 6 8 10]; +eval_dist_km = eval_dist_km(eval_dist_km <= link_length); + +% Always include final distance (if > 0) and avoid duplicates +if link_length > 0 + if isempty(eval_dist_km) || eval_dist_km(end) ~= link_length + eval_dist_km = unique([eval_dist_km link_length], 'stable'); + end +end + +% Convert to segment indices; require integer multiples of segment_length +eval_seg = eval_dist_km ./ segment_length; +if any(abs(eval_seg - round(eval_seg)) > 1e-12) + error('eval_dist_km must be integer multiples of segment_length=%g km.', segment_length); +end +eval_seg = round(eval_seg); +nEval = numel(eval_seg); +% ------------------------------------------------------------------------- + +%% Preallocate outputs (add eval distance as 4th dimension) +output_ffe = cell(length(s.wavelengthplan), length(s.rop), s.num_realiz, nEval); +output_vnle = cell(length(s.wavelengthplan), length(s.rop), s.num_realiz, nEval); +output_mlse = cell(length(s.wavelengthplan), length(s.rop), s.num_realiz, nEval); +output_dbt = cell(length(s.wavelengthplan), length(s.rop), s.num_realiz, nEval); + +s.p_launch = 3; +s.p = options.fwm_mitigation_technique; + +switch s.p + case "co" + pol_rot = 100.*ones(1,length(s.wavelengthplan)); + d_local = 0; + case "pair" + pol_rot = repmat([100,100,0,0],1,length(s.wavelengthplan)/4); + d_local = 0; + case "alt" + pol_rot = repmat([100,0,100,0],1,length(s.wavelengthplan)/4); + d_local = 0; + case "seg" + pol_rot = 100.*ones(1,length(s.wavelengthplan)); + d_local = 3; + otherwise + error('Unknown fwm_mitigation_technique: %s', string(s.p)); +end + +for realiz = 1:s.num_realiz + + % Reset per-realization storage (so each realiz writes only its slice) + signal_cell = cell(1,N); + Symbols = cell(1,N); + Tx_bits = cell(1,N); + + %% ---------- TX per channel ---------- + parfor l = 1:N + + [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ... + "fsym",fsym,"M",s.M,"order",18,"useprbs",0, ... + "fs_out",fdac, ... + "applyclipping",0,"clipfactor",1.5, ... + "applypulseform",apply_pulsef,"pulseformer",Pform, ... + "randkey",s.random_key+l+realiz, ... + "db_precode",db_precode,"db_encode",db_encode, ... + "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); + + % 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+realiz).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 ---------- + Opt_sig_wdm_fib = Opt_sig_wdm; + + 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; + Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key+realiz); + + eval_ptr = 1; % points into eval_seg + + for seg = 1: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).process(Opt_sig_wdm_fib); + + % -------- Evaluate at intermediate distance (if scheduled) -------- + if eval_ptr <= nEval && seg == eval_seg(eval_ptr) + + %%%%%% Demux at this distance %%%%%% + Opt_sig_wdm_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); + + % NOTE: For correctness and to keep changes minimal, we keep the exact DSP chain. + for ri = 1:length(s.rop) + + parfor l = 1:N + + %%%%%% ROP %%%%%% + Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ... + "amplification_db",s.rop(ri)).process(Opt_sig_wdm_demux{l}); + + %%%%%% PD Square Law %%%%%% + assert(fdac*kover==Opt_sig_wdm_rx.fs,'Sampling Frequencies do not match! Check previous steps'); + PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20, ... + "nep",1.8e-11,"randomkey",s.random_key+l+realiz).process(Opt_sig_wdm_rx); + + %%%%%% Low-pass RX (PD, El. Connectors and Scope) %%%%%% + rx_bwl = 100e9; + PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(PD_sig); + + %%%%%% Low-pass Scope %%%%%% + Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); + + %%%%%% Scope %%%%%% + 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, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols{l}, "fs_ref", fsym, "debug_plots", 1); %#ok + Rx_sig = Scpe_cell{1}; + Rx_sig = Rx_sig.normalize("mode","rms"); + + % -------------------- FFE -------------------- + 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); + + output_ffe{l,ri,realiz,eval_ptr} = ffe_results; + + % -------------------- VNLE + MLSE -------------------- + pf_ncoeffs = 1; + ffe_order = [50, 5, 5]; + eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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",1); + pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + + useviterbi = 0; + if useviterbi + mlse_ = MLSE_viterbi("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels); + else + mlse_ = MLSE("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels); + end + + [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, s.M, Rx_sig, Symbols{l},Tx_bits{l}, ... + "precode_mode", duob_mode, ... + 'showAnalysis', 0, ... + "postFFE", [], ... + "eth_style_symbol_mapping", 0); + + output_vnle{l,ri,realiz,eval_ptr} = vnle_results; + output_mlse{l,ri,realiz,eval_ptr} = mlse_results; + + % -------------------- DB target -------------------- + useviterbi = 0; + if useviterbi + mlse_db_ = MLSE_viterbi("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels); + else + mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",s.M,'trellis_states',PAMmapper(s.M,0).levels); + end + + ffe_order = [50, 5, 5]; + eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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",1); + + dbt_results = duobinary_target(eq_, mlse_db_, s.M, Rx_sig, Symbols{l},Tx_bits{l}, ... + "precode_mode", duob_mode, ... + 'showAnalysis', 0, ... + "postFFE", []); + + output_dbt{l,ri,realiz,eval_ptr} = dbt_results; + + end + end + + eval_ptr = eval_ptr + 1; + end + % ---------------------------------------------------------------- + end + + %% Save results (per realization) + res = struct(); + res.settings = s; + res.eval_dist_km = eval_dist_km; + res.ffe = output_ffe; + res.vnle = output_vnle; + res.mlse = output_mlse; + res.dbt = output_dbt; + + save(fullfile(output_root, fname), 'res', '-v7.3'); + fprintf('Saved results to: %s\n', fullfile(output_root, fname)); + disp(datetime('now','TimeZone','local','Format','yyyyMs.Mdd_HHmmss')); + +end + +end From 2354d7ac44cc6db19f81fca689e7e721ba9429a2 Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Wed, 31 Dec 2025 12:46:56 +0100 Subject: [PATCH 05/12] new par function --- projects/WDM/WDM_auswertung.m | 282 +++++++++--------- projects/WDM/WDM_model_10km.m | 30 +- projects/WDM/WDM_model_10km_queue.m | 429 ++++++++++++++++++++++++++++ 3 files changed, 590 insertions(+), 151 deletions(-) create mode 100644 projects/WDM/WDM_model_10km_queue.m diff --git a/projects/WDM/WDM_auswertung.m b/projects/WDM/WDM_auswertung.m index 508291d..60651c1 100644 --- a/projects/WDM/WDM_auswertung.m +++ b/projects/WDM/WDM_auswertung.m @@ -1,68 +1,68 @@ +%saved like this: output_ffe{ch,rop,realiz,eval_distance} -try - rop = res.settings.rop; % 12 points - wavelengthplan = res.settings.wavelengthplan; -catch - wavelengthplan = [1295,1305,1315,1325]; - wavelengthplan = calcWavelengthPlan(16,400e9,1310); - rop = -8.25:0.75:0; -end - -N = length(wavelengthplan); +dims = size(res.ffe); +rop = res.settings.rop; +wavelengthplan = res.settings.wavelengthplan; +N_distances = dims(4); %usually 2km and 10km are evaualted +distances = res.eval_dist_km; %[2,10] +N_ch = length(wavelengthplan); %16 channels figure(); hold on; -cols = cbrewer2('set2',N); % one color per wavelength (Ch) +cols = cbrewer2('set2',N_ch); % one color per wavelength (Ch) fec = 2.2e-4; fec = 3.8e-3; -Sffe = cell(1,N); -Svnle = cell(1,N); -Smlse = cell(1,N); -Sdbt = cell(1,N); +Sffe = cell(1,N_ch); +Svnle = cell(1,N_ch); +Smlse = cell(1,N_ch); +Sdbt = cell(1,N_ch); % Choose your quantile band. For your old style, use 0.04/0.99: qLow = 0.0; % lower quantile (e.g., 0.04 for old script) qHigh = 1; % upper quantile (e.g., 0.99 for old script) -cols = linspecer(N); % one color per wavelength (Ch) -cols = cbrewer2('set1',N); +cols = linspecer(N_ch); % one color per wavelength (Ch) +cols = cbrewer2('RdBu',N_ch); -for l = 1:N - % Slice 12x50 cell arrays - ffe_cells = reshape(squeeze(res.ffe(l,:,:)),length(rop),[]); - vnle_cells = reshape(squeeze(res.vnle(l,:,:)),length(rop),[]); - mlse_cells = reshape(squeeze(res.mlse(l,:,:)),length(rop),[]); - dbt_cells = reshape(squeeze(res.dbt(l,:,:)),length(rop),[]); +for eval_ptr = 2 + for ch = 1:N_ch - [Sffe{l}, noX_ffe] = fecCrossings(rop, ffe_cells, fec); + % Slice 12x50 cell arrays + ffe_cells = reshape(squeeze(res.ffe(ch,:,:,eval_ptr)),length(rop),[]); + vnle_cells = reshape(squeeze(res.vnle(ch,:,:,eval_ptr)),length(rop),[]); + mlse_cells = reshape(squeeze(res.mlse(ch,:,:,eval_ptr)),length(rop),[]); + dbt_cells = reshape(squeeze(res.dbt(ch,:,:,eval_ptr)),length(rop),[]); - [Svnle{l}, noX_ffe] = fecCrossings(rop, vnle_cells, fec); + [Sffe{ch}, noX_ffe] = fecCrossings(rop, ffe_cells, fec); - [Smlse{l}, noX_ffe] = fecCrossings(rop, mlse_cells, fec); + [Svnle{ch}, noX_ffe] = fecCrossings(rop, vnle_cells, fec); - [Sdbt{l}, noX_ffe] = fecCrossings(rop, dbt_cells, fec); + [Smlse{ch}, noX_ffe] = fecCrossings(rop, mlse_cells, fec); - % Extract BER matrices using only complete realizations (12/12 ROP filled) - ffe_mat = extractCompleteBER(ffe_cells); % 12 x K_ffe - vnle_mat = extractCompleteBER(vnle_cells); % 12 x K_vnle - mlse_mat = extractCompleteBER(mlse_cells); % 12 x K_mlse - mlse_alpha_mat = extractCompleteAlphas(mlse_cells); % 12 x K_mlse - dbt_mat = extractCompleteBER(dbt_cells); % 12 x K_dbt + [Sdbt{ch}, noX_ffe] = fecCrossings(rop, dbt_cells, fec); - showLegend = 1; % one legend entry per technique + % Extract BER matrices using only complete realizations (12/12 ROP filled) + ffe_mat = extractCompleteBER(ffe_cells); % 12 x K_ffe + vnle_mat = extractCompleteBER(vnle_cells); % 12 x K_vnle + mlse_mat = extractCompleteBER(mlse_cells); % 12 x K_mlse + mlse_alpha_mat = extractCompleteAlphas(mlse_cells); % 12 x K_mlse + dbt_mat = extractCompleteBER(dbt_cells); % 12 x K_dbt - % Plot shaded band + mean line with boundedline - % plotBandMeanBL(rop, ffe_mat, cols(l,:), sprintf('FFE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--s', showLegend); - % scatter(Sffe,fec.*ones(size(Sffe)),20,'v','MarkerFaceColor','black'); + showLegend = 1; % one legend entry per technique - % plotBandMeanBL(rop, vnle_mat, cols(l,:), sprintf('VNLE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--x', showLegend); + % Plot shaded band + mean line with boundedline + % plotBandMeanBL(rop, ffe_mat, cols(ch,:), sprintf('FFE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--s', showLegend); + % scatter(Sffe,fec.*ones(size(Sffe)),20,'v','MarkerFaceColor','black'); - plotBandMeanBL(rop, mlse_mat, cols(l,:), sprintf('VNLE+PF+MLSE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '-o', showLegend); + % plotBandMeanBL(rop, vnle_mat, cols(ch,:), sprintf('VNLE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--x', showLegend); - % plotBandMeanBL(rop, dbt_mat, cols(l,:), sprintf('DBt.+MLSE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--v', showLegend); + % plotBandMeanBL(rop, mlse_mat, cols(ch,:), sprintf('VNLE+PF+MLSE @ %.1d nm',round(wavelengthplan(ch))), qLow, qHigh, '-o', showLegend); - set(gca,'XScale','linear','YScale','log','TickLabelInterpreter','latex','FontSize',11); - yline([3.8e-3, 2.2e-4], 'HandleVisibility','off','LineWidth',1.5); + % plotBandMeanBL(rop, dbt_mat, cols(ch,:), sprintf('DBt.+MLSE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--v', showLegend); + set(gca,'XScale','linear','YScale','log','TickLabelInterpreter','latex','FontSize',11); + yline([3.8e-3, 2.2e-4], 'HandleVisibility','off','LineWidth',1.5); + + end end ylabel('BER'); @@ -76,23 +76,23 @@ legend show; %% VIOLIN S_cell = Sdbt; -S_cell =Smlse; +S_cell = Smlse; S_cell = {Svnle,Smlse,Sdbt}; S_cell = {Svnle}; figure(5); hold on; for i = 1:length(S_cell) % Pad to rectangular matrix: rows = realizations, cols = wavelengths Kmax = max(cellfun(@numel, S_cell{i})); - S_mat = NaN(Kmax, N); - for l = 1:N - k = numel(S_cell{i}{l}); + S_mat = NaN(Kmax, N_ch); + for ch = 1:N_ch + k = numel(S_cell{i}{ch}); if k > 0 - S_mat(1:k, l) = S_cell{i}{l}; + S_mat(1:k, ch) = S_cell{i}{ch}; end end - + % --- Violin plot over wavelengths (columns) --- - + cols=linspecer(3); catLabels = arrayfun(@(nm) sprintf('%d nm', nm), wavelengthplan, 'UniformOutput', false); vs = violinplot(S_mat, catLabels, ... @@ -105,9 +105,9 @@ for i = 1:length(S_cell) 'ShowData', true, ... 'ShowBox', false, ... 'Bandwidth', 0.05); - + ylim([floor(min(S_mat,[],'all')), ceil(max(S_mat,[],'all'))]) - % ylim([-8 0]); + % ylim([-8 0]); ylabel('ROP at FEC crossing'); title(sprintf('RROP to cross BER %.2e', fec)); grid on; box on; @@ -115,51 +115,43 @@ for i = 1:length(S_cell) end - - - - - - - - %% ================= helper ================= function plotBandMeanBL(x, Y, color, techLabel, qLow, qHigh, lineSpec, showLegend) - % Y: (nPoints x nRealizations) - % Remove realizations that are entirely zero (like removeZeros behavior) - badCols = all(Y == 0, 1); - Y(:, badCols) = []; - - Y(Y==0) = 1e-8; - % Stats across realizations - mu = mean(Y, 2, 'omitnan'); % mean line - lo = quantile(Y, qLow, 2); % lower bound - hi = quantile(Y, qHigh, 2); % upper bound +% Y: (nPoints x nRealizations) +% Remove realizations that are entirely zero (like removeZeros behavior) +badCols = all(Y == 0, 1); +Y(:, badCols) = []; - % Convert to asymmetric distances required by boundedline: - % b(:,1) = distance to lower side; b(:,2) = distance to upper side - b = [mu - lo, hi - mu]; +Y(Y==0) = 1e-8; +% Stats across realizations +mu = mean(Y, 2, 'omitnan'); % mean line +lo = quantile(Y, qLow, 2); % lower bound +hi = quantile(Y, qHigh, 2); % upper bound - % Call boundedline with alpha shading - [hl, hp] = boundedline(x(:), mu(:), b, lineSpec, 'alpha', ... - 'transparency', 0.18); - % Color styling - set(hl, 'Color', color, 'LineWidth', 1.4, 'MarkerSize', 4); - set(hp, 'FaceColor', color, 'HandleVisibility','off'); % patch hidden in legend +% Convert to asymmetric distances required by boundedline: +% b(:,1) = distance to lower side; b(:,2) = distance to upper side +b = [mu - lo, hi - mu]; - % Single legend entry per technique (use first wavelength only) - if showLegend - set(hl, 'DisplayName', techLabel); - else - set(hl, 'HandleVisibility','off'); - end +% Call boundedline with alpha shading +[hl, hp] = boundedline(x(:), mu(:), b, lineSpec, 'alpha', ... + 'transparency', 0.18); +% Color styling +set(hl, 'Color', color, 'LineWidth', 1.4, 'MarkerSize', 4); +set(hp, 'FaceColor', color, 'HandleVisibility','off'); % patch hidden in legend - % Optional: outline the bounds if outlinebounds is available - if exist('outlinebounds','file') == 2 - ho = outlinebounds(hl, hp); - set(ho, 'linestyle', ':', 'color', color, 'linewidth', 1, ... - 'HandleVisibility','off'); - end +% Single legend entry per technique (use first wavelength only) +if showLegend + set(hl, 'DisplayName', techLabel); +else + set(hl, 'HandleVisibility','off'); +end + +% Optional: outline the bounds if outlinebounds is available +if exist('outlinebounds','file') == 2 + ho = outlinebounds(hl, hp); + set(ho, 'linestyle', ':', 'color', color, 'linewidth', 1, ... + 'HandleVisibility','off'); +end end function [S, noCrossingMask, Y_keep] = fecCrossings(rop, cells12xR, fec) @@ -173,52 +165,52 @@ function [S, noCrossingMask, Y_keep] = fecCrossings(rop, cells12xR, fec) % noCrossingMask 1xK logical mask: true if no crossing for that realization % Y_keep 12xK numeric BER matrix used for the crossing detection - % 1) keep only complete realization columns - Y = extractCompleteBER(cells12xR); % -> 12 x K - if isempty(Y) - S = []; - noCrossingMask = []; - Y_keep = Y; - return; - end +% 1) keep only complete realization columns +Y = extractCompleteBER(cells12xR); % -> 12 x K +if isempty(Y) + S = []; + noCrossingMask = []; + Y_keep = Y; + return; +end - % 2) optionally drop realizations with mean BER > 0.1 - ok = mean(Y,1,'omitnan') <= 0.1; - Y = Y(:, ok); - if isempty(Y) - S = []; - noCrossingMask = []; - Y_keep = Y; - return; - end +% 2) optionally drop realizations with mean BER > 0.1 +ok = mean(Y,1,'omitnan') <= 0.1; +Y = Y(:, ok); +if isempty(Y) + S = []; + noCrossingMask = []; + Y_keep = Y; + return; +end - % 3) find crossings per realization - nR = size(Y,2); - S = nan(1,nR); - noCrossingMask = true(1,nR); +% 3) find crossings per realization +nR = size(Y,2); +S = nan(1,nR); +noCrossingMask = true(1,nR); - rop = rop(:); % ensure column - for j = 1:nR - y = Y(:,j); +rop = rop(:); % ensure column +for j = 1:nR + y = Y(:,j); - % sign change from >fec to <=fec (first time it drops below FEC) - above = (y > fec); - idx = find(above(1:end-1) & ~above(2:end), 1, 'first'); + % sign change from >fec to <=fec (first time it drops below FEC) + above = (y > fec); + idx = find(above(1:end-1) & ~above(2:end), 1, 'first'); - if ~isempty(idx) - % linear interpolation between (x1,y1) and (x2,y2) - x1 = rop(idx); y1 = y(idx); - x2 = rop(idx+1); y2 = y(idx+1); + if ~isempty(idx) + % linear interpolation between (x1,y1) and (x2,y2) + x1 = rop(idx); y1 = y(idx); + x2 = rop(idx+1); y2 = y(idx+1); - if isfinite(y1) && isfinite(y2) && y2 ~= y1 - t = (fec - y1) / (y2 - y1); - S(j) = x1 + t*(x2 - x1); - noCrossingMask(j) = false; - end + if isfinite(y1) && isfinite(y2) && y2 ~= y1 + t = (fec - y1) / (y2 - y1); + S(j) = x1 + t*(x2 - x1); + noCrossingMask(j) = false; end end +end - Y_keep = Y; +Y_keep = Y; end @@ -226,28 +218,28 @@ end function Y = extractCompleteBER(cellSlice) % cellSlice: 12xR cell array; each cell should be a struct with .metrics.BER % Keep only those realization columns where ALL 12 ROP entries are valid. - if isempty(cellSlice), Y = []; return; end - nR = size(cellSlice,2); - keep = false(1,nR); - for r = 1:nR - col = cellSlice(:,r); - keep(r) = all(cellfun(@(c) ~isempty(c) , col)); - end - if ~any(keep), Y = []; return; end - Y = cellfun(@(c) c.metrics.BER, cellSlice(:,keep), 'UniformOutput', true); +if isempty(cellSlice), Y = []; return; end +nR = size(cellSlice,2); +keep = false(1,nR); +for r = 1:nR + col = cellSlice(:,r); + keep(r) = all(cellfun(@(c) ~isempty(c) , col)); +end +if ~any(keep), Y = []; return; end +Y = cellfun(@(c) c.metrics.BER, cellSlice(:,keep), 'UniformOutput', true); end function Y = extractCompleteAlphas(cellSlice) % cellSlice: 12xR cell array; each cell should be a struct with .metrics.BER % Keep only those realization columns where ALL 12 ROP entries are valid. - if isempty(cellSlice), Y = []; return; end - nR = size(cellSlice,2); - keep = false(1,nR); - for r = 1:nR - col = cellSlice(:,r); - keep(r) = all(cellfun(@(c) ~isempty(c) , col)); - end - if ~any(keep), Y = []; return; end - Y = cellfun(@(c) c.metrics.Alpha, cellSlice(:,keep), 'UniformOutput', true); +if isempty(cellSlice), Y = []; return; end +nR = size(cellSlice,2); +keep = false(1,nR); +for r = 1:nR + col = cellSlice(:,r); + keep(r) = all(cellfun(@(c) ~isempty(c) , col)); +end +if ~any(keep), Y = []; return; end +Y = cellfun(@(c) c.metrics.Alpha, cellSlice(:,keep), 'UniformOutput', true); end diff --git a/projects/WDM/WDM_model_10km.m b/projects/WDM/WDM_model_10km.m index 85cf2d7..d8977eb 100644 --- a/projects/WDM/WDM_model_10km.m +++ b/projects/WDM/WDM_model_10km.m @@ -33,6 +33,7 @@ jsl = fullfile(tmpbase, sprintf('matlab_jobstorage_%s_%s', ... getenv('USER'), getenv('SLURM_JOB_ID'))); if ~exist(jsl,'dir'); mkdir(jsl); end +if 0 % Configure the local cluster explicitly and start the pool c = parcluster('local'); c.NumWorkers = cpus; @@ -44,6 +45,7 @@ if isempty(p) || p.NumWorkers ~= cpus p = parpool(c, cpus); end fprintf('parpool up with %d workers; JobStorage=%s\n', p.NumWorkers, c.JobStorageLocation); +end %% result filename (timestamp + optional job id) t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); @@ -106,7 +108,7 @@ Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",1 N = numel(s.wavelengthplan); f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9); -margin = 5e12; % some THz left and right +margin = 20e12; % some THz left and right f_span = (max(f_plan)+margin)-(min(f_plan)-margin); f_nyq = f_span/2; @@ -128,7 +130,7 @@ s.rop = -12:0.75:-0.75; % Evaluate BER at these intermediate distances (km), plus always include the final link_length if > 0. segment_length = 1; % km (must match fiber loop below) -eval_dist_km = [2 4 6 8 10]; +eval_dist_km = [2 10]; eval_dist_km = eval_dist_km(eval_dist_km <= link_length); % Always include final distance (if > 0) and avoid duplicates @@ -149,6 +151,7 @@ nEval = numel(eval_seg); %% Preallocate outputs (add eval distance as 4th dimension) output_ffe = cell(length(s.wavelengthplan), length(s.rop), s.num_realiz, nEval); +output_dfe = cell(length(s.wavelengthplan), length(s.rop), s.num_realiz, nEval); output_vnle = cell(length(s.wavelengthplan), length(s.rop), s.num_realiz, nEval); output_mlse = cell(length(s.wavelengthplan), length(s.rop), s.num_realiz, nEval); output_dbt = cell(length(s.wavelengthplan), length(s.rop), s.num_realiz, nEval); @@ -181,7 +184,7 @@ for realiz = 1:s.num_realiz Tx_bits = cell(1,N); %% ---------- TX per channel ---------- - parfor l = 1:N + for l = 1:N [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ... "fsym",fsym,"M",s.M,"order",18,"useprbs",0, ... @@ -235,6 +238,8 @@ for realiz = 1:s.num_realiz for seg = 1:nSegments + fprintf('Realiz %d/%d: Segment %d/%d \n', realiz, s.num_realiz, 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, ... @@ -247,10 +252,9 @@ for realiz = 1:s.num_realiz Opt_sig_wdm_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); - % NOTE: For correctness and to keep changes minimal, we keep the exact DSP chain. + for ri = 1:length(s.rop) - - parfor l = 1:N + for l = 1:N %%%%%% ROP %%%%%% Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ... @@ -293,6 +297,19 @@ for realiz = 1:s.num_realiz output_ffe{l,ri,realiz,eval_ptr} = ffe_results; + % -------------------- DFE -------------------- + dfe_order = [50, 0, 0]; + eq_dfe = EQ("Ne",dfe_order,"Nb",[2,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); + + dfe_results = ffe(eq_dfe,s.M,Rx_sig,Symbols{l},Tx_bits{l}, ... + "precode_mode",duob_mode, ... + 'showAnalysis',0, ... + "postFFE",[], ... + "eth_style_symbol_mapping",0); + + output_dfe{l,ri,realiz,eval_ptr} = dfe_results; + % -------------------- VNLE + MLSE -------------------- pf_ncoeffs = 1; ffe_order = [50, 5, 5]; @@ -348,6 +365,7 @@ for realiz = 1:s.num_realiz res.settings = s; res.eval_dist_km = eval_dist_km; res.ffe = output_ffe; + res.dfe = output_dfe; res.vnle = output_vnle; res.mlse = output_mlse; res.dbt = output_dbt; diff --git a/projects/WDM/WDM_model_10km_queue.m b/projects/WDM/WDM_model_10km_queue.m new file mode 100644 index 0000000..7e1832d --- /dev/null +++ b/projects/WDM/WDM_model_10km_queue.m @@ -0,0 +1,429 @@ +function WDM_model_10km_queue(options) +%WDM_model_10km_queue +% Job-queue / pipeline version of your script: +% - Main thread runs fiber propagation segment-by-segment +% - At each eval distance: demux once and enqueue per-(l,ri) DSP jobs via parfeval +% - While continuing propagation: periodically harvest completed jobs (non-blocking) +% - End of realization: drain remaining jobs, then save +% +% Save this as a NEW file, e.g. "WDM_model_10km_queue.m" + +%%% Run parameters +arguments + options.num_channels = 16; + options.channel_spacing = 400e9; + options.fiber_length_km = 2; + options.rand_key = 1; + options.num_realiz = 1; + options.fwm_mitigation_technique = "co"; +end + +%% Add the imdd_simulation framework to the path +if ispc + addpath(genpath('C:\Users\Silas\Documents\MATLAB\imdd_simulation')); +else + addpath(genpath('/work_beegfs/sutef391/imdd_simulation')); +end + +warning('off','MATLAB:datetime:AmbiguousTimeZone'); + +%% How many workers? +cpus = str2double(getenv('SLURM_CPUS_PER_TASK')); +if ~isfinite(cpus) || cpus < 1, cpus = max(1, feature('numcores')); end + +% Use a per-job, node-local JobStorageLocation to avoid stale locks on $HOME +tmpbase = getenv('TMPDIR'); +if isempty(tmpbase), tmpbase = tempdir; end +jsl = fullfile(tmpbase, sprintf('matlab_jobstorage_%s_%s', ... + getenv('USER'), getenv('SLURM_JOB_ID'))); +if ~exist(jsl,'dir'); mkdir(jsl); end + +% Start pool once +c = parcluster('local'); +c.NumWorkers = cpus; +c.JobStorageLocation = jsl; + +p = gcp('nocreate'); +if isempty(p) || p.NumWorkers ~= cpus + if ~isempty(p), delete(p); end + p = parpool(c, cpus); +end +fprintf('parpool up with %d workers; JobStorage=%s\n', p.NumWorkers, c.JobStorageLocation); + +%% result filename (timestamp + optional job id) +t = datetime('now','TimeZone','local','Format','yyyyMMdd_HHmmss'); +jobid = getenv('SLURM_JOB_ID'); if isempty(jobid), jobid = 'nojid'; end +host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end + +foldname = sprintf('%dkm_%dch_%dghz_%s', options.fiber_length_km(end), options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); +if ispc + output_root = fullfile('C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\',foldname,'\'); +else + output_root = fullfile('/work_beegfs/sutef391/results_WDM',foldname,'\'); +end +if ~exist(output_root,'dir'), mkdir(output_root); end + +fname = sprintf('WDM_%s_%s_%s_%dkm_%dch_%dghz_%s.mat', char(t), host, jobid, options.fiber_length_km(end), options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); + +%% Settings +s.num_realiz = options.num_realiz; +s.wavelengthplan = calcWavelengthPlan(options.num_channels, options.channel_spacing, 1310); +link_length = options.fiber_length_km; +s.pmd = 0.1; +s.gamma = 0.0023; + +s.M = 4; +fsym = 112e9; +fdac = 2*fsym; +fadc = 120000000000; +s.random_key = options.rand_key; + +% Laser / s.Modulator +vbias_rel = 0.5; +u_pi = 4.6; +vbias = -vbias_rel*u_pi; +laser_linewidth = 0e6; + +% EQ SETTINGS +dfe_order = [0 0 0]; +len_tr = 4096*2; +mu_ffe1 = 0.0001; +mu_ffe2 = 0.0008; +mu_ffe3 = 0.001; +mu_dc = 0.005; +mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3]; +mu_dfe = 0.0004; + +% DB Stuff +db_precode = 0; +db_encode = 0; +duob_mode = db_mode.no_db; +apply_pulsef = 0; + +rcalpha = 0.05; +Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); + +N = numel(s.wavelengthplan); +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 = -12:0.75:-0.75; + +%% ---------- Intermediate evaluation points (distance dimension) ---------- +segment_length = 1; % km (must match fiber loop below) + +eval_dist_km = [2 10]; +eval_dist_km = eval_dist_km(eval_dist_km <= link_length); + +if link_length > 0 + if isempty(eval_dist_km) || eval_dist_km(end) ~= link_length + eval_dist_km = unique([eval_dist_km link_length], 'stable'); + end +end + +eval_seg = eval_dist_km ./ segment_length; +if any(abs(eval_seg - round(eval_seg)) > 1e-12) + error('eval_dist_km must be integer multiples of segment_length=%g km.', segment_length); +end +eval_seg = round(eval_seg); +nEval = numel(eval_seg); +% ------------------------------------------------------------------------- + +%% Preallocate outputs (eval distance as 4th dimension) +output_ffe = cell(N, length(s.rop), s.num_realiz, nEval); +output_dfe = cell(N, length(s.rop), s.num_realiz, nEval); +output_vnle = cell(N, length(s.rop), s.num_realiz, nEval); +output_mlse = cell(N, length(s.rop), s.num_realiz, nEval); +output_dbt = cell(N, length(s.rop), s.num_realiz, nEval); + +s.p_launch = 3; +s.p = options.fwm_mitigation_technique; + +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 + +for realiz = 1:s.num_realiz + + % Per-realization TX storage + signal_cell = cell(1,N); + Symbols = cell(1,N); + Tx_bits = cell(1,N); + + % -------- Job queue containers -------- + F = parallel.FevalFuture.empty(0,1); + meta = struct('l',{},'ri',{},'realiz',{},'eval_ptr',{}); + % ------------------------------------- + + %% ---------- TX per channel ---------- + for l = 1:N + + [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ... + "fsym",fsym,"M",s.M,"order",18,"useprbs",0, ... + "fs_out",fdac, ... + "applyclipping",0,"clipfactor",1.5, ... + "applypulseform",apply_pulsef,"pulseformer",Pform, ... + "randkey",s.random_key+l+realiz, ... + "db_precode",db_precode,"db_encode",db_encode, ... + "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+realiz).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 ---------- + Opt_sig_wdm_fib = Opt_sig_wdm; + + 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; + Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key+realiz); + + eval_ptr = 1; + + for seg = 1:nSegments + + fprintf('Realiz %d/%d: Segment %d/%d \n', realiz, s.num_realiz, seg, nSegments); + + if 1 + 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).process(Opt_sig_wdm_fib); + end + + % -------- Evaluate at intermediate distance (enqueue jobs) -------- + if eval_ptr <= nEval && seg == eval_seg(eval_ptr) + + Opt_sig_wdm_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); + + for ri = 1:length(s.rop) + for l = 1:N + % Enqueue one job per (l,ri) + F(end+1,1) = parfeval(p, @rx_job, 5, ... + Opt_sig_wdm_demux{l}, ... % channel signal at this eval point + s.rop(ri), ... % rop [dB] + Symbols{l}, Tx_bits{l}, ...% reference + bits + s, l, realiz, ... % settings + indices + fdac, kover, fadc, fsym, ... + len_tr, mu_dc, mu_ffe, mu_dfe, dfe_order, duob_mode); + + meta(end+1) = struct('l',l,'ri',ri,'realiz',realiz,'eval_ptr',eval_ptr); + end + end + + eval_ptr = eval_ptr + 1; + end + % ---------------------------------------------------------------- + + % Non-blocking harvest: store finished jobs (if any) while we continue propagation + [F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt] = ... + collect_done(F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt, false); + + end + + % Drain remaining jobs for this realization (blocking until queue is empty) + while ~isempty(F) + [F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt] = ... + collect_done(F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt, true); + end + + %% Save results (per realization) + + res = struct(); + res.settings = s; + res.eval_dist_km = eval_dist_km; + res.ffe = output_ffe; + res.dfe = output_dfe; + res.vnle = output_vnle; + res.mlse = output_mlse; + res.dbt = output_dbt; + + save(fullfile(output_root, fname), 'res', '-v7.3'); + fprintf('Saved results to: %s\n', fullfile(output_root, fname)); + disp(datetime('now','TimeZone','local','Format','yyyyMs.Mdd_HHmmss')); + +end + +end % end main + + +%% ========================= Local functions ========================= + +function [ffe_results, dfe_results, vnle_results, mlse_results, dbt_results] = rx_job( ... + Opt_sig_chan, rop_db, Symbols_l, Tx_bits_l, s, l, realiz, ... + fdac, kover, fadc, fsym, len_tr, mu_dc, mu_ffe, mu_dfe, dfe_order, duob_mode) + +% NOTE: keep plotting OFF in workers +debug_plots = 0; + +%%%%%% ROP %%%%%% +Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ... + "amplification_db", rop_db).process(Opt_sig_chan); + +%%%%%% PD Square Law %%%%%% +assert(fdac*kover==Opt_sig_wdm_rx.fs,'Sampling Frequencies do not match! Check previous steps'); +PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20, ... + "nep",1.8e-11,"randomkey",s.random_key + l + realiz).process(Opt_sig_wdm_rx); + +%%%%%% Low-pass RX (PD, El. Connectors and Scope) %%%%%% +rx_bwl = 100e9; +PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover, ... + "filterType",filtertypes.butterworth,"active",true).process(PD_sig); + +%%%%%% Low-pass Scope %%%%%% +Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc, ... + "filterType",filtertypes.butterworth,"active",true); + +%%%%%% Scope %%%%%% +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", debug_plots); +Rx_sig = Scpe_cell{1}.normalize("mode","rms"); + +% -------------------- FFE -------------------- +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); + +% -------------------- DFE -------------------- +dfe_order2 = [50, 0, 0]; +eq_dfe = EQ("Ne",dfe_order2,"Nb",[2,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); + +dfe_results = ffe(eq_dfe,s.M,Rx_sig,Symbols_l,Tx_bits_l, ... + "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ... + "eth_style_symbol_mapping",0); + +% -------------------- VNLE + MLSE -------------------- +pf_ncoeffs = 1; +ffe_order3 = [50, 5, 5]; +eq_v = EQ("Ne",ffe_order3,"Nb",dfe_order, ... + "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",1); +pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + +mlse_ = MLSE("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels); + +[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, s.M, Rx_sig, Symbols_l, Tx_bits_l, ... + "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [], "eth_style_symbol_mapping", 0); + +% -------------------- DB target -------------------- +mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",s.M,'trellis_states',PAMmapper(s.M,0).levels); + +dbt_results = duobinary_target(eq_v, mlse_db_, s.M, Rx_sig, Symbols_l, Tx_bits_l, ... + "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", []); +end + + +function [F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt] = ... + collect_done(F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt, block) + +if nargin < 9, block = false; end +if isempty(F), return; end + +% Non-blocking mode: try to fetch as many finished as possible with timeout=0 +% Blocking mode: wait for at least one result (timeout=Inf), then keep draining with timeout=0 +if block + timeout_first = Inf; +else + timeout_first = 0; +end + +% Fetch first (maybe blocking), then drain the rest non-blocking +first = true; +while ~isempty(F) + if first + timeout = timeout_first; + first = false; + else + timeout = 0; + end + + try + [k, ffe_r, dfe_r, vnle_r, mlse_r, dbt_r] = fetchNext(F, timeout); + catch + % fetchNext throws if timeout=0 and nothing finished in some releases; treat as "nothing ready" + break; + end + + if isempty(k) + break; % nothing ready + end + + m = meta(k); + + output_ffe{m.l, m.ri, m.realiz, m.eval_ptr} = ffe_r; + output_dfe{m.l, m.ri, m.realiz, m.eval_ptr} = dfe_r; + output_vnle{m.l, m.ri, m.realiz, m.eval_ptr} = vnle_r; + output_mlse{m.l, m.ri, m.realiz, m.eval_ptr} = mlse_r; + output_dbt{m.l, m.ri, m.realiz, m.eval_ptr} = dbt_r; + + % Remove completed future & meta entry (important: indices change afterwards) + F(k) = []; + meta(k) = []; +end +end From 0186eccced0fdae1704071587eb7d18eeb48207c Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Mon, 5 Jan 2026 14:54:05 +0100 Subject: [PATCH 06/12] new chirped modulator with alpha setting dp_sync is softened --- Classes/00_signals/Signal.m | 2 +- Classes/02_optical/EML.m | 16 +- Classes/DataBaseHandler/Metricstruct.m | 4 +- projects/IMDD_base_system/imdd_it.m | 4 +- projects/IMDD_base_system/imdd_model.m | 286 ++++++-------- projects/IMDD_base_system/simulation_bwl_2.m | 15 +- projects/WDM/WDM_auswertung.m | 392 +++++++++++-------- projects/WDM/WDM_model_10km.m | 12 +- projects/WDM/WDM_model_10km_queue.m | 380 ++++++++++++++++-- 9 files changed, 719 insertions(+), 392 deletions(-) diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index b8113c8..f6dd186 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -765,7 +765,7 @@ classdef Signal pkpos = sort(pkpos); - if mean(w) > 10 || mean(p) > 10 + if mean(w) > 10 || mean(p) > 15 return else sequenceFound = 1; diff --git a/Classes/02_optical/EML.m b/Classes/02_optical/EML.m index 32a902c..4a97686 100644 --- a/Classes/02_optical/EML.m +++ b/Classes/02_optical/EML.m @@ -15,6 +15,7 @@ classdef EML u_pi randomkey randomstream + alpha %on instance creation field @@ -38,6 +39,7 @@ classdef EML options.lambda; options.power; options.linewidth = 0; + options.alpha = 0; options.ampl_imbal = 0; options.pha_imbal = 0; options.bias; @@ -101,9 +103,21 @@ classdef EML end %modulate the laserfield with the electrical signal - opt_out = obj.externalmodulation(laserfield,elec_in); + laserfield = obj.externalmodulation(laserfield,elec_in); + % add chirp + opt_out = obj.chirp(laserfield); + end + + function chirped_field = chirp(obj,laserfield) + + % Chirp + p = abs(laserfield.^2); + derv_p = [0; diff(p)]; + delta_phi = derv_p./(4*pi*p).*obj.alpha; + delta_phi = cumsum(delta_phi); + chirped_field = laserfield.*exp(1i*2*pi*delta_phi); end diff --git a/Classes/DataBaseHandler/Metricstruct.m b/Classes/DataBaseHandler/Metricstruct.m index ef00675..fe9a818 100644 --- a/Classes/DataBaseHandler/Metricstruct.m +++ b/Classes/DataBaseHandler/Metricstruct.m @@ -8,9 +8,9 @@ classdef Metricstruct date_of_processing (1,1) datetime = datetime('now') numBits (1,1) double {mustBeInteger, mustBeNonnegative} = 0 - BER (1,1) double {mustBeNumeric, mustBeNonnegative, mustBeLessThanOrEqual(BER,1)} = 0 + BER (1,1) double {mustBeNumeric, mustBeNonnegative} = 0 numBitErr (1,1) double {mustBeInteger, mustBeNonnegative} = 0 - BER_precoded (1,1) double {mustBeNumeric, mustBeNonnegative, mustBeLessThanOrEqual(BER_precoded,1)} = 0 + BER_precoded (1,1) double {mustBeNumeric, mustBeNonnegative} = 0 numBitErr_precoded (1,1) double {mustBeInteger, mustBeNonnegative} = 0 SNR (1,1) double {mustBeNumeric} = NaN diff --git a/projects/IMDD_base_system/imdd_it.m b/projects/IMDD_base_system/imdd_it.m index bd2461d..748708f 100644 --- a/projects/IMDD_base_system/imdd_it.m +++ b/projects/IMDD_base_system/imdd_it.m @@ -10,12 +10,12 @@ if 1 uloops.laser_wavelength = [1310]; uloops.M = [4]; uloops.link_length = [1]; % 1,2,3,5,6,8,10 - uloops.interference_attenuation = [0,3,6,9,12,15,18,21,24,27,30,45]; + % uloops.interference_attenuation = [0,3,6,9,12,15,18,21,24,27,30,45]; wh = DataStorage(uloops); wh.addStorage("ber"); % wh = submit_simulations(wh,"parallel",0,"simulation_mode",0); - wh = submit_handle(@imdd_model,wh,"parallel",0); + wh = submit_handle(@imdd_model,wh,"serial",0); end diff --git a/projects/IMDD_base_system/imdd_model.m b/projects/IMDD_base_system/imdd_model.m index d400350..ac6bd0c 100644 --- a/projects/IMDD_base_system/imdd_model.m +++ b/projects/IMDD_base_system/imdd_model.m @@ -92,34 +92,34 @@ end if doub_mode ~= db_mode.db_encoded if precomp == 0 && db_precode == 1 - doub_mode = db_mode.db_precoded; - + doub_mode = db_mode.db_precoded; + db_precode = 1; % preceded data (in my measurement set, this corresponds to low precomp too!) - discard_precode = 0; % + discard_precode = 0; % emulate_precode = 0; legendentry = 'low precomp; precoded'; disp('low precomp; precoded') elseif precomp == 1 && db_precode == 1 - doub_mode = db_mode.db_emulate; - + doub_mode = db_mode.db_emulate; + db_precode = 0; % preceded data (in my measurement set, this corresponds to low precomp too!) - discard_precode = 0; % + discard_precode = 0; % emulate_precode = 1; legendentry = 'high precomp; precoded'; disp('high precomp; precoded') elseif precomp == 0 && db_precode == 0 doub_mode = db_mode.db_discard; - + db_precode = 1; % preceded data (in my measurement set, this corresponds to low precomp too!) - discard_precode = 1; % + discard_precode = 1; % emulate_precode = 0; legendentry = 'no precomp; not precoded'; disp('no precomp; not precoded') elseif precomp == 1 && db_precode == 0 - doub_mode = db_mode.no_db; - + doub_mode = db_mode.no_db; + db_precode = 0; % preceded data (in my measurement set, this corresponds to low precomp too!) - discard_precode = 0; % + discard_precode = 0; % emulate_precode = 0; legendentry = 'high precomp; not precoded'; disp('high precomp; not precoded') @@ -137,135 +137,73 @@ end f_nyquist = fsym/2; -%%% run the simulation or measurement or ... -if simulation_mode - Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); - rcalpha = 1; - Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha); - db_precode = 0; - db_encode = 0; - apply_pulsef = 1; - [Digi_sig,Symbols,Tx_bits] = PAMsource(... - "fsym",fsym,"M",M,"order",18,"useprbs",0,... - "fs_out",fdac,... - "applyclipping",0,"clipfactor",1.5,... - "applypulseform",apply_pulsef,"pulseformer",Pform,... - "randkey",random_key,... - "db_precode",db_precode,"db_encode",db_encode,... - "mrds_code",0,"mrds_blocklength",512).process(); +Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); +rcalpha = 1; +Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha); - Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1); +db_precode = 0; +db_encode = 0; +apply_pulsef = 1; +[Digi_sig,Symbols,Tx_bits] = PAMsource(... + "fsym",fsym,"M",M,"order",18,"useprbs",0,... + "fs_out",fdac,... + "applyclipping",0,"clipfactor",1.5,... + "applypulseform",apply_pulsef,"pulseformer",Pform,... + "randkey",random_key,... + "db_precode",db_precode,"db_encode",db_encode,... + "mrds_code",0,"mrds_blocklength",512).process(); + +Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1); - %%%%% AWG - % El_sig = M8199A("kover",kover).process(Digi_sig); - El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); - % El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0); - % El_sig = El_sig.setPower(0,"dBm"); +%%%%% AWG +% El_sig = M8199A("kover",kover).process(Digi_sig); +El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); +% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0); +% El_sig = El_sig.setPower(0,"dBm"); - %%%%% Low-pass el. components %%%%%% - tx_bwl = tx_bw_nyquist.*f_nyquist; - % tx_bwl = 80e9; - El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig); - % El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',1); +%%%%% Low-pass el. components %%%%%% +tx_bwl = tx_bw_nyquist.*f_nyquist; +% tx_bwl = 80e9; +El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig); +% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',1); - %%%%% Electrical Driver Amplifier %%%%%% - El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig); - El_sig = El_sig.normalize("mode","oneone"); +%%%%% Electrical Driver Amplifier %%%%%% +El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig); +El_sig = El_sig.normalize("mode","oneone"); - %%%%% MODULATE E/O CONVERSION %%%%%% - [Opt_sig] = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",laser_wavelength,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",random_key+1).process(El_sig); +%%%%% MODULATE E/O CONVERSION %%%%%% +[Opt_sig] = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",laser_wavelength,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",random_key+1).process(El_sig); - Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig); +Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig); - %%%%%% ROP %%%%%% - Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig); +%%%%%% ROP %%%%%% +Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig); - %%%%%% PD Square Law %%%%%% - Rx_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11).process(Rx_sig); +%%%%%% PD Square Law %%%%%% +Rx_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11).process(Rx_sig); - %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% - rx_bwl = rx_bw_nyquist.*f_nyquist; - % rx_bwl = 80e9; - Rx_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(Rx_sig); +%%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% +rx_bwl = rx_bw_nyquist.*f_nyquist; +% rx_bwl = 80e9; +Rx_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(Rx_sig); - % %%%%%% Low-pass Scope %%%%%% - Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); +% %%%%%% Low-pass Scope %%%%%% +Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); - % Rx_sig.spectrum("displayname",'Analog Rx Spectrum','fignum',100,'normalizeTo0dB',1); +% Rx_sig.spectrum("displayname",'Analog Rx Spectrum','fignum',100,'normalizeTo0dB',1); - %%%%%% Scope %%%%%% - 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',1,'H_lpf',Lp_scpe).process(Rx_sig); +%%%%%% Scope %%%%%% +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',1,'H_lpf',Lp_scpe).process(Rx_sig); - Scpe_cell{1} = Scpe_sig; +Scpe_cell{1} = Scpe_sig; -else - profile on - basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; - database = DBHandler("pathToDB",[basePath,'silas_labor.db']); - profile off - basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; - useGui = 0; - % db = DBHandler("pathToDB",[basePath,'silas_labor.db']); - filterParams = database.tables; - % filterParams.Runs.run_id = 2958; % no db - % filterParams.Runs.run_id = 2937; % no db - filterParams.Configurations = struct( ... - 'bitrate', bitrate, ... - 'db_mode', db_precode+db_encode, ... - 'fiber_length', link_length, ... - 'interference_attenuation', [], ... - 'interference_path_length', [], ... - 'is_mpi', is_mpi, ... - 'pam_level', M, ... - 'precomp_amp', [], ... - 'rop_attenuation', 0, ... - 'symbolrate', [], ... - 'v_awg', [], ... - 'v_bias', [], ... - 'wavelength', laser_wavelength ... - ); - - selectedFields = {'Runs.run_id','Runs.tx_bits_path', 'Runs.tx_symbols_path', 'Runs.rx_sync_path','Runs.rx_raw_path',... - 'Configurations.db_mode','Configurations.pam_level','Configurations.bitrate','Configurations.symbolrate','Configurations.fiber_length','Configurations.wavelength','Configurations.precomp_amp','Measurements.power_rop','Configurations.v_bias',... - 'Configurations.interference_attenuation'}; - - [dataTable,sql_query] = database.queryDB(filterParams, selectedFields); - [~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices - dataTable = dataTable(uniqueIdx,:); % Extract unique configurations for each run_id - fprintf('Found %d entries for requested Configuration. IDs are: %s \n \n',size(dataTable,1),jsonencode(dataTable.run_id(1:min(size(dataTable,1),100)))); - - Tx_bits = load([basePath, char(dataTable.tx_bits_path(end))]); - Tx_bits = Tx_bits.Bits; - - Symbols = load([basePath, char(dataTable.tx_symbols_path(end))]); - Symbols = Symbols.Symbols; - - Scpe_load = load([basePath, char(dataTable.rx_sync_path(end))]); - Scpe_cell = Scpe_load.S; - - - % Raw_signal = load([basePath, char(dataTable.rx_raw_path(1))]); - % Raw_signal.Scpe_sig_raw.plot("displayname",'0db atten','fignum',10101) - % Raw_signal = Raw_signal.Scpe_sig_raw; - % - % Raw_signal = Filter('filtdegree',4,"f_cutoff",Symbols.fs.*0.55,"fs",Raw_signal.fs,"filterType",filtertypes.gaussian,"active",true).process(Raw_signal); - % - % Scpe_cell{1}.eye(fsym,M,"displayname",'eye','fignum',227); - % - % Raw_signal.spectrum("normalizeTo0dB",0,"fignum",11,"fft_length",2^12); - % Raw_signal.move_it_spectrum("fignum",334); - % Raw_signal.move_it_spectrum("fignum",334); - - fsym = Symbols.fs; - -end if db_precode Symbols_precoded = Symbols; @@ -291,74 +229,74 @@ for occ = 1%:proc_occ Scpe_sig = Filter('filtdegree',4,"f_cutoff",Symbols.fs.*0.5,"fs",Scpe_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scpe_sig); Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); - % + % % Pform = Pulseformer("fsym",Scpe_sig.fs,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha,"matched",0); - % + % % Scpe_sig_matched = Pform.process(Scpe_sig); - % + % % Scpe_sig.spectrum("normalizeTo0dB",0,"fignum",336,"displayname","scope "); % Scpe_sig_matched.spectrum("normalizeTo0dB",0,"fignum",336,"displayname","matched"); %%% EQUALIZING - - - % eq_mlse = FFE_DCremoval("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr,"mu_dd",mu_ffe(1),"mu_tr",0,"order",ffe_order(1),"sps",2,"decide",0,"dc_buffer_len",1,"mu_dc",0.05); - % eq_mlse = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr,"mu_dd",mu_ffe(1),"mu_tr",0,"order",ffe_order(1),"sps",2,"decide",0); - % eq_mlse = FFE_DCremoval("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr,"mu_dd",mu_ffe(1),"mu_tr",0,"order",ffe_order(1),"sps",2,"decide",0,"dc_buffer_len",512,"mu_dc",0.05); - - mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3]; - vnle_order=[vnle_order1,vnle_order2,vnle_order3]; - - % %%%%% VNLE + DFE %%%% - if 0 - - eq_vnle_dfe = EQ("Ne",vnle_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_2 = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",2001,"sps",1,"decide",0); - - [result] = vnle(eq_vnle_dfe,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",doub_mode,"showAnalysis",1,"postFFE",[]); - vnle_dfe_package{occ} = result; - - end - %%%%% VNLE + PF + MLSE %%%% - if 1 - - % len_tr = length(Symbols)-1000; - eq_vnle_ = EQ("Ne",vnle_order,"Nb",dfe_order,"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",1); - % eq_vnle_ = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0,"order",vnle_order,"sps",2,"decide",0); - pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); - mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); - - [result] = vnle_postfilter_mlse(eq_vnle_,pf_,mlse_,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",doub_mode,'showAnalysis',1); - vnle_pf_package{occ} = result; - - end - %%%%% Duobinary Targeting %%%% - if 1 + % eq_mlse = FFE_DCremoval("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr,"mu_dd",mu_ffe(1),"mu_tr",0,"order",ffe_order(1),"sps",2,"decide",0,"dc_buffer_len",1,"mu_dc",0.05); + % eq_mlse = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr,"mu_dd",mu_ffe(1),"mu_tr",0,"order",ffe_order(1),"sps",2,"decide",0); + % eq_mlse = FFE_DCremoval("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr,"mu_dd",mu_ffe(1),"mu_tr",0,"order",ffe_order(1),"sps",2,"decide",0,"dc_buffer_len",512,"mu_dc",0.05); - mlse_db = MLSE_viterbi("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); - eq_db = EQ("Ne",vnle_order,"Nb",dfe_order,"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",1); + mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3]; + vnle_order=[vnle_order1,vnle_order2,vnle_order3]; - [result] = duobinary_target(eq_db, mlse_db, M, Scpe_sig, Symbols, Tx_bits, "precode_mode", doub_mode,'showAnalysis',0); - dbtgt_package{occ} = result; - + % %%%%% VNLE + DFE %%%% + if 0 - end - - %%%%%% %db signaling => db encoded %%%%% - if 0 - mlse_db_enc = MLSE_viterbi("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); - eq_db_enc = EQ("Ne",vnle_order,"Nb",dfe_order,"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",1); - [result] = duobinary_signaling(eq_db_enc, mlse_db_enc,M, Scpe_sig ,Symbols, Tx_bits); - dbenc_package{occ} = result; - end + eq_vnle_dfe = EQ("Ne",vnle_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_2 = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",2001,"sps",1,"decide",0); + + [result] = vnle(eq_vnle_dfe,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",doub_mode,"showAnalysis",1,"postFFE",[]); + vnle_dfe_package{occ} = result; + + end + %%%%% VNLE + PF + MLSE %%%% + if 1 + + % len_tr = length(Symbols)-1000; + eq_vnle_ = EQ("Ne",vnle_order,"Nb",dfe_order,"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",1); + % eq_vnle_ = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0,"order",vnle_order,"sps",2,"decide",0); + pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); + + [result] = vnle_postfilter_mlse(eq_vnle_,pf_,mlse_,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",doub_mode,'showAnalysis',1); + vnle_pf_package{occ} = result; + + end + + + %%%%% Duobinary Targeting %%%% + if 1 + + mlse_db = MLSE_viterbi("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); + eq_db = EQ("Ne",vnle_order,"Nb",dfe_order,"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",1); + + [result] = duobinary_target(eq_db, mlse_db, M, Scpe_sig, Symbols, Tx_bits, "precode_mode", doub_mode,'showAnalysis',0); + dbtgt_package{occ} = result; + + + end + + %%%%%% %db signaling => db encoded %%%%% + if 0 + mlse_db_enc = MLSE_viterbi("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); + eq_db_enc = EQ("Ne",vnle_order,"Nb",dfe_order,"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",1); + [result] = duobinary_signaling(eq_db_enc, mlse_db_enc,M, Scpe_sig ,Symbols, Tx_bits); + dbenc_package{occ} = result; + end % autoArrangeFigures; disp('- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ') fprintf('\n') - + end diff --git a/projects/IMDD_base_system/simulation_bwl_2.m b/projects/IMDD_base_system/simulation_bwl_2.m index dec8036..e336ad2 100644 --- a/projects/IMDD_base_system/simulation_bwl_2.m +++ b/projects/IMDD_base_system/simulation_bwl_2.m @@ -59,7 +59,7 @@ Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",1 db_precode = 0; db_encode = 0; -duob_mode = db_mode.db_precoded; +duob_mode = db_mode.no_db; apply_pulsef = 1; [Digi_sig,Symbols,Tx_bits] = PAMsource(... "fsym",fsym,"M",M,"order",18,"useprbs",0,... @@ -72,13 +72,12 @@ apply_pulsef = 1; Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1); -%% proof of concept -Symbols_db = Duobinary().encode(Symbols); -mim_decoded = Duobinary().decode(Symbols_db,"M",M); -rx_bits_mim_decoded = PAMmapper(M,0,"eth_style",0).demap(mim_decoded); -rx_bits_mim_decoded_.signal = circshift(rx_bits_mim_decoded.signal,0); -[~,~,ber_mim_decode,~] = calc_ber(rx_bits_mim_decoded_.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); -fprintf('BER mim: %.2e \n',ber_mim_decode); +% %% proof of concept memoryless inverse mapping (direct db targeting and decoding) +% Symbols_db = Duobinary().encode(Symbols); +% mim_decoded = Duobinary().decode(Symbols_db,"M",M); +% rx_bits_mim_decoded = PAMmapper(M,0,"eth_style",0).demap(mim_decoded); +% [~,~,ber_mim_decode,~] = calc_ber(rx_bits_mim_decoded_.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); +% fprintf('BER mim: %.2e \n',ber_mim_decode); %% diff --git a/projects/WDM/WDM_auswertung.m b/projects/WDM/WDM_auswertung.m index 60651c1..a6a666c 100644 --- a/projects/WDM/WDM_auswertung.m +++ b/projects/WDM/WDM_auswertung.m @@ -1,207 +1,290 @@ +%% Robust eval/plot script for res.* (handles missing/partial dims) +% res.ffe stored like: output_ffe{ch, rop, realiz, eval_distance} +% BUT: some dimensions may be singleton or not fully filled (e.g. only 0 km, only 1 realization) -%saved like this: output_ffe{ch,rop,realiz,eval_distance} +% -------------------- Basic metadata -------------------- +rop = res.settings.rop(:); +wavelengthplan = res.settings.wavelengthplan(:); +distances = res.eval_dist_km(:); % can be [0], [2 10], etc. +N_ch = numel(wavelengthplan); +N_rop = numel(rop); + +% -------------------- Determine dims robustly -------------------- +% Ensure we have 4D addressing even if MATLAB collapses trailing singletons dims = size(res.ffe); -rop = res.settings.rop; -wavelengthplan = res.settings.wavelengthplan; -N_distances = dims(4); %usually 2km and 10km are evaualted -distances = res.eval_dist_km; %[2,10] -N_ch = length(wavelengthplan); %16 channels -figure(); hold on; -cols = cbrewer2('set2',N_ch); % one color per wavelength (Ch) +if numel(dims) < 4 + dims(end+1:4) = 1; +end +N_realiz = dims(3); +N_distances = dims(4); -fec = 2.2e-4; -fec = 3.8e-3; -Sffe = cell(1,N_ch); -Svnle = cell(1,N_ch); -Smlse = cell(1,N_ch); -Sdbt = cell(1,N_ch); - -% Choose your quantile band. For your old style, use 0.04/0.99: -qLow = 0.0; % lower quantile (e.g., 0.04 for old script) -qHigh = 1; % upper quantile (e.g., 0.99 for old script) -cols = linspecer(N_ch); % one color per wavelength (Ch) -cols = cbrewer2('RdBu',N_ch); - -for eval_ptr = 2 - for ch = 1:N_ch - - % Slice 12x50 cell arrays - ffe_cells = reshape(squeeze(res.ffe(ch,:,:,eval_ptr)),length(rop),[]); - vnle_cells = reshape(squeeze(res.vnle(ch,:,:,eval_ptr)),length(rop),[]); - mlse_cells = reshape(squeeze(res.mlse(ch,:,:,eval_ptr)),length(rop),[]); - dbt_cells = reshape(squeeze(res.dbt(ch,:,:,eval_ptr)),length(rop),[]); - - [Sffe{ch}, noX_ffe] = fecCrossings(rop, ffe_cells, fec); - - [Svnle{ch}, noX_ffe] = fecCrossings(rop, vnle_cells, fec); - - [Smlse{ch}, noX_ffe] = fecCrossings(rop, mlse_cells, fec); - - [Sdbt{ch}, noX_ffe] = fecCrossings(rop, dbt_cells, fec); - - % Extract BER matrices using only complete realizations (12/12 ROP filled) - ffe_mat = extractCompleteBER(ffe_cells); % 12 x K_ffe - vnle_mat = extractCompleteBER(vnle_cells); % 12 x K_vnle - mlse_mat = extractCompleteBER(mlse_cells); % 12 x K_mlse - mlse_alpha_mat = extractCompleteAlphas(mlse_cells); % 12 x K_mlse - dbt_mat = extractCompleteBER(dbt_cells); % 12 x K_dbt - - showLegend = 1; % one legend entry per technique - - % Plot shaded band + mean line with boundedline - % plotBandMeanBL(rop, ffe_mat, cols(ch,:), sprintf('FFE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--s', showLegend); - % scatter(Sffe,fec.*ones(size(Sffe)),20,'v','MarkerFaceColor','black'); - - % plotBandMeanBL(rop, vnle_mat, cols(ch,:), sprintf('VNLE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--x', showLegend); - - % plotBandMeanBL(rop, mlse_mat, cols(ch,:), sprintf('VNLE+PF+MLSE @ %.1d nm',round(wavelengthplan(ch))), qLow, qHigh, '-o', showLegend); - - % plotBandMeanBL(rop, dbt_mat, cols(ch,:), sprintf('DBt.+MLSE @ %.1d nm',round(wavelengthplan(l))), qLow, qHigh, '--v', showLegend); - - set(gca,'XScale','linear','YScale','log','TickLabelInterpreter','latex','FontSize',11); - yline([3.8e-3, 2.2e-4], 'HandleVisibility','off','LineWidth',1.5); - - end +% If res.eval_dist_km length differs from dims(4), trust dims(4) +if numel(distances) ~= N_distances + % best-effort fallback + distances = (1:N_distances).'; end -ylabel('BER'); -xlabel('ROP'); -title('BER vs. ROP'); -xlim([min(rop) max(rop)]); -ylim([1e-5 0.3]); -grid on; -legend show; +% -------------------- Plot settings -------------------- +fec = 3.8e-3; % choose one; you can switch to 2.2e-4 if needed -%% VIOLIN +qLow = 0.00; +qHigh = 1.00; + +% Color per wavelength/channel +try + cols = ccbrewer2('Set1', N_ch); +catch + cols = linspecer(N_ch); +end + +% Containers for FEC crossings per channel (per eval distance) +Sffe = cell(N_distances, N_ch); +Sdfe = cell(N_distances, N_ch); +Svnle = cell(N_distances, N_ch); +Smlse = cell(N_distances, N_ch); +Sdbt = cell(N_distances, N_ch); + +% -------------------- Choose which eval distances to plot -------------------- +% If you want all: eval_list = 1:N_distances; +% If you want a specific distance (e.g., last): eval_list = N_distances; +eval_list = 1;%1:N_distances; + +% -------------------- BER vs ROP figures (one figure per eval distance) -------------------- +for eval_ptr = eval_list + + figure('Name', sprintf('BER vs ROP @ %s', distLabel(distances, eval_ptr))); + hold on; -S_cell = Sdbt; -S_cell = Smlse; -S_cell = {Svnle,Smlse,Sdbt}; -S_cell = {Svnle}; -figure(5); hold on; -for i = 1:length(S_cell) - % Pad to rectangular matrix: rows = realizations, cols = wavelengths - Kmax = max(cellfun(@numel, S_cell{i})); - S_mat = NaN(Kmax, N_ch); for ch = 1:N_ch - k = numel(S_cell{i}{ch}); - if k > 0 - S_mat(1:k, ch) = S_cell{i}{ch}; + + % ---- Extract cell slices robustly: [N_rop x N_realiz] ---- + ffe_cells = sliceCells4D(res.ffe , ch, eval_ptr, N_rop); + dfe_cells = sliceCells4D(res.dfe , ch, eval_ptr, N_rop); + vnle_cells = sliceCells4D(res.vnle, ch, eval_ptr, N_rop); + mlse_cells = sliceCells4D(res.mlse, ch, eval_ptr, N_rop); + dbt_cells = sliceCells4D(res.dbt , ch, eval_ptr, N_rop); + + % ---- FEC crossings (works with incomplete columns) ---- + [Sffe{eval_ptr,ch}, ~] = fecCrossings(rop, ffe_cells, fec); + [Sdfe{eval_ptr,ch}, ~] = fecCrossings(rop, dfe_cells, fec); + [Svnle{eval_ptr,ch}, ~] = fecCrossings(rop, vnle_cells, fec); + [Smlse{eval_ptr,ch}, ~] = fecCrossings(rop, mlse_cells, fec); + [Sdbt{eval_ptr,ch}, ~] = fecCrossings(rop, dbt_cells, fec); + + % ---- Extract BER matrices using only complete realizations ---- + ffe_mat = extractCompleteBER(ffe_cells); % N_rop x K + dfe_mat = extractCompleteBER(dfe_cells); + vnle_mat = extractCompleteBER(vnle_cells); + mlse_mat = extractCompleteBER(mlse_cells); + dbt_mat = extractCompleteBER(dbt_cells); + + showLegend = (ch == 1); % one legend entry per technique + + % Plot bands + mean (only if non-empty) + if ~isempty(ffe_mat) + plotBandMeanBL(rop, ffe_mat, cols(ch,:), sprintf('FFE @ %dnm', round(wavelengthplan(ch))), qLow, qHigh, '--s', showLegend); end + % if ~isempty(dfe_mat) + % plotBandMeanBL(rop, dfe_mat, cols(ch,:), sprintf('DFE @ %dnm', round(wavelengthplan(ch))), qLow, qHigh, '-o', showLegend); + % end + % if ~isempty(vnle_mat) + % plotBandMeanBL(rop, vnle_mat, cols(ch,:), sprintf('VNLE @ %dnm', round(wavelengthplan(ch))), qLow, qHigh, '--x', showLegend); + % end + % if ~isempty(mlse_mat) + % plotBandMeanBL(rop, mlse_mat, cols(ch,:), sprintf('VNLE+PF+MLSE @ %dnm', round(wavelengthplan(ch))), qLow, qHigh, '-o', showLegend); + % end + % if ~isempty(dbt_mat) + % plotBandMeanBL(rop, dbt_mat, cols(ch,:), sprintf('DBt+MLSE @ %dnm', round(wavelengthplan(ch))), qLow, qHigh, '--v', showLegend); + % end + end - % --- Violin plot over wavelengths (columns) --- - - cols=linspecer(3); - catLabels = arrayfun(@(nm) sprintf('%d nm', nm), wavelengthplan, 'UniformOutput', false); - vs = violinplot(S_mat, catLabels, ... - 'ViolinColor', cols(i,:), ... - 'ViolinAlpha', 0.10, ... - 'MarkerSize', 20, ... - 'ShowMedian', true, ... - 'EdgeColor', cols(i,:), ... - 'ShowWhiskers', false, ... - 'ShowData', true, ... - 'ShowBox', false, ... - 'Bandwidth', 0.05); - - ylim([floor(min(S_mat,[],'all')), ceil(max(S_mat,[],'all'))]) - % ylim([-8 0]); - ylabel('ROP at FEC crossing'); - title(sprintf('RROP to cross BER %.2e', fec)); - grid on; box on; - + set(gca,'XScale','linear','YScale','log','TickLabelInterpreter','latex','FontSize',11); + yline([3.8e-3, 2.2e-4], 'HandleVisibility','off','LineWidth',1.5); + ylabel('BER'); + xlabel('ROP [dB]'); + title(sprintf('BER vs. ROP @ %s', distLabel(distances, eval_ptr))); + xlim([min(rop) max(rop)]); + ylim([1e-5 0.3]); + grid on; + legend show; end -%% ================= helper ================= +%% -------------------- VIOLIN of ROP at FEC crossing -------------------- +% Build technique groups; each group is {N_distances x N_ch} cell entries +% You can pick a distance to show (e.g., eval_ptr=1 for 0 km, or last) +eval_ptr_violin = min(N_distances, max(1, N_distances)); % default: last available +% eval_ptr_violin = 1; + +% Choose which technique(s) to show +techNames = {'VNLE'}; +S_groups = {Svnle(eval_ptr_violin,:)}; + +% Only plot if violinplot exists +if exist('violinplot','file') == 2 + figure('Name', sprintf('FEC crossing violin @ %s', distLabel(distances, eval_ptr_violin))); hold on; + + colsTech = linspecer(numel(S_groups)); + + for i = 1:numel(S_groups) + S_cell = S_groups{i}; % 1 x N_ch cell, each cell is 1xK crossings + + % Pad to rectangular: rows = realizations (max K), cols = wavelengths (N_ch) + Kmax = max(cellfun(@numel, S_cell)); + if isempty(Kmax) || Kmax == 0 + continue; + end + S_mat = zeros(Kmax, N_ch); + for ch = 1:N_ch + k = numel(S_cell{ch}); + if k > 0 + S_mat(1:k, ch) = S_cell{ch}(:); + end + end + + catLabels = arrayfun(@(nm) sprintf('%d nm', nm), round(wavelengthplan), 'UniformOutput', false); + violinplot(S_mat, catLabels, ... + 'ViolinColor', colsTech(i,:), ... + 'ViolinAlpha', 0.10, ... + 'MarkerSize', 20, ... + 'ShowMedian', true, ... + 'EdgeColor', colsTech(i,:), ... + 'ShowWhiskers', false, ... + 'ShowData', true, ... + 'ShowBox', false, ... + 'Bandwidth', 0.05); + + % Add a legend proxy + plot(nan, nan, 'o', 'Color', colsTech(i,:), 'DisplayName', techNames{i}); + end + + ylim([-10,-6]) + ylabel('ROP at FEC crossing'); + title(sprintf('ROP to cross BER %.2e @ %s', fec, distLabel(distances, eval_ptr_violin))); + grid on; box on; + legend show; +else + fprintf('violinplot.m not found on path -> skipping violin plot.\n'); +end + + +%% ================= helpers ================= + +function cells2D = sliceCells4D(C4, ch, eval_ptr, N_rop) +% Returns a [N_rop x N_realiz] cell array (with N_realiz inferred) +% Works even if trailing dimensions are singleton. +% +% If C4 is smaller than expected, missing entries are returned as []. + +dims = size(C4); +if numel(dims) < 4, dims(end+1:4) = 1; end +N_realiz = dims(3); + +cells2D = cell(N_rop, N_realiz); + +% bounds (in case stored arrays are smaller) +chMax = dims(1); +ropMax = dims(2); +rMax = dims(3); +eMax = dims(4); + +if ch > chMax || eval_ptr > eMax + return; % all empty +end + +ropUse = min(N_rop, ropMax); +rUse = min(N_realiz, rMax); + +% Extract and place into a consistent sized cell matrix +tmp = squeeze(C4(ch, 1:ropUse, 1:rUse, eval_ptr)); +% squeeze may return vector/empty if rUse==1 etc. Normalize: +tmp = reshape(tmp, ropUse, rUse); + +cells2D(1:ropUse, 1:rUse) = tmp; +end + +function lbl = distLabel(distances, eval_ptr) +if eval_ptr <= numel(distances) + d = distances(eval_ptr); + if isfinite(d) + lbl = sprintf('%.0f km', d); + return; + end +end +lbl = sprintf('eval\\_%d', eval_ptr); +end + function plotBandMeanBL(x, Y, color, techLabel, qLow, qHigh, lineSpec, showLegend) % Y: (nPoints x nRealizations) -% Remove realizations that are entirely zero (like removeZeros behavior) +if isempty(Y), return; end + +% Remove realizations that are entirely zero badCols = all(Y == 0, 1); Y(:, badCols) = []; +if isempty(Y), return; end + +% Avoid log(0) Y(Y==0) = 1e-8; -% Stats across realizations -mu = mean(Y, 2, 'omitnan'); % mean line -lo = quantile(Y, qLow, 2); % lower bound -hi = quantile(Y, qHigh, 2); % upper bound -% Convert to asymmetric distances required by boundedline: -% b(:,1) = distance to lower side; b(:,2) = distance to upper side -b = [mu - lo, hi - mu]; +mu = mean(Y, 2, 'omitnan'); +lo = quantile(Y, qLow, 2); +hi = quantile(Y, qHigh, 2); +b = [mu - lo, hi - mu]; -% Call boundedline with alpha shading -[hl, hp] = boundedline(x(:), mu(:), b, lineSpec, 'alpha', ... - 'transparency', 0.18); -% Color styling -set(hl, 'Color', color, 'LineWidth', 1.4, 'MarkerSize', 4); -set(hp, 'FaceColor', color, 'HandleVisibility','off'); % patch hidden in legend +% boundedline optional; fallback to simple plot if missing +if 0%exist('boundedline','file') == 2 + [hl, hp] = boundedline(x(:), mu(:), b, lineSpec, 'alpha', 'transparency', 0.18); + set(hl, 'Color', color, 'LineWidth', 1.4, 'MarkerSize', 4); + set(hp, 'FaceColor', color, 'HandleVisibility','off'); +else + hl = plot(x(:), mu(:), lineSpec, 'LineWidth', 1.4, 'MarkerSize', 4); + set(hl, 'Color', color); +end -% Single legend entry per technique (use first wavelength only) if showLegend set(hl, 'DisplayName', techLabel); else set(hl, 'HandleVisibility','off'); end -% Optional: outline the bounds if outlinebounds is available -if exist('outlinebounds','file') == 2 +if 0%exist('outlinebounds','file') == 2 && exist('boundedline','file') == 2 ho = outlinebounds(hl, hp); - set(ho, 'linestyle', ':', 'color', color, 'linewidth', 1, ... - 'HandleVisibility','off'); + set(ho, 'linestyle', ':', 'color', color, 'linewidth', 1, 'HandleVisibility','off'); end end -function [S, noCrossingMask, Y_keep] = fecCrossings(rop, cells12xR, fec) -% cells12xR: 12xR cell array (one wavelength + scheme slice) -% each cell must be a struct with .metrics.BER -% rop: 12x1 numeric vector of ROP points -% fec: scalar FEC threshold (e.g., 3.8e-3) -% -% Outputs: -% S 1xK vector of crossing ROP per kept realization (NaN if none) -% noCrossingMask 1xK logical mask: true if no crossing for that realization -% Y_keep 12xK numeric BER matrix used for the crossing detection - -% 1) keep only complete realization columns -Y = extractCompleteBER(cells12xR); % -> 12 x K +function [S, noCrossingMask] = fecCrossings(rop, cellsNxR, fec) +% cellsNxR: N_rop x R cell array; each cell is struct with .metrics.BER +Y = extractCompleteBER(cellsNxR); % -> N_rop x K if isempty(Y) S = []; noCrossingMask = []; - Y_keep = Y; return; end -% 2) optionally drop realizations with mean BER > 0.1 ok = mean(Y,1,'omitnan') <= 0.1; Y = Y(:, ok); if isempty(Y) S = []; noCrossingMask = []; - Y_keep = Y; return; end -% 3) find crossings per realization nR = size(Y,2); S = nan(1,nR); noCrossingMask = true(1,nR); -rop = rop(:); % ensure column +rop = rop(:); for j = 1:nR y = Y(:,j); - - % sign change from >fec to <=fec (first time it drops below FEC) above = (y > fec); idx = find(above(1:end-1) & ~above(2:end), 1, 'first'); - if ~isempty(idx) - % linear interpolation between (x1,y1) and (x2,y2) x1 = rop(idx); y1 = y(idx); x2 = rop(idx+1); y2 = y(idx+1); - if isfinite(y1) && isfinite(y2) && y2 ~= y1 t = (fec - y1) / (y2 - y1); S(j) = x1 + t*(x2 - x1); @@ -209,37 +292,34 @@ for j = 1:nR end end end - -Y_keep = Y; end - - function Y = extractCompleteBER(cellSlice) -% cellSlice: 12xR cell array; each cell should be a struct with .metrics.BER -% Keep only those realization columns where ALL 12 ROP entries are valid. +% Keep only those realization columns where ALL N_rop entries are non-empty if isempty(cellSlice), Y = []; return; end nR = size(cellSlice,2); keep = false(1,nR); + for r = 1:nR col = cellSlice(:,r); - keep(r) = all(cellfun(@(c) ~isempty(c) , col)); + keep(r) = all(cellfun(@(c) ~isempty(c), col)); end + if ~any(keep), Y = []; return; end + Y = cellfun(@(c) c.metrics.BER, cellSlice(:,keep), 'UniformOutput', true); end function Y = extractCompleteAlphas(cellSlice) -% cellSlice: 12xR cell array; each cell should be a struct with .metrics.BER -% Keep only those realization columns where ALL 12 ROP entries are valid. if isempty(cellSlice), Y = []; return; end nR = size(cellSlice,2); keep = false(1,nR); + for r = 1:nR col = cellSlice(:,r); - keep(r) = all(cellfun(@(c) ~isempty(c) , col)); + keep(r) = all(cellfun(@(c) ~isempty(c), col)); end + if ~any(keep), Y = []; return; end Y = cellfun(@(c) c.metrics.Alpha, cellSlice(:,keep), 'UniformOutput', true); end - diff --git a/projects/WDM/WDM_model_10km.m b/projects/WDM/WDM_model_10km.m index d8977eb..779365c 100644 --- a/projects/WDM/WDM_model_10km.m +++ b/projects/WDM/WDM_model_10km.m @@ -67,8 +67,8 @@ fname = sprintf('WDM_%s_%s_%s_%dkm_%dch_%dghz_%s.mat', char(t), host, jobid, opt s.num_realiz = options.num_realiz; s.wavelengthplan = calcWavelengthPlan(options.num_channels, options.channel_spacing, 1310); link_length = options.fiber_length_km; -s.pmd = 0.1; -s.gamma = 0.0023; +s.pmd = 0;%0.1; +s.gamma = 0;%0.0023; s.M = 4; fsym = 112e9; @@ -108,7 +108,7 @@ Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",1 N = numel(s.wavelengthplan); f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9); -margin = 20e12; % some THz left and right +margin = 2e12; % some THz left and right f_span = (max(f_plan)+margin)-(min(f_plan)-margin); f_nyq = f_span/2; @@ -124,13 +124,13 @@ signal_cell = {}; Symbols = {}; Tx_bits = {}; -s.rop = -12:0.75:-0.75; +s.rop = -12;%:0.75:-0; %% ---------- Intermediate evaluation points (distance dimension) ---------- % Evaluate BER at these intermediate distances (km), plus always include the final link_length if > 0. segment_length = 1; % km (must match fiber loop below) -eval_dist_km = [2 10]; +eval_dist_km = [10]; eval_dist_km = eval_dist_km(eval_dist_km <= link_length); % Always include final distance (if > 0) and avoid duplicates @@ -209,7 +209,7 @@ for realiz = 1:s.num_realiz % 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+realiz).process(El_sig); + "linewidth",laser_linewidth,"randomkey",s.random_key+l+realiz,"alpha",0.8).process(El_sig); signal_cell{l} = Polarization_Controller("mode","rot_power","desired_power",pol_rot(l)).process(Eml_out); end diff --git a/projects/WDM/WDM_model_10km_queue.m b/projects/WDM/WDM_model_10km_queue.m index 7e1832d..24b7e4e 100644 --- a/projects/WDM/WDM_model_10km_queue.m +++ b/projects/WDM/WDM_model_10km_queue.m @@ -1,18 +1,14 @@ function WDM_model_10km_queue(options) -%WDM_model_10km_queue -% Job-queue / pipeline version of your script: -% - Main thread runs fiber propagation segment-by-segment -% - At each eval distance: demux once and enqueue per-(l,ri) DSP jobs via parfeval -% - While continuing propagation: periodically harvest completed jobs (non-blocking) -% - End of realization: drain remaining jobs, then save -% -% Save this as a NEW file, e.g. "WDM_model_10km_queue.m" +%WDM_model_10km_queue_memopt +% Queue/pipeline version with: +% (a) reduced variable lifetime / fewer unnecessary copies (clear large temporaries early) +% (b) worker-side memory monitoring (RSS logging to per-worker files) %%% Run parameters arguments options.num_channels = 16; options.channel_spacing = 400e9; - options.fiber_length_km = 2; + options.fiber_length_km = 10; options.rand_key = 1; options.num_realiz = 1; options.fwm_mitigation_technique = "co"; @@ -65,12 +61,16 @@ if ~exist(output_root,'dir'), mkdir(output_root); end fname = sprintf('WDM_%s_%s_%s_%dkm_%dch_%dghz_%s.mat', char(t), host, jobid, options.fiber_length_km(end), options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); +% Worker memory logs directory +memlog_dir = fullfile(output_root, 'memlogs'); +if ~exist(memlog_dir,'dir'), mkdir(memlog_dir); end + %% Settings s.num_realiz = options.num_realiz; s.wavelengthplan = calcWavelengthPlan(options.num_channels, options.channel_spacing, 1310); link_length = options.fiber_length_km; -s.pmd = 0.1; -s.gamma = 0.0023; +s.pmd = 0;%0.1; +s.gamma = 0;%0.0023; s.M = 4; fsym = 112e9; @@ -105,7 +105,7 @@ Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",1 N = numel(s.wavelengthplan); f_plan = physconst('lightspeed')./(s.wavelengthplan.*1e-9); -margin = 25e12; % some THz left and right +margin = 2e12; % some THz left and right f_span = (max(f_plan)+margin)-(min(f_plan)-margin); f_nyq = f_span/2; @@ -117,11 +117,11 @@ s.f_opt = fdac*kover*upsample_pow; s.f_opt_nyq = s.f_opt/2; s.rop = -12:0.75:-0.75; - +% s.rop = -12:1:0; %% ---------- Intermediate evaluation points (distance dimension) ---------- segment_length = 1; % km (must match fiber loop below) -eval_dist_km = [2 10]; +eval_dist_km = [0]; eval_dist_km = eval_dist_km(eval_dist_km <= link_length); if link_length > 0 @@ -167,7 +167,7 @@ end for realiz = 1:s.num_realiz - % Per-realization TX storage + % Per-realization TX storage (needed later for DSP: Symbols/Tx_bits) signal_cell = cell(1,N); Symbols = cell(1,N); Tx_bits = cell(1,N); @@ -181,42 +181,62 @@ for realiz = 1:s.num_realiz for l = 1:N [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource( ... - "fsym",fsym,"M",s.M,"order",18,"useprbs",0, ... + "fsym",fsym,"M",s.M,"order",17,"useprbs",0, ... "fs_out",fdac, ... "applyclipping",0,"clipfactor",1.5, ... "applypulseform",apply_pulsef,"pulseformer",Pform, ... "randkey",s.random_key+l+realiz, ... "db_precode",db_precode,"db_encode",db_encode, ... "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode ... - ).process(); + ).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+realiz).process(El_sig); + "linewidth",laser_linewidth,"randomkey",s.random_key+l+realiz,"alpha",0.8).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); + % IMPORTANT: signal_cell is not needed anymore after multiplex (Symbols/Tx_bits remain) + clear 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 ---------- Opt_sig_wdm_fib = Opt_sig_wdm; + % Opt_sig_wdm no longer needed as separate handle/object + clear Opt_sig_wdm + 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); @@ -225,20 +245,70 @@ for realiz = 1:s.num_realiz zdw = 1310; randomize_D = true; - Dvec = getDispersionVector(nSegments, d_local, zdw, randomize_D, s.random_key+realiz); + + % 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+realiz); + else + Dvec = []; + end eval_ptr = 1; + % =================== Queue throttle (prevents OOM) =================== + % Limit how many futures can be outstanding (running + queued + finished-not-yet-fetched). + % Start conservatively; you can raise to 4*NumWorkers if stable. + maxInFlight = max(2, p.NumWorkers); + % ===================================================================== + + % -------- Evaluate at 0 km (BTB) if requested -------- + if eval_ptr <= nEval && eval_seg(eval_ptr) == 0 + disp('0 km before demux.'); + Opt_sig_wdm_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); + + cnt = 0; + total = length(s.rop)*N; + fprintf('total of %d jobs to enqueue at 0 km\n', total); + + for ri = 1:length(s.rop) + for l = 1:N + cnt = cnt + 1; + + % ---- Throttle before enqueueing more futures ---- + [F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt] = ... + throttle_inflight(F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt, maxInFlight); + + % Enqueue one job per (l,ri) + F(end+1,1) = parfeval(p, @rx_job, 5, ... + Opt_sig_wdm_demux{l}, ... + s.rop(ri), ... + Symbols{l}, Tx_bits{l}, ... + s, l, ri, realiz, eval_ptr, ... + fdac, kover, fadc, fsym, ... + len_tr, mu_dc, mu_ffe, mu_dfe, dfe_order, duob_mode, ... + memlog_dir); + + fprintf('Enqueued job %d/%d for realiz %d/%d, l=%d/%d, ri=%d/%d at 0 km (inflight=%d/%d)\n', ... + cnt, total, realiz, s.num_realiz, l, N, ri, length(s.rop), numel(F), maxInFlight); + + meta(end+1) = struct('l',l,'ri',ri,'realiz',realiz,'eval_ptr',eval_ptr); + end + end + + clear Opt_sig_wdm_demux + eval_ptr = eval_ptr + 1; + end + % ----------------------------------------------------- + for seg = 1:nSegments fprintf('Realiz %d/%d: Segment %d/%d \n', realiz, s.num_realiz, seg, nSegments); - if 1 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).process(Opt_sig_wdm_fib); - end % -------- Evaluate at intermediate distance (enqueue jobs) -------- if eval_ptr <= nEval && seg == eval_seg(eval_ptr) @@ -246,39 +316,52 @@ for realiz = 1:s.num_realiz Opt_sig_wdm_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); + cnt = 0; + total = length(s.rop)*N; + fprintf('total of %d jobs to enqueue at 0 km\n', total); + for ri = 1:length(s.rop) for l = 1:N - % Enqueue one job per (l,ri) + + cnt = cnt + 1; + + % ---- Throttle before enqueueing more futures ---- + [F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt] = ... + throttle_inflight(F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt, maxInFlight); + F(end+1,1) = parfeval(p, @rx_job, 5, ... - Opt_sig_wdm_demux{l}, ... % channel signal at this eval point - s.rop(ri), ... % rop [dB] - Symbols{l}, Tx_bits{l}, ...% reference + bits - s, l, realiz, ... % settings + indices + Opt_sig_wdm_demux{l}, ... + s.rop(ri), ... + Symbols{l}, Tx_bits{l}, ... + s, l, ri, realiz, eval_ptr, ... fdac, kover, fadc, fsym, ... - len_tr, mu_dc, mu_ffe, mu_dfe, dfe_order, duob_mode); + len_tr, mu_dc, mu_ffe, mu_dfe, dfe_order, duob_mode, ... + memlog_dir); + + fprintf('Enqueued job %d/%d for realiz %d/%d, l=%d/%d, ri=%d/%d at 0 km (inflight=%d/%d)\n', ... + cnt, total, realiz, s.num_realiz, l, N, ri, length(s.rop), numel(F), maxInFlight); meta(end+1) = struct('l',l,'ri',ri,'realiz',realiz,'eval_ptr',eval_ptr); end end + clear Opt_sig_wdm_demux eval_ptr = eval_ptr + 1; end % ---------------------------------------------------------------- - % Non-blocking harvest: store finished jobs (if any) while we continue propagation + % Non-blocking harvest (your existing line) [F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt] = ... collect_done(F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt, false); end - % Drain remaining jobs for this realization (blocking until queue is empty) while ~isempty(F) [F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt] = ... collect_done(F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt, true); end %% Save results (per realization) - res = struct(); res.settings = s; res.eval_dist_km = eval_dist_km; @@ -292,6 +375,9 @@ for realiz = 1:s.num_realiz fprintf('Saved results to: %s\n', fullfile(output_root, fname)); disp(datetime('now','TimeZone','local','Format','yyyyMs.Mdd_HHmmss')); + % Per-realization large arrays that are no longer needed + clear Opt_sig_wdm_fib Symbols Tx_bits Dvec + end end % end main @@ -300,25 +386,39 @@ end % end main %% ========================= Local functions ========================= function [ffe_results, dfe_results, vnle_results, mlse_results, dbt_results] = rx_job( ... - Opt_sig_chan, rop_db, Symbols_l, Tx_bits_l, s, l, realiz, ... - fdac, kover, fadc, fsym, len_tr, mu_dc, mu_ffe, mu_dfe, dfe_order, duob_mode) + Opt_sig_chan, rop_db, Symbols_l, Tx_bits_l, s, l, ri, realiz, eval_ptr, ... + fdac, kover, fadc, fsym, len_tr, mu_dc, mu_ffe, mu_dfe, dfe_order, duob_mode, memlog_dir) % NOTE: keep plotting OFF in workers debug_plots = 0; +% Create per-worker logfile (avoid contention) +logfile = make_worker_logfile(memlog_dir); + +log_mem(logfile, 'job_start', l, ri, realiz, eval_ptr, rop_db, Opt_sig_chan); + %%%%%% ROP %%%%%% Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power", ... "amplification_db", rop_db).process(Opt_sig_chan); +% Opt_sig_chan no longer needed after amp +clear Opt_sig_chan +log_mem(logfile, 'after_amp', l, ri, realiz, eval_ptr, rop_db, Opt_sig_wdm_rx); + %%%%%% PD Square Law %%%%%% assert(fdac*kover==Opt_sig_wdm_rx.fs,'Sampling Frequencies do not match! Check previous steps'); PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20, ... "nep",1.8e-11,"randomkey",s.random_key + l + realiz).process(Opt_sig_wdm_rx); +% Opt_sig_wdm_rx not needed after PD +clear Opt_sig_wdm_rx +log_mem(logfile, 'after_pd', l, ri, realiz, eval_ptr, rop_db, PD_sig); + %%%%%% Low-pass RX (PD, El. Connectors and Scope) %%%%%% rx_bwl = 100e9; PD_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover, ... "filterType",filtertypes.butterworth,"active",true).process(PD_sig); +log_mem(logfile, 'after_rx_lpf', l, ri, realiz, eval_ptr, rop_db, PD_sig); %%%%%% Low-pass Scope %%%%%% Lp_scpe = Filter('filtdegree',4,"f_cutoff",80e9,"fs",fadc, ... @@ -330,11 +430,28 @@ Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc, ... "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); +% PD_sig no longer needed after scope +clear PD_sig Lp_scpe +log_mem(logfile, 'after_scope', l, ri, realiz, eval_ptr, rop_db, Scpe_sig); + Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym); +% Scpe_sig no longer needed after resample +clear Scpe_sig +log_mem(logfile, 'after_resample', l, ri, realiz, eval_ptr, rop_db, Scpe_sig_2sps); + [~, Scpe_cell, ~, ~] = Scpe_sig_2sps.tsynch("reference", Symbols_l, "fs_ref", fsym, "debug_plots", debug_plots); + +% Scpe_sig_2sps no longer needed after sync +clear Scpe_sig_2sps +log_mem(logfile, 'after_sync', l, ri, realiz, eval_ptr, rop_db); + Rx_sig = Scpe_cell{1}.normalize("mode","rms"); +% Scpe_cell no longer needed +clear Scpe_cell +log_mem(logfile, 'after_rxsig', l, ri, realiz, eval_ptr, rop_db, Rx_sig); + % -------------------- FFE -------------------- ffe_order = [50, 0, 0]; eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0], ... @@ -345,10 +462,11 @@ eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,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); +clear eq_ffe +log_mem(logfile, 'after_ffe', l, ri, realiz, eval_ptr, rop_db); % -------------------- DFE -------------------- -dfe_order2 = [50, 0, 0]; -eq_dfe = EQ("Ne",dfe_order2,"Nb",[2,0,0], ... +eq_dfe = EQ("Ne",ffe_order,"Nb",[2,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); @@ -356,6 +474,8 @@ eq_dfe = EQ("Ne",dfe_order2,"Nb",[2,0,0], ... dfe_results = ffe(eq_dfe,s.M,Rx_sig,Symbols_l,Tx_bits_l, ... "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ... "eth_style_symbol_mapping",0); +clear eq_dfe +log_mem(logfile, 'after_dfe', l, ri, realiz, eval_ptr, rop_db); % -------------------- VNLE + MLSE -------------------- pf_ncoeffs = 1; @@ -371,11 +491,22 @@ mlse_ = MLSE("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).leve [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, s.M, Rx_sig, Symbols_l, Tx_bits_l, ... "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [], "eth_style_symbol_mapping", 0); +clear eq_v pf_ mlse_ +log_mem(logfile, 'after_vnle_mlse', l, ri, realiz, eval_ptr, rop_db); + % -------------------- DB target -------------------- mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",s.M,'trellis_states',PAMmapper(s.M,0).levels); - -dbt_results = duobinary_target(eq_v, mlse_db_, s.M, Rx_sig, Symbols_l, Tx_bits_l, ... +ffe_order = [50, 5, 5]; +eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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",1); +dbt_results = duobinary_target(eq_,mlse_db_, s.M, Rx_sig, Symbols_l, Tx_bits_l, ... "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", []); + +clear mlse_db_ +log_mem(logfile, 'job_end', l, ri, realiz, eval_ptr, rop_db); + +% Rx_sig no longer needed +clear Rx_sig Symbols_l Tx_bits_l end @@ -385,15 +516,12 @@ function [F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt] if nargin < 9, block = false; end if isempty(F), return; end -% Non-blocking mode: try to fetch as many finished as possible with timeout=0 -% Blocking mode: wait for at least one result (timeout=Inf), then keep draining with timeout=0 if block timeout_first = Inf; else timeout_first = 0; end -% Fetch first (maybe blocking), then drain the rest non-blocking first = true; while ~isempty(F) if first @@ -406,12 +534,11 @@ while ~isempty(F) try [k, ffe_r, dfe_r, vnle_r, mlse_r, dbt_r] = fetchNext(F, timeout); catch - % fetchNext throws if timeout=0 and nothing finished in some releases; treat as "nothing ready" break; end if isempty(k) - break; % nothing ready + break; end m = meta(k); @@ -422,8 +549,177 @@ while ~isempty(F) output_mlse{m.l, m.ri, m.realiz, m.eval_ptr} = mlse_r; output_dbt{m.l, m.ri, m.realiz, m.eval_ptr} = dbt_r; - % Remove completed future & meta entry (important: indices change afterwards) F(k) = []; meta(k) = []; end end + + +%% ========================= Memory logging helpers ========================= + +function logfile = make_worker_logfile(memlog_dir) +% One file per worker process to avoid write contention. +pid = get_pid_safe(); +t = getCurrentTask(); +if isempty(t) + tid = -1; +else + tid = t.ID; +end +logfile = fullfile(memlog_dir, sprintf('memlog_pid%d_task%d.txt', pid, tid)); +end + +function log_mem(logfile, tag, l, ri, realiz, eval_ptr, rop_db, varargin) +% Append one line with RSS/VmSize plus optional "largest variable" info. +% Uses /proc on Linux where available. +ts = datetime('now','TimeZone','local','Format','yyyy-MM-dd HH:mm:ss.SSS'); + +[rssMB, vmsMB] = proc_mem_mb(); + +% Optional: include size of a specific variable/object if provided +extra = ""; +if ~isempty(varargin) + try + x = varargin{1}; %#ok + w = whos('x'); + extra = sprintf(' | x_bytes=%d', w.bytes); + catch + extra = " | x_bytes=NA"; + end +end + +line = sprintf('%s | %s | l=%d ri=%d realiz=%d eval=%d rop=%.3f | RSS=%.1fMB Vm=%.1fMB%s\n', ... + char(ts), tag, l, ri, realiz, eval_ptr, rop_db, rssMB, vmsMB, extra); + +fid = fopen(logfile, 'a'); +if fid ~= -1 + fwrite(fid, line); + fclose(fid); +end +end + +function [rssMB, vmsMB] = proc_mem_mb() +% RSS/VmSize from /proc (Linux). Falls back to NaN if unavailable. +rssMB = NaN; vmsMB = NaN; + +if isunix + try + txt = fileread('/proc/self/status'); + rssMB = parse_kb_field(txt, 'VmRSS:') / 1024; + vmsMB = parse_kb_field(txt, 'VmSize:') / 1024; + return; + catch + % fall through + end +end + +% Fallback (Windows): memory() sometimes works +if ispc + try + m = memory; + % MemUsedMATLAB is bytes + rssMB = double(m.MemUsedMATLAB) / 1024^2; + vmsMB = NaN; + catch + end +end +end + +function kb = parse_kb_field(txt, key) +kb = NaN; +idx = strfind(txt, key); +if isempty(idx), return; end +i = idx(1) + length(key); +% read until end of line +j = find(txt(i:end)==newline, 1, 'first') + i - 2; +val = strtrim(txt(i:j)); +% format: "123456 kB" +parts = split(val); +kb = str2double(parts{1}); +end + +function pid = get_pid_safe() +pid = -1; +try + pid = feature('getpid'); +catch + % no-op +end +end + +function [F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt] = ... + throttle_inflight(F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt, maxInFlight) +%THROTTLE_INFLIGHT +% Enforces a HARD cap on outstanding futures (running + queued + finished-not-yet-fetched). +% Blocks until numel(F) < maxInFlight, then returns. + +while numel(F) >= maxInFlight + + % Wait until at least one finishes OR fails + % (wait('finished',1) only wakes for finished, so we also handle failures via periodic checks) + try + wait(F, 'finished', 1); + catch + % If wait fails for some reason, fall back to a short pause + pause(0.1); + end + + % Harvest finished and failed futures (robust snapshot) + n = numel(F); + + % Safety: meta must match F length + if numel(meta) ~= n + % Keep them aligned (rare; indicates earlier mismatch) + m = min(numel(meta), n); + F = F(1:m); + meta = meta(1:m); + n = m; + end + + states = cell(1,n); + for k = 1:n + states{k} = F(k).State; + end + + doneFinished = strcmp(states, 'finished'); + doneFailed = strcmp(states, 'failed'); + done = doneFinished | doneFailed; + + idxDone = find(done); + + if isempty(idxDone) + pause(0.05); + continue; + end + + % Fetch outputs for finished; mark failed as empty results + for jj = 1:numel(idxDone) + ii = idxDone(jj); + m = meta(ii); + + if doneFinished(ii) + [ffe_r, dfe_r, vnle_r, mlse_r, dbt_r] = fetchOutputs(F(ii)); + else + % Failed + try + err = F(ii).Error; + warning('rx_job failed: realiz=%d eval=%d l=%d ri=%d | %s', ... + m.realiz, m.eval_ptr, m.l, m.ri, err.message); + catch + end + ffe_r = []; dfe_r = []; vnle_r = []; mlse_r = []; dbt_r = []; + end + + output_ffe{m.l, m.ri, m.realiz, m.eval_ptr} = ffe_r; + output_dfe{m.l, m.ri, m.realiz, m.eval_ptr} = dfe_r; + output_vnle{m.l, m.ri, m.realiz, m.eval_ptr} = vnle_r; + output_mlse{m.l, m.ri, m.realiz, m.eval_ptr} = mlse_r; + output_dbt{m.l, m.ri, m.realiz, m.eval_ptr} = dbt_r; + end + + % Remove harvested futures/meta entries using numeric indices (safe) + F(idxDone) = []; + meta(idxDone) = []; +end + +end From 805011f339ca40c5d8122a43f96da112ff45ea53 Mon Sep 17 00:00:00 2001 From: sutef391 Date: Mon, 5 Jan 2026 14:57:46 +0100 Subject: [PATCH 07/12] ber greater 1 is allowed --- Classes/DataBaseHandler/Metricstruct.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/DataBaseHandler/Metricstruct.m b/Classes/DataBaseHandler/Metricstruct.m index ef00675..fe9a818 100644 --- a/Classes/DataBaseHandler/Metricstruct.m +++ b/Classes/DataBaseHandler/Metricstruct.m @@ -8,9 +8,9 @@ classdef Metricstruct date_of_processing (1,1) datetime = datetime('now') numBits (1,1) double {mustBeInteger, mustBeNonnegative} = 0 - BER (1,1) double {mustBeNumeric, mustBeNonnegative, mustBeLessThanOrEqual(BER,1)} = 0 + BER (1,1) double {mustBeNumeric, mustBeNonnegative} = 0 numBitErr (1,1) double {mustBeInteger, mustBeNonnegative} = 0 - BER_precoded (1,1) double {mustBeNumeric, mustBeNonnegative, mustBeLessThanOrEqual(BER_precoded,1)} = 0 + BER_precoded (1,1) double {mustBeNumeric, mustBeNonnegative} = 0 numBitErr_precoded (1,1) double {mustBeInteger, mustBeNonnegative} = 0 SNR (1,1) double {mustBeNumeric} = NaN From 01a38814554d2074153519b39e3b7dddd8f02075 Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Wed, 7 Jan 2026 08:30:14 +0100 Subject: [PATCH 08/12] few changes in WDM stuff cleaned up the base system... but its not yet a good minimal example... --- projects/IMDD_base_system/imdd_it.m | 532 +++++++++--------- projects/IMDD_base_system/imdd_model.m | 215 +++---- .../submit_handle.m | 0 projects/WDM/WDM_auswertung.m | 333 +---------- projects/WDM/WDM_model_10km_queue.m | 102 +++- projects/WDM/plot_BER_vs_ROP.m | 199 +++++++ projects/WDM/plot_FEC_violin.m | 213 +++++++ 7 files changed, 841 insertions(+), 753 deletions(-) rename projects/{Messung_Zürich => IMDD_base_system}/submit_handle.m (100%) create mode 100644 projects/WDM/plot_BER_vs_ROP.m create mode 100644 projects/WDM/plot_FEC_violin.m diff --git a/projects/IMDD_base_system/imdd_it.m b/projects/IMDD_base_system/imdd_it.m index 748708f..c9d9cf4 100644 --- a/projects/IMDD_base_system/imdd_it.m +++ b/projects/IMDD_base_system/imdd_it.m @@ -1,117 +1,133 @@ -% basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; -% db = DBHandler("pathToDB",[basePath,'silas_labor.db']); + if 1 uloops = struct; uloops.precomp = [1]; - uloops.db_precode = [0]; - uloops.bitrate = [224].*1e9; %[300,330,360,390,420,450,480] [224,336,360,390,420,448] for MPI + uloops.bitrate = [300].*1e9; %[300,330,360,390,420,450,480] [224,336,360,390,420,448] for MPI % uloops.laser_wavelength = [1293,1297.5,1302,1306.5,1310,1313.4,1318,1322.7,1327.4]; - uloops.laser_wavelength = [1310]; + uloops.laser_wavelength = [1293]; uloops.M = [4]; - uloops.link_length = [1]; % 1,2,3,5,6,8,10 - % uloops.interference_attenuation = [0,3,6,9,12,15,18,21,24,27,30,45]; + uloops.link_length = [0:2:10]; % 1,2,3,5,6,8,10 + uloops.alpha = [0.5,-0.5,0]; + wh = DataStorage(uloops); wh.addStorage("ber"); - % wh = submit_simulations(wh,"parallel",0,"simulation_mode",0); - wh = submit_handle(@imdd_model,wh,"serial",0); + wh = submit_handle(@imdd_model,wh,"parallel",1); end -wh_ana = wh_master; - -cols = cbrewer2('Paired',8); - -figure() - -for precomp = [0,1] - wavelength=uloops.laser_wavelength; - for m = [6] - - baudrate = wh_ana.parameter.bitrate.values; - - - %VNLE - precode = 1; - a = wh_ana.getStoValue('ber',precomp, precode, baudrate , wavelength, m, uloops.link_length); - ber_vnle_pc = cellfun(@(x) x.vnle_pf_package{1,1}.ber_vnle, a); - %MLSE - ber_mlse_pc = cellfun(@(x) x.vnle_pf_package{1,1}.ber_mlse, a); - %DB - ber_dbtgt_pc = cellfun(@(x) x.dbtgt_package{1,1}.ber, a); - - precode = 0; - a = wh_ana.getStoValue('ber',precomp, precode, baudrate , wavelength, m, uloops.link_length); - ber_vnle = cellfun(@(x) x.vnle_pf_package{1,1}.ber_vnle, a); - %MLSE - ber_mlse = cellfun(@(x) x.vnle_pf_package{1,1}.ber_mlse, a); - %DB - ber_dbtgt = cellfun(@(x) x.dbtgt_package{1,1}.ber, a); - - if precomp - legndname1 = ['Pre-Emphasis']; - else - legndname1 = ['No Pre-Emphasis']; - end - - - baudrate = floor( uloops.bitrate.*1e-9 ./log2(m) ) .* 2.5 .* 1e9; - - subplot(1,3,1) - hold on - title(sprintf('%d km | %d nm | PAM %d',uloops.link_length,wavelength,m)); - title(sprintf('PAM %d',m)); - plot(baudrate.*1e-9,ber_vnle,'DisplayName',['Pre-Emphasis: ', num2str(precomp), '| Diff.-Code: 0'],'Color',cols(1+precomp,:),'LineStyle','-','HandleVisibility','on'); - plot(baudrate.*1e-9,ber_vnle_pc,'DisplayName',['Pre-Emphasis: ', num2str(precomp), '| Diff.-Code: 1'],'Color',cols(1+precomp,:),'LineStyle','-.','HandleVisibility','on'); - xticks(baudrate.*1e-9); - set(gca, 'YScale', 'log'); - ylim([5e-5 0.4]); - xlim([min(baudrate(2).*1e-9), max(baudrate.*1e-9) ]); - yline([3.8e-3, 2e-2],'HandleVisibility','off'); - legend - beautifyBERplot() - xlabel('Bit Rate in Gbps'); - ylabel('BER'); - - - - subplot(1,3,2) - hold on - % title(sprintf('%d km | %d nm | PAM %d',uloops.link_length,wavelength,m)); - title(sprintf('PAM %d',m)); - plot(baudrate.*1e-9,ber_dbtgt,'DisplayName',['Pre-Emphasis: ', num2str(precomp), '| Diff.-Code: 0'],'Color',cols(5+precomp,:),'LineStyle','-','HandleVisibility','on'); - plot(baudrate.*1e-9,ber_dbtgt_pc,'DisplayName',['Pre-Emphasis: ', num2str(precomp), '| Diff.-Code: 1'],'Color',cols(5+precomp,:),'LineStyle','-.','HandleVisibility','on'); - xticks(baudrate.*1e-9); - set(gca, 'YScale', 'log'); - ylim([5e-5 0.4]); - xlim([min(baudrate(2).*1e-9), max(baudrate.*1e-9) ]); - yline([3.8e-3, 2e-2],'HandleVisibility','off'); - legend - beautifyBERplot() - xlabel('Bit Rate in Gbps'); - ylabel('BER'); - - - subplot(1,3,3) - hold on - % title(sprintf('%d km | %d nm | PAM %d',uloops.link_length,wavelength,m)); - title(sprintf('PAM %d',m)); - plot(baudrate.*1e-9,ber_mlse,'DisplayName',['Pre-Emphasis: ', num2str(precomp), '| Diff.-Code: 0'],'Color',cols(3+precomp,:),'LineStyle','-','HandleVisibility','on'); - plot(baudrate.*1e-9,ber_mlse_pc,'DisplayName',['Pre-Emphasis: ', num2str(precomp), '| Diff.-Code: 1'],'Color',cols(3+precomp,:),'LineStyle','-.','HandleVisibility','on'); - xticks(baudrate.*1e-9); - set(gca, 'YScale', 'log'); - ylim([5e-5 0.4]); - xlim([min(baudrate(2).*1e-9), max(baudrate.*1e-9) ]); - yline([3.8e-3, 2e-2],'HandleVisibility','off'); - legend - beautifyBERplot() - xlabel('Bit Rate in Gbps'); - ylabel('BER'); - end +%% +figure +hold on +for alpha = uloops.alpha + a=wh.getStoValue('ber',1, [300].*1e9 , 1293, 4, uloops.link_length,alpha); + ffe = cellfun(@(x) x.ffe_results.metrics.BER, a); + plot(uloops.link_length,ffe,'DisplayName',sprintf('Alpha: %d',alpha),'LineStyle','-','HandleVisibility','on'); end -% -% + +set(gca, 'YScale', 'log'); +ylim([5e-5 0.4]); +yline([3.8e-3, 2e-2],'HandleVisibility','off'); +legend +beautifyBERplot() +ylabel('BER'); + + +% +% wh_ana = wh_master; +% +% cols = cbrewer2('Paired',8); +% +% figure() +% +% for precomp = [0,1] +% wavelength=uloops.laser_wavelength; +% for m = [6] +% +% baudrate = wh_ana.parameter.bitrate.values; +% +% +% %VNLE +% precode = 1; +% a = wh_ana.getStoValue('ber',precomp, precode, baudrate , wavelength, m, uloops.link_length); +% ber_vnle_pc = cellfun(@(x) x.vnle_pf_package{1,1}.ber_vnle, a); +% %MLSE +% ber_mlse_pc = cellfun(@(x) x.vnle_pf_package{1,1}.ber_mlse, a); +% %DB +% ber_dbtgt_pc = cellfun(@(x) x.dbtgt_package{1,1}.ber, a); +% +% precode = 0; +% a = wh_ana.getStoValue('ber',precomp, precode, baudrate , wavelength, m, uloops.link_length); +% ber_vnle = cellfun(@(x) x.vnle_pf_package{1,1}.ber_vnle, a); +% %MLSE +% ber_mlse = cellfun(@(x) x.vnle_pf_package{1,1}.ber_mlse, a); +% %DB +% ber_dbtgt = cellfun(@(x) x.dbtgt_package{1,1}.ber, a); +% +% if precomp +% legndname1 = ['Pre-Emphasis']; +% else +% legndname1 = ['No Pre-Emphasis']; +% end +% +% +% baudrate = floor( uloops.bitrate.*1e-9 ./log2(m) ) .* 2.5 .* 1e9; +% +% subplot(1,3,1) +% hold on +% title(sprintf('%d km | %d nm | PAM %d',uloops.link_length,wavelength,m)); +% title(sprintf('PAM %d',m)); +% plot(baudrate.*1e-9,ber_vnle,'DisplayName',['Pre-Emphasis: ', num2str(precomp), '| Diff.-Code: 0'],'Color',cols(1+precomp,:),'LineStyle','-','HandleVisibility','on'); +% plot(baudrate.*1e-9,ber_vnle_pc,'DisplayName',['Pre-Emphasis: ', num2str(precomp), '| Diff.-Code: 1'],'Color',cols(1+precomp,:),'LineStyle','-.','HandleVisibility','on'); +% xticks(baudrate.*1e-9); +% set(gca, 'YScale', 'log'); +% ylim([5e-5 0.4]); +% xlim([min(baudrate(2).*1e-9), max(baudrate.*1e-9) ]); +% yline([3.8e-3, 2e-2],'HandleVisibility','off'); +% legend +% beautifyBERplot() +% xlabel('Bit Rate in Gbps'); +% ylabel('BER'); +% +% +% +% subplot(1,3,2) +% hold on +% % title(sprintf('%d km | %d nm | PAM %d',uloops.link_length,wavelength,m)); +% title(sprintf('PAM %d',m)); +% plot(baudrate.*1e-9,ber_dbtgt,'DisplayName',['Pre-Emphasis: ', num2str(precomp), '| Diff.-Code: 0'],'Color',cols(5+precomp,:),'LineStyle','-','HandleVisibility','on'); +% plot(baudrate.*1e-9,ber_dbtgt_pc,'DisplayName',['Pre-Emphasis: ', num2str(precomp), '| Diff.-Code: 1'],'Color',cols(5+precomp,:),'LineStyle','-.','HandleVisibility','on'); +% xticks(baudrate.*1e-9); +% set(gca, 'YScale', 'log'); +% ylim([5e-5 0.4]); +% xlim([min(baudrate(2).*1e-9), max(baudrate.*1e-9) ]); +% yline([3.8e-3, 2e-2],'HandleVisibility','off'); +% legend +% beautifyBERplot() +% xlabel('Bit Rate in Gbps'); +% ylabel('BER'); +% +% +% subplot(1,3,3) +% hold on +% % title(sprintf('%d km | %d nm | PAM %d',uloops.link_length,wavelength,m)); +% title(sprintf('PAM %d',m)); +% plot(baudrate.*1e-9,ber_mlse,'DisplayName',['Pre-Emphasis: ', num2str(precomp), '| Diff.-Code: 0'],'Color',cols(3+precomp,:),'LineStyle','-','HandleVisibility','on'); +% plot(baudrate.*1e-9,ber_mlse_pc,'DisplayName',['Pre-Emphasis: ', num2str(precomp), '| Diff.-Code: 1'],'Color',cols(3+precomp,:),'LineStyle','-.','HandleVisibility','on'); +% xticks(baudrate.*1e-9); +% set(gca, 'YScale', 'log'); +% ylim([5e-5 0.4]); +% xlim([min(baudrate(2).*1e-9), max(baudrate.*1e-9) ]); +% yline([3.8e-3, 2e-2],'HandleVisibility','off'); +% legend +% beautifyBERplot() +% xlabel('Bit Rate in Gbps'); +% ylabel('BER'); +% end +% end +% % +% % % cols = linspecer(7);%cbrewer2('Set2',10); % % @@ -163,163 +179,163 @@ end % end - -m = 6; -ir = [2,2.5,3]; -cols = linspecer(6); -baudrate_gather = []; -for i = 1:3 - m = uloops.M(i); - - %%% GET VNLE VALS - precode = 0; - precomp = 1; - a = wh_master.getStoValue('ber',precomp, precode, uloops.bitrate , uloops.laser_wavelength, m, uloops.link_length); - ber_vnle(i,:) = cellfun(@(x) x.vnle_pf_package{1,1}.ber_vnle, a); - - %%% GET DB VALS - precode = 1; - precomp = 0; - a = wh_master.getStoValue('ber',precomp, precode, uloops.bitrate , uloops.laser_wavelength, m, uloops.link_length); - ber_db(i,:) = cellfun(@(x) x.dbtgt_package{1,1}.ber, a); - - %%% GET MLSE VALS - precode = 0; - precomp = 0; - a = wh_master.getStoValue('ber',precomp, precode, uloops.bitrate , uloops.laser_wavelength, m, uloops.link_length); - ber_mlse(i,:) = cellfun(@(x) x.vnle_pf_package{1,1}.ber_mlse, a); - - inf_rate_pam(i,:) = cellfun(@(x) x.vnle_pf_package{1,1}.air, a); - inf_rate_pam(i,:) = inf_rate_pam(i,:)./log2(m); - - bitrate = floor( uloops.bitrate.*1e-9 ./log2(m) ) .* ir(i) .* 1e9; - baudrate = floor( uloops.bitrate.*1e-9 ./log2(m) ) .* 1e9; - baudrate_gather = union(baudrate_gather,baudrate); - baudrate_ticks = 100:20:240; - bitrate_ticks = 300:30:480; - - tp = TransmissionPerformance; - netRatesVNLE = tp.calculateNetRate(bitrate, 'NGMI', inf_rate_pam(i,:), 'BER', ber_vnle(i,:)); - - %%% NGMI - figure(12) - hold on - title(sprintf('Performance at 1310 nm')); - plot(baudrate.*1e-9,inf_rate_pam(i,:),'DisplayName',sprintf('NGMI; PAM %d',m),'Color',cols(i,:),'LineStyle','-'); - xlabel('Baud rate in GBd'); - ylabel('NGMI') - beautifyBERplot() - xticks(baudrate_ticks); - xlim([min(baudrate_ticks) max(baudrate_ticks)]); - - - %%% AIR - figure(14) - hold on - title(sprintf('Performance at 1310 nm')); - plot(baudrate.*1e-9,inf_rate_pam(i,:).*bitrate.*1e-9,'DisplayName',sprintf('AIR; PAM %d',m),'Color',cols(i,:),'LineStyle','-'); - xlabel('Baud rate in GBd'); - ylabel('AIR'); - beautifyBERplot() - xticks(baudrate_ticks); - xlim([min(baudrate_ticks) max(baudrate_ticks)]); - - %%% RATES - figure(16) - hold on - title(sprintf('Performance at 1310 nm')); - if i == 1 - hv = 'on'; - else - hv = 'off'; - end - plot(baudrate*1e-9,netRatesVNLE.SDHD.NetRate.*1e-9,'DisplayName',sprintf('SD+HD FEC',m),'Color',cols(i,:),'LineStyle','-','HandleVisibility',hv,'Marker','o'); - plot(baudrate.*1e-9,netRatesVNLE.HD.NetRate.*1e-9,'DisplayName',sprintf('HD FEC',m),'Color',cols(i,:),'LineStyle',':','HandleVisibility',hv,'Marker','diamond'); - plot(baudrate.*1e-9,netRatesVNLE.KP4_hamming.NetRate*1e-9,'DisplayName',sprintf('KP4+Hamming',m),'Color',cols(i,:),'LineStyle','-.','HandleVisibility',hv,'Marker','square'); - xticks(baudrate_ticks); - xlim([min(baudrate_ticks) max(baudrate_ticks)]); - xlabel('Baud rate in GBd'); - ylabel('Net Bitrate in Gbps') - beautifyBERplot() - ylim([250 410]) - - %%% CODE OVERHEAD IN % - figure(18) - hold on - title(sprintf('Performance at 1310 nm')); - plot(baudrate*1e-9,100.*(1-netRatesVNLE.SDHD.CodeRate)./netRatesVNLE.SDHD.CodeRate,'DisplayName',sprintf('SD+HD FEC',m),'Color',cols(i,:),'LineStyle','-','HandleVisibility',hv,'Marker','o'); - plot(baudrate.*1e-9,100.*(1-netRatesVNLE.HD.CodeRate)./netRatesVNLE.HD.CodeRate,'DisplayName',sprintf('HD FEC',m),'Color',cols(i,:),'LineStyle',':','HandleVisibility',hv,'Marker','diamond'); - plot(baudrate.*1e-9,100.*(1-netRatesVNLE.KP4_hamming.CodeRate)./netRatesVNLE.KP4_hamming.CodeRate,'DisplayName',sprintf('KP4+Hamming',m),'Color',cols(i,:),'LineStyle','-.','HandleVisibility',hv,'Marker','square'); - xticks(baudrate_ticks); - xlim([min(baudrate_ticks) max(baudrate_ticks)]); - xlabel('Baud rate in GBd'); - ylabel('FEC Overhead in %') - beautifyBERplot() - - %%% CLASSIC BER - figure(22) - subplot(1,4,i) - hold on - plot(baudrate*1e-9,ber_vnle(i,:),'DisplayName',sprintf('Tx precomp + VNLE',m),'Color',cols(i,:),'LineStyle','-','HandleVisibility','on','Marker','o'); - plot(baudrate*1e-9,ber_mlse(i,:),'DisplayName',sprintf('VNLE + PF + MLSE',m),'Color',cols(i,:),'LineStyle',':','HandleVisibility','on','Marker','square'); - plot(baudrate*1e-9,ber_db(i,:),'DisplayName',sprintf('Diff. Code + DB tgt.',m),'Color',cols(i,:),'LineStyle','--','HandleVisibility','on','Marker','diamond'); - yline(4.85e-3,'HandleVisibility','off'); - yline(2e-2,'HandleVisibility','off'); - xticks(baudrate*1e-9); - xlim([min(baudrate*1e-9) max(baudrate*1e-9)]); - ylim([1e-4 0.3]); - xlabel('Baudrate in GBd'); - if i == 1 - ylabel('BER') - end - beautifyBERplot() - set(gca, 'YScale', 'log'); -legend - subplot(1,4,4) - hold on - if m == 4 - - plot(bitrate*1e-9,ber_db(i,:),'DisplayName',sprintf('Diff. Code + DB tgt.'),'Color',cols(i,:),'LineStyle','--','HandleVisibility','on','Marker','diamond'); - - elseif m == 6 - - plot(bitrate*1e-9,ber_vnle(i,:),'DisplayName',sprintf('VNLE + PF + MLSE'),'Color',cols(i,:),'LineStyle','-','HandleVisibility','on','Marker','o'); - % plot(bitrate*1e-9,ber_mlse(i,:),'DisplayName',sprintf('MLSE',m),'Color',cols(i,:),'LineStyle',':','HandleVisibility',hv,'Marker','square'); - - elseif m ==8 - - plot(bitrate*1e-9,ber_vnle(i,:),'DisplayName',sprintf('Tx precomp + VNLE'),'Color',cols(i,:),'LineStyle','-','HandleVisibility','on','Marker','o'); - % plot(bitrate*1e-9,ber_mlse(i,:),'DisplayName',sprintf('MLSE',m),'Color',cols(i,:),'LineStyle',':','HandleVisibility',hv,'Marker','square'); - - end - yline(4.85e-3,'HandleVisibility','off'); - yline(2e-2,'HandleVisibility','off'); - xticks(bitrate_ticks); - xlim([min(bitrate_ticks) max(bitrate_ticks)]); - ylim([1e-4 0.3]); - xlabel('Gross Bitrate in Gbps'); - % ylabel('BER') - beautifyBERplot() - set(gca, 'YScale', 'log'); - - -end - - - -figure() -title(sprintf('%d km | 1310 nm | PAM %d | VNLE',uloops.link_length,uloops.M)); -hold on -line([min(uloops.bitrate.*1e-9) max(uloops.bitrate.*1e-9)],[min(uloops.bitrate.*1e-9) max(uloops.bitrate.*1e-9)],'Color',[.7,.7,.7],'Marker','none','Handlevisibility','off'); -plot(uloops.bitrate.*1e-9,cellfun(@min, inf_rate_vnle).*uloops.bitrate./log2(uloops.M).*1e-9,'DisplayName',sprintf('AIR'),'Color',cols(1,:),'LineStyle',':'); -plot(uloops.bitrate.*1e-9,netRatesVNLE.SDHD.NetRate.*1e-9,'DisplayName',sprintf('SD+HD'),'Color',cols(2,:),'LineStyle',':'); -plot(uloops.bitrate.*1e-9,netRatesVNLE.HD.NetRate.*1e-9,'DisplayName',sprintf('HD'),'Color',cols(3,:),'LineStyle',':'); -plot(uloops.bitrate.*1e-9,netRatesVNLE.KP4_hamming.NetRate.*1e-9,'DisplayName',sprintf('KP4+Hamming'),'Color',cols(4,:),'LineStyle',':'); -beautifyBERplot() -xlim([min(uloops.bitrate.*1e-9) max(uloops.bitrate.*1e-9)]) -xlabel('Gross Bitrate in Gbps'); -ylabel('Net Bitrate in Gbps'); -legend +% +% m = 6; +% ir = [2,2.5,3]; +% cols = linspecer(6); +% baudrate_gather = []; +% for i = 1:3 +% m = uloops.M(i); +% +% %%% GET VNLE VALS +% precode = 0; +% precomp = 1; +% a = wh_master.getStoValue('ber',precomp, precode, uloops.bitrate , uloops.laser_wavelength, m, uloops.link_length); +% ber_vnle(i,:) = cellfun(@(x) x.vnle_pf_package{1,1}.ber_vnle, a); +% +% %%% GET DB VALS +% precode = 1; +% precomp = 0; +% a = wh_master.getStoValue('ber',precomp, precode, uloops.bitrate , uloops.laser_wavelength, m, uloops.link_length); +% ber_db(i,:) = cellfun(@(x) x.dbtgt_package{1,1}.ber, a); +% +% %%% GET MLSE VALS +% precode = 0; +% precomp = 0; +% a = wh_master.getStoValue('ber',precomp, precode, uloops.bitrate , uloops.laser_wavelength, m, uloops.link_length); +% ber_mlse(i,:) = cellfun(@(x) x.vnle_pf_package{1,1}.ber_mlse, a); +% +% inf_rate_pam(i,:) = cellfun(@(x) x.vnle_pf_package{1,1}.air, a); +% inf_rate_pam(i,:) = inf_rate_pam(i,:)./log2(m); +% +% bitrate = floor( uloops.bitrate.*1e-9 ./log2(m) ) .* ir(i) .* 1e9; +% baudrate = floor( uloops.bitrate.*1e-9 ./log2(m) ) .* 1e9; +% baudrate_gather = union(baudrate_gather,baudrate); +% baudrate_ticks = 100:20:240; +% bitrate_ticks = 300:30:480; +% +% tp = TransmissionPerformance; +% netRatesVNLE = tp.calculateNetRate(bitrate, 'NGMI', inf_rate_pam(i,:), 'BER', ber_vnle(i,:)); +% +% %%% NGMI +% figure(12) +% hold on +% title(sprintf('Performance at 1310 nm')); +% plot(baudrate.*1e-9,inf_rate_pam(i,:),'DisplayName',sprintf('NGMI; PAM %d',m),'Color',cols(i,:),'LineStyle','-'); +% xlabel('Baud rate in GBd'); +% ylabel('NGMI') +% beautifyBERplot() +% xticks(baudrate_ticks); +% xlim([min(baudrate_ticks) max(baudrate_ticks)]); +% +% +% %%% AIR +% figure(14) +% hold on +% title(sprintf('Performance at 1310 nm')); +% plot(baudrate.*1e-9,inf_rate_pam(i,:).*bitrate.*1e-9,'DisplayName',sprintf('AIR; PAM %d',m),'Color',cols(i,:),'LineStyle','-'); +% xlabel('Baud rate in GBd'); +% ylabel('AIR'); +% beautifyBERplot() +% xticks(baudrate_ticks); +% xlim([min(baudrate_ticks) max(baudrate_ticks)]); +% +% %%% RATES +% figure(16) +% hold on +% title(sprintf('Performance at 1310 nm')); +% if i == 1 +% hv = 'on'; +% else +% hv = 'off'; +% end +% plot(baudrate*1e-9,netRatesVNLE.SDHD.NetRate.*1e-9,'DisplayName',sprintf('SD+HD FEC',m),'Color',cols(i,:),'LineStyle','-','HandleVisibility',hv,'Marker','o'); +% plot(baudrate.*1e-9,netRatesVNLE.HD.NetRate.*1e-9,'DisplayName',sprintf('HD FEC',m),'Color',cols(i,:),'LineStyle',':','HandleVisibility',hv,'Marker','diamond'); +% plot(baudrate.*1e-9,netRatesVNLE.KP4_hamming.NetRate*1e-9,'DisplayName',sprintf('KP4+Hamming',m),'Color',cols(i,:),'LineStyle','-.','HandleVisibility',hv,'Marker','square'); +% xticks(baudrate_ticks); +% xlim([min(baudrate_ticks) max(baudrate_ticks)]); +% xlabel('Baud rate in GBd'); +% ylabel('Net Bitrate in Gbps') +% beautifyBERplot() +% ylim([250 410]) +% +% %%% CODE OVERHEAD IN % +% figure(18) +% hold on +% title(sprintf('Performance at 1310 nm')); +% plot(baudrate*1e-9,100.*(1-netRatesVNLE.SDHD.CodeRate)./netRatesVNLE.SDHD.CodeRate,'DisplayName',sprintf('SD+HD FEC',m),'Color',cols(i,:),'LineStyle','-','HandleVisibility',hv,'Marker','o'); +% plot(baudrate.*1e-9,100.*(1-netRatesVNLE.HD.CodeRate)./netRatesVNLE.HD.CodeRate,'DisplayName',sprintf('HD FEC',m),'Color',cols(i,:),'LineStyle',':','HandleVisibility',hv,'Marker','diamond'); +% plot(baudrate.*1e-9,100.*(1-netRatesVNLE.KP4_hamming.CodeRate)./netRatesVNLE.KP4_hamming.CodeRate,'DisplayName',sprintf('KP4+Hamming',m),'Color',cols(i,:),'LineStyle','-.','HandleVisibility',hv,'Marker','square'); +% xticks(baudrate_ticks); +% xlim([min(baudrate_ticks) max(baudrate_ticks)]); +% xlabel('Baud rate in GBd'); +% ylabel('FEC Overhead in %') +% beautifyBERplot() +% +% %%% CLASSIC BER +% figure(22) +% subplot(1,4,i) +% hold on +% plot(baudrate*1e-9,ber_vnle(i,:),'DisplayName',sprintf('Tx precomp + VNLE',m),'Color',cols(i,:),'LineStyle','-','HandleVisibility','on','Marker','o'); +% plot(baudrate*1e-9,ber_mlse(i,:),'DisplayName',sprintf('VNLE + PF + MLSE',m),'Color',cols(i,:),'LineStyle',':','HandleVisibility','on','Marker','square'); +% plot(baudrate*1e-9,ber_db(i,:),'DisplayName',sprintf('Diff. Code + DB tgt.',m),'Color',cols(i,:),'LineStyle','--','HandleVisibility','on','Marker','diamond'); +% yline(4.85e-3,'HandleVisibility','off'); +% yline(2e-2,'HandleVisibility','off'); +% xticks(baudrate*1e-9); +% xlim([min(baudrate*1e-9) max(baudrate*1e-9)]); +% ylim([1e-4 0.3]); +% xlabel('Baudrate in GBd'); +% if i == 1 +% ylabel('BER') +% end +% beautifyBERplot() +% set(gca, 'YScale', 'log'); +% legend +% subplot(1,4,4) +% hold on +% if m == 4 +% +% plot(bitrate*1e-9,ber_db(i,:),'DisplayName',sprintf('Diff. Code + DB tgt.'),'Color',cols(i,:),'LineStyle','--','HandleVisibility','on','Marker','diamond'); +% +% elseif m == 6 +% +% plot(bitrate*1e-9,ber_vnle(i,:),'DisplayName',sprintf('VNLE + PF + MLSE'),'Color',cols(i,:),'LineStyle','-','HandleVisibility','on','Marker','o'); +% % plot(bitrate*1e-9,ber_mlse(i,:),'DisplayName',sprintf('MLSE',m),'Color',cols(i,:),'LineStyle',':','HandleVisibility',hv,'Marker','square'); +% +% elseif m ==8 +% +% plot(bitrate*1e-9,ber_vnle(i,:),'DisplayName',sprintf('Tx precomp + VNLE'),'Color',cols(i,:),'LineStyle','-','HandleVisibility','on','Marker','o'); +% % plot(bitrate*1e-9,ber_mlse(i,:),'DisplayName',sprintf('MLSE',m),'Color',cols(i,:),'LineStyle',':','HandleVisibility',hv,'Marker','square'); +% +% end +% yline(4.85e-3,'HandleVisibility','off'); +% yline(2e-2,'HandleVisibility','off'); +% xticks(bitrate_ticks); +% xlim([min(bitrate_ticks) max(bitrate_ticks)]); +% ylim([1e-4 0.3]); +% xlabel('Gross Bitrate in Gbps'); +% % ylabel('BER') +% beautifyBERplot() +% set(gca, 'YScale', 'log'); +% +% +% end +% +% +% +% figure() +% title(sprintf('%d km | 1310 nm | PAM %d | VNLE',uloops.link_length,uloops.M)); +% hold on +% line([min(uloops.bitrate.*1e-9) max(uloops.bitrate.*1e-9)],[min(uloops.bitrate.*1e-9) max(uloops.bitrate.*1e-9)],'Color',[.7,.7,.7],'Marker','none','Handlevisibility','off'); +% plot(uloops.bitrate.*1e-9,cellfun(@min, inf_rate_vnle).*uloops.bitrate./log2(uloops.M).*1e-9,'DisplayName',sprintf('AIR'),'Color',cols(1,:),'LineStyle',':'); +% plot(uloops.bitrate.*1e-9,netRatesVNLE.SDHD.NetRate.*1e-9,'DisplayName',sprintf('SD+HD'),'Color',cols(2,:),'LineStyle',':'); +% plot(uloops.bitrate.*1e-9,netRatesVNLE.HD.NetRate.*1e-9,'DisplayName',sprintf('HD'),'Color',cols(3,:),'LineStyle',':'); +% plot(uloops.bitrate.*1e-9,netRatesVNLE.KP4_hamming.NetRate.*1e-9,'DisplayName',sprintf('KP4+Hamming'),'Color',cols(4,:),'LineStyle',':'); +% beautifyBERplot() +% xlim([min(uloops.bitrate.*1e-9) max(uloops.bitrate.*1e-9)]) +% xlabel('Gross Bitrate in Gbps'); +% ylabel('Net Bitrate in Gbps'); +% legend % plot(uloops.bitrate.*1e-9,ber_vnle,'DisplayName',sprintf('NGMI MLSE; %d km',len),'Color',cols(1,:),'LineStyle','-'); diff --git a/projects/IMDD_base_system/imdd_model.m b/projects/IMDD_base_system/imdd_model.m index ac6bd0c..06b1812 100644 --- a/projects/IMDD_base_system/imdd_model.m +++ b/projects/IMDD_base_system/imdd_model.m @@ -19,19 +19,13 @@ fdac = 256e9; fadc = 256e9; random_key = 1; -interference_attenuation = 0; -is_mpi = 1; - -precomp = 0; -db_precode = 0; - -db_encode = 0; - rcalpha = 0.05; kover = 16; + vbias_rel = 0.5; -u_pi = 2.9; +u_pi = 3; vbias = -vbias_rel*u_pi; + laser_wavelength = 1293; laser_linewidth = 0; tx_bw_nyquist = 0.8; @@ -40,7 +34,7 @@ tx_bw_nyquist = 0.8; link_length = 1; % RX -rop = -5; +rop = -8; rx_bw_nyquist = 0.8; vnle_order1 = 50; @@ -68,7 +62,7 @@ mu_dfe = 0.0004; dfe_ = sum(dfe_order)>0; -doub_mode = db_mode.no_db; +duob_mode = db_mode.no_db; %%% change specific parameter if given in varargin % Parse optional input arguments @@ -90,43 +84,6 @@ if ~isempty(varargin) end end -if doub_mode ~= db_mode.db_encoded - if precomp == 0 && db_precode == 1 - doub_mode = db_mode.db_precoded; - - db_precode = 1; % preceded data (in my measurement set, this corresponds to low precomp too!) - discard_precode = 0; % - emulate_precode = 0; - legendentry = 'low precomp; precoded'; - disp('low precomp; precoded') - elseif precomp == 1 && db_precode == 1 - doub_mode = db_mode.db_emulate; - - db_precode = 0; % preceded data (in my measurement set, this corresponds to low precomp too!) - discard_precode = 0; % - emulate_precode = 1; - legendentry = 'high precomp; precoded'; - disp('high precomp; precoded') - elseif precomp == 0 && db_precode == 0 - doub_mode = db_mode.db_discard; - - db_precode = 1; % preceded data (in my measurement set, this corresponds to low precomp too!) - discard_precode = 1; % - emulate_precode = 0; - legendentry = 'no precomp; not precoded'; - disp('no precomp; not precoded') - elseif precomp == 1 && db_precode == 0 - doub_mode = db_mode.no_db; - - db_precode = 0; % preceded data (in my measurement set, this corresponds to low precomp too!) - discard_precode = 0; % - emulate_precode = 0; - legendentry = 'high precomp; not precoded'; - disp('high precomp; not precoded') - end -else - -end fsym_ = floor( bitrate*1e-9./log2(M) ).*1e9; @@ -137,28 +94,19 @@ end f_nyquist = fsym/2; - - -Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); -rcalpha = 1; Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha); -db_precode = 0; -db_encode = 0; -apply_pulsef = 1; [Digi_sig,Symbols,Tx_bits] = PAMsource(... "fsym",fsym,"M",M,"order",18,"useprbs",0,... "fs_out",fdac,... "applyclipping",0,"clipfactor",1.5,... "applypulseform",apply_pulsef,"pulseformer",Pform,... "randkey",random_key,... - "db_precode",db_precode,"db_encode",db_encode,... + 'duobinary_mode',duob_mode,... "mrds_code",0,"mrds_blocklength",512).process(); Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1); - - %%%%% AWG % El_sig = M8199A("kover",kover).process(Digi_sig); El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); @@ -172,13 +120,17 @@ El_sig = Filter('filtdegree',4,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",fi % El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',1); %%%%% Electrical Driver Amplifier %%%%%% -El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig); +% El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig); El_sig = El_sig.normalize("mode","oneone"); +scaling = 0.6*(u_pi/2-abs(vbias-u_pi/2)); +El_sig = El_sig .* scaling; %%%%% MODULATE E/O CONVERSION %%%%%% -[Opt_sig] = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",laser_wavelength,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",random_key+1).process(El_sig); +[Opt_sig] = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",laser_wavelength,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",random_key+1,"alpha",alpha).process(El_sig); -Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig); +Opt_sig.spectrum("displayname",'Opt Spectrum','fignum',10,'normalizeTo0dB',1); + +Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig); %%%%%% ROP %%%%%% Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig); @@ -202,110 +154,75 @@ Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc,... "samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0,... "adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',1,'H_lpf',Lp_scpe).process(Rx_sig); -Scpe_cell{1} = Scpe_sig; - - -if db_precode - Symbols_precoded = Symbols; -end - output = struct(); -vnle_pf_package = {}; -vnle_dfe_package = {}; -dbtgt_package = {}; + +%%%%%% Sample to 2x fsym %%%%%% +Scpe_sig = Scpe_sig.resample("fs_out",2*fsym); +Scpe_sig.signal = Scpe_sig.signal(1:2*length(Symbols)); + +%%%%%% Sync Rx signal with reference %%%%%% +[Scpe_sig,~] = Scpe_sig.tsynch("reference",Symbols,"fs_ref",fsym,"debug_plots",1); + +Scpe_sig = Filter('filtdegree',4,"f_cutoff",Symbols.fs.*0.5,"fs",Scpe_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scpe_sig); + +Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); + +%%% EQUALIZING -proc_occ = min(1,length(Scpe_cell)); -for occ = 1%:proc_occ +% -------------------- FFE -------------------- +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); - Scpe_sig = Scpe_cell{occ}; +output.ffe_results = ffe(eq_ffe,M,Scpe_sig,Symbols,Tx_bits, ... + "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ... + "eth_style_symbol_mapping",0); - %%%%%% Sample to 2x fsym %%%%%% - Scpe_sig = Scpe_sig.resample("fs_out",2*fsym); +output.ffe_results.metrics.print - %%%%%% Sync Rx signal with reference %%%%%% - [Scpe_sig,~] = Scpe_sig.tsynch("reference",Symbols,"fs_ref",fsym); +% -------------------- DFE -------------------- +eq_dfe = EQ("Ne",ffe_order,"Nb",[2,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); - Scpe_sig = Filter('filtdegree',4,"f_cutoff",Symbols.fs.*0.5,"fs",Scpe_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scpe_sig); +output.dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits, ... + "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ... + "eth_style_symbol_mapping",0); - Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); - % - % Pform = Pulseformer("fsym",Scpe_sig.fs,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha,"matched",0); - % - % Scpe_sig_matched = Pform.process(Scpe_sig); - % - % Scpe_sig.spectrum("normalizeTo0dB",0,"fignum",336,"displayname","scope "); - % Scpe_sig_matched.spectrum("normalizeTo0dB",0,"fignum",336,"displayname","matched"); - - %%% EQUALIZING +output.dfe_results.metrics.print("description",'DFE'); - % eq_mlse = FFE_DCremoval("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr,"mu_dd",mu_ffe(1),"mu_tr",0,"order",ffe_order(1),"sps",2,"decide",0,"dc_buffer_len",1,"mu_dc",0.05); - % eq_mlse = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr,"mu_dd",mu_ffe(1),"mu_tr",0,"order",ffe_order(1),"sps",2,"decide",0); - % eq_mlse = FFE_DCremoval("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr,"mu_dd",mu_ffe(1),"mu_tr",0,"order",ffe_order(1),"sps",2,"decide",0,"dc_buffer_len",512,"mu_dc",0.05); +% -------------------- VNLE + MLSE -------------------- +pf_ncoeffs = 1; +ffe_order3 = [50, 5, 5]; +eq_v = EQ("Ne",ffe_order3,"Nb",dfe_order, ... + "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",1); +pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); - mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3]; - vnle_order=[vnle_order1,vnle_order2,vnle_order3]; +mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); - % %%%%% VNLE + DFE %%%% - if 0 - - eq_vnle_dfe = EQ("Ne",vnle_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_2 = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",2001,"sps",1,"decide",0); - - [result] = vnle(eq_vnle_dfe,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",doub_mode,"showAnalysis",1,"postFFE",[]); - vnle_dfe_package{occ} = result; - - end - %%%%% VNLE + PF + MLSE %%%% - if 1 - - % len_tr = length(Symbols)-1000; - eq_vnle_ = EQ("Ne",vnle_order,"Nb",dfe_order,"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",1); - % eq_vnle_ = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0,"order",vnle_order,"sps",2,"decide",0); - pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); - mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); - - [result] = vnle_postfilter_mlse(eq_vnle_,pf_,mlse_,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",doub_mode,'showAnalysis',1); - vnle_pf_package{occ} = result; - - end +[output.vnle_results, output.mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [], "eth_style_symbol_mapping", 0); - %%%%% Duobinary Targeting %%%% - if 1 +% -------------------- DB target -------------------- +mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels); +ffe_order = [50, 5, 5]; +eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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",1); +output.dbt_results = duobinary_target(eq_,mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", []); - mlse_db = MLSE_viterbi("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); - eq_db = EQ("Ne",vnle_order,"Nb",dfe_order,"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",1); - - [result] = duobinary_target(eq_db, mlse_db, M, Scpe_sig, Symbols, Tx_bits, "precode_mode", doub_mode,'showAnalysis',0); - dbtgt_package{occ} = result; +output.dbt_results.metrics.print("description",'Duobinary'); - end - - %%%%%% %db signaling => db encoded %%%%% - if 0 - mlse_db_enc = MLSE_viterbi("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); - eq_db_enc = EQ("Ne",vnle_order,"Nb",dfe_order,"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",1); - [result] = duobinary_signaling(eq_db_enc, mlse_db_enc,M, Scpe_sig ,Symbols, Tx_bits); - dbenc_package{occ} = result; - end - - - % autoArrangeFigures; - disp('- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ') - fprintf('\n') - - -end - -output.vnle_dfe_package = vnle_dfe_package; -output.vnle_pf_package = vnle_pf_package; -output.dbtgt_package = dbtgt_package; - -if ~isempty(curFolder) - cd(curFolder); -end +disp('- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ') +fprintf('\n') end \ No newline at end of file diff --git a/projects/Messung_Zürich/submit_handle.m b/projects/IMDD_base_system/submit_handle.m similarity index 100% rename from projects/Messung_Zürich/submit_handle.m rename to projects/IMDD_base_system/submit_handle.m diff --git a/projects/WDM/WDM_auswertung.m b/projects/WDM/WDM_auswertung.m index a6a666c..4c213fa 100644 --- a/projects/WDM/WDM_auswertung.m +++ b/projects/WDM/WDM_auswertung.m @@ -1,325 +1,12 @@ -%% Robust eval/plot script for res.* (handles missing/partial dims) -% res.ffe stored like: output_ffe{ch, rop, realiz, eval_distance} -% BUT: some dimensions may be singleton or not fully filled (e.g. only 0 km, only 1 realization) +filename = "C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\WDM_20260106_131152_n261_14408161_10km_16ch_400ghz_co_alpha0_5.mat"; +filename = "C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\WDM_20260106_131152_n262_14408165_10km_16ch_400ghz_co_alpha-0_5.mat"; +filename = "C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\WDM_20260106_134921_n223_14409338_10km_16ch_400ghz_co_alpha0_0.mat"; +res = load(filename,'res'); +res = res.res; -% -------------------- Basic metadata -------------------- -rop = res.settings.rop(:); -wavelengthplan = res.settings.wavelengthplan(:); -distances = res.eval_dist_km(:); % can be [0], [2 10], etc. +%% +% Routine A: plot BER curves and compute crossings +S = plot_BER_vs_ROP(res, 'fec', 3.8e-3); -N_ch = numel(wavelengthplan); -N_rop = numel(rop); - -% -------------------- Determine dims robustly -------------------- -% Ensure we have 4D addressing even if MATLAB collapses trailing singletons -dims = size(res.ffe); -if numel(dims) < 4 - dims(end+1:4) = 1; -end -N_realiz = dims(3); -N_distances = dims(4); - -% If res.eval_dist_km length differs from dims(4), trust dims(4) -if numel(distances) ~= N_distances - % best-effort fallback - distances = (1:N_distances).'; -end - -% -------------------- Plot settings -------------------- -fec = 3.8e-3; % choose one; you can switch to 2.2e-4 if needed - -qLow = 0.00; -qHigh = 1.00; - -% Color per wavelength/channel -try - cols = ccbrewer2('Set1', N_ch); -catch - cols = linspecer(N_ch); -end - -% Containers for FEC crossings per channel (per eval distance) -Sffe = cell(N_distances, N_ch); -Sdfe = cell(N_distances, N_ch); -Svnle = cell(N_distances, N_ch); -Smlse = cell(N_distances, N_ch); -Sdbt = cell(N_distances, N_ch); - -% -------------------- Choose which eval distances to plot -------------------- -% If you want all: eval_list = 1:N_distances; -% If you want a specific distance (e.g., last): eval_list = N_distances; -eval_list = 1;%1:N_distances; - -% -------------------- BER vs ROP figures (one figure per eval distance) -------------------- -for eval_ptr = eval_list - - figure('Name', sprintf('BER vs ROP @ %s', distLabel(distances, eval_ptr))); - hold on; - - for ch = 1:N_ch - - % ---- Extract cell slices robustly: [N_rop x N_realiz] ---- - ffe_cells = sliceCells4D(res.ffe , ch, eval_ptr, N_rop); - dfe_cells = sliceCells4D(res.dfe , ch, eval_ptr, N_rop); - vnle_cells = sliceCells4D(res.vnle, ch, eval_ptr, N_rop); - mlse_cells = sliceCells4D(res.mlse, ch, eval_ptr, N_rop); - dbt_cells = sliceCells4D(res.dbt , ch, eval_ptr, N_rop); - - % ---- FEC crossings (works with incomplete columns) ---- - [Sffe{eval_ptr,ch}, ~] = fecCrossings(rop, ffe_cells, fec); - [Sdfe{eval_ptr,ch}, ~] = fecCrossings(rop, dfe_cells, fec); - [Svnle{eval_ptr,ch}, ~] = fecCrossings(rop, vnle_cells, fec); - [Smlse{eval_ptr,ch}, ~] = fecCrossings(rop, mlse_cells, fec); - [Sdbt{eval_ptr,ch}, ~] = fecCrossings(rop, dbt_cells, fec); - - % ---- Extract BER matrices using only complete realizations ---- - ffe_mat = extractCompleteBER(ffe_cells); % N_rop x K - dfe_mat = extractCompleteBER(dfe_cells); - vnle_mat = extractCompleteBER(vnle_cells); - mlse_mat = extractCompleteBER(mlse_cells); - dbt_mat = extractCompleteBER(dbt_cells); - - showLegend = (ch == 1); % one legend entry per technique - - % Plot bands + mean (only if non-empty) - if ~isempty(ffe_mat) - plotBandMeanBL(rop, ffe_mat, cols(ch,:), sprintf('FFE @ %dnm', round(wavelengthplan(ch))), qLow, qHigh, '--s', showLegend); - end - % if ~isempty(dfe_mat) - % plotBandMeanBL(rop, dfe_mat, cols(ch,:), sprintf('DFE @ %dnm', round(wavelengthplan(ch))), qLow, qHigh, '-o', showLegend); - % end - % if ~isempty(vnle_mat) - % plotBandMeanBL(rop, vnle_mat, cols(ch,:), sprintf('VNLE @ %dnm', round(wavelengthplan(ch))), qLow, qHigh, '--x', showLegend); - % end - % if ~isempty(mlse_mat) - % plotBandMeanBL(rop, mlse_mat, cols(ch,:), sprintf('VNLE+PF+MLSE @ %dnm', round(wavelengthplan(ch))), qLow, qHigh, '-o', showLegend); - % end - % if ~isempty(dbt_mat) - % plotBandMeanBL(rop, dbt_mat, cols(ch,:), sprintf('DBt+MLSE @ %dnm', round(wavelengthplan(ch))), qLow, qHigh, '--v', showLegend); - % end - - end - - set(gca,'XScale','linear','YScale','log','TickLabelInterpreter','latex','FontSize',11); - yline([3.8e-3, 2.2e-4], 'HandleVisibility','off','LineWidth',1.5); - ylabel('BER'); - xlabel('ROP [dB]'); - title(sprintf('BER vs. ROP @ %s', distLabel(distances, eval_ptr))); - xlim([min(rop) max(rop)]); - ylim([1e-5 0.3]); - grid on; - legend show; -end - - -%% -------------------- VIOLIN of ROP at FEC crossing -------------------- -% Build technique groups; each group is {N_distances x N_ch} cell entries -% You can pick a distance to show (e.g., eval_ptr=1 for 0 km, or last) -eval_ptr_violin = min(N_distances, max(1, N_distances)); % default: last available -% eval_ptr_violin = 1; - -% Choose which technique(s) to show -techNames = {'VNLE'}; -S_groups = {Svnle(eval_ptr_violin,:)}; - -% Only plot if violinplot exists -if exist('violinplot','file') == 2 - figure('Name', sprintf('FEC crossing violin @ %s', distLabel(distances, eval_ptr_violin))); hold on; - - colsTech = linspecer(numel(S_groups)); - - for i = 1:numel(S_groups) - S_cell = S_groups{i}; % 1 x N_ch cell, each cell is 1xK crossings - - % Pad to rectangular: rows = realizations (max K), cols = wavelengths (N_ch) - Kmax = max(cellfun(@numel, S_cell)); - if isempty(Kmax) || Kmax == 0 - continue; - end - S_mat = zeros(Kmax, N_ch); - for ch = 1:N_ch - k = numel(S_cell{ch}); - if k > 0 - S_mat(1:k, ch) = S_cell{ch}(:); - end - end - - catLabels = arrayfun(@(nm) sprintf('%d nm', nm), round(wavelengthplan), 'UniformOutput', false); - violinplot(S_mat, catLabels, ... - 'ViolinColor', colsTech(i,:), ... - 'ViolinAlpha', 0.10, ... - 'MarkerSize', 20, ... - 'ShowMedian', true, ... - 'EdgeColor', colsTech(i,:), ... - 'ShowWhiskers', false, ... - 'ShowData', true, ... - 'ShowBox', false, ... - 'Bandwidth', 0.05); - - % Add a legend proxy - plot(nan, nan, 'o', 'Color', colsTech(i,:), 'DisplayName', techNames{i}); - end - - ylim([-10,-6]) - ylabel('ROP at FEC crossing'); - title(sprintf('ROP to cross BER %.2e @ %s', fec, distLabel(distances, eval_ptr_violin))); - grid on; box on; - legend show; -else - fprintf('violinplot.m not found on path -> skipping violin plot.\n'); -end - - -%% ================= helpers ================= - -function cells2D = sliceCells4D(C4, ch, eval_ptr, N_rop) -% Returns a [N_rop x N_realiz] cell array (with N_realiz inferred) -% Works even if trailing dimensions are singleton. -% -% If C4 is smaller than expected, missing entries are returned as []. - -dims = size(C4); -if numel(dims) < 4, dims(end+1:4) = 1; end -N_realiz = dims(3); - -cells2D = cell(N_rop, N_realiz); - -% bounds (in case stored arrays are smaller) -chMax = dims(1); -ropMax = dims(2); -rMax = dims(3); -eMax = dims(4); - -if ch > chMax || eval_ptr > eMax - return; % all empty -end - -ropUse = min(N_rop, ropMax); -rUse = min(N_realiz, rMax); - -% Extract and place into a consistent sized cell matrix -tmp = squeeze(C4(ch, 1:ropUse, 1:rUse, eval_ptr)); -% squeeze may return vector/empty if rUse==1 etc. Normalize: -tmp = reshape(tmp, ropUse, rUse); - -cells2D(1:ropUse, 1:rUse) = tmp; -end - -function lbl = distLabel(distances, eval_ptr) -if eval_ptr <= numel(distances) - d = distances(eval_ptr); - if isfinite(d) - lbl = sprintf('%.0f km', d); - return; - end -end -lbl = sprintf('eval\\_%d', eval_ptr); -end - -function plotBandMeanBL(x, Y, color, techLabel, qLow, qHigh, lineSpec, showLegend) -% Y: (nPoints x nRealizations) -if isempty(Y), return; end - -% Remove realizations that are entirely zero -badCols = all(Y == 0, 1); -Y(:, badCols) = []; - -if isempty(Y), return; end - -% Avoid log(0) -Y(Y==0) = 1e-8; - -mu = mean(Y, 2, 'omitnan'); -lo = quantile(Y, qLow, 2); -hi = quantile(Y, qHigh, 2); -b = [mu - lo, hi - mu]; - -% boundedline optional; fallback to simple plot if missing -if 0%exist('boundedline','file') == 2 - [hl, hp] = boundedline(x(:), mu(:), b, lineSpec, 'alpha', 'transparency', 0.18); - set(hl, 'Color', color, 'LineWidth', 1.4, 'MarkerSize', 4); - set(hp, 'FaceColor', color, 'HandleVisibility','off'); -else - hl = plot(x(:), mu(:), lineSpec, 'LineWidth', 1.4, 'MarkerSize', 4); - set(hl, 'Color', color); -end - -if showLegend - set(hl, 'DisplayName', techLabel); -else - set(hl, 'HandleVisibility','off'); -end - -if 0%exist('outlinebounds','file') == 2 && exist('boundedline','file') == 2 - ho = outlinebounds(hl, hp); - set(ho, 'linestyle', ':', 'color', color, 'linewidth', 1, 'HandleVisibility','off'); -end -end - -function [S, noCrossingMask] = fecCrossings(rop, cellsNxR, fec) -% cellsNxR: N_rop x R cell array; each cell is struct with .metrics.BER -Y = extractCompleteBER(cellsNxR); % -> N_rop x K -if isempty(Y) - S = []; - noCrossingMask = []; - return; -end - -ok = mean(Y,1,'omitnan') <= 0.1; -Y = Y(:, ok); -if isempty(Y) - S = []; - noCrossingMask = []; - return; -end - -nR = size(Y,2); -S = nan(1,nR); -noCrossingMask = true(1,nR); - -rop = rop(:); -for j = 1:nR - y = Y(:,j); - above = (y > fec); - idx = find(above(1:end-1) & ~above(2:end), 1, 'first'); - if ~isempty(idx) - x1 = rop(idx); y1 = y(idx); - x2 = rop(idx+1); y2 = y(idx+1); - if isfinite(y1) && isfinite(y2) && y2 ~= y1 - t = (fec - y1) / (y2 - y1); - S(j) = x1 + t*(x2 - x1); - noCrossingMask(j) = false; - end - end -end -end - -function Y = extractCompleteBER(cellSlice) -% Keep only those realization columns where ALL N_rop entries are non-empty -if isempty(cellSlice), Y = []; return; end -nR = size(cellSlice,2); -keep = false(1,nR); - -for r = 1:nR - col = cellSlice(:,r); - keep(r) = all(cellfun(@(c) ~isempty(c), col)); -end - -if ~any(keep), Y = []; return; end - -Y = cellfun(@(c) c.metrics.BER, cellSlice(:,keep), 'UniformOutput', true); -end - -function Y = extractCompleteAlphas(cellSlice) -if isempty(cellSlice), Y = []; return; end -nR = size(cellSlice,2); -keep = false(1,nR); - -for r = 1:nR - col = cellSlice(:,r); - keep(r) = all(cellfun(@(c) ~isempty(c), col)); -end - -if ~any(keep), Y = []; return; end -Y = cellfun(@(c) c.metrics.Alpha, cellSlice(:,keep), 'UniformOutput', true); -end +%% Routine B: violin plot (independent) +plot_FEC_violin(res, 'tech','VNLE', 'fec',3.8e-3, 'eval_ptr',3, 'ylim',[-10 -3]); diff --git a/projects/WDM/WDM_model_10km_queue.m b/projects/WDM/WDM_model_10km_queue.m index 24b7e4e..e81cbb2 100644 --- a/projects/WDM/WDM_model_10km_queue.m +++ b/projects/WDM/WDM_model_10km_queue.m @@ -12,6 +12,7 @@ arguments options.rand_key = 1; options.num_realiz = 1; options.fwm_mitigation_technique = "co"; + options.chirpalpha = 0; end %% Add the imdd_simulation framework to the path @@ -59,7 +60,9 @@ else end if ~exist(output_root,'dir'), mkdir(output_root); end -fname = sprintf('WDM_%s_%s_%s_%dkm_%dch_%dghz_%s.mat', char(t), host, jobid, options.fiber_length_km(end), options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); +fname = sprintf('WDM_%s_%s_%s_%dkm_%dch_%dghz_%s_alpha%0.1f', char(t), host, jobid, options.fiber_length_km(end), options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique, options.chirpalpha); +fname = strrep(fname,'.','_'); +fname = [fname,'.mat']; % Worker memory logs directory memlog_dir = fullfile(output_root, 'memlogs'); @@ -116,12 +119,12 @@ upsample_pow = 2^nextpow2(upsample_required); s.f_opt = fdac*kover*upsample_pow; s.f_opt_nyq = s.f_opt/2; -s.rop = -12:0.75:-0.75; -% s.rop = -12:1:0; +s.rop = -10:0.5:0; + %% ---------- Intermediate evaluation points (distance dimension) ---------- segment_length = 1; % km (must match fiber loop below) -eval_dist_km = [0]; +eval_dist_km = [2,4,6,8,10]; eval_dist_km = eval_dist_km(eval_dist_km <= link_length); if link_length > 0 @@ -208,7 +211,9 @@ for realiz = 1:s.num_realiz % 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+realiz,"alpha",0.8).process(El_sig); + "linewidth",laser_linewidth,"randomkey",s.random_key+l+realiz,"alpha",options.chirpalpha).process(El_sig); + + s.alpha = options.chirpalpha; % El_sig not needed after EML clear El_sig @@ -244,7 +249,7 @@ for realiz = 1:s.num_realiz nSegments = round(nSegments); zdw = 1310; - randomize_D = true; + randomize_D = false; % Guard for 0 km: avoid calling getDispersionVector(0,...) if it doesn't support it if nSegments > 0 @@ -256,7 +261,6 @@ for realiz = 1:s.num_realiz eval_ptr = 1; % =================== Queue throttle (prevents OOM) =================== % Limit how many futures can be outstanding (running + queued + finished-not-yet-fetched). - % Start conservatively; you can raise to 4*NumWorkers if stable. maxInFlight = max(2, p.NumWorkers); % ===================================================================== @@ -300,6 +304,10 @@ for realiz = 1:s.num_realiz end % ----------------------------------------------------- + save_results_per_realization( ... + s, eval_dist_km, ... + output_ffe, output_dfe, output_vnle, output_mlse, output_dbt, ... + output_root, fname); for seg = 1:nSegments @@ -318,7 +326,6 @@ for realiz = 1:s.num_realiz cnt = 0; total = length(s.rop)*N; - fprintf('total of %d jobs to enqueue at 0 km\n', total); for ri = 1:length(s.rop) for l = 1:N @@ -338,8 +345,8 @@ for realiz = 1:s.num_realiz len_tr, mu_dc, mu_ffe, mu_dfe, dfe_order, duob_mode, ... memlog_dir); - fprintf('Enqueued job %d/%d for realiz %d/%d, l=%d/%d, ri=%d/%d at 0 km (inflight=%d/%d)\n', ... - cnt, total, realiz, s.num_realiz, l, N, ri, length(s.rop), numel(F), maxInFlight); + fprintf('Seg: %d - Enqueued job %d/%d for realiz %d/%d, l=%d/%d, ri=%d/%d (inflight=%d/%d)\n', ... + seg, cnt, total, realiz, s.num_realiz, l, N, ri, length(s.rop), numel(F), maxInFlight); meta(end+1) = struct('l',l,'ri',ri,'realiz',realiz,'eval_ptr',eval_ptr); end @@ -356,24 +363,38 @@ for realiz = 1:s.num_realiz end - while ~isempty(F) + drainIterMax = 10; % 10 * 60s = 10 minutes + for drainIter = 1:drainIterMax + + if isempty(F) + break; + end + + % Status line: how many futures are in which states? + st = {F.State}; + nUnavail = sum(strcmp(st,'unavailable')); + nFinished = sum(strcmp(st,'finished')); + nRunning = sum(strcmp(st,'running')); + nQueued = sum(strcmp(st,'queued')); + nFailed = sum(strcmp(st,'failed')); + fprintf('[drain %d/%d] F=%d | finished=%d running=%d queued=%d failed=%d unavailable=%d\n', ... + drainIter, drainIterMax, numel(F), nFinished, nRunning, nQueued, nFailed, nUnavail); + + % Try to fetch at least one result (blocking) [F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt] = ... collect_done(F, meta, output_ffe, output_dfe, output_vnle, output_mlse, output_dbt, true); + + % If still not empty, wait a bit before the next drain attempt + if ~isempty(F) + pause(60); + end end - %% Save results (per realization) - res = struct(); - res.settings = s; - res.eval_dist_km = eval_dist_km; - res.ffe = output_ffe; - res.dfe = output_dfe; - res.vnle = output_vnle; - res.mlse = output_mlse; - res.dbt = output_dbt; - save(fullfile(output_root, fname), 'res', '-v7.3'); - fprintf('Saved results to: %s\n', fullfile(output_root, fname)); - disp(datetime('now','TimeZone','local','Format','yyyyMs.Mdd_HHmmss')); + save_results_per_realization( ... + s, eval_dist_km, ... + output_ffe, output_dfe, output_vnle, output_mlse, output_dbt, ... + output_root, fname); % Per-realization large arrays that are no longer needed clear Opt_sig_wdm_fib Symbols Tx_bits Dvec @@ -524,6 +545,16 @@ end first = true; while ~isempty(F) + + idxUn = strcmp({F.State}, 'unavailable'); + if any(idxUn) + warning('collect_done: dropping %d unavailable futures.', sum(idxUn)); + F(idxUn) = []; + meta(idxUn) = []; + if isempty(F), break; end + end + + if first timeout = timeout_first; first = false; @@ -555,6 +586,31 @@ end end +function save_results_per_realization( ... + s, eval_dist_km, ... + output_ffe, output_dfe, output_vnle, output_mlse, output_dbt, ... + output_root, fname) + +% Assemble result struct +res = struct(); +res.settings = s; +res.eval_dist_km = eval_dist_km; +res.ffe = output_ffe; +res.dfe = output_dfe; +res.vnle = output_vnle; +res.mlse = output_mlse; +res.dbt = output_dbt; + +% Save (HDF5-based for large data) +save(fullfile(output_root, fname), 'res', '-v7.3'); + +fprintf('Saved results to: %s\n', fullfile(output_root, fname)); +disp(datetime('now','TimeZone','local','Format','yyyyMs.Mdd_HHmmss')); + +end + + + %% ========================= Memory logging helpers ========================= function logfile = make_worker_logfile(memlog_dir) diff --git a/projects/WDM/plot_BER_vs_ROP.m b/projects/WDM/plot_BER_vs_ROP.m new file mode 100644 index 0000000..b8ac31f --- /dev/null +++ b/projects/WDM/plot_BER_vs_ROP.m @@ -0,0 +1,199 @@ +%% ========================= +% Routine A: BER vs ROP plots +%% ========================= +function S = plot_BER_vs_ROP(res, varargin) +% S = plot_BER_vs_ROP(res, 'fec', 3.8e-3, 'eval_list', [], 'colormap', 'Spectral') +% +% Creates one BER-vs-ROP figure per evaluated distance. +% Returns S struct with FEC crossings: S.Sffe, S.Sdfe, S.Svnle, S.Smlse, S.Sdbt +% +% res.* assumed: res.ffe{ch,rop,realiz,eval_distance} etc. + +p = inputParser; +p.addParameter('fec', 3.8e-3, @(x)isnumeric(x)&&isscalar(x)); +p.addParameter('eval_list', [], @(x)isnumeric(x)); +p.addParameter('colormap', 'Spectral', @(x)ischar(x) || isstring(x)); +p.parse(varargin{:}); +fec = p.Results.fec; + +% -------------------- Basic metadata -------------------- +rop = res.settings.rop(:); +wavelengthplan = res.settings.wavelengthplan(:); +distances = res.eval_dist_km(:); + +N_ch = numel(wavelengthplan); +N_rop = numel(rop); + +% -------------------- Determine dims robustly -------------------- +dims = size(res.ffe); +if numel(dims) < 4, dims(end+1:4) = 1; end +N_distances = dims(4); + +if numel(distances) ~= N_distances + distances = (1:N_distances).'; +end + +% -------------------- Choose eval distances -------------------- +eval_list = p.Results.eval_list; +if isempty(eval_list) + eval_list = 1:N_distances; +end + +% -------------------- Colors -------------------- +try + cols = cbrewer2(char(p.Results.colormap), N_ch); +catch + cols = linspecer(N_ch); +end + +% -------------------- Crossings containers -------------------- +S = struct(); +S.rop = rop; +S.wavelengthplan = wavelengthplan; +S.distances = distances; +S.fec = fec; + +S.Sffe = cell(N_distances, N_ch); +S.Sdfe = cell(N_distances, N_ch); +S.Svnle = cell(N_distances, N_ch); +S.Smlse = cell(N_distances, N_ch); +S.Sdbt = cell(N_distances, N_ch); + +% -------------------- Plot per distance -------------------- +for eval_ptr = eval_list + + figure('Name', sprintf('BER vs ROP @ %s', distLabel(distances, eval_ptr))); + hold on; + + for ch = 1:N_ch + + % Extract cell slices + ffe_cells = sliceCells4D(res.ffe , ch, eval_ptr, N_rop); + dfe_cells = sliceCells4D(res.dfe , ch, eval_ptr, N_rop); + vnle_cells = sliceCells4D(res.vnle, ch, eval_ptr, N_rop); + mlse_cells = sliceCells4D(res.mlse, ch, eval_ptr, N_rop); + dbt_cells = sliceCells4D(res.dbt , ch, eval_ptr, N_rop); + + % Crossings (only depends on cells) + [S.Sffe{eval_ptr,ch}, ~] = fecCrossings(rop, ffe_cells, fec); + [S.Sdfe{eval_ptr,ch}, ~] = fecCrossings(rop, dfe_cells, fec); + [S.Svnle{eval_ptr,ch}, ~] = fecCrossings(rop, vnle_cells, fec); + [S.Smlse{eval_ptr,ch}, ~] = fecCrossings(rop, mlse_cells, fec); + [S.Sdbt{eval_ptr,ch}, ~] = fecCrossings(rop, dbt_cells, fec); + + % BER matrices (complete realizations only) + ffe_mat = extractCompleteBER(ffe_cells); % N_rop x K + + if ~isempty(ffe_mat) + % Your current style: plot each realization curve (no boundedline) + plot(rop, ffe_mat, 'Color', cols(ch,:), ... + 'DisplayName', sprintf('FFE @ %dnm', round(wavelengthplan(ch)))); + end + end + + set(gca,'XScale','linear','YScale','log', ... + 'TickLabelInterpreter','latex','FontSize',11); + + yline([3.8e-3, 2.2e-4], 'HandleVisibility','off','LineWidth',1.5); + + ylabel('BER'); + xlabel('ROP [dB]'); + title(sprintf('BER vs. ROP @ %s', distLabel(distances, eval_ptr))); + xlim([min(rop) max(rop)]); + ylim([1e-5 0.3]); + grid on; + legend show; + beautifyBERplot; +end +end + + + +%% ========================= +% Shared helpers (unchanged) +%% ========================= + +function cells2D = sliceCells4D(C4, ch, eval_ptr, N_rop) +dims = size(C4); +if numel(dims) < 4, dims(end+1:4) = 1; end +N_realiz = dims(3); + +cells2D = cell(N_rop, N_realiz); + +chMax = dims(1); +ropMax = dims(2); +rMax = dims(3); +eMax = dims(4); + +if ch > chMax || eval_ptr > eMax + return; +end + +ropUse = min(N_rop, ropMax); +rUse = min(N_realiz, rMax); + +tmp = squeeze(C4(ch, 1:ropUse, 1:rUse, eval_ptr)); +tmp = reshape(tmp, ropUse, rUse); + +cells2D(1:ropUse, 1:rUse) = tmp; +end + +function lbl = distLabel(distances, eval_ptr) +if eval_ptr <= numel(distances) + d = distances(eval_ptr); + if isfinite(d) + lbl = sprintf('%.0f km', d); + return; + end +end +lbl = sprintf('eval\\_%d', eval_ptr); +end + +function [S, noCrossingMask] = fecCrossings(rop, cellsNxR, fec) +Y = extractCompleteBER(cellsNxR); +if isempty(Y) + S = []; + noCrossingMask = []; + return; +end + +ok = mean(Y,1,'omitnan') <= 0.1; +Y = Y(:, ok); +if isempty(Y) + S = []; + noCrossingMask = []; + return; +end + +nR = size(Y,2); +S = nan(1,nR); +noCrossingMask = true(1,nR); + +rop = rop(:); +for j = 1:nR + y = Y(:,j); + above = (y > fec); + idx = find(above(1:end-1) & ~above(2:end), 1, 'first'); + if ~isempty(idx) + x1 = rop(idx); y1 = y(idx); + x2 = rop(idx+1); y2 = y(idx+1); + if isfinite(y1) && isfinite(y2) && y2 ~= y1 + t = (fec - y1) / (y2 - y1); + S(j) = x1 + t*(x2 - x1); + noCrossingMask(j) = false; + end + end +end +end + +function Y = extractCompleteBER(cellSlice) +if isempty(cellSlice), Y = []; return; end +nR = size(cellSlice,2); +keep = false(1,nR); +for r = 1:nR + col = cellSlice(:,r); + keep(r) = all(cellfun(@(c) ~isempty(c), col)); +end +if ~any(keep), Y = []; return; end +Y = cellfun(@(c) c.metrics.BER, cellSlice(:,keep), 'UniformOutput', true); +end diff --git a/projects/WDM/plot_FEC_violin.m b/projects/WDM/plot_FEC_violin.m new file mode 100644 index 0000000..5ce9217 --- /dev/null +++ b/projects/WDM/plot_FEC_violin.m @@ -0,0 +1,213 @@ +function plot_FEC_violin(res, varargin) +% plot_fec_violin_from_res Self-contained violin plot (no dependency on other routines) +% +% Usage: +% plot_fec_violin_from_res('WDM_20260106_....mat', 'tech', 'FFE', 'fec', 3.8e-3, 'eval_ptr', 2, 'ylim', [-10 -6]); +% plot_fec_violin_from_res(res, 'tech', 'FFE', 'fec', 3.8e-3, 'eval_ptr', 2); +% +% Notes: +% - Works with partially-filled res.* cell arrays (missing eval distances / missing realizations). +% - Uses only complete realizations per channel (all ROP points present). +% - Requires violinplot.m on path. + +p = inputParser; +p.addParameter('tech', 'FFE', @(x)ischar(x) || isstring(x)); +p.addParameter('fec', 3.8e-3, @(x)isnumeric(x)&&isscalar(x)); +p.addParameter('eval_ptr', [], @(x)isnumeric(x)&&isscalar(x)); +p.addParameter('ylim', [], @(x)isnumeric(x)&&numel(x)==2); +p.addParameter('title_prefix', 'ROP at FEC crossing', @(x)ischar(x)||isstring(x)); +p.parse(varargin{:}); + +if exist('violinplot','file') ~= 2 + error('violinplot.m not found on path. Add it to path first.'); +end + + +% ---------- Metadata ---------- +rop = res.settings.rop(:); +wavelengthplan = res.settings.wavelengthplan(:); +distances = res.eval_dist_km(:); + +N_ch = numel(wavelengthplan); +N_rop = numel(rop); + +% Determine distances dim robustly from the stored cell arrays (prefer ffe) +dims = size(res.ffe); +if numel(dims) < 4, dims(end+1:4) = 1; end +N_distances = dims(4); +if numel(distances) ~= N_distances + distances = (1:N_distances).'; +end + +% Choose eval distance +eval_ptr = p.Results.eval_ptr; +if isempty(eval_ptr) + eval_ptr = N_distances; % default: last available +end +if eval_ptr < 1 || eval_ptr > N_distances + error('eval_ptr=%d out of range. Available: 1..%d', eval_ptr, N_distances); +end + +% Choose technique field +tech = upper(string(p.Results.tech)); +switch tech + case "FFE", C4 = res.ffe; + case "DFE", C4 = res.dfe; + case "VNLE", C4 = res.vnle; + case "MLSE", C4 = res.mlse; + case "DBT", C4 = res.dbt; + otherwise + error('Unknown tech "%s". Use one of: FFE, DFE, VNLE, MLSE, DBT.', tech); +end + +fec = p.Results.fec; + +% ---------- Compute crossings per channel ---------- + +% ---------- Plot ---------- +figure('Name', sprintf('%s violin @ %s', tech, distLabel(distances, eval_ptr))); +hold on; +c = linspecer(N_distances); +for eval_ptr = 1:N_distances + S_cell = cell(1, N_ch); + + for ch = 1:N_ch + cells = sliceCells4D(C4, ch, eval_ptr, N_rop); % N_rop x N_realiz cell + S_cell{ch} = fecCrossings_only(rop, cells, fec); % 1 x K crossings (may be empty) + end + + % Pad to matrix for violinplot: rows=realizations (max K), cols=channels + Kmax = max(cellfun(@numel, S_cell)); + if isempty(Kmax) || Kmax == 0 + warning('No FEC crossings found for %s at eval_ptr=%d (%s).', tech, eval_ptr, distLabel(distances, eval_ptr)); + return; + end + + S_mat = NaN(Kmax, N_ch); + for ch = 1:N_ch + k = numel(S_cell{ch}); + if k > 0 + S_mat(1:k, ch) = S_cell{ch}(:); + end + end + + catLabels = arrayfun(@(nm) sprintf('%d nm', nm), round(wavelengthplan), 'UniformOutput', false); + + if numel(S_mat)>numel(wavelengthplan) + S_mat(isnan(S_mat)) = 0; + v=violinplot(S_mat, catLabels, ... + 'ViolinColor', c(eval_ptr,:), ... + 'ViolinAlpha', 0.10, ... + 'MarkerSize', 20, ... + 'ShowMedian', true, ... + 'EdgeColor', c(eval_ptr,:), ... + 'ShowWhiskers', false, ... + 'ShowData', true, ... + 'ShowBox', false, ... + 'Bandwidth', 0.05); + else + channs = 1:numel(wavelengthplan); + scatter( wavelengthplan ,S_mat,10,c(eval_ptr,:),'Marker','o','LineWidth',2,'DisplayName',distLabel(distances, eval_ptr)); + + channs = 1:numel(wavelengthplan); + scatter( round(wavelengthplan(S_mat==0)),S_mat(S_mat==0),10,c(eval_ptr,:),'Marker','o','HandleVisibility','off'); + end + +end + +ylabel('ROP at FEC crossing'); +title(sprintf('%s: %s | BER %.2e | %s', ... + p.Results.title_prefix, tech, fec, distLabel(distances, eval_ptr))); +grid on; box on; + +if ~isempty(p.Results.ylim) + ylim(p.Results.ylim); +end + +end + + +%% ================= helpers (self-contained) ================= + +function cells2D = sliceCells4D(C4, ch, eval_ptr, N_rop) +dims = size(C4); +if numel(dims) < 4, dims(end+1:4) = 1; end +N_realiz = dims(3); + +cells2D = cell(N_rop, N_realiz); + +chMax = dims(1); +ropMax = dims(2); +rMax = dims(3); +eMax = dims(4); + +if ch > chMax || eval_ptr > eMax + return; +end + +ropUse = min(N_rop, ropMax); +rUse = min(N_realiz, rMax); + +tmp = squeeze(C4(ch, 1:ropUse, 1:rUse, eval_ptr)); +tmp = reshape(tmp, ropUse, rUse); + +cells2D(1:ropUse, 1:rUse) = tmp; +end + +function lbl = distLabel(distances, eval_ptr) +if eval_ptr <= numel(distances) + d = distances(eval_ptr); + if isfinite(d) + lbl = sprintf('%.0f km', d); + return; + end +end +lbl = sprintf('eval\\_%d', eval_ptr); +end + +function S = fecCrossings_only(rop, cellsNxR, fec) +% Returns 1xK crossing positions for all complete realizations (drops bad ones). +Y = extractCompleteBER(cellsNxR); % N_rop x K +if isempty(Y), S = []; return; end + +% Optional quality gate (same as your previous scripts) +ok = mean(Y,1,'omitnan') <= 0.1; +Y = Y(:, ok); +if isempty(Y), S = []; return; end + +nR = size(Y,2); +S = nan(1,nR); + +rop = rop(:); +for j = 1:nR + y = Y(:,j); + above = (y > fec); + idx = find(above(1:end-1) & ~above(2:end), 1, 'first'); + if ~isempty(idx) + x1 = rop(idx); y1 = y(idx); + x2 = rop(idx+1); y2 = y(idx+1); + if isfinite(y1) && isfinite(y2) && y2 ~= y1 + t = (fec - y1) / (y2 - y1); + S(j) = x1 + t*(x2 - x1); + end + end +end + +S = S(isfinite(S)); % keep only successful crossings +end + +function Y = extractCompleteBER(cellSlice) +% Keep only those realization columns where ALL N_rop entries are non-empty +if isempty(cellSlice), Y = []; return; end +nR = size(cellSlice,2); +keep = false(1,nR); + +for r = 1:nR + col = cellSlice(:,r); + keep(r) = all(cellfun(@(c) ~isempty(c), col)); +end + +if ~any(keep), Y = []; return; end + +Y = cellfun(@(c) c.metrics.BER, cellSlice(:,keep), 'UniformOutput', true); +end From 5c39a7f98a0a08121dfc540883320d9b33882057 Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Wed, 7 Jan 2026 15:07:38 +0100 Subject: [PATCH 09/12] new minimal example new project: FSO stuff --- Classes/00_signals/Electricalsignal.m | 3 - Classes/00_signals/Opticalsignal.m | 3 +- Classes/00_signals/Signal.m | 22 +-- Functions/Metrics/air_garcia_implementation.m | 1 - projects/FSO_transmission/first_analysis.m | 12 ++ projects/IMDD_base_system/imdd_it.m | 7 +- projects/IMDD_base_system/imdd_model.m | 6 +- projects/IMDD_base_system/minimal_example.m | 156 ++++++++++++++++++ 8 files changed, 185 insertions(+), 25 deletions(-) create mode 100644 projects/FSO_transmission/first_analysis.m create mode 100644 projects/IMDD_base_system/minimal_example.m diff --git a/Classes/00_signals/Electricalsignal.m b/Classes/00_signals/Electricalsignal.m index 7da0e69..e9b4bf7 100644 --- a/Classes/00_signals/Electricalsignal.m +++ b/Classes/00_signals/Electricalsignal.m @@ -108,9 +108,6 @@ classdef Electricalsignal < Signal obj.signal = obj.signal*scling; end - - - end end diff --git a/Classes/00_signals/Opticalsignal.m b/Classes/00_signals/Opticalsignal.m index b39511e..214e9e2 100644 --- a/Classes/00_signals/Opticalsignal.m +++ b/Classes/00_signals/Opticalsignal.m @@ -70,8 +70,7 @@ classdef Opticalsignal < Signal s = mean( abs(obj.signal-mean(obj.signal)).^2 ); cspr = 10*log10(c / s); - - + end diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index f6dd186..0f3578e 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -98,7 +98,6 @@ classdef Signal end - %% CONVERT TO Opticalsignal function o_sig = Opticalsignal(obj, options) @@ -273,7 +272,6 @@ classdef Signal SignalCopy = []; ModifierName = class(CallingModifier); - cell = {SignalType , TimeStamp , Length , SignalPower(1) , Nase, SignalCopy, ModifierName, ModifierCopy, Description}; obj.logbook = [obj.logbook; cell]; @@ -363,8 +361,7 @@ classdef Signal options.fft_length = 2^(nextpow2(length(obj.signal))-9); end - - + if options.normalizeToNyquist == 0 [p_lin,f_Hz] = pwelch(obj.signal, hanning(options.fft_length), ... options.fft_length/2, options.fft_length, ... @@ -488,7 +485,6 @@ classdef Signal end - function move_it_spectrum(obj,options) arguments obj @@ -630,7 +626,6 @@ classdef Signal case power_notation.W %pow = pow % Watt - end end @@ -668,7 +663,7 @@ classdef Signal %% PAPR of signal function papr_db = papr_db(obj) - %PAPR The peak-to-average power ratio (PAPR) is the peak amplitude squared (giving the peak power) + % PAPR The peak-to-average power ratio (PAPR) is the peak amplitude squared (giving the peak power) % divided by the RMS value squared (giving the average power).[1] It is the square of the crest factor. % papr = max(abs(timesignal))^2 / rms(timesignal)^2; ODER papr = peak2rms(sig)^2; @@ -722,7 +717,7 @@ classdef Signal end - %% + %% function [obj,S,inverted,sequenceFound,sequenceStarts] = tsynch(obj,options) % time sync and cut arguments @@ -732,14 +727,11 @@ classdef Signal options.debug_plots = 0; end - - S = {}; inverted = -1; sequenceFound = 0; sequenceStarts = []; - %normalize the signal a = obj.normalize("mode","oneone").signal; @@ -777,8 +769,6 @@ classdef Signal findpeaks(abs(co./max(co)),'MinPeakDistance',length(b)/2,'MinPeakHeight',0.2,'NPeaks',maxpeaknum,'SortStr','descend') end - - shifts = lags(pkpos); sequenceStarts = shifts; shifts = shifts(shifts>=0); @@ -831,7 +821,6 @@ classdef Signal end - %% function obj = filter(obj,a,b) @@ -921,6 +910,7 @@ classdef Signal end + %% function eye(obj,fsym,M,options) @@ -981,8 +971,8 @@ classdef Signal maxA = max(sig(100:end-100))*1.3; minA = min(sig(100:end-100))*1.3; - maxA = 0.12; - minA = -0.08; + % maxA = 0.12; + % minA = -0.08; difference= maxA-minA; diff --git a/Functions/Metrics/air_garcia_implementation.m b/Functions/Metrics/air_garcia_implementation.m index c71a26a..c560bf5 100644 --- a/Functions/Metrics/air_garcia_implementation.m +++ b/Functions/Metrics/air_garcia_implementation.m @@ -11,7 +11,6 @@ if nargin == 4 M_training = []; end - % if input is complex, separate into real and imaginary parts if any(imag(x(:))~=0) || any(imag(r(:))~=0) x = [real(x); imag(x)]; diff --git a/projects/FSO_transmission/first_analysis.m b/projects/FSO_transmission/first_analysis.m new file mode 100644 index 0000000..530e692 --- /dev/null +++ b/projects/FSO_transmission/first_analysis.m @@ -0,0 +1,12 @@ + +datas = load("C:\Users\Silas\Downloads\FSO_FP_QCL_60umUTC\FSO_FP_QCL_60umUTC\6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=225mA_RoP=28.02mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat"); + +fsym = 6e9; +fs = 80e9; + +rawsignal = datas.tr.lastData(1).trace.ch3.RawData; + +Scope_sig = Electricalsignal(rawsignal,"fs",fs); + +Scope_sig.plot("displayname",'raw','fignum',100); + diff --git a/projects/IMDD_base_system/imdd_it.m b/projects/IMDD_base_system/imdd_it.m index c9d9cf4..a495d74 100644 --- a/projects/IMDD_base_system/imdd_it.m +++ b/projects/IMDD_base_system/imdd_it.m @@ -1,6 +1,7 @@ if 1 + uloops = struct; uloops.precomp = [1]; uloops.bitrate = [300].*1e9; %[300,330,360,390,420,450,480] [224,336,360,390,420,448] for MPI @@ -8,12 +9,12 @@ if 1 uloops.laser_wavelength = [1293]; uloops.M = [4]; uloops.link_length = [0:2:10]; % 1,2,3,5,6,8,10 - uloops.alpha = [0.5,-0.5,0]; + uloops.alpha = [0]; wh = DataStorage(uloops); wh.addStorage("ber"); - wh = submit_handle(@imdd_model,wh,"parallel",1); + wh = submit_handle(@imdd_model,wh,"parallel",0); end @@ -34,6 +35,8 @@ beautifyBERplot() ylabel('BER'); + + % % wh_ana = wh_master; % diff --git a/projects/IMDD_base_system/imdd_model.m b/projects/IMDD_base_system/imdd_model.m index 06b1812..5a89483 100644 --- a/projects/IMDD_base_system/imdd_model.m +++ b/projects/IMDD_base_system/imdd_model.m @@ -92,9 +92,13 @@ if fsym_ ~= fsym % fprintf('Adapted symbolrate to %d GBd, to match provided bitrate of %d GBit/s using PAM %d \n',fsym.*1e-9,bitrate.*1e-9, M); end + + + + f_nyquist = fsym/2; -Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha); +Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); [Digi_sig,Symbols,Tx_bits] = PAMsource(... "fsym",fsym,"M",M,"order",18,"useprbs",0,... diff --git a/projects/IMDD_base_system/minimal_example.m b/projects/IMDD_base_system/minimal_example.m new file mode 100644 index 0000000..89f24ff --- /dev/null +++ b/projects/IMDD_base_system/minimal_example.m @@ -0,0 +1,156 @@ +% minimal example IM/DD + +M = 4; +fsym = 180e9; + +apply_pulsef = 1; +fdac = 256e9; +fadc = 256e9; +random_key = 1; + +rcalpha = 0.05; +kover = 16; + +duob_mode = db_mode.no_db; + +vbias_rel = 0.5; +u_pi = 3; +vbias = -vbias_rel*u_pi; + +laser_wavelength = 1293; +laser_linewidth = 0; +tx_bw_nyquist = 0.8; + +% Channel +link_length = 1; + +% RX +rop = -8; +rx_bw_nyquist = 0.8; + +vnle_order1 = 50; +vnle_order2 = 7; +vnle_order3 = 7; + +vnle_order=[vnle_order1,vnle_order2,vnle_order3]; +dfe_order = [0 0 0]; + +pf_ncoeffs = 1; + +alpha = 0; + +len_tr = 4096*2; + +mu_ffe1 = 0.0001; +mu_ffe2 = 0.0008; +mu_ffe3 = 0.001; +mu_dc = 0.005; +% mu_dc = 0; + +mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3]; +mu_dfe = 0.0004; + + +Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); + +[Digi_sig,Symbols,Tx_bits] = PAMsource(... + "fsym",fsym,"M",M,"order",18,"useprbs",0,... + "fs_out",fdac,... + "applyclipping",0,"clipfactor",1.5,... + "applypulseform",apply_pulsef,"pulseformer",Pform,... + "randkey",random_key,... + 'duobinary_mode',duob_mode,... + "mrds_code",0,"mrds_blocklength",512).process(); + +%%%%% AWG +El_sig = M8199A("kover",kover).process(Digi_sig); +% El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); +El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0); +% El_sig = El_sig.setPower(0,"dBm"); + +%%%%% Electrical Driver Amplifier %%%%%% +% El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",3).process(El_sig); +El_sig = El_sig.normalize("mode","oneone"); +scaling = 0.6*(u_pi/2-abs(vbias-u_pi/2)); +El_sig = El_sig .* scaling; + +%%%%% MODULATE E/O CONVERSION %%%%%% +[Opt_sig] = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",laser_wavelength,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",random_key+1,"alpha",alpha).process(El_sig); + +Opt_sig.spectrum("displayname",'Opt Spectrum','fignum',10,'normalizeTo0dB',1); + +% Opt_sig.eye(fsym,M,"displayname",'eye adter modulator','fignum',2026); + +Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig); + +%%%%%% ROP %%%%%% +Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig); + +%%%%%% PD Square Law %%%%%% +Rx_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11).process(Rx_sig); + +%%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% +rx_bwl = 80e9; +Rx_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(Rx_sig); + +% %%%%%% Low-pass Scope %%%%%% +Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); + +%%%%%% Scope %%%%%% +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',1,'H_lpf',Lp_scpe).process(Rx_sig); + +%%%%%% Sample to 2x fsym %%%%%% +Scpe_sig = Scpe_sig.resample("fs_out",2*fsym); +Scpe_sig.signal = Scpe_sig.signal(1:2*length(Symbols)); + +%%%%%% Sync Rx signal with reference %%%%%% +[Scpe_sig,~] = Scpe_sig.tsynch("reference",Symbols,"fs_ref",fsym,"debug_plots",0); +Scpe_sig.spectrum("displayname",'Opt Spectrum','fignum',11,'normalizeTo0dB',1); + +Scpe_sig = Filter('filtdegree',4,"f_cutoff",Symbols.fs.*0.5,"fs",Scpe_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scpe_sig); + +Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); + +% -------------------- FFE -------------------- +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); + +output.ffe_results = ffe(eq_ffe,M,Scpe_sig,Symbols,Tx_bits, ... + "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ... + "eth_style_symbol_mapping",0); + +output.ffe_results.metrics.print + +% -------------------- VNLE + MLSE -------------------- +pf_ncoeffs = 1; +ffe_order3 = [50, 5, 5]; +eq_v = EQ("Ne",ffe_order3,"Nb",dfe_order, ... + "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",1); +pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + +mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); + +[output.vnle_results, output.mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [], "eth_style_symbol_mapping", 0); + + +% -------------------- DB target -------------------- +mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels); +ffe_order = [50, 5, 5]; +eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"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",1); +output.dbt_results = duobinary_target(eq_,mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", []); + +output.dbt_results.metrics.print("description",'Duobinary'); + + + From 062c5dfbaf28dcfb1d8a6d8b7a533e61d48a33de Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Thu, 8 Jan 2026 13:20:35 +0100 Subject: [PATCH 10/12] FSO data: - demystifyied dataset - matched filter - timing sync (whole sig, not symbol based) - EQ is worse, I guess due to symbol timing recovery Pulsef: - can be used as matched filter now, the only thing I really changed was the sampling behavior - before it assumed that the input is always fsym, now it can be anything... fdac is output frequency for both methods --- Classes/00_signals/Signal.m | 10 +- Classes/01_transmit/Pulseformer.m | 252 +++++++++++++-------- Classes/Moveit_wrapper.m | 2 +- Functions/EQ_structures/ffe.m | 8 +- projects/FSO_transmission/first_analysis.m | 139 +++++++++++- test/matched_filter_minimal.m | 19 +- 6 files changed, 313 insertions(+), 117 deletions(-) diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index 0f3578e..5905556 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -757,11 +757,11 @@ classdef Signal pkpos = sort(pkpos); - if mean(w) > 10 || mean(p) > 15 - return - else - sequenceFound = 1; - end + % if mean(w) > 15 || mean(p) > 15 + % return + % else + % sequenceFound = 1; + % end if options.debug_plots figure(121212);clf diff --git a/Classes/01_transmit/Pulseformer.m b/Classes/01_transmit/Pulseformer.m index 4efe336..d137924 100644 --- a/Classes/01_transmit/Pulseformer.m +++ b/Classes/01_transmit/Pulseformer.m @@ -43,7 +43,7 @@ classdef Pulseformer function signalclass_out = process(obj,signalclass_in) % actual processing of the signal (steps 1. - 3.) - signalclass_in.signal = obj.process_(signalclass_in.signal); + signalclass_in.signal = obj.process_(signalclass_in); % append to logbook lbdesc = 'Applied Pulseshaping'; @@ -65,109 +65,171 @@ classdef Pulseformer % Cant be seen from outside! So put all your functions here that can/ % shall not be called from outside - function data_out = process_(obj,data_in) - %METHOD1 Summary of this method goes here - % Detailed explanation goes here - arguments(Input) - obj - data_in - end - - arguments(Output) - data_out - end - - if ~rem(obj.fdac,obj.fsym) - %ist ein Vielfaches - sps = obj.fdac / obj.fsym; - p = sps; - q = 1; - else - %ist kein Vielfaches - p = obj.fsym / gcd(obj.fdac, obj.fsym); %upsampling p->->-> - q = obj.fdac/ gcd(obj.fdac, obj.fsym); %downsampling <-q - sps= q; %sps während dem pulse shaping - end - - if obj.pulse == pulseform.rc - filtertype = 'normal'; - elseif pulseform.rrc - filtertype = 'sqrt'; - end - - %Bau das Filter (hier rc) - racos_len = obj.pulselength*2; - h = rcosdesign(obj.alpha,racos_len,sps,filtertype); - % h = h./ max(h); - - if obj.matched - h = conj(fliplr(h)); - end - - manual_cyclic_convolution = 0; - upfirdn_convolution = 1; - - if manual_cyclic_convolution - - % Apply filter the long way (from move_it) - data_in = data_in'; - blen = length(data_in)*sps; - - % oversample symbol sequence - symbolov=zeros(size(data_in,1),blen); - symbolov(:,1:sps:blen-sps+1)=data_in; - H=fft(h,blen); - - % Convolution of Bit sequence with impulse response - data_out=ifft( fft(symbolov.') .* repmat( H,size(data_in,1),1 ).' ).'; - data_out = circshift(data_out,[0 -(obj.pulselength*sps)]); +function data_out = process_(obj, data_in_signal) + % Extract the incoming sampling rate + % Safety check: If fs is missing (e.g. raw symbols), assume it is fsym + if isprop(data_in_signal, 'fs') && ~isempty(data_in_signal.fs) + f_in = data_in_signal.fs; + else + f_in = obj.fsym; + end - if rem(obj.fdac,obj.fsym) - data_out = data_out(1:q:end); - end + f_out = obj.fdac; - end + % 1. Calculate Resampling Factors (P and Q) + % We need rational approximation: f_out/f_in = p/q + [p, q] = rat(f_out / f_in); - if upfirdn_convolution + % 2. Calculate SPS for the Filter Design + % The filter operates at the INTERMEDIATE rate (f_in * p). + % We need to know how many samples represent one symbol AT THAT RATE. + fs_intermediate = f_in * p; + sps_filter = fs_intermediate / obj.fsym; - %Apply Filter using Matlab build in fctn. + % 3. Filter Design + if obj.pulse == pulseform.rc + filtertype = 'normal'; + elseif obj.pulse == pulseform.rrc % assuming enum logic holds + filtertype = 'sqrt'; + end - data_out_ = upfirdn(data_in,h,p,q); + % Standard RRC Design + % Note: rcosdesign sps must be integer? Usually yes, but for polyphase it can handle it. + % If sps_filter is not integer, rcosdesign might complain. + % For your setup (powers of 2), it will likely be integer. + racos_len = obj.pulselength; % span in symbols + h = rcosdesign(obj.alpha, racos_len, sps_filter, filtertype); - %cut signal, which is longer due to fir filter - st = round(p/q*racos_len/2); %we need to cut y_out - en = round(st + (length(data_in)*p/q)); - data_out = data_out_(st:en); + % Matched Filter Flip (Complex Conjugate Time Reversal) + if obj.matched + h = conj(fliplr(h)); + end - end + % 4. Processing + % Apply upfirdn using the calculated P and Q + data_out_ = upfirdn(data_in_signal.signal, h, p, q); - if upfirdn_convolution && manual_cyclic_convolution - figure() - subplot(2,1,1) - title("Convolution vs. Upfirdn and Cut") - hold on - % plot(data_out_(1:200),'DisplayName','Matlab upfirdn'); - plot(data_out(1:200),'DisplayName','By Hand cyclic convolution') - subplot(2,1,2) - hold on - plot(data_out(1:2000),'DisplayName','OUT'); - plot(data_in(1:2000),'DisplayName','IN'); - end + % 5. Trim Tail (Group Delay Correction) + % The delay of linear phase filter is (N-1)/2 samples @ intermediate rate + delay_samples_intermediate = (length(h) - 1) / 2; + + % Convert delay to output samples + delay_samples_out = delay_samples_intermediate / q; + + % We usually want to trim the "start" transient + st = floor(delay_samples_out) + 1; + + % Calculate expected output length + len_out = ceil(length(data_in_signal.signal) * p / q); + + % Cut + data_out = data_out_(st : st + len_out - 1); +end - %scaling?! see pulsef module line 696 -% scale = max(max([abs(real(data_out)) abs(imag(data_out))])); %find max value from real and imag part -% data_out = data_out./scale; - - % data_out = data_out'; - - %Check output integrity - if abs(round(p/q * length(data_in)) - length(data_out)) > 4 - warning('Check signal length after pulse shaping'); - %disp('Check signal length after pulse shaping'); - end - - - end +% +% function data_out = process_(obj,data_in) +% %METHOD1 Summary of this method goes here +% % Detailed explanation goes here +% arguments(Input) +% obj +% data_in +% end +% +% arguments(Output) +% data_out +% end +% +% if ~rem(obj.fdac,obj.fsym) +% %ist ein Vielfaches +% sps = obj.fdac / obj.fsym; +% p = sps; +% q = 1; +% else +% %ist kein Vielfaches +% p = obj.fsym / gcd(obj.fdac, obj.fsym); %upsampling p->->-> +% q = obj.fdac/ gcd(obj.fdac, obj.fsym); %downsampling <-q +% sps= q; %sps während dem pulse shaping +% end +% +% if obj.pulse == pulseform.rc +% filtertype = 'normal'; +% elseif pulseform.rrc +% filtertype = 'sqrt'; +% end +% +% %Bau das Filter (hier rc) +% racos_len = obj.pulselength*2; +% h = rcosdesign(obj.alpha,racos_len,sps,filtertype); +% % h = h./ max(h); +% +% if obj.matched +% h = conj(fliplr(h)); +% end +% +% manual_cyclic_convolution = 0; +% upfirdn_convolution = 1; +% +% if manual_cyclic_convolution +% +% % Apply filter the long way (from move_it) +% data_in = data_in'; +% blen = length(data_in)*sps; +% +% % oversample symbol sequence +% symbolov=zeros(size(data_in,1),blen); +% symbolov(:,1:sps:blen-sps+1)=data_in; +% H=fft(h,blen); +% +% % Convolution of Bit sequence with impulse response +% data_out=ifft( fft(symbolov.') .* repmat( H,size(data_in,1),1 ).' ).'; +% data_out = circshift(data_out,[0 -(obj.pulselength*sps)]); +% +% if rem(obj.fdac,obj.fsym) +% data_out = data_out(1:q:end); +% end +% +% end +% +% if upfirdn_convolution +% +% %Apply Filter using Matlab build in fctn. +% +% data_out_ = upfirdn(data_in,h,p,q); +% +% %cut signal, which is longer due to fir filter +% st = round(p/q*racos_len/2); %we need to cut y_out +% en = round(st + (length(data_in)*p/q)); +% data_out = data_out_(st:en); +% +% end +% +% if upfirdn_convolution && manual_cyclic_convolution +% figure() +% subplot(2,1,1) +% title("Convolution vs. Upfirdn and Cut") +% hold on +% % plot(data_out_(1:200),'DisplayName','Matlab upfirdn'); +% plot(data_out(1:200),'DisplayName','By Hand cyclic convolution') +% subplot(2,1,2) +% hold on +% plot(data_out(1:2000),'DisplayName','OUT'); +% plot(data_in(1:2000),'DisplayName','IN'); +% end +% +% %scaling?! see pulsef module line 696 +% % scale = max(max([abs(real(data_out)) abs(imag(data_out))])); %find max value from real and imag part +% % data_out = data_out./scale; +% +% % data_out = data_out'; +% +% %Check output integrity +% if abs(round(p/q * length(data_in)) - length(data_out)) > 4 +% warning('Check signal length after pulse shaping'); +% %disp('Check signal length after pulse shaping'); +% end +% +% +% end end diff --git a/Classes/Moveit_wrapper.m b/Classes/Moveit_wrapper.m index 621ab67..8c7ea8a 100644 --- a/Classes/Moveit_wrapper.m +++ b/Classes/Moveit_wrapper.m @@ -27,7 +27,7 @@ classdef Moveit_wrapper < handle % Ensure the function exists if ~exist(obj.moveit_function_name, 'file') - error('Function "%s" does not exist.', obj.moveit_function_name); + error('Function "%s" does not exist. The move-it module must be on path for Matlab, otherwise the wrapper can not call it...', obj.moveit_function_name); end % Step 1: Get default parameters by calling moveit module diff --git a/Functions/EQ_structures/ffe.m b/Functions/EQ_structures/ffe.m index 6cfefc5..df4138e 100644 --- a/Functions/EQ_structures/ffe.m +++ b/Functions/EQ_structures/ffe.m @@ -131,11 +131,11 @@ switch precode_mode tx_bits_precoded = mapper.demap(tx_symbols_precoded); rx_bits = mapper.demap(eq_signal_hd_precoded); - [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits.signal, tx_bits_precoded.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1); + [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits.signal, tx_bits_precoded.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); % B) Just determine BER rx_bits = mapper.demap(eq_signal_hd); - [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1); + [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); case db_mode.db_precoded % Data is precoded on TX side @@ -143,12 +143,12 @@ switch precode_mode eq_signal_hd_decoded = Duobinary().encode(eq_signal_hd, "M", M); eq_signal_hd_decoded = Duobinary().decode(eq_signal_hd_decoded, "M", M); rx_bits_decoded = mapper.demap(eq_signal_hd_decoded); - [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits_decoded.signal, tx_bits.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1); + [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits_decoded.signal, tx_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); % B) Omit the Coding by comparing with demapped TX symbol sequence tx_bits_demapped = mapper.demap(tx_symbols); rx_bits = mapper.demap(eq_signal_hd); - [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits_demapped.signal, "skip_front", 30000, "skip_end", 150, "returnErrorLocation", 1); + [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits_demapped.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); end end diff --git a/projects/FSO_transmission/first_analysis.m b/projects/FSO_transmission/first_analysis.m index 530e692..c535c19 100644 --- a/projects/FSO_transmission/first_analysis.m +++ b/projects/FSO_transmission/first_analysis.m @@ -1,12 +1,141 @@ -datas = load("C:\Users\Silas\Downloads\FSO_FP_QCL_60umUTC\FSO_FP_QCL_60umUTC\6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=225mA_RoP=28.02mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat"); +base = "C:\Users\Silas\Nextcloud\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC"; +mode = 0; %0 oder 1 +M = 2; -fsym = 6e9; -fs = 80e9; +all_files = dir(fullfile(base, "**/*.mat")); -rawsignal = datas.tr.lastData(1).trace.ch3.RawData; +if M == 2 + tx_data = load("C:\Users\Silas\Nextcloud\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat"); + filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat"); +elseif M == 4 + tx_data = load("C:\Users\Silas\Nextcloud\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat"); + filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat"); +end -Scope_sig = Electricalsignal(rawsignal,"fs",fs); +if mode == 1 + [f, p] = uigetfile(fullfile(base, "**/*.mat")); + if f~=0 + filename = fullfile(p,f); + end +end + + +datas = load(filename); + +%% +str = filename; +M_ = str2double(regexp(str, 'M=([^_]+)', 'tokens', 'once')); +assert(M==M_); +fsym = str2double(regexp(str, 'Rs=([^_]+)', 'tokens', 'once')); +fs = str2double(regexp(str, 'Fs=([^_]+)', 'tokens', 'once')); +I = sscanf(char(regexp(str, 'I=([^_]+)', 'tokens', 'once')), '%f'); +rop = sscanf(char(regexp(str, 'RoP=([^_]+)', 'tokens', 'once')), '%f'); +L = sscanf(char(regexp(str, 'L=([^_]+)', 'tokens', 'once')), '%f'); +pulseshape = string( regexp(str, 'PS=([^_]+)', 'tokens', 'once')); +rolloff = str2double(regexp(str, 'rolloff=([^_]+)', 'tokens', 'once')); +mode = string( regexp(str, 'Mode=([^\.]+)', 'tokens', 'once')); + +%% +% Tx data + +Bits = Informationsignal(tx_data.tx_data,"fs",fsym); +Symbols = Informationsignal(real(tx_data.tx_PAM_sym),"fs",fsym); + +mapping_style = M==4; % Pam2 is like move-it; PAM-4 is different, same mapping like ETH peopled used in Zurich... hence the "eth_style" argument here and there +PM = PAMmapper(M,0,"eth_style",mapping_style); % one should rename "eth style" as this is simply a different mapping scheme + +Symbols_ = PM.map(Bits) .* PM.scaling; +assert(isequal(Symbols.signal,Symbols_.signal)); + +Bits_ = PM.demap(Symbols); +[bits,errors,ber,errorIndice] = calc_ber(Bits_.signal,Bits.signal); +assert(ber == 0); + +%% For comparison, apply pulsef on Tx Symbols +Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rc","pulselength",16,"alpha",rolloff); +Digi_sig_compare = Pform.process(Symbols); +MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff); +Rx_sig_compare = MF.process(Digi_sig_compare); + +%% + +% Rx Data +traceData = datas.tr.lastData(2).trace.ch3; + +%FYI: Voltage=(RawData−YReference)×YIncrement+YOrigin +scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrement + traceData.YOrigin; +demystified = isequal(traceData.YData,scoperead_volts); +assert(demystified); + +Scope_sig = Electricalsignal(traceData.YData,"fs",fs); Scope_sig.plot("displayname",'raw','fignum',100); +Scope_sig.spectrum("displayname",'raw','fignum',101) +% 1) matched filter +% pulse is symmetric, hence we can use pulsef firectly as matched filter. +% It feels off (bit I think correct) that the fsym is now the output freq.!! +% -> output 2 sps to omit timing recovery!? +Pform = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1); +Rx_matched = Pform.process(Scope_sig); +Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1); + +% timing sync -> at this point we still have no symbol timing recovery, we +% try to do this with 2sps EQ! +[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); + +%% not working.. +Rx_synced = Rx_synced_cell{1}; +len_tr = 4096*2; +mu_ffe1 = 0.0001; +mu_ffe2 = 0.0008; +mu_ffe3 = 0.001; +mu_dc = 0.005; +mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3]; +mu_dfe = 0.0004; +duob_mode = db_mode.no_db; + +Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',2); + +Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',0); +Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',0); +Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',0); + +if M == 2 + ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature +elseif M == 4 + ber_in_paper = 10^(-2.5); +end + +%% -------------------- FFE -------------------- +% requires some more digging what is going on :-) +eq_ffe = EQ("Ne",[50, 5, 5],"Nb",[2,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",1); + +ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ... + "precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ... + "eth_style_symbol_mapping",mapping_style); + +% ffe_results.metrics.print +fprintf('My EQ: %.1e \n',ffe_results.metrics.BER); +fprintf('Paper: %.1e \n \n',ber_in_paper); + +%% -------------------- VNLE + MLSE -------------------- + +pf_ncoeffs = 1; +eq_v = EQ("Ne",[100, 5, 5],"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",1); +pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); +mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels); + +[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ... + "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style); + +mlse_results.metrics.print +fprintf('My EQ: %.1e \n',mlse_results.metrics.BER); +fprintf('Paper: %.1e \n \n',ber_in_paper); \ No newline at end of file diff --git a/test/matched_filter_minimal.m b/test/matched_filter_minimal.m index b298fac..3f2ff9f 100644 --- a/test/matched_filter_minimal.m +++ b/test/matched_filter_minimal.m @@ -13,7 +13,6 @@ db_precode = 0; db_encode = 0; -rcalpha = 0.05; kover = 16; vbias_rel = 0.5; u_pi = 2.9; @@ -47,15 +46,21 @@ if M == 6 bitpattern = bitpattern(1:end-mod(length(bitpattern),5)); end -bits = Informationsignal(bitpattern); +bits = Informationsignal(bitpattern.'); symbols = PAMmapper(M,0).map(bits); symbols.fs = fsym; -Pform = Pulseformer("fsym",fsym,"fs",fsym,"alpha",0.6,"pulse","rrc","pulselength",64,"matched_sps",4,"output_sps",2); -Pform.process(symbols); +symbols.spectrum("displayname",'Symbols','fignum',1); +%% RRC Shaping +rcalpha = 1; +Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha); +Digi_sig = Pform.process(symbols); +Digi_sig.spectrum("displayname",'Signal after pluse shaping','fignum',1); + +%% 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); -MF = Pulseformer("fsym",fsym,"fdac",fdac,"pulse","rrc","pulselength",1024,"alpha",rcalpha,"matched",0); -Digi_sig = MF.process(Digi_sig); -Digi_sig.spectrum("displayname",'Signal after shaping','fignum',1); From 5dfade331a658887c0d26988ff00e615a4536fa6 Mon Sep 17 00:00:00 2001 From: magf Date: Thu, 8 Jan 2026 15:21:55 +0100 Subject: [PATCH 11/12] First version of a timing recovery implementation (not done yet) --- Classes/04_DSP/Timing_Recovery.m | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Classes/04_DSP/Timing_Recovery.m diff --git a/Classes/04_DSP/Timing_Recovery.m b/Classes/04_DSP/Timing_Recovery.m new file mode 100644 index 0000000..ba16f10 --- /dev/null +++ b/Classes/04_DSP/Timing_Recovery.m @@ -0,0 +1,47 @@ +classdef Timing_Recovery < handle + + properties(Access=public) + timing_error_detector + sps + damping_factor + normalized_loop_bandwidth + detector_gain + end + + methods(Access=public) + function obj = FFE(options) + arguments(Input) + + options.timing_error_detector = 'Gardner'; + options.sps = 2; + options.damping_factor = 1.0; + options.normalized_loop_bandwidth = 0.005; + options.detector_gain = 1; + + end + + fn = fieldnames(options); + for n = 1:numel(fn) + obj.(fn{n}) = options.(fn{n}); + end + + obj.e = zeros(obj.order,1); + obj.error = 0; + + end + + function data_out = process(obj, data_in) + + timing_synchronization = comm.SymbolSynchronizer( ... + "TimingErrorDetector", obj.timing_error_detector, ... + "SamplesPerSymbol", obj.sps, ... + "DampingFactor", obj.damping_factor, ... + "NormalizedLoopBandwidth", obj.normalized_loop_bandwidth, ... + "DetectorGain", obj.detector_gain); + + data_out.signal = timing_synchronization(data_in.signal); + + end + end +end + From dd6094d51e70f3c8ec7bc9aa161129707913c878 Mon Sep 17 00:00:00 2001 From: "silas (home)" Date: Mon, 12 Jan 2026 08:12:42 +0100 Subject: [PATCH 12/12] chirped WDM analsyis minor stuff in FSO link with Magnus --- projects/FSO_transmission/first_analysis.m | 40 +++- projects/WDM/WDM_auswertung.m | 61 ++++- projects/WDM/WDM_model_10km_queue.m | 205 ++++++++++++---- projects/WDM/plot_BER_vs_ROP.m | 1 - projects/WDM/plot_FEC_violin.m | 266 +++++++++++---------- 5 files changed, 389 insertions(+), 184 deletions(-) diff --git a/projects/FSO_transmission/first_analysis.m b/projects/FSO_transmission/first_analysis.m index c535c19..c47a2b1 100644 --- a/projects/FSO_transmission/first_analysis.m +++ b/projects/FSO_transmission/first_analysis.m @@ -81,9 +81,23 @@ Pform = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"al Rx_matched = Pform.process(Scope_sig); Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1); -% timing sync -> at this point we still have no symbol timing recovery, we -% try to do this with 2sps EQ! -[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); + +%% + +sys = comm.SymbolSynchronizer('TimingErrorDetector', 'Gardner (non-data-aided)', ... + 'SamplesPerSymbol', 2, ... + 'DampingFactor', 0.7, ... + 'NormalizedLoopBandwidth', 0.01); +Rx_symbolsync = Rx_matched; +[Rx_symbolsync.signal, timing_error] = sys(Rx_matched.signal); + +plot(timing_error); % If this is a ramp, you have drift! + +%% timing sync -> at this point we still have no symbol timing recovery, we +% % try to do this with 2sps EQ! + +[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_symbolsync.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); + %% not working.. Rx_synced = Rx_synced_cell{1}; @@ -136,6 +150,22 @@ mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_styl [vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ... "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style); -mlse_results.metrics.print +mlse_results.metrics.print("description",'MLSE') fprintf('My EQ: %.1e \n',mlse_results.metrics.BER); -fprintf('Paper: %.1e \n \n',ber_in_paper); \ No newline at end of file +fprintf('Paper: %.1e \n \n',ber_in_paper); + +%% -------------------- DB target -------------------- +mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels); + +eq_ = EQ("Ne",[50, 5, 5],"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",1); + +dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ... + "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style); + +dbt_results.metrics.print("description",'Duobinary'); +mlse_results.metrics.print +fprintf('My EQ: %.1e \n',dbt_results.metrics.BER); +fprintf('Paper: %.1e \n \n',ber_in_paper); + + diff --git a/projects/WDM/WDM_auswertung.m b/projects/WDM/WDM_auswertung.m index 4c213fa..98b42b9 100644 --- a/projects/WDM/WDM_auswertung.m +++ b/projects/WDM/WDM_auswertung.m @@ -1,7 +1,58 @@ -filename = "C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\WDM_20260106_131152_n261_14408161_10km_16ch_400ghz_co_alpha0_5.mat"; -filename = "C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\WDM_20260106_131152_n262_14408165_10km_16ch_400ghz_co_alpha-0_5.mat"; -filename = "C:\Users\Silas\Documents\MATLAB\Datensätze\FWM_2025\WDM_20260106_134921_n223_14409338_10km_16ch_400ghz_co_alpha0_0.mat"; -res = load(filename,'res'); + +base = "C:\Users\Silas\Nextcloud\Cluster"; +all_files = dir(fullfile(base, "**/*.mat")); + +schemes = ["co","pair","alt","seg"]; + +% Preallocate as table (minimal + convenient) +T = table('Size',[0 10], ... + 'VariableTypes', ["string","string","string","datetime","double","double","double","double","double","double"], ... + 'VariableNames', ["folder","file","scheme","date","node","jobid","L_km","Nch","df_GHz","alpha"]); + +% filename parser +rx = "^WDM_(?\d{8})_(?