Unify batch job processing with a shared runBatch core
This commit is contained in:
209
Functions/Job_Processing/runBatch.m
Normal file
209
Functions/Job_Processing/runBatch.m
Normal file
@@ -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<NASGU>
|
||||
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
|
||||
@@ -27,32 +27,17 @@ nRunIds = numel(run_ids);
|
||||
nJobsPerRunId = submit_options.wh.getLastLinIndice();
|
||||
totalJobs = nRunIds * nJobsPerRunId;
|
||||
|
||||
% Preallocate
|
||||
wh = submit_options.wh;
|
||||
results = cell(nJobsPerRunId, nRunIds);
|
||||
futures = parallel.FevalFuture.empty(totalJobs,0);
|
||||
jobIndices = zeros(totalJobs,2);
|
||||
jobs = repmat(struct('args', {{}}, 'label', "", 'meta', struct()), 1, totalJobs);
|
||||
|
||||
% 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
|
||||
jobCounter = 0;
|
||||
for r = 1:nRunIds
|
||||
for k = 1:nJobsPerRunId
|
||||
jobCounter = jobCounter + 1;
|
||||
jobIndices(jobCounter,:) = [r,k];
|
||||
optionalVars = buildOptionalVars(k, submit_options.wh);
|
||||
|
||||
opt = buildOptionalVars(k, submit_options.wh);
|
||||
futures(jobCounter) = parfeval( ...
|
||||
p, @dsp_runid, 1, ...
|
||||
jobs(jobCounter).args = { ...
|
||||
run_ids(r), ...
|
||||
"database_type", dsp_options.database_type, ...
|
||||
"dataBase", dsp_options.dataBase, ...
|
||||
@@ -61,130 +46,34 @@ switch submit_mode
|
||||
"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);
|
||||
"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
|
||||
|
||||
%—– 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;
|
||||
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
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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], ...
|
||||
|
||||
@@ -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])
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
for lin_idx = 1:wh.getLastLinIndice
|
||||
optionalVars = struct();
|
||||
jobs = repmat(struct('args', {{}}, 'label', "", 'meta', struct()), 1, wh.getLastLinIndice);
|
||||
|
||||
if ~isempty(wh.getDimension)
|
||||
% Build the optionalVars struct
|
||||
[parametervalues, parameternames] = wh.getPhysIndicesByLinIndex(lin_idx);
|
||||
for lin_idx = 1:wh.getLastLinIndice
|
||||
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
|
||||
mode = processingMode.parallel;
|
||||
end
|
||||
|
||||
runBatch(funcHandle, jobs, ...
|
||||
"mode", mode, ...
|
||||
"waitbar", options.waitbar, ...
|
||||
"waitbarMessage", "Processing Simulations...", ...
|
||||
"resultHandler", @storeResult, ...
|
||||
"errorHandler", @handleError);
|
||||
|
||||
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
|
||||
|
||||
%%% 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
|
||||
end
|
||||
|
||||
if options.parallel
|
||||
%%% 4) Setup waitbar
|
||||
h = waitbar(0, 'Processing Simulations...');
|
||||
updateWaitbar = @(~) waitbar(mean(arrayfun(@(f) strcmp(f.State, 'finished'), results)), h);
|
||||
function storeResult(result, job, ~)
|
||||
wh.addValueToStorageByLinIdx(result, 'ber', job.meta.lin_idx);
|
||||
end
|
||||
|
||||
fprintf('Fetching results... \n');
|
||||
|
||||
updateWaitbarFutures = afterEach(results, updateWaitbar, 0);
|
||||
afterAll(updateWaitbarFutures, @(~) delete(h), 0);
|
||||
|
||||
%%% 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 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
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user