Strengthen WDM and IM/DD optical tests
This commit is contained in:
@@ -6,6 +6,7 @@ classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
|
||||
% - the reduced end-to-end chain runs without errors
|
||||
% - key signal objects have the expected lengths and sampling rates
|
||||
% - the DSP outputs return finite, bounded metrics
|
||||
% - the intermediate optical and electrical stages stay physically sane
|
||||
% - MLSE does not regress relative to the preceding FFE stage
|
||||
%
|
||||
% Thresholds are intentionally provisional in this first iteration.
|
||||
@@ -19,7 +20,7 @@ classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
|
||||
function runReducedImddWorkflowOnce(testCase)
|
||||
% Run the deterministic reduced workflow once and share the
|
||||
% resulting signals/metrics across all test methods.
|
||||
testCase.workflow = runReducedWorkflow();
|
||||
testCase.workflow = buildReducedImddWorkflow("minimal");
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,18 +32,30 @@ classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
|
||||
testCase.verifyClass(wf.Symbols, 'Informationsignal');
|
||||
testCase.verifyClass(wf.Digi_sig, 'Informationsignal');
|
||||
testCase.verifyClass(wf.El_sig, 'Electricalsignal');
|
||||
testCase.verifyClass(wf.Opt_sig_tx, 'Opticalsignal');
|
||||
testCase.verifyClass(wf.Opt_sig, 'Opticalsignal');
|
||||
testCase.verifyClass(wf.Rx_sig_after_pd, 'Electricalsignal');
|
||||
testCase.verifyClass(wf.Scpe_sig_pre_mf, 'Informationsignal');
|
||||
testCase.verifyClass(wf.Scpe_sig, 'Informationsignal');
|
||||
testCase.verifyClass(wf.Synced_sig_centered, 'Informationsignal');
|
||||
testCase.verifyClass(wf.Synced_sig, 'Informationsignal');
|
||||
|
||||
testCase.verifyGreaterThan(length(wf.Tx_bits.signal), 0);
|
||||
testCase.verifyGreaterThan(length(wf.Symbols.signal), 0);
|
||||
testCase.verifyEqual(wf.Digi_sig.fs, wf.params.fdac);
|
||||
% Tx_bits is a bit-level container and does not carry a
|
||||
% sampling-rate contract on this path.
|
||||
testCase.verifyEqual(wf.Symbols.fs, wf.params.fsym);
|
||||
testCase.verifyEqual(wf.El_sig.fs, wf.params.fdac * wf.params.kover);
|
||||
testCase.verifyEqual(wf.Opt_sig_tx.fs, wf.El_sig.fs);
|
||||
testCase.verifyEqual(wf.Opt_sig.fs, wf.Opt_sig_tx.fs);
|
||||
testCase.verifyEqual(wf.Rx_sig_after_pd.fs, wf.Opt_sig.fs);
|
||||
testCase.verifyEqual(wf.Scpe_sig_pre_mf.fs, wf.params.fadc);
|
||||
|
||||
% After the matched filter, the signal is intentionally reduced
|
||||
% to 2 samples per symbol for the downstream DSP chain.
|
||||
testCase.verifyEqual(wf.Scpe_sig.fs, 2 * wf.params.fsym);
|
||||
testCase.verifyEqual(wf.Synced_sig_centered.fs, 2 * wf.params.fsym);
|
||||
testCase.verifyEqual(wf.Synced_sig.fs, 2 * wf.params.fsym);
|
||||
|
||||
% The synchronized signal is explicitly cropped to 2 samples per
|
||||
@@ -56,9 +69,12 @@ classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
|
||||
finiteSignals = {
|
||||
wf.Digi_sig.signal
|
||||
wf.El_sig.signal
|
||||
wf.Opt_sig_tx.signal
|
||||
wf.Opt_sig.signal
|
||||
wf.Rx_sig_after_pd.signal
|
||||
wf.Scpe_sig_pre_mf.signal
|
||||
wf.Scpe_sig.signal
|
||||
wf.Synced_sig_centered.signal
|
||||
wf.Synced_sig.signal
|
||||
};
|
||||
|
||||
@@ -68,6 +84,20 @@ classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
|
||||
testCase.verifyFalse(any(isinf(sig), 'all'));
|
||||
end
|
||||
|
||||
txOptPower = signalPower(wf.Opt_sig_tx.signal);
|
||||
fiberOptPower = signalPower(wf.Opt_sig.signal);
|
||||
pdPower = signalPower(wf.Rx_sig_after_pd.signal);
|
||||
preMfVariance = signalVariance(wf.Scpe_sig_pre_mf.signal);
|
||||
mfVariance = signalVariance(wf.Scpe_sig.signal);
|
||||
centeredMean = mean(wf.Synced_sig_centered.signal(:));
|
||||
|
||||
testCase.verifyGreaterThan(txOptPower, 0);
|
||||
testCase.verifyGreaterThan(fiberOptPower, 0);
|
||||
testCase.verifyGreaterThan(pdPower, 0);
|
||||
testCase.verifyGreaterThan(preMfVariance, 0);
|
||||
testCase.verifyGreaterThan(mfVariance, 0);
|
||||
testCase.verifyLessThanOrEqual(abs(centeredMean), 1e-12);
|
||||
|
||||
testCase.verifyGreaterThanOrEqual(wf.ffe_results.metrics.BER, 0);
|
||||
testCase.verifyLessThanOrEqual(wf.ffe_results.metrics.BER, 1);
|
||||
testCase.verifyGreaterThanOrEqual(wf.mlse_results.metrics.BER, 0);
|
||||
@@ -105,217 +135,11 @@ classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function workflow = runReducedWorkflow()
|
||||
params = reducedWorkflowParameters();
|
||||
|
||||
% -------------------- TX --------------------
|
||||
txPulse = Pulseformer( ...
|
||||
"fsym", params.fsym, ...
|
||||
"fdac", params.fdac, ...
|
||||
"pulse", "rrc", ...
|
||||
"pulselength", 12, ...
|
||||
"alpha", params.rcalpha);
|
||||
|
||||
[digiSig, symbols, txBits] = PAMsource( ...
|
||||
"fsym", params.fsym, ...
|
||||
"M", params.M, ...
|
||||
"order", params.sourceOrder, ...
|
||||
"useprbs", false, ...
|
||||
"fs_out", params.fdac, ...
|
||||
"applyclipping", false, ...
|
||||
"applypulseform", true, ...
|
||||
"pulseformer", txPulse, ...
|
||||
"randkey", params.randomKey, ...
|
||||
"duobinary_mode", db_mode.no_db, ...
|
||||
"mrds_code", 0).process();
|
||||
|
||||
elSig = AWG( ...
|
||||
"fdac", params.fdac, ...
|
||||
"f_cutoff", params.fsym, ...
|
||||
"lpf_active", false, ...
|
||||
"kover", params.kover, ...
|
||||
"bit_resolution", 8, ...
|
||||
"upsampling_method", "samplehold", ...
|
||||
"precomp_sinc_rolloff", 1).process(digiSig);
|
||||
|
||||
elSig = elSig.normalize("mode", "oneone");
|
||||
elSig = elSig .* params.driverScaling;
|
||||
|
||||
% -------------------- Optical Channel --------------------
|
||||
optSig = EML( ...
|
||||
"mode", eml_mode.im_cosinus, ...
|
||||
"power", params.opticalPowerDbm, ...
|
||||
"fsimu", elSig.fs, ...
|
||||
"lambda", params.laserWavelengthNm, ...
|
||||
"bias", params.vbias, ...
|
||||
"u_pi", params.uPi, ...
|
||||
"linewidth", 0, ...
|
||||
"randomkey", params.randomKey + 1, ...
|
||||
"alpha", 0).process(elSig);
|
||||
|
||||
optSig = Fiber( ...
|
||||
"fsimu", optSig.fs, ...
|
||||
"fiber_length", params.linkLengthKm, ...
|
||||
"alpha", params.fiberAlphaDbPerKm, ...
|
||||
"D", 0, ...
|
||||
"lambda0", 1310, ...
|
||||
"gamma", 0, ...
|
||||
"Dslope", 0.07).process(optSig);
|
||||
|
||||
rxOptSig = Amplifier( ...
|
||||
"amp_mode", "ideal_no_noise", ...
|
||||
"gain_mode", "output_power", ...
|
||||
"amplification_db", params.ropDbm).process(optSig);
|
||||
|
||||
rxSigAfterPd = Photodiode( ...
|
||||
"fsimu", params.fdac * params.kover, ...
|
||||
"dark_current", 0, ...
|
||||
"responsivity", 1, ...
|
||||
"temperature", 20, ...
|
||||
"nep", 0, ...
|
||||
"randomkey", params.randomKey + 2).process(rxOptSig);
|
||||
|
||||
rxSigFiltered = Filter( ...
|
||||
"filtdegree", 4, ...
|
||||
"f_cutoff", params.rxElectricalBandwidthHz, ...
|
||||
"fs", params.fdac * params.kover, ...
|
||||
"filterType", filtertypes.butterworth, ...
|
||||
"active", true).process(rxSigAfterPd);
|
||||
|
||||
scopeLpf = Filter( ...
|
||||
"filtdegree", 4, ...
|
||||
"f_cutoff", params.scopeBandwidthHz, ...
|
||||
"fs", params.fadc, ...
|
||||
"filterType", filtertypes.butterworth, ...
|
||||
"active", true);
|
||||
|
||||
scpeSig = Scope( ...
|
||||
"fsimu", params.fdac * params.kover, ...
|
||||
"fadc", params.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", scopeLpf).process(rxSigFiltered);
|
||||
|
||||
rxMatchedFilter = Pulseformer( ...
|
||||
"fsym", params.fsym, ...
|
||||
"fdac", 2 * params.fsym, ...
|
||||
"pulse", "rrc", ...
|
||||
"pulselength", 12, ...
|
||||
"alpha", params.rcalpha, ...
|
||||
"matched", 1);
|
||||
scpeSig = rxMatchedFilter.process(scpeSig);
|
||||
|
||||
[syncedSig, ~] = scpeSig.tsynch("reference", symbols, "fs_ref", params.fsym, "debug_plots", 0);
|
||||
syncedSig = syncedSig - mean(syncedSig.signal);
|
||||
syncedSig.signal = syncedSig.signal(1 : 2 * length(symbols));
|
||||
|
||||
% -------------------- DSP --------------------
|
||||
ffeEq = FFE( ...
|
||||
"epochs_tr", 2, ...
|
||||
"epochs_dd", 1, ...
|
||||
"len_tr", params.lenTr, ...
|
||||
"mu_dd", 1e-4, ...
|
||||
"mu_tr", 1e-2, ...
|
||||
"order", 21, ...
|
||||
"sps", 2, ...
|
||||
"decide", 0, ...
|
||||
"adaption_technique", adaption_method.nlms, ...
|
||||
"dd_mode", 1);
|
||||
|
||||
ffeResults = ffe( ...
|
||||
ffeEq, ...
|
||||
params.M, ...
|
||||
syncedSig, ...
|
||||
symbols, ...
|
||||
txBits, ...
|
||||
"precode_mode", db_mode.no_db, ...
|
||||
"showAnalysis", 0, ...
|
||||
"postFFE", [], ...
|
||||
"eth_style_symbol_mapping", 0);
|
||||
|
||||
mlseEq = FFE( ...
|
||||
"epochs_tr", 2, ...
|
||||
"epochs_dd", 1, ...
|
||||
"len_tr", params.lenTr, ...
|
||||
"mu_dd", 1e-4, ...
|
||||
"mu_tr", 1e-2, ...
|
||||
"order", 21, ...
|
||||
"sps", 2, ...
|
||||
"decide", 0, ...
|
||||
"adaption_technique", adaption_method.nlms, ...
|
||||
"dd_mode", 1);
|
||||
postfilter = Postfilter("ncoeff", 1, "useBurg", 1);
|
||||
mlse = MLSE( ...
|
||||
"duobinary_output", 0, ...
|
||||
"M", params.M, ...
|
||||
"trellis_states", PAMmapper(params.M, 0).levels);
|
||||
|
||||
[vnleResults, mlseResults] = vnle_postfilter_mlse( ...
|
||||
mlseEq, ...
|
||||
postfilter, ...
|
||||
mlse, ...
|
||||
params.M, ...
|
||||
syncedSig, ...
|
||||
symbols, ...
|
||||
txBits, ...
|
||||
"precode_mode", db_mode.no_db, ...
|
||||
"showAnalysis", 0, ...
|
||||
"postFFE", [], ...
|
||||
"eth_style_symbol_mapping", 0);
|
||||
|
||||
workflow = struct();
|
||||
workflow.params = params;
|
||||
workflow.Digi_sig = digiSig;
|
||||
workflow.Symbols = symbols;
|
||||
workflow.Tx_bits = txBits;
|
||||
workflow.El_sig = elSig;
|
||||
workflow.Opt_sig = optSig;
|
||||
workflow.Rx_sig_after_pd = rxSigAfterPd;
|
||||
workflow.Scpe_sig = scpeSig;
|
||||
workflow.Synced_sig = syncedSig;
|
||||
workflow.ffe_results = ffeResults;
|
||||
workflow.vnle_results = vnleResults;
|
||||
workflow.mlse_results = mlseResults;
|
||||
function p = signalPower(signal)
|
||||
p = mean(abs(signal(:)).^2);
|
||||
end
|
||||
|
||||
function params = reducedWorkflowParameters()
|
||||
params = struct();
|
||||
|
||||
% Smaller, deterministic version of the IM/DD base workflow.
|
||||
params.M = 4;
|
||||
params.fsym = 16e9;
|
||||
params.fdac = 64e9;
|
||||
params.fadc = 64e9;
|
||||
params.kover = 2;
|
||||
params.randomKey = 1;
|
||||
params.sourceOrder = 12;
|
||||
params.rcalpha = 0.05;
|
||||
params.lenTr = 256;
|
||||
|
||||
% Driver / modulator operating point.
|
||||
params.uPi = 3;
|
||||
params.vbiasRel = 0.5;
|
||||
params.vbias = -params.vbiasRel * params.uPi;
|
||||
params.driverScaling = 0.6 * (params.uPi / 2 - abs(params.vbias - params.uPi / 2));
|
||||
|
||||
% Optical path.
|
||||
params.laserWavelengthNm = 1293;
|
||||
params.opticalPowerDbm = 3;
|
||||
params.linkLengthKm = 1;
|
||||
params.fiberAlphaDbPerKm = 0.3;
|
||||
params.ropDbm = 0;
|
||||
|
||||
% Receiver filtering.
|
||||
params.rxElectricalBandwidthHz = 40e9;
|
||||
params.scopeBandwidthHz = 25e9;
|
||||
function v = signalVariance(signal)
|
||||
centered = signal(:) - mean(signal(:));
|
||||
v = mean(abs(centered).^2);
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user