function workflow = buildReducedImddWorkflow(scenario) %BUILDREDUCEDIMDDWORKFLOW Build a reduced deterministic IM/DD workflow. % % The helper keeps the stage order aligned with the project workflow while % allowing tests to switch between representative scenarios: % - "minimal": reduced regression baseline % - "no-impairment": near-ideal baseline with impairment knobs neutralized % - "baseline": reference point for monotonicity checks % - "impaired": intentionally degraded variant for monotonicity checks arguments scenario (1, 1) string = "minimal" end params = scenarioParameters(scenario); workflow = runReducedWorkflow(params, scenario); end function workflow = runReducedWorkflow(params, scenario) % -------------------- TX -------------------- txPulse = Pulseformer( ... "fsym", params.fsym, ... "fdac", params.fdac, ... "pulse", "rrc", ... "pulselength", params.pulseLength, ... "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", params.awgLpfActive, ... "kover", params.kover, ... "bit_resolution", params.awgBitResolution, ... "upsampling_method", params.awgUpsamplingMethod, ... "precomp_sinc_rolloff", params.awgPrecompSincRolloff, ... "normalize2dac", params.awgNormalize2dac).process(digiSig); elSig = elSig.normalize("mode", "oneone"); elSig = elSig .* params.driverScaling; % -------------------- Optical Channel -------------------- optSigTx = 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", optSigTx.fs, ... "fiber_length", params.linkLengthKm, ... "alpha", params.fiberAlphaDbPerKm, ... "D", 0, ... "lambda0", 1310, ... "gamma", 0, ... "Dslope", 0.07).process(optSigTx); 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( ... "active", params.rxFilterActive, ... "filterType", filtertypes.butterworth, ... "f_cutoff", params.rxElectricalBandwidthHz, ... "fs", params.fdac * params.kover, ... "signal_length", 0, ... "filtdegree", 4, ... "lowpass", 1).process(rxSigAfterPd); scopeLpf = Filter( ... "active", params.scopeLpfActive, ... "filterType", filtertypes.butterworth, ... "f_cutoff", params.scopeBandwidthHz, ... "fs", params.fadc, ... "signal_length", 0, ... "filtdegree", 4, ... "lowpass", 1); scpeSigPreMf = Scope( ... "fsimu", params.fdac * params.kover, ... "fadc", params.fadc, ... "adcresolution", params.scopeAdcResolution, ... "quantbuffer", params.scopeQuantBuffer, ... "delay", 0, ... "fixed_delay", 0, ... "filtertype", filtertypes.butterworth, ... "samplingdelay", 0, ... "rand_samplingdelay", 0, ... "freq_offset", 0, ... "samp_jitter", 0, ... "block_dc", 1, ... "lpf_active", params.scopeLpfActive, ... "H_lpf", scopeLpf).process(rxSigFiltered); rxMatchedFilter = Pulseformer( ... "fsym", params.fsym, ... "fdac", 2 * params.fsym, ... "pulse", "rrc", ... "pulselength", params.pulseLength, ... "alpha", params.rcalpha, ... "matched", 1); scpeSig = rxMatchedFilter.process(scpeSigPreMf); [syncedSig, ~] = scpeSig.tsynch( ... "reference", symbols, ... "fs_ref", params.fsym, ... "debug_plots", 0); syncedCenteredSig = syncedSig - mean(syncedSig.signal); syncedSig = syncedCenteredSig; 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.scenario = scenario; workflow.params = params; workflow.Digi_sig = digiSig; workflow.Symbols = symbols; workflow.Tx_bits = txBits; workflow.El_sig = elSig; workflow.Opt_sig_tx = optSigTx; workflow.Opt_sig = optSig; workflow.Rx_sig_after_pd = rxSigAfterPd; workflow.Rx_sig_filtered = rxSigFiltered; workflow.Scpe_sig_pre_mf = scpeSigPreMf; workflow.Scpe_sig = scpeSig; workflow.Synced_sig_centered = syncedCenteredSig; workflow.Synced_sig = syncedSig; workflow.ffe_results = ffeResults; workflow.vnle_results = vnleResults; workflow.mlse_results = mlseResults; end function params = scenarioParameters(scenario) params = commonParameters(); switch lower(string(scenario)) case "minimal" params.ropDbm = 0; params.rxElectricalBandwidthHz = 40e9; params.scopeBandwidthHz = 25e9; case "no-impairment" params.randomKey = 11; params.linkLengthKm = 0; params.fiberAlphaDbPerKm = 0; params.ropDbm = 3; params.rxElectricalBandwidthHz = 200e9; params.scopeBandwidthHz = 200e9; params.rxFilterActive = false; params.scopeLpfActive = false; params.scopeAdcResolution = 24; params.scopeQuantBuffer = 0.05; params.awgLpfActive = false; params.awgBitResolution = 12; params.awgUpsamplingMethod = upsampling_mode.resample; params.awgNormalize2dac = true; case "baseline" params.ropDbm = 0; params.rxElectricalBandwidthHz = 40e9; params.scopeBandwidthHz = 25e9; case "impaired" params.ropDbm = -15; params.rxElectricalBandwidthHz = 12e9; params.scopeBandwidthHz = 12e9; otherwise error("buildReducedImddWorkflow:UnknownScenario", ... "Unknown reduced IM/DD scenario '%s'.", scenario); end end function params = commonParameters() params = struct(); 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.pulseLength = 12; params.lenTr = 256; 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)); params.laserWavelengthNm = 1293; params.opticalPowerDbm = 3; params.linkLengthKm = 1; params.fiberAlphaDbPerKm = 0.3; params.ropDbm = 0; params.rxElectricalBandwidthHz = 40e9; params.scopeBandwidthHz = 25e9; params.rxFilterActive = true; params.scopeLpfActive = true; params.scopeAdcResolution = 8; params.scopeQuantBuffer = 0.1; params.awgLpfActive = false; params.awgBitResolution = 8; params.awgUpsamplingMethod = upsampling_mode.samplehold; params.awgPrecompSincRolloff = 1; params.awgNormalize2dac = false; end