From 2691b90d65289f904d779fe1d9dd75b1a8873d09 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Wed, 25 Mar 2026 11:51:26 +0100 Subject: [PATCH] Unify batch job processing with a shared runBatch core --- Functions/Job_Processing/runBatch.m | 209 +++++++ Functions/Job_Processing/submitJobs.m | 219 ++----- projects/IMDD_base_system/imdd_it.m | 2 +- projects/IMDD_base_system/imdd_model.m | 2 +- .../model_linewidth_evaluation.m | 181 ------ projects/IMDD_base_system/parameters.m | 46 -- projects/IMDD_base_system/rx_simulation.m | 24 - projects/IMDD_base_system/simulation_bwl.m | 569 ------------------ projects/IMDD_base_system/simulation_bwl_2.m | 191 ------ projects/IMDD_base_system/submit_handle.m | 87 ++- .../IMDD_base_system/submit_simulations.m | 83 --- projects/IMDD_base_system/tx_simulation.m | 31 - 12 files changed, 290 insertions(+), 1354 deletions(-) create mode 100644 Functions/Job_Processing/runBatch.m delete mode 100644 projects/IMDD_base_system/model_linewidth_evaluation.m delete mode 100644 projects/IMDD_base_system/parameters.m delete mode 100644 projects/IMDD_base_system/rx_simulation.m delete mode 100644 projects/IMDD_base_system/simulation_bwl.m delete mode 100644 projects/IMDD_base_system/simulation_bwl_2.m delete mode 100644 projects/IMDD_base_system/submit_simulations.m delete mode 100644 projects/IMDD_base_system/tx_simulation.m diff --git a/Functions/Job_Processing/runBatch.m b/Functions/Job_Processing/runBatch.m new file mode 100644 index 0000000..70d78d7 --- /dev/null +++ b/Functions/Job_Processing/runBatch.m @@ -0,0 +1,209 @@ +function results = runBatch(workerFcn, jobs, options) +%RUNBATCH Execute a list of jobs in serial or parallel. +% results = runBatch(workerFcn, jobs) executes each jobs(i).args cell +% with workerFcn and returns a cell array aligned with the input jobs. +% +% Supported job fields: +% .args cell array of positional arguments for workerFcn +% .label string/char used for progress messages +% .meta arbitrary metadata for wrapper-specific bookkeeping + + arguments + workerFcn (1,1) function_handle + jobs (1,:) struct = struct('args', {}) + options.mode = processingMode.serial + options.waitbar (1,1) logical = true + options.waitbarMessage (1,1) string = "Processing Jobs..." + options.numWorkers (1,1) double {mustBeNonnegative, mustBeInteger} = 0 + options.idleTimeout (1,1) double {mustBePositive} = 300 + options.cancelExistingQueue (1,1) logical = true + options.resultHandler = [] + options.errorHandler = [] + end + + mode = normalizeProcessingMode(options.mode); + jobs = normalizeJobs(jobs); + nJobs = numel(jobs); + results = cell(1, nJobs); + h = []; + + if nJobs == 0 + return + end + + if options.waitbar + h = waitbar(0, char(options.waitbarMessage)); + cleanupWaitbar = onCleanup(@() closeWaitbar(h)); %#ok + end + + switch mode + case processingMode.parallel + pool = setupParallelPool(options.numWorkers, options.idleTimeout, options.cancelExistingQueue); + futures = parallel.FevalFuture.empty(nJobs, 0); + + for idx = 1:nJobs + fprintf('[%s] Submitted.\n', jobs(idx).label); + futures(idx) = parfeval(pool, workerFcn, 1, jobs(idx).args{:}); + end + + consumedIdx = false(nJobs, 1); + for completedCount = 1:nJobs + try + [idx, value] = fetchNext(futures); + consumedIdx(idx) = true; + results{idx} = value; + fprintf('[%s] Completed (%d/%d).\n', jobs(idx).label, completedCount, nJobs); + + if ~isempty(options.resultHandler) + options.resultHandler(value, jobs(idx), idx); + end + catch fetchErr + idxErr = findErroredFuture(futures, consumedIdx); + if isempty(idxErr) + rethrow(fetchErr); + end + + consumedIdx(idxErr) = true; + errInfo = extractFutureError(futures(idxErr)); + results{idxErr} = errInfo; + + if ~isempty(options.errorHandler) + options.errorHandler(errInfo, jobs(idxErr), idxErr); + else + defaultErrorHandler(errInfo, jobs(idxErr), idxErr); + end + end + + updateWaitbar(options.waitbar, h, completedCount, nJobs); + end + + case processingMode.serial + for idx = 1:nJobs + try + fprintf('[%s] Running.\n', jobs(idx).label); + value = feval(workerFcn, jobs(idx).args{:}); + results{idx} = value; + fprintf('[%s] Completed (%d/%d).\n', jobs(idx).label, idx, nJobs); + + if ~isempty(options.resultHandler) + options.resultHandler(value, jobs(idx), idx); + end + catch ME + results{idx} = ME; + + if ~isempty(options.errorHandler) + options.errorHandler(ME, jobs(idx), idx); + else + defaultErrorHandler(ME, jobs(idx), idx); + end + end + + updateWaitbar(options.waitbar, h, idx, nJobs); + end + + otherwise + error('runBatch:UnknownMode', 'Unknown processing mode "%s".', string(mode)); + end +end + +function mode = normalizeProcessingMode(mode) + if isa(mode, 'processingMode') + return + end + + modeString = string(mode); + switch modeString + case "serial" + mode = processingMode.serial; + case "parallel" + mode = processingMode.parallel; + otherwise + error('runBatch:InvalidMode', 'Unsupported processing mode "%s".', modeString); + end +end + +function jobs = normalizeJobs(jobs) + if isempty(jobs) + jobs = struct('args', {}, 'label', {}, 'meta', {}); + return + end + + for idx = 1:numel(jobs) + if ~isfield(jobs, 'args') || isempty(jobs(idx).args) + jobs(idx).args = {}; + end + + if ~iscell(jobs(idx).args) + error('runBatch:InvalidArgs', 'jobs(%d).args must be a cell array.', idx); + end + + if ~isfield(jobs, 'label') || isempty(jobs(idx).label) + jobs(idx).label = sprintf('Job %d', idx); + else + jobs(idx).label = string(jobs(idx).label); + end + + if ~isfield(jobs, 'meta') + jobs(idx).meta = struct(); + end + end +end + +function updateWaitbar(useWaitbar, h, completedCount, totalCount) + if ~useWaitbar || ~isgraphics(h) + return + end + + waitbar(completedCount / totalCount, h, ... + sprintf('Completed %d/%d jobs', completedCount, totalCount)); + drawnow; +end + +function closeWaitbar(h) + if isgraphics(h) + delete(h); + end +end + +function pool = setupParallelPool(numWorkers, idleTimeout, cancelExistingQueue) + pool = gcp('nocreate'); + + if isempty(pool) + if numWorkers > 0 + pool = parpool('local', numWorkers, 'IdleTimeout', idleTimeout); + else + pool = parpool('local', 'IdleTimeout', idleTimeout); + end + elseif numWorkers > 0 && pool.NumWorkers ~= numWorkers + delete(pool); + pool = parpool('local', numWorkers, 'IdleTimeout', idleTimeout); + end + + if cancelExistingQueue + queue = pool.FevalQueue; + if ~isempty(queue.QueuedFutures) || ~isempty(queue.RunningFutures) + queuedCount = numel(queue.QueuedFutures) + numel(queue.RunningFutures); + cancelAll(queue); + fprintf('Canceled %d unfinished jobs from the current pool queue.\n', queuedCount); + end + end +end + +function idxErr = findErroredFuture(futures, consumedIdx) + readMask = arrayfun(@(future) future.Read, futures).'; + idxErr = find(readMask & ~consumedIdx, 1); +end + +function errInfo = extractFutureError(future) + errInfo = future.Error; + if iscell(errInfo) + errInfo = errInfo{1}; + end +end + +function defaultErrorHandler(ME, job, jobIndex) + fprintf('[%s | #%d] ERROR [%s]: %s\n', job.label, jobIndex, ME.identifier, ME.message); + for st = ME.stack' + fprintf(' %s:%d (%s)\n', st.file, st.line, st.name); + end +end diff --git a/Functions/Job_Processing/submitJobs.m b/Functions/Job_Processing/submitJobs.m index 5826814..26fe083 100644 --- a/Functions/Job_Processing/submitJobs.m +++ b/Functions/Job_Processing/submitJobs.m @@ -27,164 +27,53 @@ nRunIds = numel(run_ids); nJobsPerRunId = submit_options.wh.getLastLinIndice(); totalJobs = nRunIds * nJobsPerRunId; -% Preallocate -results = cell(nJobsPerRunId, nRunIds); -futures = parallel.FevalFuture.empty(totalJobs,0); -jobIndices = zeros(totalJobs,2); - -% Optional waitbar -if submit_options.waitbar - h = waitbar(0, 'Processing Jobs...'); - cleanupObj = onCleanup(@() delete(h)); -end - -switch submit_mode - case processingMode.parallel - %—– SET UP POOL & QUEUE —– - p = setupParallelPool(11, 300); % 10 workers, 300s idle timeout - - % === submit all futures === - jobCounter = 0; - for r = 1:nRunIds - for k = 1:nJobsPerRunId - jobCounter = jobCounter + 1; - jobIndices(jobCounter,:) = [r,k]; - - opt = buildOptionalVars(k, submit_options.wh); - futures(jobCounter) = parfeval( ... - p, @dsp_runid, 1, ... - run_ids(r), ... - "database_type", dsp_options.database_type, ... - "dataBase", dsp_options.dataBase, ... - "append_to_db", dsp_options.append_to_db, ... - "load_file_path", dsp_options.load_file_path, ... - "max_occurences", dsp_options.max_occurences, ... - "storage_path", dsp_options.storage_path, ... - "mode", dsp_options.mode, ... - "parameters", opt ... - ); - - fprintf('[RunID %d, Job %d] Submitted to pool.\n', run_ids(r), k); - end - end - - %—– W A I T B A R U P D A T E —– - if submit_options.waitbar - futureArray = futures; - updateWB = @() waitbar( ... - sum(arrayfun(@(f) strcmp(f.State,'finished'), futureArray))/totalJobs, ... - h, sprintf('Completed %d/%d jobs', ... - sum(arrayfun(@(f) strcmp(f.State,'finished'), futureArray)), totalJobs) ... - ); - afterEach(futureArray, updateWB, 0); - end - - % —– before the loop —– - % Keep track of which futures we've already handled: - consumedIdx = false(totalJobs,1); - - % —– fetch in completion order, handling successes and errors —– - for n = 1:totalJobs - try - % This returns the value AND the linear index in 'futures' - [idx, val] = fetchNext(futures); - - duration = futures(idx).RunningDuration; - startDT = futures(idx).StartDateTime; - finishDT = futures(idx).FinishDateTime; - duration = finishDT - startDT; - - % Mark it consumed - consumedIdx(idx) = true; - - % Map back to (r,k) and store - r = jobIndices(idx,1); - k = jobIndices(idx,2); - - fprintf('[%s] JobID %d/%d (%.1f%%) — %s — RunID %d — Subjob %d — fetched.\n', ... - datestr(now,'yyyy-mm-dd HH:MM:SS'), ... - r, totalJobs, 100*n/totalJobs,char(duration), ... - run_ids(r), k); - - % Update waitbar - if submit_options.waitbar - waitbar(n/totalJobs, h, ... - sprintf('Fetched %d/%d (%.1f% Percent)', n, totalJobs, 100*n/totalJobs)); - drawnow; % force the GUI to refresh - end - - storeResult(val, k, submit_options.wh); - results{k,r} = val; - - catch fetchErr - % fetchNext has already set Read=true on the errored future. - % Find the one Read==true that we have _not_ yet consumed. - readMask = arrayfun(@(f) f.Read, futures); - idxErr = find(readMask & ~consumedIdx', 1); - consumedIdx(idxErr) = true; - - % Pull the _real_ exception out of the future object - errInfo = futures(idxErr).Error; - if iscell(errInfo) - origME = errInfo{1}; - else - origME = errInfo; - end - - % Map back to (r,k) and log - r = jobIndices(idxErr,1); - k = jobIndices(idxErr,2); - handleError(origME, k, run_ids(r)); - results{k,r} = origME; - end - end - - - case processingMode.serial - %—– SERIAL EXECUTION —– - jobCounter = 0; - for r = 1:nRunIds - for k = 1:nJobsPerRunId - jobCounter = jobCounter + 1; - optionalVars = buildOptionalVars(k, submit_options.wh); - try - fprintf('[RunID %d, Job %d] Running in linear mode...\n', run_ids(r), k); - val = dsp_runid( run_ids(r), ... - "database_type", dsp_options.database_type, ... - "dataBase", dsp_options.dataBase, ... - "append_to_db", dsp_options.append_to_db, ... - "load_file_path", dsp_options.load_file_path, ... - "max_occurences", dsp_options.max_occurences, ... - "storage_path", dsp_options.storage_path, ... - "mode", dsp_options.mode, ... - "parameters", optionalVars ); - - fprintf('[RunID %d, Job %d] Completed successfully.\n', run_ids(r), k); - storeResult(val, k, submit_options.wh); - results{k,r} = val; - - catch ME - handleError(ME, k, run_ids(r)); - results{k,r} = ME; - end - - if submit_options.waitbar - waitbar(jobCounter/totalJobs, h, ... - sprintf('Completed %d/%d jobs', jobCounter, totalJobs)); - end - end - end - - otherwise - error('Unknown submit_mode "%s".', string(submit_mode)) -end - wh = submit_options.wh; +results = cell(nJobsPerRunId, nRunIds); +jobs = repmat(struct('args', {{}}, 'label', "", 'meta', struct()), 1, totalJobs); + +jobCounter = 0; +for r = 1:nRunIds + for k = 1:nJobsPerRunId + jobCounter = jobCounter + 1; + optionalVars = buildOptionalVars(k, submit_options.wh); + + jobs(jobCounter).args = { ... + run_ids(r), ... + "database_type", dsp_options.database_type, ... + "dataBase", dsp_options.dataBase, ... + "append_to_db", dsp_options.append_to_db, ... + "load_file_path", dsp_options.load_file_path, ... + "max_occurences", dsp_options.max_occurences, ... + "storage_path", dsp_options.storage_path, ... + "mode", dsp_options.mode, ... + "parameters", optionalVars}; + jobs(jobCounter).label = sprintf('RunID %d, Job %d', run_ids(r), k); + jobs(jobCounter).meta.runIndex = r; + jobs(jobCounter).meta.jobIndex = k; + jobs(jobCounter).meta.run_id = run_ids(r); + end +end + +linearResults = runBatch(@dsp_runid, jobs, ... + "mode", submit_mode, ... + "waitbar", submit_options.waitbar, ... + "waitbarMessage", "Processing Jobs...", ... + "numWorkers", 11, ... + "idleTimeout", 300, ... + "cancelExistingQueue", true, ... + "resultHandler", @storeResult, ... + "errorHandler", @handleError); + +for idx = 1:totalJobs + r = jobs(idx).meta.runIndex; + k = jobs(idx).meta.jobIndex; + results{k, r} = linearResults{idx}; +end %% Local helpers %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - function handleError(ME, jobIndex, run_id) - fprintf('[RunID %d, Job %d] ERROR [%s]: %s\n', run_id, jobIndex, ... + function handleError(ME, job, ~) + fprintf('[RunID %d, Job %d] ERROR [%s]: %s\n', job.meta.run_id, job.meta.jobIndex, ... ME.identifier, ME.message); for st = ME.stack' fprintf(' %s:%d (%s)\n', st.file, st.line, st.name); @@ -202,8 +91,9 @@ wh = submit_options.wh; end end - function storeResult(val, jobIndex, wh) + function storeResult(val, job, ~) if ~isempty(wh) + jobIndex = job.meta.jobIndex; wh.addValueToStorageByLinIdx(val.ffe_package, 'ffe_package', jobIndex); wh.addValueToStorageByLinIdx(val.mlse_package, 'mlse_package', jobIndex); wh.addValueToStorageByLinIdx(val.vnle_package, 'vnle_package', jobIndex); @@ -212,24 +102,5 @@ wh = submit_options.wh; wh.addValueToStorageByLinIdx(val.mlmlse_package,'mlmlse_package',jobIndex); end end - function p = setupParallelPool(numWorkers, idleTimeout) - % Ensure a pool exists at the right size & timeout - p = gcp('nocreate'); - if isempty(p) || p.NumWorkers~=numWorkers - if ~isempty(p) - delete(p); - end - p = parpool('local', numWorkers, 'IdleTimeout', idleTimeout); - end - - % Cancel anything left in the pool's default queue - q = p.FevalQueue; - if ~isempty(q.QueuedFutures) || ~isempty(q.RunningFutures) - cancelAll(q); - fprintf('Canceled %d unfetched jobs from old queue.\n', ... - numel(q.QueuedFutures)+numel(q.RunningFutures)); - end - end - end diff --git a/projects/IMDD_base_system/imdd_it.m b/projects/IMDD_base_system/imdd_it.m index cd451f0..3bc9185 100644 --- a/projects/IMDD_base_system/imdd_it.m +++ b/projects/IMDD_base_system/imdd_it.m @@ -25,7 +25,7 @@ end figure hold on for alpha = uloops.alpha - a=wh.getStoValue('ber',1, [300].*1e9 , 1293, 4, uloops.link_length,alpha,uloops.duob_mode,uloops.decoding_mode); + a=wh.getStoValue('ber',0, [300].*1e9 , 1293, 4, uloops.link_length,alpha,uloops.duob_mode,uloops.decoding_mode); % ffe = cellfun(@(x) x.ffe_results.metrics.BER, a); ffe = cellfun(@(x) x.dbt_results.metrics.BER, a); plot(uloops.link_length,ffe,'DisplayName',sprintf('Alpha: %d',alpha),'LineStyle','-','HandleVisibility','on'); diff --git a/projects/IMDD_base_system/imdd_model.m b/projects/IMDD_base_system/imdd_model.m index adade73..b2e8c6a 100644 --- a/projects/IMDD_base_system/imdd_model.m +++ b/projects/IMDD_base_system/imdd_model.m @@ -173,7 +173,7 @@ Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); %%% EQUALIZING -if 0 +if 1 % -------------------- FFE -------------------- ffe_order = [50, 0, 0]; eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0], ... diff --git a/projects/IMDD_base_system/model_linewidth_evaluation.m b/projects/IMDD_base_system/model_linewidth_evaluation.m deleted file mode 100644 index e648184..0000000 --- a/projects/IMDD_base_system/model_linewidth_evaluation.m +++ /dev/null @@ -1,181 +0,0 @@ -%%% Run parameters -% TX -M = 4; - -apply_pulsef = 1; -fdac = 256e9; -fadc = 256e9; -random_key = 2; - -rcalpha = 0.05; -kover = 8; -vbias_rel = 0.5; -u_pi = 3.2; -vbias = -vbias_rel*u_pi; -laser_wavelength = 1300; -laser_linewidth = logspace(0,6.2,24); - -% Channel -link_length = 10; - -alpha = 0; - -doub_mode = db_mode.no_db; -cols = linspecer(6); -rop = [-6]; -bwl = [0.5:0.1:1.5]; -fsym = [200:16:256].*1e9; -% nonlin_mod = [0.5:0.01:0.75]; -fsym = ones(size(laser_linewidth)).*fsym(1); -nonlin_mod = ones(size(laser_linewidth)).*0.5; - -ffe_results = {}; -mlse_results_lin= {}; - -parfor r = 1:length(laser_linewidth) - - Pform = Pulseformer("fsym",fsym(r),"fdac",4*fsym(r),"pulse","rc","pulselength",16,"alpha",rcalpha); - - db_precode = 0; - db_encode = 0; - duob_mode = db_mode.no_db; - apply_pulsef = 1; - - [Digi_sig,Symbols,Tx_bits] = PAMsource(... - "fsym",fsym(r),"M",M,"order",18,"useprbs",1,... - "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,"duobinary_mode",duob_mode).process(); - - El_sig = M8199B("kover",kover).process(Digi_sig); - - %%%%% Electrical Driver Amplifier %%%%%% - El_sig = El_sig.normalize("mode","oneone"); - - %%%%% MODULATE E/O CONVERSION %%%%% - u_pi = 3.2; - vbias = -u_pi*nonlin_mod(r); - [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(r),"randomkey",random_key+1).process(El_sig); - - %%%%%% Fiber %%%%%% - mpi = 1; - if mpi - Combined_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); - else - - % 2) ping pong fiber propagation - mpi_path = 00; - Interference_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",mpi_path*2,"alpha",0,"D",0,"lambda0",1310,"gamma",0).process(Opt_sig); - - Interference_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",-30).process(Interference_sig); - - [Main_sig,dly] = Opt_sig.delay("delay_meter",mpi_path*2); - - % Add - Combined_sig = Main_sig + Interference_sig; - - % Cut (due to the delays there is a jump in the signals) - if dly == 0;dly = 1;end - Combined_sig.signal = Combined_sig.signal(ceil(dly):end); - - % Fiber - Combined_sig = Fiber("fsimu",Combined_sig.fs,"fiber_length",2,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.08).process(Combined_sig); - end - - - %%%%%% ROP %%%%%% - Opt_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Combined_sig); - - %%%%%% PD Square Law %%%%%% - PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11,"randomkey",random_key).process(Opt_sig); - - %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% - rx_bwl = 70e9; - 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); - - %%%%%% 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); - - Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym(r)); - % Symbols.signal = Symbols.signal(1:Scpe_sig_2sps.length/2); - - % 2sps - [~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 0); - Rx_sig_2sps = Scpe_cell{1}; - Rx_sig_2sps = Rx_sig_2sps.normalize("mode","rms"); - - % 1sps - Scpe_sig_1sps = Scpe_sig.resample("fs_out",1*fsym(r)); - - [~, Scpe_cell_1sps, ~, found_sync] = Scpe_sig_1sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 0); - Rx_sig_1sps = Scpe_cell_1sps{1}; - Rx_sig_1sps = Rx_sig_1sps.normalize("mode","rms"); - - - %% RUN DSP - 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_ffe2 mu_ffe3]; - mu_dfe = 0.0004; - pf_ncoeffs = 1; - ffe_order = [50, 3, 3]; - mu_lms = 0.0005; - eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",2^13,"mu_dd",mu_lms,"mu_tr",mu_lms,"order",50,"sps",2,"dd_mode",1,"adaption_technique","lms"); - % eq_ = EQ("Ne",ffe_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",1,"DCmu",0.00,"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,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0); - - [ffe_results{r}, mlse_results_lin{r}] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Rx_sig_2sps, Symbols, Tx_bits, ... - "precode_mode", duob_mode,... - 'showAnalysis', 0, ... - "postFFE", [],... - "eth_style_symbol_mapping", 0); - - - ffe_results{r}.metrics.print; - mlse_results_lin{r}.metrics.print; - -end - - -figure(1);hold on; -plot(laser_linewidth.*1e-6,cellfun(@(x) x.metrics.BER, ffe_results),'DisplayName','VNLE') -plot(laser_linewidth.*1e-6,cellfun(@(x) x.metrics.BER, mlse_results_lin),'DisplayName','VNLE+MLSE') -xlabel('Linewidth [GHz]'); -ylabel('BER') -set(gca,'YScale','log'); -legend; -ylim([1e-5 1e-1]); -beautifyBERplot; - -figure();hold on; -plot(laser_linewidth.*1e-6,cellfun(@(x) x.metrics.AIR.*1e-9, ffe_results),'DisplayName','FFE') -plot(laser_linewidth.*1e-6,cellfun(@(x) x.metrics.AIR.*1e-9, mlse_results_lin),'DisplayName','FFE+MLSE') -xlabel('Linewidth [GHz]'); -ylabel('AIR [GBd]') -% set(gca,'YScale','log'); -legend; -beautifyBERplot("logscale",0); - -figure(); hold on -stem(calcWavelengthPlan(16,400e9,1310),ones(16,1),'DisplayName','16x400','Marker','.','LineWidth',1); -stem(calcWavelengthPlan(8,800e9,1310),ones(8,1),'DisplayName','8x800','Marker','.','LineWidth',1); -stem(calcWavelengthPlan(16,800e9,1310),ones(16,1),'DisplayName','16x800','Marker','.','LineWidth',1); -ylim([0,1.2]); -ylabel('wavelength [nm]'); -xlim([1270, 1350]) \ No newline at end of file diff --git a/projects/IMDD_base_system/parameters.m b/projects/IMDD_base_system/parameters.m deleted file mode 100644 index e329a37..0000000 --- a/projects/IMDD_base_system/parameters.m +++ /dev/null @@ -1,46 +0,0 @@ -% TX - M = 4; - fsym = 180e9; - f_nyquist = fsym/2; - apply_pulsef = 0; - fdac = 2*fsym;%256e9; - fadc = 2*fsym;%256e9; - fdac = 256e9; - fadc = 256e9; - random_key = 1; - db_precode = 0; - emulate_precode = 0; - discard_precode = 0; - db_encode = 0; - % duob_mode = db_mode.db_emulate; - emulate_db = 1; - rcalpha = 0.05; - kover = 16; - vbias_rel = 0.5; - u_pi = 2.9; - vbias = -vbias_rel*u_pi; - laser_wavelength = 1310; - laser_linewidth = 0; - tx_bw_nyquist = 1.5; - -% Channel - link_length = 1; - -% RX - rop = -8; - rx_bw_nyquist = 0.7; - -% EQ - eq_mode = equalizer_structure.vnle_pf_mlse; - ffe_order=[50,0,0]; - vnle_order=[50,7,7]; - dfe_order = [2 0 0]; - - len_tr = 4096*2; - mu_ffe = [0.0004 0.0004 0.0004]; - mu_dfe = 0.0004; - mu_dc = 0.00; - - dfe_ = sum(dfe_order)>0; - - \ No newline at end of file diff --git a/projects/IMDD_base_system/rx_simulation.m b/projects/IMDD_base_system/rx_simulation.m deleted file mode 100644 index f105bf4..0000000 --- a/projects/IMDD_base_system/rx_simulation.m +++ /dev/null @@ -1,24 +0,0 @@ - - - %%%%%% 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_sig = Filter('filtdegree',4,"f_cutoff",rx_bw_nyquist.*f_nyquist,"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); - - % 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); - diff --git a/projects/IMDD_base_system/simulation_bwl.m b/projects/IMDD_base_system/simulation_bwl.m deleted file mode 100644 index e70c40d..0000000 --- a/projects/IMDD_base_system/simulation_bwl.m +++ /dev/null @@ -1,569 +0,0 @@ -%%% Run parameters -% TX -M = 4; -m = floor(log2(M)*10)/10; -fsym = 224e9; - -apply_pulsef = 1; -fdac = 256e9; -fadc = 256e9; -random_key = 2; - -rcalpha = 0.05; -kover = 8; -vbias_rel = 0.5; -u_pi = 3.2; -vbias = -vbias_rel*u_pi; -laser_wavelength = 1310; -laser_linewidth = 1e6; - - -% Channel -link_length = 0; - -vnle_order1 = 50; -vnle_order2 = 0; -vnle_order3 = 0; - -vnle_order=[vnle_order1,vnle_order2,vnle_order3]; -dfe_order = [0 0 0]; - -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; - -dfe_ = sum(dfe_order)>0; - -doub_mode = db_mode.no_db; -cols = linspecer(6); -rop = [-6]; -bwl = [0.5:0.1:1.5]; -fsym = [192:16:256].*1e9; -fsym = 208e9; - -ber_vnle = []; -ber_mlse = []; -ber_mlse_burg = []; -ber_viterbi = []; -ber_db = []; -ber_db_diff_precoded = []; -gmi_vnle_bitwise = []; -gmi_mlse = []; -gmi_mlse_db = []; - -for r = 1:length(fsym) - - Pform = Pulseformer("fsym",fsym(r),"fdac",4*fsym(r),"pulse","rc","pulselength",16,"alpha",rcalpha); - - db_precode = 0; - db_encode = 0; - duob_mode = db_mode.no_db; - apply_pulsef = 1; - - [Digi_sig,Symbols,Tx_bits] = PAMsource(... - "fsym",fsym(r),"M",M,"order",19,"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,"duobinary_mode",duob_mode).process(); - - % El_sig = AWG("fdac",fdac,"f_cutoff",fsym(r),"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0).process(Digi_sig); - El_sig = M8199B("kover",kover).process(Digi_sig); - % AWG("fdac",fdac,"f_cutoff",fsym(r),"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0).process(Digi_sig); - - %%%%% Low-pass el. components %%%%%% - % tx_bwl = 100e9; - % El_sig = Filter('filtdegree',3,"f_cutoff",tx_bwl,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig); - - %%%%% Electrical Driver Amplifier %%%%%% - El_sig = El_sig.normalize("mode","oneone"); - % El_sig = El_sig.setPower(1,"dBm"); - % figure;histogram(El_sig.signal); - - %%%%% MODULATE E/O CONVERSION %%%%% - u_pi = 3.2; - vbias = -u_pi*0.5; - [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); - - if 0 - figure(15); - hold on - scatter(El_sig.signal(1:100000)+vbias,(abs(Opt_sig.signal(1:100000)).^2)*1e3,0.1,'.','DisplayName','Modulator TF') - xlabel('Input in V') - ylabel('abs(Eopt)2 in mW','Interpreter','latex') - ylim([0 2]); - xlim([-3.2 0]); - - Opt_sig.eye(fsym(r),M,"fignum",103837); - end - - %%%%%% Fiber %%%%%% - 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 %%%%%% - Opt_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig); - - % Opt_sig.eye(fsym(r),M,"fignum",103838); - - % % Opt_sig.signal = Opt_sig.signal + 5*abs(mean(Opt_sig.signal)); - % Opt_sig.move_it_spectrum("displayname",'Opt Sig after Amp','fignum',1223323); - % Pc = abs(mean(Opt_sig.signal)).^2; % carrier power - % Ptot = mean(abs(Opt_sig.signal).^2); % total power - % Ps = max(Ptot - Pc, eps); - % Pcdb = 10*log10(Pc); - % Psdb = 10*log10(Ps); - % - % cspr_dB = 10*log10(Pc / Ps); - % - % % Minimal in-place CSPR set (real, nonnegative field constraint) - % E = Opt_sig.signal; % real field samples - % target_cspr_dB = 20; % <-- set your target CSPR (dB) - % - % % Decompose into DC + zero-mean waveform - % m = mean(E); - % x0 = E - m; % zero-mean modulation - % Ps0 = mean(x0.^2); % sideband power (fixed if shape kept) - % - % % Current CSPR (for reference) - % Pc_cur = m^2; - % Ptot_cur = mean(E.^2); - % Ps_cur = max(Ptot_cur - Pc_cur, eps); - % cspr_in = 10*log10(Pc_cur / Ps_cur); - % - % % Bias needed for target CSPR, and minimal bias to keep E>=0 - % R_tgt = 10^(target_cspr_dB/10); % Pc/Ps - % a_req = sqrt(R_tgt * Ps0); % required DC bias - % a_min = -min(x0); % to avoid negatives everywhere - % a = max(a_req, a_min); % if infeasible, lands at CSPR_min - % - % % Apply bias (preserves waveform shape) - % E_new = a + x0; - % - % % Achieved CSPR - % Pc_new = mean(E_new)^2; - % Ptot_new = mean(E_new.^2); - % Ps_new = max(Ptot_new - Pc_new, eps); - % cspr_out = 10*log10(Pc_new / Ps_new); - % - % % (Optional) show feasibility info - % cspr_min = 10*log10((a_min^2)/max(Ps0,eps)); - % disp(table(cspr_in, target_cspr_dB, cspr_min, cspr_out)); - % - % % Use E_new as your adjusted field - % Opt_sig.signal = E_new; - - %%%%%% PD Square Law %%%%%% - PD_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11,"randomkey",random_key).process(Opt_sig); - - %%%%%% Low-pass RX (PD, El. Connectors and Scope %%%%%% - rx_bwl = 70e9; - 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); - - %%%%%% 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); - - Scpe_sig_2sps = Scpe_sig.resample("fs_out",2*fsym(r)); - % Scpe_sig_resampled.signal = Scpe_sig_resampled.signal(1:2*length(Symbols)); - - [~, Scpe_cell, ~, found_sync] = Scpe_sig_2sps.tsynch("reference", Symbols, "fs_ref", fsym(r), "debug_plots", 0); - Rx_sig = Scpe_cell{1}; - Rx_sig = Rx_sig.normalize("mode","rms"); - - if 1 - %Duobinary Targeting - - eq_ = EQ("Ne",[vnle_order1,vnle_order2,vnle_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); - - db_ref_sequence = Duobinary().encode(Symbols); - db_ref_constellation = unique(db_ref_sequence.signal); - [eq_signal, eq_noise] = eq_.process(Rx_sig,db_ref_sequence); - - viterbi = 0; - if viterbi - mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); - mlse_.DIR = [1,1]; - [eq_signal_whitened] = mlse_.process(eq_signal); - else - mlse_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).get_levels ./ PAMmapper(M,0).get_scaling); - mlse_.DIR = [1,1]; - [eq_signal_whitened,LLR,gmi_mlse_db(r)] = mlse_.process(eq_signal,Symbols); - end - - mlse_sig_hd = PAMmapper(M,0,"eth_style",0).quantize(eq_signal_whitened); - mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd,"M",M); - mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded,"M",M); - - tx_symbols_precoded = Duobinary().encode(Symbols); - tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded); - - tx_bits_precoded = PAMmapper(M,0,"eth_style",0).demap(tx_symbols_precoded); - - rx_bits_mlse = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd_precoded); - [~,errors_db_diff_precoded,ber_db_diff_precoded(r),a] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - burst_db_pre(r,:) = count_error_bursts(a, 15)./numel(Tx_bits.signal); - - %B) Just determine BER - rx_bits_mlse = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd); - [bits_mlse,errors_db,ber_db(r),a] = calc_ber(rx_bits_mlse.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - burst_db(r,:) = count_error_bursts(a, 15)./numel(Tx_bits.signal); - - fprintf('BER ber_db_diff_precoded: %.2e \n',ber_db_diff_precoded(r)); - fprintf('BER Vber_dbNLE: %.2e \n',ber_db(r)); - % figure();hold on;stem(1:15,burst_db(r,:),'LineWidth',1,'Color',cols(1,:));stem(1:15,burst_db_pre(r,:),'LineWidth',1,'Color',cols(2,:));set(gca, 'yscale', 'log'); - - end - - - % FFE or VNLE - eq_ = EQ("Ne",[vnle_order1,vnle_order2,vnle_order3],"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.00,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); - % eq = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0,"order",[50,2,2],"sps",2,"decide",0); - - [eq_signal_fullresp, eq_noise] = eq_.process(Rx_sig, Symbols); - showEQNoisePSD(eq_noise, "fignum",1273876,"displayname",'noise after EQ'); - [mi_gomez(r)] = calc_air(eq_signal_fullresp, Symbols, "skip_front", 100, "skip_end", 100); - [gmi_vnle_bitwise(r)] = calc_ngmi(eq_signal_fullresp,Symbols); - [gmi_bitwise_2(r)] = calc_gmi_bitwise(eq_signal_fullresp,Symbols); - snr_vnle(r) = calc_snr(Symbols, eq_signal_fullresp-Symbols); - - % eq_signal_fullresp.plot("displayname",'bla','fignum',199); - % eq_signal_fullresp.eye(fsym(r),M,"fignum",103837); - - % Hard decision on VNLE output - eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_fullresp); - rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd); - [~,tot_err,ber_vnle(r),a] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - burst_vnle(r,:) = count_error_bursts(a, 10)./tot_err; - - % showLevelConfusionMatrix(eq_signal_hd,Symbols,"M",M,"fignum",200,"displayname",'bla'); - % showLevelScatter(eq_signal_fullresp,Symbols,"displayname",'VNLE Out','f_sym',fsym(r),'fignum',201); - % show2Dconstellation(eq_signal_fullresp,Symbols,"displayname",'VNLE Out','fignum',2241); - - fprintf('BER VNLE: %.2e \n',ber_vnle(r)); - fprintf('NGMI VNLE: %.2f \n',gmi_vnle_bitwise(r)./m); - - if 1 - - % Process through postfilter and MLSE - pf_ncoeffs = 1; - if fsym(r) < 200e9 - pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1,"coefficients",[1,0.1]); - else - pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1,"coefficients",[1,0.85]); - end - - % showEQNoisePSD(eq_noise,"postfilter_taps",pf_.coefficients,"displayname",'Postfilter Burg based'); - alpha(r) = pf_.coefficients(2); - alpha_vec = max(0,round(alpha(r),2)-0.2):0.025:round(alpha(r),2)+0.4; - alpha_vec = unique(sort([alpha_vec, 1, alpha(r)])); - - gmi_mlse_ = zeros(size(alpha_vec)); - ber_mlse_ = zeros(size(alpha_vec)); - parfor a=1:numel(alpha_vec) - - mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).get_levels ./ PAMmapper(M,0).get_scaling,'DIR',[1,alpha_vec(a)]); - - pf_ = Postfilter("ncoeff",1,"useBurg",0,"coefficients",[1,alpha_vec(a)]); - [eq_signal_whitened,whitened_noise] = pf_.process(eq_signal_fullresp, eq_noise); - - [signalclass_hd,LLR,gmi_mlse_(a)] = mlse_.process(eq_signal_whitened,Symbols); - - mlse_sig_hd = PAMmapper(M, 0, "eth_style", 0).quantize(signalclass_hd); - - rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd); - - [~,tot_err,ber_mlse_(a),errpos] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - % burst_mlse(r,:) = count_error_bursts(errpos, 10); - - % if 0 - % fprintf('BER MLSE: %.2e \n',ber_mlse(r)); - % fprintf('NGMI MLSE: %.5f \n',gmi_mlse(r)./m); - % - % showLevelConfusionMatrix(mlse_sig_hd,Symbols,"M",M,"fignum",300,"displayname",'bla'); - % - % levels = sort(unique(Symbols.signal(:)).'); % 1×6 - % pairs = reshape(mlse_sig_hd.signal,2,[]).'; - % isedge = ismember(pairs, [levels(1) levels(end)]); - % isforbidden = sum(isedge,2)==2; - % fprintf('Found %d forbidden transitions (even→odd edges).\n', nnz(isforbidden)); - % - % - % % Process through postfilter and MLSE - % pf_ncoeffs = 1; - % pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); - % mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); - % [eq_signal_whitened,whitened_noise] = pf_.process(eq_signal_fullresp, eq_noise); - % mlse_.DIR = pf_.coefficients; - % mlse_output = mlse_.process(eq_signal_whitened); - % mlse_sig_hd = PAMmapper(M, 0, "eth_style", 0).quantize(mlse_output); - % rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd); - % [~,~,ber_viterbi(r),~] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - % fprintf('Viterbi BER: %.2e \n',ber_viterbi(r)); - % end - end - - [ber_mlse(r),idx] = min(ber_mlse_); - gmi_mlse(r) = gmi_mlse_(idx); - ber_mlse_burg(r) = ber_mlse_(alpha_vec==alpha(r)); - best_alpha(r) = alpha_vec(idx); - - end - - % IR target in EQ - if 1 - - % alpha_vec = max(0,round(alpha(r),2)-0.1):0.01:min(1,round(alpha(r),2)+0.1); - plot_stuff = 0; - gmi_mlse_pr_tgt_ = zeros(size(alpha_vec)); - ber_mlse_pr_tgt_ = zeros(size(alpha_vec)); - for a = 1:numel(alpha_vec) - - alpha_vec(a) = 0.9; - eq_ = EQ("Ne",[vnle_order1,vnle_order2,vnle_order3],"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.00,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); - Symbols_filt = Symbols.filter([1,alpha_vec(a)],1); - [eq_signal_prtgt, eq_noise] = eq_.process(Rx_sig, Symbols_filt); - - showLevelHistogram(eq_signal_prtgt,Symbols_filt,"displayname",'VNLE Out','fignum',201); - - if plot_stuff - % Plot the response for respective EQ targets - Symbols_filt.spectrum("displayname",'IDEAL Filtered Reference','fignum',240587); - eq_signal_whitened.spectrum("displayname",'Full tgt. EQ + PF','fignum',240587); - eq_signal_prtgt.spectrum("displayname",'Partial Resp. Target EQ','fignum',240587); - - noise_pf_out = Symbols_filt-eq_signal_whitened; - noise_pr_tgt = Symbols_filt-eq_signal_prtgt; - - noise_pf_out.spectrum("displayname",'Ideal PR - Whitening Out','fignum',240588); - noise_pr_tgt.spectrum("displayname",'Ideal PR - PR Target Out','fignum',240588); - end - - mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).get_levels ./ PAMmapper(M,0).get_scaling,'DIR',[1,alpha_vec(a)],'debug',0); - - [signalclass_hd,LLR,gmi_mlse_pr_tgt_(a)] = mlse_.process(eq_signal_prtgt,Symbols); - - mlse_sig_hd = PAMmapper(M, 0, "eth_style", 0).quantize(signalclass_hd); - - rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd); - - [~,tot_err,ber_mlse_pr_tgt_(a),errpos] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - - % burst_mlse_(a,:) = count_error_bursts(errpos, 10); - - % fprintf('BER MLSE: %.2e \n',ber_mlse_pr_tgt_(a)); - % fprintf('NGMI MLSE: %.5f \n',gmi_mlse_pr_tgt_(a)./m); - - end - - [ber_mlse_pr_tgt(r),idx] = min(ber_mlse_pr_tgt_); - gmi_mlse_pr_tgt(r) = gmi_mlse_pr_tgt_(idx); - best_alpha_pr_tgt(r) = alpha_vec(idx); - - end - - - cols = cbrewer2('paired',8); - figure(); hold on - title(sprintf('%d GBd',fsym(r).*1e-9)); - scatter(alpha_vec,ber_mlse_,15,'Marker','o','LineWidth',1,'DisplayName','MLSE','MarkerEdgeColor',cols(1,:)); - scatter(best_alpha(r),ber_mlse(r),15,'Marker','o','LineWidth',2,'DisplayName','MLSE','MarkerEdgeColor',cols(2,:)); - scatter(alpha(r),ber_mlse_burg(r),25,'Marker','+','LineWidth',2,'DisplayName','MLSE','MarkerEdgeColor',cols(2,:)); - - scatter(1,ber_db_diff_precoded(r),15,'Marker','diamond','LineWidth',2,'DisplayName','Duobinary','MarkerEdgeColor',cols(4,:)); - scatter(1,ber_db(r),15,'Marker','diamond','LineWidth',2,'DisplayName','Duobinary','MarkerEdgeColor',cols(4,:)); - - scatter(alpha_vec,ber_mlse_pr_tgt_,15,'Marker','x','LineWidth',1,'DisplayName','MLSE Partial Resp tgt','MarkerEdgeColor',cols(5,:)); - scatter(best_alpha_pr_tgt(r),ber_mlse_pr_tgt(r),25,'Marker','x','LineWidth',2,'DisplayName','MLSE','MarkerEdgeColor',cols(6,:)); - - set(gca,"YScale","log"); - % ylim([1e-6 0.5]); - % xlim([0.1 1]); - drawnow; - - - -end - - - - -% --- style control (one variable controls both marker size and linewidth) --- -STYLE_BASE = 2; % adjust this single number to scale markers & lines -MARKER_SIZE = STYLE_BASE; % marker size (MATLAB MarkerSize) -LINE_WIDTH = max(1.5, STYLE_BASE/3); % line width (keeps lines reasonable when STYLE_BASE large) - -% --- color map / method -> color assignment (keeps colors consistent) --- -cols = cbrewer2('Paired',8); -cols = linspecer(6); -d = 0; -cm.VNLE = cols(1 + d, :); -cm.MLSE = cols(2 + d, :); -cm.DB_precode = cols(3 + d, :); -cm.DB = cols(4 + d, :); % duobinary - -% prepare x values in GBd -xGHz = fsym .* 1e-9; -xticks_vals = xGHz; -xtick_labels = arrayfun(@(v) sprintf('%d', round(v)), xticks_vals, 'UniformOutput', false); - -% common marker settings (filled, same face+edge color) -mk.VNLE = {'Marker','none','MarkerFaceColor',cm.MLSE,'MarkerEdgeColor',cm.VNLE,'MarkerSize',MARKER_SIZE}; -mk.MLSE = {'Marker','none','MarkerFaceColor',cm.MLSE,'MarkerEdgeColor',cm.MLSE,'MarkerSize',MARKER_SIZE}; -mk.DB_precode = {'Marker','none','MarkerFaceColor',cm.DB_precode,'MarkerEdgeColor',cm.DB_precode,'MarkerSize',MARKER_SIZE}; -mk.DB = {'Marker','none','MarkerFaceColor',cm.DB,'MarkerEdgeColor',cm.DB,'MarkerSize',MARKER_SIZE}; - -% ---------------- FIGURE 11 : alpha (VNLE) ---------------- -figure(110+M); clf; hold on; -plot(xGHz, alpha, ... - 'DisplayName','VNLE', ... - mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); -xlabel('Baudrate in GBd'); -ylabel('alpha'); -set(gca, 'XTick', xticks_vals, 'XTickLabel', xtick_labels); -grid on; -legend('Location','best'); - -% ---------------- FIGURE 15 : GMI ---------------- -figure(111+M); clf; hold on; -plot(xGHz, mi_gomez, ... - 'DisplayName','MI VNLE', ... - mk.VNLE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.VNLE); -plot(xGHz, gmi_vnle_bitwise, ... - 'DisplayName','GMI VNLE', ... - mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); -% duobinary has only one GMI curve (DB output) -plot(xGHz, gmi_mlse_db, ... - 'DisplayName','GMI DB tgt.', ... - mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); -% MLSE symbol-wise (if present) -plot(xGHz, gmi_mlse, ... - 'DisplayName','GMI MLSE', ... - mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); - -ylim([log2(M)-1, log2(M)]); -xlabel('Baudrate in GBd'); -ylabel('GMI'); -set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end)); -grid on; -legend('Location','best'); -% xlim([184, 256]) - -% ---------------- FIGURE 13 : BER ---------------- -figure(312+M); hold on; -plot(xGHz, ber_vnle, ... - 'DisplayName','VNLE', ... - mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); -plot(xGHz, ber_mlse, ... - 'DisplayName','MLSE', ... - mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); -plot(xGHz, ber_viterbi, ... - 'DisplayName','Viterbi', ... - mk.MLSE{:}, 'LineStyle','--','LineWidth',LINE_WIDTH,'Color',cm.MLSE); - -yline(4.85e-3,'LineWidth',1,'HandleVisibility','off'); -yline(2.2e-4,'LineWidth',1,'HandleVisibility','off'); - -plot(xGHz, ber_db, ... - 'DisplayName','DB tgt.', ... - mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); - -plot(xGHz, ber_db_diff_precoded, ... - 'DisplayName','Prec. + DB tgt.', ... - mk.DB{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB); - -xlabel('Baudrate in GBd'); -ylabel('BER'); -set(gca, 'yscale', 'log'); -set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end)); -grid on; -legend('Location','best'); -% xlim([184, 256]) - -% ---------------- FIGURE 15 : Information Rates ---------------- -tp = TransmissionPerformance; - - -m = floor(log2(M)*10)/10; -figure(113+M); clf; hold on; - -netrates_vnle = tp.calculateNetRate(fsym.* m, ... - 'NGMI', gmi_vnle_bitwise./m, ... - 'BER', ber_vnle); -% -plot(xGHz, gmi_vnle_bitwise.*xGHz, ... - 'DisplayName','GMI*R VNLE', ... - mk.VNLE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.VNLE); - -plot(xGHz, netrates_vnle.SDHD.NetRate.*1e-9, ... - 'DisplayName','SD+HD VNLE', ... - mk.VNLE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.VNLE); -plot(xGHz, netrates_vnle.HD.NetRate.*1e-9, ... - 'DisplayName','Staircase VNLE', ... - mk.VNLE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.VNLE); - - -% -% MLSE symbol-wise (if present) -plot(xGHz, gmi_mlse.*xGHz, ... - 'DisplayName','GMI*R MLSE', ... - mk.MLSE{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.MLSE); - -netrates_mlse = tp.calculateNetRate(fsym.* m, ... - 'NGMI', gmi_mlse./m, ... - 'BER', ber_mlse); -plot(xGHz, netrates_mlse.SDHD.NetRate.*1e-9, ... - 'DisplayName','SD+HD MLSE', ... - mk.MLSE{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.MLSE); -plot(xGHz, netrates_mlse.HD.NetRate.*1e-9, ... - 'DisplayName','Staircase MLSE', ... - mk.MLSE{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.MLSE); - - -% duobinary has only one GMI curve (DB output) -plot(xGHz, gmi_mlse_db.*xGHz, ... - 'DisplayName','GMI*R DB tgt.', ... - mk.DB{:}, 'LineStyle','-','LineWidth',LINE_WIDTH,'Color',cm.DB); - -netrates_db = tp.calculateNetRate(fsym.* m, ... - 'NGMI', gmi_mlse_db./m, ... - 'BER', ber_db); - -plot(xGHz, netrates_db.SDHD.NetRate.*1e-9, ... - 'DisplayName','SD+HD DB', ... - mk.DB{:}, 'LineStyle',':','LineWidth',LINE_WIDTH,'Color',cm.DB); -plot(xGHz, netrates_db.HD.NetRate.*1e-9, ... - 'DisplayName','Staircase DB', ... - mk.DB{:}, 'LineStyle','-.','LineWidth',LINE_WIDTH,'Color',cm.DB); - - - -% ylim([log2(M)-1, log2(M)]); -xlabel('Baudrate in GBd'); -ylabel('AIR in Gbps'); -set(gca, 'XTick', xticks_vals(1:2:end), 'XTickLabel', xtick_labels(1:2:end)); -grid on; -legend('Location','best'); -xlim([184, 256]) - -% Auxiliary nested helper for numerically stable log-sum-exp -function s = logsumexp(a) -% LOGSUMEXP Compute log(sum(exp(a))) in a numerically stable way -m = max(a); -s = m + log(sum(exp(a - m))); -end diff --git a/projects/IMDD_base_system/simulation_bwl_2.m b/projects/IMDD_base_system/simulation_bwl_2.m deleted file mode 100644 index e336ad2..0000000 --- a/projects/IMDD_base_system/simulation_bwl_2.m +++ /dev/null @@ -1,191 +0,0 @@ -%%% Run parameters -% TX -M = 4; -fsym = 180e9; - -apply_pulsef = 1; -fdac = 256e9; -fadc = 256e9; -random_key = 1; - -db_precode = 0; -db_encode = 0; - -rcalpha = 0.05; -kover = 16; -vbias_rel = 0.5; -u_pi = 2.9; -vbias = -vbias_rel*u_pi; -laser_wavelength = 1310; -laser_linewidth = 0; -tx_bw_nyquist = 1; - -% Channel -link_length = 1; - -% RX -rop = 0; -rx_bw_nyquist = 0.99; - -vnle_order1 = 50; -vnle_order2 = 3; -vnle_order3 = 3; - -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; - - -dfe_ = sum(dfe_order)>0; - -doub_mode = db_mode.no_db; - - -Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); - -db_precode = 0; -db_encode = 0; -duob_mode = db_mode.no_db; -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,... - "mrds_code",0,"mrds_blocklength",512,"duobinary_mode",duob_mode).process(); - -Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1); - - -% %% 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); - -%% - - -%%%%% 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 %%%%%% -f_nyquist = fsym/2; -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"); - -%%%%% 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); - -%%%%%% 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 = 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); - -% 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); - -Scpe_sig_resampled = Scpe_sig.resample("fs_out",2*fsym); - -[~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); - -eq_ = EQ("Ne",[vnle_order1,vnle_order2,vnle_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_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); -mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); - -if duob_mode == db_mode.no_db - - % FFE or VNLE - [eq_signal_sd, eq_noise] = eq_.process(Scpe_cell{1}, Symbols); - - % Hard decision on VNLE output - eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); - - % Process through postfilter and MLSE - [mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise); - mlse_.DIR = pf_.coefficients; - mlse_sig_sd = mlse_.process(mlse_sig_sd,Symbols); - - - % BER - rx_bits = PAMmapper(M,0,"eth_style",0).demap(eq_signal_hd); - [~,tot_err,ber_vnle,a] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - - rx_bits = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_sd); - [~,tot_err,ber_mlse,a] = calc_ber(rx_bits.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - -elseif duob_mode == db_mode.db_precoded - - %% - [EQ_sig, Noi] = eq_.process(Scpe_cell{1},Duobinary().encode(Symbols)); - - showLevelHistogram(EQ_sig,Duobinary().encode(Symbols),"displayname",101); - - mim_decoded = Duobinary().decode(EQ_sig,"M",M); - - showLevelHistogram(mim_decoded,Symbols,"displayname",101); - - 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); - - %% - mlse_sig_hd = MLSE("DIR",[1,1],"duobinary_output",1,"M",M,"trellis_states",PAMmapper(M,0).levels).process(EQ_sig,Symbols); - - mlse_sig_hd_decoded = Duobinary().encode(mlse_sig_hd,"M",M); - - mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded,"M",M); - - rx_bits_mlse_decoded = PAMmapper(M,0,"eth_style",0).demap(mlse_sig_hd_decoded); - - [~,errors_db_diff_precoded,ber_db_diff_precoded,a] = calc_ber(rx_bits_mlse_decoded.signal,Tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - - %% - -end \ No newline at end of file diff --git a/projects/IMDD_base_system/submit_handle.m b/projects/IMDD_base_system/submit_handle.m index ae59869..7bb83d4 100644 --- a/projects/IMDD_base_system/submit_handle.m +++ b/projects/IMDD_base_system/submit_handle.m @@ -4,69 +4,50 @@ arguments funcHandle wh options.parallel = 1; -end - -%%% 2) SUBMIT SIMULATION -% Initialize job results -if options.parallel - curpool = gcp('nocreate'); - if isempty(curpool) - parpool; - else - % stop all forgotten or unfetched jobs from queue - if ~isempty(curpool.FevalQueue.QueuedFutures) || ~isempty(curpool.FevalQueue.RunningFutures) - oldq = length(curpool.FevalQueue.QueuedFutures) + length(curpool.FevalQueue.RunningFutures); - curpool.FevalQueue.cancelAll - fprintf('Canceled %d unfetched jobs from old queue.', oldq); - end - end - results = parallel.FevalFuture.empty(); -else - results = []; + options.waitbar = true; end fprintf('Requested %d loops\n', wh.getLastLinIndice); +jobs = repmat(struct('args', {{}}, 'label', "", 'meta', struct()), 1, wh.getLastLinIndice); + for lin_idx = 1:wh.getLastLinIndice - optionalVars = struct(); - - if ~isempty(wh.getDimension) - % Build the optionalVars struct - [parametervalues, parameternames] = wh.getPhysIndicesByLinIndex(lin_idx); - for pidx = 1:numel(parameternames) - optionalVars.(parameternames{pidx}) = parametervalues{pidx}; - end - end - - %%% SIMULATION HERE - if options.parallel - numOutputs = 1; - results(lin_idx) = parfeval(funcHandle, numOutputs, optionalVars); - else - finalresults{lin_idx} = feval(funcHandle, optionalVars); - wh.addValueToStorageByLinIdx(finalresults{lin_idx}, 'ber', lin_idx); - end + optionalVars = buildOptionalVars(lin_idx, wh); + jobs(lin_idx).args = {optionalVars}; + jobs(lin_idx).label = sprintf('Job %d', lin_idx); + jobs(lin_idx).meta.lin_idx = lin_idx; end +mode = processingMode.serial; if options.parallel - %%% 4) Setup waitbar - h = waitbar(0, 'Processing Simulations...'); - updateWaitbar = @(~) waitbar(mean(arrayfun(@(f) strcmp(f.State, 'finished'), results)), h); + mode = processingMode.parallel; +end - fprintf('Fetching results... \n'); - - updateWaitbarFutures = afterEach(results, updateWaitbar, 0); - afterAll(updateWaitbarFutures, @(~) delete(h), 0); +runBatch(funcHandle, jobs, ... + "mode", mode, ... + "waitbar", options.waitbar, ... + "waitbarMessage", "Processing Simulations...", ... + "resultHandler", @storeResult, ... + "errorHandler", @handleError); - %%% 7) Fetch final results iteratively as they finish - for i = 1:length(results) - try - [ridx, result] = fetchNext(results); - wh.addValueToStorageByLinIdx(result, 'ber', ridx); - catch ME - fprintf('A job failed or could not be fetched. Error: %s\n', ME.message); + function optionalVars = buildOptionalVars(lin_idx, dataStorage) + optionalVars = struct(); + if ~isempty(dataStorage.getDimension) + [parametervalues, parameternames] = dataStorage.getPhysIndicesByLinIndex(lin_idx); + for pidx = 1:numel(parameternames) + optionalVars.(parameternames{pidx}) = parametervalues{pidx}; + end + end + end + + function storeResult(result, job, ~) + wh.addValueToStorageByLinIdx(result, 'ber', job.meta.lin_idx); + end + + function handleError(ME, job, ~) + fprintf('[%s] ERROR [%s]: %s\n', job.label, ME.identifier, ME.message); + for st = ME.stack' + fprintf(' %s:%d (%s)\n', st.file, st.line, st.name); end end end - -end diff --git a/projects/IMDD_base_system/submit_simulations.m b/projects/IMDD_base_system/submit_simulations.m deleted file mode 100644 index d8e1d24..0000000 --- a/projects/IMDD_base_system/submit_simulations.m +++ /dev/null @@ -1,83 +0,0 @@ -function wh = submit_simulations(wh,options) - -arguments - wh - options.parallel = 1; - options.simulation_mode = 1; -end - -%%% 2) SUBMIT SIMULATION -% Initialize job results -if options.parallel - curpool = gcp('nocreate'); - if isempty(curpool) - parpool; - else - % stop all forgotten or unfetched jobs from queue - if ~isempty(curpool.FevalQueue.QueuedFutures) || ~isempty(curpool.FevalQueue.RunningFutures) - oldq = length(curpool.FevalQueue.QueuedFutures) +length(curpool.FevalQueue.RunningFutures); - curpool.FevalQueue.cancelAll - fprintf('Canceled %d unfetched jobs from old queue.',oldq); - end - - end - results = parallel.FevalFuture.empty(); -else - results = []; -end - -fprintf('Requested %d loops',wh.getLastLinIndice); - -lin_idx = 1; -for lin_idx = 1:wh.getLastLinIndice - optionalVars = struct(); - if ~isempty(wh.getDimension) - % Build the optionalVars struct - [parametervalues,parameternames]=wh.getPhysIndicesByLinIndex(lin_idx); - for pidx = 1:numel(parameternames) - optionalVars.(parameternames{pidx}) = parametervalues{pidx}; - end - end - - %%% SIMULATION HERE - if options.parallel - - numOutputs = 1; - results(lin_idx) = parfeval(@imdd_model, numOutputs, options.simulation_mode, optionalVars); - - else - - finalresults{lin_idx} = imdd_model(options.simulation_mode,optionalVars); - wh.addValueToStorageByLinIdx(finalresults{lin_idx}, 'ber', lin_idx); - - end -end - - - -if options.parallel - - %%% 4) Setup waitbar - h = waitbar(0, 'Processing Simulations...'); - % Helper function to compute progress - updateWaitbar = @(~) waitbar(mean(arrayfun(@(f) strcmp(f.State, 'finished'), results)), h); - - fprintf('Fetching results... \n'); - - % Update the waitbar after each simulation - updateWaitbarFutures = afterEach(results, updateWaitbar, 0); - - % Close the waitbar after all simulations complete - afterAll(updateWaitbarFutures, @(~) delete(h), 0); - - %%% 7) Fetch final results after all computations - fetchOutputs(results); - - for ridx = 1:length(results) - wh.addValueToStorageByLinIdx(results(ridx).OutputArguments{1}, 'ber', ridx); - end - -end - - -end \ No newline at end of file diff --git a/projects/IMDD_base_system/tx_simulation.m b/projects/IMDD_base_system/tx_simulation.m deleted file mode 100644 index 1cd6216..0000000 --- a/projects/IMDD_base_system/tx_simulation.m +++ /dev/null @@ -1,31 +0,0 @@ - - - Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",rcalpha); - - [Digi_sig,Symbols,Tx_bits] = PAMsource(... - "fsym",fsym,"M",M,"order",19,"useprbs",1,... - "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"); - - %%%%% Low-pass el. components %%%%%% - El_sig = Filter('filtdegree',4,"f_cutoff",tx_bw_nyquist.*f_nyquist,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig); - - %%%%% 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); -