Files
imdd_silas/Tests/integration/IMDD_base_system_minimal_integration_test.m
2026-03-25 08:07:31 +01:00

146 lines
6.7 KiB
Matlab

classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
% First integration test for the reduced IM/DD base-system workflow.
%
% The goal of this test is not to freeze the full waveform exactly.
% Instead, it checks stable system-level contracts:
% - 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.
% Once the team has reviewed repeated runs, these can be tightened.
properties
workflow
end
methods (TestClassSetup)
function runReducedImddWorkflowOnce(testCase)
% Run the deterministic reduced workflow once and share the
% resulting signals/metrics across all test methods.
testCase.workflow = buildReducedImddWorkflow("minimal");
end
end
methods (Test, TestTags = {'integration', 'slow', 'imdd'})
function reducedWorkflowBuildsExpectedSignalStages(testCase)
wf = testCase.workflow;
testCase.verifyClass(wf.Tx_bits, 'Informationsignal');
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
% transmitted symbol before equalization.
testCase.verifyEqual(length(wf.Synced_sig.signal), 2 * length(wf.Symbols.signal));
end
function reducedWorkflowProducesFiniteSignalsAndMetrics(testCase)
wf = testCase.workflow;
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
};
for idx = 1:numel(finiteSignals)
sig = finiteSignals{idx};
testCase.verifyFalse(any(isnan(sig), 'all'));
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);
testCase.verifyLessThanOrEqual(wf.mlse_results.metrics.BER, 1);
testCase.verifyTrue(isfinite(wf.ffe_results.metrics.GMI));
testCase.verifyTrue(isfinite(wf.ffe_results.metrics.AIR));
testCase.verifyTrue(isfinite(wf.mlse_results.metrics.GMI));
testCase.verifyTrue(isfinite(wf.mlse_results.metrics.AIR));
end
function reducedWorkflowMeetsProvisionalPerformanceChecks(testCase)
wf = testCase.workflow;
% These are deliberately loose first-pass regression guards.
% Tighten them only after repeated baseline runs are reviewed.
% First observed baseline on 2026-03-24 for this reduced setup:
% FFE BER ~= 2.58e-1
%
% This threshold is intentionally loose for the first
% integration-test iteration. Tighten it only after the reduced
% workflow has been reviewed across repeated runs and code
% changes.
provisionalMaxFfeBer = 3e-1;
provisionalMaxMlseBer = 3e-1;
testCase.verifyLessThanOrEqual(wf.ffe_results.metrics.BER, provisionalMaxFfeBer);
testCase.verifyLessThanOrEqual(wf.mlse_results.metrics.BER, provisionalMaxMlseBer);
% MLSE should not regress relative to the direct FFE path on
% this deterministic reduced setup.
testCase.verifyLessThanOrEqual( ...
wf.mlse_results.metrics.BER, ...
wf.ffe_results.metrics.BER + 1e-12);
end
end
end
function p = signalPower(signal)
p = mean(abs(signal(:)).^2);
end
function v = signalVariance(signal)
centered = signal(:) - mean(signal(:));
v = mean(abs(centered).^2);
end