Strengthen WDM and IM/DD optical tests

This commit is contained in:
Silas Oettinghaus
2026-03-25 08:07:31 +01:00
parent 0e6be4a584
commit 76be57515d
26 changed files with 1606 additions and 566 deletions

View File

@@ -46,7 +46,7 @@ classdef Optical_Demultiplex < handle
if isempty(obj.wavelengthplan) if isempty(obj.wavelengthplan)
obj.wavelengthplan = signalclass_in.lambda; %meter obj.wavelengthplan = signalclass_in.lambda; %meter
else else
if all(500e-9 < obj.wavelengthplan) && all(obj.wavelengthplan < 1500e-9) %check if given in nm if all(500 < obj.wavelengthplan) && all(obj.wavelengthplan < 1500) %check if given in nm
obj.wavelengthplan = obj.wavelengthplan.*1e-9; obj.wavelengthplan = obj.wavelengthplan.*1e-9;
end end
end end

View File

@@ -1,120 +0,0 @@
function loss_curve_digitized(filename)
% PLOT_WPD_DATASETS Reads and plots scattering data from a CSV file.
% filename: String containing the path to the CSV file (e.g., 'wpd_datasets.csv')
%
% This function expects a CSV with the following structure:
% Row 1: Dataset Names (every 2nd column)
% Row 2: Variable Names (X, Y, X, Y...)
% Row 3+: Numeric Data (potentially with NaNs for unequal lengths)
% --- 1. Import Data ---
if nargin < 1
filename = 'wpd_datasets.csv'; % Default filename
end
% Read the numeric data, skipping the first 2 header lines
% 'TreatAsMissing' ensures empty cells become NaNs
raw_data = readmatrix(filename, 'NumHeaderLines', 2);
% Read the first line separately to parse Dataset Names
fid = fopen(filename, 'r');
if fid == -1
error('Could not open file: %s', filename);
end
header_line = fgetl(fid);
fclose(fid);
% Split header by comma to get names
raw_names = split(header_line, ',');
% Extract non-empty names (assuming names are in col 1, 3, 5...)
dataset_names = raw_names(~cellfun('isempty', raw_names));
% --- 2. Setup Plot ---
figure('Color', 'w', 'Position', [100, 100, 800, 600]);
ax = gca;
hold(ax, 'on');
line_styles = {'-', '-', '-', '-'};
% --- 3. Iterate and Plot Each Dataset ---
num_datasets = length(dataset_names);
for i = 1:num_datasets
% Calculate column indices for X and Y
% Dataset 1: Cols 1,2 | Dataset 2: Cols 3,4 | etc.
col_x = (i-1)*2 + 1;
col_y = (i-1)*2 + 2;
% Extract data
if col_y > size(raw_data, 2)
warning('Data columns missing for dataset %d', i);
break;
end
X = raw_data(:, col_x);
Y = raw_data(:, col_y);
% Remove NaNs (missing data due to unequal lengths)
valid_mask = ~isnan(X) & ~isnan(Y);
X = X(valid_mask);
Y = Y(valid_mask);
[X, sortIdx] = sort(X);
Y = Y(sortIdx);
% 3. Smooth the Data
if numel(X) > 20
if 1
Y = smoothdata(Y, 'sgolay', 15);
else
Y = movmean(Y, 15);
end
end
% Plotting
% Using semilogy because scattering data often spans orders of magnitude
% (Adjust to 'plot' if linear scale is preferred)
p = plot(ax, X, Y, ...
'LineStyle', line_styles{mod(i-1, length(line_styles)) + 1}, ...
'Marker', 'none', ...
'Color', 'black', ...
'LineWidth', 1.5, ...
'MarkerSize', 6, ...
'DisplayName', dataset_names{i});
% Optional: Fill marker faces for better visibility
% p.MarkerFaceColor = p.Color;
% p.MarkerFaceAlpha = 0.3; % Semi-transparent fill
end
% --- 4. Styling and Formatting ---
% Axis Labels (Inferred from typical scattering plots)
xlabel(ax, 'Wavelength (\mu m)', 'FontSize', 12, 'FontWeight', 'bold');
ylabel(ax, 'Intensity / Cross-Section (a.u.)', 'FontSize', 12, 'FontWeight', 'bold');
% Title
title(ax, 'Dataset Comparison', 'FontSize', 14);
% Legend
legend(ax, 'Location', 'best', 'Interpreter', 'none', 'Box', 'on');
% Grid
grid(ax, 'on');
ax.GridAlpha = 0.3;
ax.MinorGridAlpha = 0.1;
% Set Log Scale for Y (likely required for this data type)
set(ax, 'YScale', 'log');
% Enhance axis appearance
set(ax, 'Box', 'on', 'LineWidth', 1.2, 'FontSize', 10);
hold(ax, 'off');
end

View File

@@ -1,189 +0,0 @@
Rayleigh,,Experimental,,Infrared Absorption,
X,Y,X,Y,X,Y
0.7066005680911753,3.651009696525016,0.7072188355785799,4.848577786727532,1.4943918372804705,0.009324755400827079
0.7362740977034739,3.1008424465551374,0.7104385926007812,5.059236053617898,1.5870823136443164,0.04795688074913024
0.7704634954089999,2.559836201011587,0.7143024334187478,5.3165954733738054,1.6443739975494367,0.12666181795273013
0.8246453070916075,1.9963763545469397,0.7194723146571098,4.679249634272495,1.6900695253042852,0.3029389206853443
0.8846384359818439,1.4197559292837703,0.7194729766137344,4.646181068667172,1.7344762235109785,0.7194296259968607
0.9375334039861705,1.0762856922437043,0.7227053108118038,4.236869693191961,1.7975573800551148,2.1896107578891244
0.9929980876073115,0.9010620061822204,0.7272205169484065,4.147543538340177,,
1.0420225952274251,0.6977835411732736,0.7291487965959542,4.420849736815974,,
1.0845923638007697,0.5842349714248559,0.7310784001567514,4.645798669234298,,
1.129741777340298,0.4856980392071641,0.7355902965102309,4.71201487210429,,
1.1981066716622326,0.3841608204561696,0.738821306795051,4.358286713153517,,
1.24841405122088,0.31935666463679646,0.7414069093708566,4.059822180897163,,
1.289688370679823,0.2850148847474179,0.742705668268381,3.676053825899206,,
1.3541801567910463,0.23691184007300667,0.7440031032526563,3.376112267508452,,
1.4083573347772815,0.19416797258075097,0.7452965664971838,3.235432943532804,,
1.4618851333147547,0.16723647350379714,0.7459426361628229,3.1898499103097238,,
1.50315548103395,0.15574103754742674,0.7549743723492774,3.0137121806723157,,
1.5386251028515106,0.1419883611511702,0.7620678995388148,2.971117042589562,,
1.596665459699088,0.12316075278891304,0.765291628300764,2.971049114206588,,
1.6437444767994136,0.10759838849626362,0.7756075603390012,2.9708317538173317,,
1.683725994970454,0.09949510555249974,0.7762523060913911,2.9708181693210105,,
1.717258069747722,0.09398483395032568,0.7839919029465676,2.8875658983600925,,
1.7636903552257834,0.08387517688001396,0.7885090949530442,2.767180560538318,,
1.7940013490676008,0.07701567091052414,0.7917348095848672,2.7088647150205,,
,,0.7936723566251598,2.6144535948583694,,
,,0.7962546494178423,2.5233214143921474,,
,,0.8085087904529968,2.417989089048743,,
,,0.8104423657535417,2.435165393892523,,
,,0.8201175237791369,2.3335556930105406,,
,,0.8285065000830756,2.158298098247211,,
,,0.8317335386281479,2.083056634772393,,
,,0.8356046609689853,2.024737963659226,,
,,0.8407639509013534,1.996148150112612,,
,,0.8459232408337212,1.967962031984024,,
,,0.854951005280428,1.9401206794865944,,
,,0.8601116191260452,1.8857864945455538,,
,,0.8691446792257491,1.7565635929668848,,
,,0.8743052930713663,1.7073700288086895,,
,,0.8788211611645935,1.6595617469926904,,
,,0.8826916215488064,1.624580544751916,,
,,0.8891397410293294,1.6130257674984603,,
,,0.896234592132116,1.5678305544227285,,
,,0.903974850943917,1.5131252291764425,,
,,0.90784531132813,1.4812307005435945,,
,,0.9155829223134326,1.470682044636039,,
,,0.9220303798373308,1.4706147972609174,,
,,0.9310574823274131,1.460128389693264,,
,,0.9349259568417521,1.4600883304432553,,
,,0.9433056657529459,1.4913980336424177,,
,,0.9516853746641398,1.5233791328756299,,
,,0.956844002639883,1.512557980118849,,
,,0.9671632444612435,1.4597545460975296,,
,,0.9710370146285795,1.379199997932108,,
,,0.9736206313345113,1.3123773159521164,,
,,0.9774937395452226,1.2487807938448074,,
,,0.9826543533908398,1.213807976266501,,
,,0.9903926263327669,1.1966468122906808,,
,,0.9942630867169798,1.171423198750918,,
,,1.0045796807118417,1.1630595787371243,,
,,1.0097376467309604,1.163017033545686,,
,,1.0219851681998686,1.1963787227370668,,
,,1.0290753856062829,1.2220446908209617,,
,,1.0348774354211667,1.2306917801168409,,
,,1.0471421677623152,1.052809309517664,,
,,1.0510159379296513,0.9947114748787712,,
,,1.0445565651865096,1.1302083245776995,,
,,1.0458487045177878,1.0985863367418764,,
,,1.0542449623445975,0.9398239868494617,,
,,1.0613444471437565,0.8692481092753317,,
,,1.0690886776953055,0.8039684355922487,,
,,1.0768322462902296,0.7488836107091129,,
,,1.0800586228786773,0.7279206840193796,,
,,1.0858633205200596,0.7125673758820761,,
,,1.1013418522737881,0.677981252075927,,
,,1.105210326788127,0.6779626513688453,,
,,1.1193973811672016,0.6589337368401503,,
,,1.1206888585418553,0.6450561487842132,,
,,1.123272475247787,0.613802971613336,,
,,1.129077834845794,0.5966103416145675,,
,,1.1335910551125228,0.5965912453535225,,
,,1.1413266802279516,0.6050805792103379,,
,,1.147134687652457,0.5716821968068484,,
,,1.1581006610960811,0.5401075330241505,,
,,1.167774495208427,0.524964710284852,,
,,1.17357786893656,0.521233298948351,,
,,1.1890511050372914,0.5248855006959243,,
,,1.1974327998183592,0.5248543002000403,,
,,1.2051671010205385,0.5399272739697006,,
,,1.2161264548979165,0.5475977747039503,,
,,1.2225739124218147,0.5475727356323816,,
,,1.2232186581742046,0.5475702317881956,,
,,1.2257923455307669,0.5795255128326332,,
,,1.2264284858470365,0.635494312878344,,
,,1.2264218662807902,0.6822012488404363,,
,,1.2322133247896798,0.7695840678419278,,
,,1.2354304339853828,0.8261273126225834,,
,,1.2386382757883407,0.9793976812064772,,
,,1.2399224716401234,1.0365631680970249,,
,,1.2405632456527655,1.0816189618908738,,
,,1.2457119442791393,1.1944819289049535,,
,,1.2508619668187624,1.3005429730851226,,
,,1.2521488104970433,1.3379536598639075,,
,,1.2586068593269357,1.1943726953174758,,
,,1.2605516878900995,1.0662340854711274,,
,,1.263782036218295,0.9932116176633944,,
,,1.267659116168754,0.9057092045091463,,
,,1.2721796179583538,0.8377104911187728,,
,,1.277345527456968,0.7693377754021644,,
,,1.2818653672899432,0.7166420946785479,,
,,1.2954156193961235,0.639704291832398,,
,,1.298640672071322,0.6306801565443763,,
,,1.3018657247465204,0.6217833222901857,,
,,1.3147579919678183,0.6396165438362177,,
,,1.3179764250767705,0.6769403979817149,,
,,1.3244132912946747,0.7582491296621654,,
,,1.3295613279644238,0.8433295073010272,,
,,1.336000180052202,0.9247376324361347,,
,,1.3430738485430005,1.1278180580973283,,
,,1.3449988184074253,1.2455302076050567,,
,,1.3482112939067554,1.4050952527481475,,
,,1.351423769406086,1.585102197634859,,
,,1.3533487392705106,1.7505418140103943,,
,,1.3565618767264658,1.9608478952847546,,
,,1.3604177740649368,2.243642151072621,,
,,1.3681421459177467,2.5671506730013776,,
,,1.3720059867357133,2.6977396395227498,,
,,1.3700631440424236,2.9583331848749403,,
,,1.3745670969164077,3.267039328784058,,
,,1.3758446732019438,3.711862721055264,,
,,1.377124897313979,4.099294432098773,,
,,1.381620906708467,4.929210899363566,,
,,1.381610315402473,5.521521237053952,,
,,1.382885243861511,6.453816707451638,,
,,1.3828733286422676,7.332602493027707,,
,,1.3835048352621646,8.450022001983974,,
,,1.3847837354609505,9.465318121186383,,
,,1.3892850405084358,10.753821633797266,,
,,1.3950817946703227,11.46214012981349,,
,,1.406041810504325,11.542823048802346,,
,,1.4086254272102567,10.983569572223233,,
,,1.4125144223799593,8.815541223987582,,
,,1.4138145051907332,7.8697993951128655,,
,,1.4164106990725327,6.544455667464655,,
,,1.4170686839574151,5.678973743658009,,
,,1.419664877839215,4.722584406045411,,
,,1.422267691287261,3.6583795017467264,,
,,1.4274475018749921,2.894876829955794,,
,,1.4293982880477776,2.4244991059437035,,
,,1.4319891862765801,2.133892660567146,,
,,1.4365249130685465,1.6766244945281037,,
,,1.442346821582169,1.3648832144532748,,
,,1.44946086942707,1.0800176935856405,,
,,1.4539886527395407,0.9239641429706197,,
,,1.4617467843802068,0.7363233849854021,,
,,1.4701536335130105,0.5623407795094862,,
,,1.477900511891058,0.5055620724926202,,
,,1.4856487141823544,0.44811473438788657,,
,,1.4940482817922873,0.3699994982234866,,
,,1.5005089784486785,0.32105511462339426,,
,,1.5089019264923649,0.2845721176091456,,
,,1.5166481429137875,0.2576601983353233,,
,,1.5179396202884412,0.2522337011156551,,
,,1.5359997828782272,0.23327413205434572,,
,,1.5430966198508878,0.22196482072259285,,
,,1.5469697280615993,0.21120862244291763,,
,,1.5501980905199209,0.20097457800069773,,
,,1.565674636403775,0.1953318790229525,,
,,1.5688977032090996,0.19671762962317987,,
,,1.5785662416684487,0.20236426842871638,,
,,1.5837215598610688,0.2081796533603784,,
,,1.5914545371499988,0.21721757235346942,,
,,1.5991901622654274,0.22030852031094905,,
,,1.6088620105078997,0.21873658192521878,,
,,1.61208309144335,0.22502554836218797,,
,,1.6262602164730557,0.2432590831170723,,
,,1.626903638312196,0.2467330049135315,,
,,1.6281904819904773,0.25383038758817866,,
,,1.6410748057322797,0.28430548752659124,,
,,1.660414530477476,0.29244621369149987,,
,,1.6642816810785659,0.29661578287603696,,
,,1.6707152375133467,0.34423590359793155,,
,,1.6739290369259265,0.3828666146961718,,
,,1.6758540067903511,0.4228270071256609,,
,,1.690028483993558,0.4702407837690221,,
,,1.6964732936909575,0.48374976893430716,,
,,1.6996923887565347,0.5083600695634991,,
,,1.7003318388559272,0.5380344919079171,,
1 Rayleigh Experimental Infrared Absorption
2 X Y X Y X Y
3 0.7066005680911753 3.651009696525016 0.7072188355785799 4.848577786727532 1.4943918372804705 0.009324755400827079
4 0.7362740977034739 3.1008424465551374 0.7104385926007812 5.059236053617898 1.5870823136443164 0.04795688074913024
5 0.7704634954089999 2.559836201011587 0.7143024334187478 5.3165954733738054 1.6443739975494367 0.12666181795273013
6 0.8246453070916075 1.9963763545469397 0.7194723146571098 4.679249634272495 1.6900695253042852 0.3029389206853443
7 0.8846384359818439 1.4197559292837703 0.7194729766137344 4.646181068667172 1.7344762235109785 0.7194296259968607
8 0.9375334039861705 1.0762856922437043 0.7227053108118038 4.236869693191961 1.7975573800551148 2.1896107578891244
9 0.9929980876073115 0.9010620061822204 0.7272205169484065 4.147543538340177
10 1.0420225952274251 0.6977835411732736 0.7291487965959542 4.420849736815974
11 1.0845923638007697 0.5842349714248559 0.7310784001567514 4.645798669234298
12 1.129741777340298 0.4856980392071641 0.7355902965102309 4.71201487210429
13 1.1981066716622326 0.3841608204561696 0.738821306795051 4.358286713153517
14 1.24841405122088 0.31935666463679646 0.7414069093708566 4.059822180897163
15 1.289688370679823 0.2850148847474179 0.742705668268381 3.676053825899206
16 1.3541801567910463 0.23691184007300667 0.7440031032526563 3.376112267508452
17 1.4083573347772815 0.19416797258075097 0.7452965664971838 3.235432943532804
18 1.4618851333147547 0.16723647350379714 0.7459426361628229 3.1898499103097238
19 1.50315548103395 0.15574103754742674 0.7549743723492774 3.0137121806723157
20 1.5386251028515106 0.1419883611511702 0.7620678995388148 2.971117042589562
21 1.596665459699088 0.12316075278891304 0.765291628300764 2.971049114206588
22 1.6437444767994136 0.10759838849626362 0.7756075603390012 2.9708317538173317
23 1.683725994970454 0.09949510555249974 0.7762523060913911 2.9708181693210105
24 1.717258069747722 0.09398483395032568 0.7839919029465676 2.8875658983600925
25 1.7636903552257834 0.08387517688001396 0.7885090949530442 2.767180560538318
26 1.7940013490676008 0.07701567091052414 0.7917348095848672 2.7088647150205
27 0.7936723566251598 2.6144535948583694
28 0.7962546494178423 2.5233214143921474
29 0.8085087904529968 2.417989089048743
30 0.8104423657535417 2.435165393892523
31 0.8201175237791369 2.3335556930105406
32 0.8285065000830756 2.158298098247211
33 0.8317335386281479 2.083056634772393
34 0.8356046609689853 2.024737963659226
35 0.8407639509013534 1.996148150112612
36 0.8459232408337212 1.967962031984024
37 0.854951005280428 1.9401206794865944
38 0.8601116191260452 1.8857864945455538
39 0.8691446792257491 1.7565635929668848
40 0.8743052930713663 1.7073700288086895
41 0.8788211611645935 1.6595617469926904
42 0.8826916215488064 1.624580544751916
43 0.8891397410293294 1.6130257674984603
44 0.896234592132116 1.5678305544227285
45 0.903974850943917 1.5131252291764425
46 0.90784531132813 1.4812307005435945
47 0.9155829223134326 1.470682044636039
48 0.9220303798373308 1.4706147972609174
49 0.9310574823274131 1.460128389693264
50 0.9349259568417521 1.4600883304432553
51 0.9433056657529459 1.4913980336424177
52 0.9516853746641398 1.5233791328756299
53 0.956844002639883 1.512557980118849
54 0.9671632444612435 1.4597545460975296
55 0.9710370146285795 1.379199997932108
56 0.9736206313345113 1.3123773159521164
57 0.9774937395452226 1.2487807938448074
58 0.9826543533908398 1.213807976266501
59 0.9903926263327669 1.1966468122906808
60 0.9942630867169798 1.171423198750918
61 1.0045796807118417 1.1630595787371243
62 1.0097376467309604 1.163017033545686
63 1.0219851681998686 1.1963787227370668
64 1.0290753856062829 1.2220446908209617
65 1.0348774354211667 1.2306917801168409
66 1.0471421677623152 1.052809309517664
67 1.0510159379296513 0.9947114748787712
68 1.0445565651865096 1.1302083245776995
69 1.0458487045177878 1.0985863367418764
70 1.0542449623445975 0.9398239868494617
71 1.0613444471437565 0.8692481092753317
72 1.0690886776953055 0.8039684355922487
73 1.0768322462902296 0.7488836107091129
74 1.0800586228786773 0.7279206840193796
75 1.0858633205200596 0.7125673758820761
76 1.1013418522737881 0.677981252075927
77 1.105210326788127 0.6779626513688453
78 1.1193973811672016 0.6589337368401503
79 1.1206888585418553 0.6450561487842132
80 1.123272475247787 0.613802971613336
81 1.129077834845794 0.5966103416145675
82 1.1335910551125228 0.5965912453535225
83 1.1413266802279516 0.6050805792103379
84 1.147134687652457 0.5716821968068484
85 1.1581006610960811 0.5401075330241505
86 1.167774495208427 0.524964710284852
87 1.17357786893656 0.521233298948351
88 1.1890511050372914 0.5248855006959243
89 1.1974327998183592 0.5248543002000403
90 1.2051671010205385 0.5399272739697006
91 1.2161264548979165 0.5475977747039503
92 1.2225739124218147 0.5475727356323816
93 1.2232186581742046 0.5475702317881956
94 1.2257923455307669 0.5795255128326332
95 1.2264284858470365 0.635494312878344
96 1.2264218662807902 0.6822012488404363
97 1.2322133247896798 0.7695840678419278
98 1.2354304339853828 0.8261273126225834
99 1.2386382757883407 0.9793976812064772
100 1.2399224716401234 1.0365631680970249
101 1.2405632456527655 1.0816189618908738
102 1.2457119442791393 1.1944819289049535
103 1.2508619668187624 1.3005429730851226
104 1.2521488104970433 1.3379536598639075
105 1.2586068593269357 1.1943726953174758
106 1.2605516878900995 1.0662340854711274
107 1.263782036218295 0.9932116176633944
108 1.267659116168754 0.9057092045091463
109 1.2721796179583538 0.8377104911187728
110 1.277345527456968 0.7693377754021644
111 1.2818653672899432 0.7166420946785479
112 1.2954156193961235 0.639704291832398
113 1.298640672071322 0.6306801565443763
114 1.3018657247465204 0.6217833222901857
115 1.3147579919678183 0.6396165438362177
116 1.3179764250767705 0.6769403979817149
117 1.3244132912946747 0.7582491296621654
118 1.3295613279644238 0.8433295073010272
119 1.336000180052202 0.9247376324361347
120 1.3430738485430005 1.1278180580973283
121 1.3449988184074253 1.2455302076050567
122 1.3482112939067554 1.4050952527481475
123 1.351423769406086 1.585102197634859
124 1.3533487392705106 1.7505418140103943
125 1.3565618767264658 1.9608478952847546
126 1.3604177740649368 2.243642151072621
127 1.3681421459177467 2.5671506730013776
128 1.3720059867357133 2.6977396395227498
129 1.3700631440424236 2.9583331848749403
130 1.3745670969164077 3.267039328784058
131 1.3758446732019438 3.711862721055264
132 1.377124897313979 4.099294432098773
133 1.381620906708467 4.929210899363566
134 1.381610315402473 5.521521237053952
135 1.382885243861511 6.453816707451638
136 1.3828733286422676 7.332602493027707
137 1.3835048352621646 8.450022001983974
138 1.3847837354609505 9.465318121186383
139 1.3892850405084358 10.753821633797266
140 1.3950817946703227 11.46214012981349
141 1.406041810504325 11.542823048802346
142 1.4086254272102567 10.983569572223233
143 1.4125144223799593 8.815541223987582
144 1.4138145051907332 7.8697993951128655
145 1.4164106990725327 6.544455667464655
146 1.4170686839574151 5.678973743658009
147 1.419664877839215 4.722584406045411
148 1.422267691287261 3.6583795017467264
149 1.4274475018749921 2.894876829955794
150 1.4293982880477776 2.4244991059437035
151 1.4319891862765801 2.133892660567146
152 1.4365249130685465 1.6766244945281037
153 1.442346821582169 1.3648832144532748
154 1.44946086942707 1.0800176935856405
155 1.4539886527395407 0.9239641429706197
156 1.4617467843802068 0.7363233849854021
157 1.4701536335130105 0.5623407795094862
158 1.477900511891058 0.5055620724926202
159 1.4856487141823544 0.44811473438788657
160 1.4940482817922873 0.3699994982234866
161 1.5005089784486785 0.32105511462339426
162 1.5089019264923649 0.2845721176091456
163 1.5166481429137875 0.2576601983353233
164 1.5179396202884412 0.2522337011156551
165 1.5359997828782272 0.23327413205434572
166 1.5430966198508878 0.22196482072259285
167 1.5469697280615993 0.21120862244291763
168 1.5501980905199209 0.20097457800069773
169 1.565674636403775 0.1953318790229525
170 1.5688977032090996 0.19671762962317987
171 1.5785662416684487 0.20236426842871638
172 1.5837215598610688 0.2081796533603784
173 1.5914545371499988 0.21721757235346942
174 1.5991901622654274 0.22030852031094905
175 1.6088620105078997 0.21873658192521878
176 1.61208309144335 0.22502554836218797
177 1.6262602164730557 0.2432590831170723
178 1.626903638312196 0.2467330049135315
179 1.6281904819904773 0.25383038758817866
180 1.6410748057322797 0.28430548752659124
181 1.660414530477476 0.29244621369149987
182 1.6642816810785659 0.29661578287603696
183 1.6707152375133467 0.34423590359793155
184 1.6739290369259265 0.3828666146961718
185 1.6758540067903511 0.4228270071256609
186 1.690028483993558 0.4702407837690221
187 1.6964732936909575 0.48374976893430716
188 1.6996923887565347 0.5083600695634991
189 1.7003318388559272 0.5380344919079171

View File

@@ -1,10 +1,64 @@
classdef Opticalsignal_test < IMDDTestCase classdef Opticalsignal_test < IMDDTestCase
% Auto-generated placeholder for Opticalsignal. methods (Test, TestTags = {'unit', 'fast', 'signals', 'optical'})
% Target: C:/Users/Silas/Documents/MATLAB/imdd_simulation/Classes/00_signals/Opticalsignal.m function constructorWiresOpticalMetadata(testCase)
opt = makeOpticalsignal([1; 2; 3], 12.5e9, 1310e-9, 0.25, 2);
methods (Test, TestTags = {'placeholder', 'todo'}) testCase.verifyClass(opt, 'Opticalsignal');
function testNotImplemented(testCase) testCase.verifyEqual(opt.signal, [1; 2; 3]);
testCase.assumeFail("Tests for Opticalsignal are not implemented yet."); testCase.verifyEqual(opt.fs, 12.5e9);
testCase.verifyEqual(opt.lambda, 1310e-9);
testCase.verifyEqual(opt.nase, 0.25);
testCase.verifyEqual(opt.polrot, 2);
testCase.verifyEqual(height(opt.logbook), 0);
end
function delayBySamplesAndMetersGiveTheSameResult(testCase)
fs = physconst('LightSpeed') / 1.4677;
opt = makeOpticalsignal([1; 0; -1; 2], fs, 1550e-9, 0, 0);
[bySamples, delayNs] = opt.delay("delay_samples", 2);
[byMeters, delayNm] = opt.delay("delay_meter", 2);
testCase.verifyEqual(delayNs, 2);
testCase.verifyEqual(delayNm, 2);
testCase.verifyEqual(bySamples.signal, byMeters.signal);
testCase.verifyEqual(bySamples.fs, opt.fs);
testCase.verifyClass(bySamples, 'Opticalsignal');
testCase.verifyClass(byMeters, 'Opticalsignal');
end
function csprMatchesKnownCarrierToSidebandRatio(testCase)
opt = makeOpticalsignal([3; 1; 3; 1], 10, 1550e-9, 0, 0);
actual = opt.cspr();
expected = 10 * log10(abs(mean(opt.signal)).^2 / mean(abs(opt.signal - mean(opt.signal)).^2));
testCase.verifyEqual(actual, expected, "AbsTol", 1e-12);
end
function inheritedPlusAndLengthPreserveOpticalSemantics(testCase)
left = makeOpticalsignal([1; 2; 3], 5, 1310e-9, 0.1, 1);
right = makeOpticalsignal([4; 5; 6], 5, 1310e-9, 0.2, 1);
sumSig = left + right;
testCase.verifyClass(sumSig, 'Opticalsignal');
testCase.verifyEqual(sumSig.signal, [5; 7; 9]);
testCase.verifyEqual(sumSig.fs, left.fs);
testCase.verifyEqual(sumSig.lambda, left.lambda);
testCase.verifyEqual(sumSig.nase, left.nase);
testCase.verifyEqual(sumSig.polrot, left.polrot);
testCase.verifyEqual(sumSig.length(), 3);
end end
end end
end end
function opt = makeOpticalsignal(signal, fs, lambda, nase, polrot)
base = Signal(signal);
opt = Opticalsignal(signal, ...
"fs", fs, ...
"logbook", base.logbook, ...
"lambda", lambda, ...
"nase", nase, ...
"polrot", polrot);
end

View File

@@ -1,10 +1,42 @@
classdef Signalgenerator_test < IMDDTestCase classdef Signalgenerator_test < IMDDTestCase
% Auto-generated placeholder for Signalgenerator. methods (Test, TestTags = {'unit', 'fast', 'transmit', 'signalgenerator'})
% Target: C:/Users/Silas/Documents/MATLAB/imdd_simulation/Classes/01_transmit/Signalgenerator.m function constructorStoresDefaultConfiguration(testCase)
gen = Signalgenerator();
methods (Test, TestTags = {'placeholder', 'todo'}) testCase.verifyEqual(gen.form, signalform.sine);
function testNotImplemented(testCase) testCase.verifyEqual(gen.length, 1024);
testCase.assumeFail("Tests for Signalgenerator are not implemented yet."); testCase.verifyEqual(gen.fs, 1000);
testCase.verifyEqual(gen.fsig, 50);
end
function buildSignalProducesExpectedSineWave(testCase)
gen = Signalgenerator( ...
"form", signalform.sine, ...
"length", 4, ...
"fs", 4, ...
"fsig", 1);
signal = gen.build_signal();
expected = [0 1 0 -1];
testCase.verifySize(signal, [1 4]);
testCase.verifyEqual(signal, expected, "AbsTol", 1e-12);
end
function processWrapsGeneratedSignalInInformationsignal(testCase)
gen = Signalgenerator( ...
"form", signalform.sine, ...
"length", 4, ...
"fs", 4, ...
"fsig", 1);
out = gen.process();
testCase.verifyClass(out, 'Informationsignal');
testCase.verifySize(out.signal, [1 4]);
testCase.verifyEqual(out.fs, 4);
testCase.verifyEqual(out.signal, [0 1 0 -1], "AbsTol", 1e-12);
testCase.verifyEqual(height(out.logbook), 1);
end end
end end
end end

View File

@@ -1,10 +1,69 @@
classdef Amplifier_test < IMDDTestCase classdef Amplifier_test < IMDDTestCase
% Auto-generated placeholder for Amplifier. methods (Test, TestTags = {'unit', 'fast', 'etc', 'amplifier'})
% Target: C:/Users/Silas/Documents/MATLAB/imdd_simulation/Classes/02_etc/Amplifier.m function constructorStoresConfiguredModesAndSettings(testCase)
amp = Amplifier( ...
"amp_mode", amp_mode.ideal_no_noise, ...
"gain_mode", gain_mode.gain, ...
"nase_mode", nase_mode.pass_ase, ...
"amplification_db", 7.5, ...
"noifig", 12);
methods (Test, TestTags = {'placeholder', 'todo'}) testCase.verifyEqual(amp.amp_mode, amp_mode.ideal_no_noise);
function testNotImplemented(testCase) testCase.verifyEqual(amp.gain_mode, gain_mode.gain);
testCase.assumeFail("Tests for Amplifier are not implemented yet."); testCase.verifyEqual(amp.nase_mode, nase_mode.pass_ase);
testCase.verifyEqual(amp.amplification_db, 7.5);
testCase.verifyEqual(amp.noifig, 12);
end
function gainModeAmplifiesOpticalSignalAndPreservesMetadata(testCase)
inputSignal = makeOpticalSignal([1; -1; 1; -1], 64e9, 1310e-9, 0, 0);
amp = Amplifier( ...
"amp_mode", amp_mode.ideal_no_noise, ...
"gain_mode", gain_mode.gain, ...
"nase_mode", nase_mode.pass_ase, ...
"amplification_db", 6);
outputSignal = amp.process(inputSignal);
expectedScale = 10^(6/20);
testCase.verifyClass(outputSignal, 'Opticalsignal');
testCase.verifyEqual(outputSignal.signal, expectedScale * inputSignal.signal, "AbsTol", 1e-12);
testCase.verifyEqual(outputSignal.nase, inputSignal.nase * expectedScale^2, "AbsTol", 1e-12);
testCase.verifyEqual(outputSignal.fs, inputSignal.fs);
testCase.verifyEqual(outputSignal.lambda, inputSignal.lambda);
testCase.verifyEqual(outputSignal.polrot, inputSignal.polrot);
testCase.verifyEqual(height(outputSignal.logbook), height(inputSignal.logbook) + 1);
testCase.verifyEqual(string(outputSignal.logbook.Description(end)), "Optical Amplifier ");
end
function outputPowerModeTargetsRequestedAveragePower(testCase)
inputSignal = makeOpticalSignal([1; -1; 1; -1], 64e9, 1310e-9, 0, 0);
amp = Amplifier( ...
"amp_mode", amp_mode.ideal_no_noise, ...
"gain_mode", gain_mode.output_power, ...
"nase_mode", nase_mode.pass_ase, ...
"amplification_db", 33);
outputSignal = amp.process(inputSignal);
expectedPowerW = 10^(33/10 - 3);
expectedScale = sqrt(expectedPowerW / mean(abs(inputSignal.signal).^2));
testCase.verifyClass(outputSignal, 'Opticalsignal');
testCase.verifyEqual(outputSignal.signal, expectedScale * inputSignal.signal, "AbsTol", 1e-12);
testCase.verifyEqual(outputSignal.power("unit", power_notation.W), expectedPowerW, "AbsTol", 1e-12);
testCase.verifyEqual(outputSignal.fs, inputSignal.fs);
testCase.verifyEqual(outputSignal.lambda, inputSignal.lambda);
end end
end end
end end
function sig = makeOpticalSignal(values, fs, lambda, nase, polrot)
sig = Opticalsignal(values, ...
"fs", fs, ...
"logbook", Signal(values, "fs", fs).logbook, ...
"lambda", lambda, ...
"nase", nase, ...
"polrot", polrot);
end

View File

@@ -1,10 +1,71 @@
classdef Filter_test < IMDDTestCase classdef Filter_test < IMDDTestCase
% Auto-generated placeholder for Filter. methods (Test, TestTags = {'unit', 'fast', 'filter', 'etc'})
% Target: C:/Users/Silas/Documents/MATLAB/imdd_simulation/Classes/02_etc/Filter.m function constructorBuildsFilterWhenSamplingRateAndLengthAreKnown(testCase)
flt = Filter( ...
"active", true, ...
"filterType", filtertypes.butterworth, ...
"f_cutoff", 3.5, ...
"fs", 8, ...
"signal_length", 8, ...
"filtdegree", 3, ...
"lowpass", 1);
methods (Test, TestTags = {'placeholder', 'todo'}) testCase.verifyEqual(flt.active, true);
function testNotImplemented(testCase) testCase.verifyEqual(flt.filterType, filtertypes.butterworth);
testCase.assumeFail("Tests for Filter are not implemented yet."); testCase.verifyEqual(flt.f_cutoff, 3.5);
testCase.verifyEqual(flt.fs, 8);
testCase.verifyEqual(flt.signal_length, 8);
testCase.verifyNotEmpty(flt.H);
testCase.verifyNotEmpty(flt.w);
testCase.verifyEqual(numel(flt.H), 8);
testCase.verifyEqual(numel(flt.w), 8);
end
function inactiveFilterIsExactPassThrough(testCase)
flt = Filter( ...
"active", false, ...
"filterType", filtertypes.butterworth, ...
"f_cutoff", 3.5, ...
"fs", 8, ...
"signal_length", 8, ...
"filtdegree", 3);
sig = Signal([1; 2; 3], "fs", 8);
numIn = [1; -2; 3];
sigOut = flt.process(sig);
numOut = flt.process(numIn);
testCase.verifyClass(sigOut, "Signal");
testCase.verifyEqual(sigOut.signal, sig.signal);
testCase.verifyEqual(sigOut.fs, sig.fs);
testCase.verifyEqual(height(sigOut.logbook), height(sig.logbook));
testCase.verifyEqual(numOut, numIn);
end
function activeButterworthFilterPreservesConstantSignals(testCase)
flt = Filter( ...
"active", true, ...
"filterType", filtertypes.butterworth, ...
"f_cutoff", 3.5, ...
"fs", 8, ...
"signal_length", 8, ...
"filtdegree", 3, ...
"lowpass", 1);
sig = Signal(ones(8, 1), "fs", 8);
numIn = ones(8, 1);
sigOut = flt.process(sig);
numOut = flt.process_(numIn);
testCase.verifyEqual(size(sigOut.signal), size(sig.signal));
testCase.verifyEqual(sigOut.signal, ones(8, 1), "AbsTol", 1e-12);
testCase.verifyEqual(sigOut.fs, sig.fs);
testCase.verifyEqual(height(sigOut.logbook), 1);
testCase.verifyEqual(size(numOut), size(numIn));
testCase.verifyEqual(numOut, ones(8, 1), "AbsTol", 1e-12);
end end
end end
end end

View File

@@ -1,10 +1,78 @@
classdef DP_Fiber_test < IMDDTestCase classdef DP_Fiber_test < IMDDTestCase
% Auto-generated placeholder for DP_Fiber. methods (Test, TestTags = {'unit', 'fast', 'optical', 'dp_fiber'})
% Target: C:/Users/Silas/Documents/MATLAB/imdd_simulation/Classes/02_optical/DP_Fiber.m function constructorStoresConfigurationAndStartsWithEmptyState(testCase)
fiber = makeIdentityFiber();
methods (Test, TestTags = {'placeholder', 'todo'}) testCase.verifyEqual(fiber.L, 0);
function testNotImplemented(testCase) testCase.verifyEqual(fiber.dz, 1);
testCase.assumeFail("Tests for DP_Fiber are not implemented yet."); testCase.verifyEqual(fiber.lambda, 1550);
testCase.verifyEqual(fiber.rng, 7);
testCase.verifyEqual(fiber.gamma, 0);
testCase.verifyEqual(fiber.fa, 64e9);
testCase.verifyEqual(fiber.X_alpha, 0);
testCase.verifyEqual(fiber.D, 0);
testCase.verifyEqual(fiber.Ds, 0);
testCase.verifyEqual(fiber.Dpmd, 0);
testCase.verifyEqual(fiber.n_waveplates, 1);
testCase.verifyEqual(fiber.useGPU, false);
testCase.verifyEqual(fiber.useSingle, false);
testCase.verifyTrue(isstruct(fiber.state));
testCase.verifyEmpty(fieldnames(fiber.state));
end
function processPreservesAZeroImpairmentDualPolarizationSignal(testCase)
fiber = makeIdentityFiber();
inputMatrix = [ ...
1 + 1i, 2 - 1i; ...
0.5, -0.25i; ...
-1 + 0.2i, 0.75 - 0.5i; ...
0, 0.25 + 0.75i];
sigIn = Signal(inputMatrix, "fs", fiber.fa);
sigOut = fiber.process(sigIn);
testCase.verifyClass(sigOut, "Signal");
testCase.verifyEqual(sigOut.fs, sigIn.fs);
testCase.verifyEqual(size(sigOut.signal), size(sigIn.signal));
testCase.verifyEqual(sigOut.signal, sigIn.signal, "AbsTol", 1e-12);
testCase.verifyEqual(height(sigOut.logbook), 1);
end
function process_ProducesIdentityOutputForRawMatrixInput(testCase)
fiber = makeIdentityFiber();
inputMatrix = [ ...
1 + 1i, 2 - 1i; ...
0.5, -0.25i; ...
-1 + 0.2i, 0.75 - 0.5i; ...
0, 0.25 + 0.75i];
sigOut = fiber.process_(inputMatrix, fiber.fa);
testCase.verifyEqual(size(sigOut), size(inputMatrix));
testCase.verifyEqual(sigOut, inputMatrix, "AbsTol", 1e-12);
end end
end end
end end
function fiber = makeIdentityFiber()
fiber = DP_Fiber( ...
"L", 0, ...
"dz", 1, ...
"lambda", 1550, ...
"rng", 7, ...
"gamma", 0, ...
"fa", 64e9, ...
"X_alpha", 0, ...
"D", 0, ...
"Ds", 0, ...
"Dpmd", 0, ...
"beat_len", 1e9, ...
"corr_len", 1e9, ...
"manakov", 0, ...
"SS_dphimax", 5e-3, ...
"SS_dzmax", 1, ...
"SS_dzmin", 1, ...
"n_waveplates", 1, ...
"useGPU", false, ...
"useSingle", false);
end

View File

@@ -1,10 +1,100 @@
classdef EML_test < IMDDTestCase classdef EML_test < IMDDTestCase
% Auto-generated placeholder for EML. methods (Test, TestTags = {'unit', 'fast', 'optical', 'eml'})
% Target: C:/Users/Silas/Documents/MATLAB/imdd_simulation/Classes/02_optical/EML.m function constructorStoresDerivedScalarsForDeterministicIqMode(testCase)
eml = EML( ...
"mode", eml_mode.iq_linear, ...
"fsimu", 64e9, ...
"lambda", 1550, ...
"power", 0, ...
"linewidth", 0, ...
"alpha", 0, ...
"ampl_imbal", 0, ...
"pha_imbal", 0, ...
"bias", 0, ...
"u_pi", 1, ...
"randomkey", 7);
methods (Test, TestTags = {'placeholder', 'todo'}) testCase.verifyEqual(eml.mode, eml_mode.iq_linear);
function testNotImplemented(testCase) testCase.verifyEqual(eml.fsimu, 64e9);
testCase.assumeFail("Tests for EML are not implemented yet."); testCase.verifyEqual(eml.lambda, 1550);
testCase.verifyEqual(eml.power, 0);
testCase.verifyEqual(eml.linewidth, 0);
testCase.verifyEqual(eml.alpha, 0);
testCase.verifyEqual(eml.field, sqrt(1e-3), "AbsTol", 1e-12);
testCase.verifyEqual(eml.noisefactor, 0, "AbsTol", 1e-12);
testCase.verifyEqual(eml.phase, 0, "AbsTol", 1e-12);
end
function process_ProducesDeterministicIqLinearOutput(testCase)
eml = EML( ...
"mode", eml_mode.iq_linear, ...
"fsimu", 32e9, ...
"lambda", 1310, ...
"power", 3, ...
"linewidth", 0, ...
"alpha", 0, ...
"ampl_imbal", 0, ...
"pha_imbal", 0, ...
"bias", 0, ...
"u_pi", 2, ...
"randomkey", 11);
inputSig = makeElectricalsignal([1 + 1i; -2 + 0.5i; 0.25 - 0.75i], 32e9);
[optField, emlOut] = eml.process_(inputSig.signal);
expectedField = eml.field .* inputSig.signal ./ eml.u_pi;
testCase.verifyEqual(optField, expectedField, "AbsTol", 1e-12);
testCase.verifyEqual(emlOut.signal_len, length(inputSig));
testCase.verifyEqual(emlOut.phase, 0, "AbsTol", 1e-12);
end
function processReturnsOpticalsignalWithUpdatedMetadata(testCase)
eml = EML( ...
"mode", eml_mode.iq_linear, ...
"fsimu", 64e9, ...
"lambda", 1550, ...
"power", 0, ...
"linewidth", 0, ...
"alpha", 0, ...
"ampl_imbal", 0, ...
"pha_imbal", 0, ...
"bias", 0, ...
"u_pi", 1, ...
"randomkey", 19);
inputSig = makeElectricalsignal([1 + 1i; -1 - 1i], 64e9);
[outputSig, emlOut] = eml.process(inputSig);
expectedField = eml.field .* inputSig.signal ./ eml.u_pi;
testCase.verifyClass(outputSig, 'Opticalsignal');
testCase.verifyEqual(outputSig.signal, expectedField, "AbsTol", 1e-12);
testCase.verifyEqual(outputSig.fs, eml.fsimu);
testCase.verifyEqual(outputSig.lambda, eml.lambda * 1e-9, "AbsTol", 1e-15);
testCase.verifyEqual(height(outputSig.logbook), height(inputSig.logbook) + 1);
testCase.verifyTrue(contains(string(outputSig.logbook.Description(end)), "nm Laser"));
testCase.verifyEqual(emlOut.signal_len, length(inputSig));
end end
end end
end end
function sig = makeElectricalsignal(values, fs)
sig = Electricalsignal(values, ...
"fs", fs, ...
"logbook", emptyLogbook());
end
function lb = emptyLogbook()
SignalType = [];
TimeStamp = [];
Length = [];
SignalPower = [];
Nase = [];
SignalCopy = [];
ModifierName = [];
ModifierCopy = {};
Description = [];
lb = table(SignalType, TimeStamp, Length, SignalPower, Nase, SignalCopy, ModifierName, ModifierCopy, Description);
end

View File

@@ -1,10 +1,96 @@
classdef Optical_Demultiplex_test < IMDDTestCase classdef Optical_Demultiplex_test < IMDDTestCase
% Auto-generated placeholder for Optical_Demultiplex. methods (Test, TestTags = {'unit', 'fast', 'optical', 'demux'})
% Target: C:/Users/Silas/Documents/MATLAB/imdd_simulation/Classes/02_optical/Optical_Demultiplex.m function processReturnsOneCellPerChannelAndPreservesMetadata(testCase)
fs = 32e9;
lambdaCenterNm = 1310;
lambdaA = 1309e-9;
lambdaB = 1311e-9;
methods (Test, TestTags = {'placeholder', 'todo'}) sigA = makeOpticalSignal( ...
function testNotImplemented(testCase) [1 2; 1 2; 1 2; 1 2], ...
testCase.assumeFail("Tests for Optical_Demultiplex are not implemented yet."); fs, ...
lambdaA, ...
0.10);
sigB = makeOpticalSignal( ...
[3 4; 3 4; 3 4; 3 4], ...
fs, ...
lambdaB, ...
0.90);
mux = Optical_Multiplex( ...
"fs_in", fs, ...
"fs_out", fs, ...
"lambda_center", lambdaCenterNm, ...
"delta_f", 0, ...
"random_key", 0, ...
"attenuation", 0, ...
"filtype", 3, ...
"B", 200e9);
muxOut = mux.process({sigA, sigB});
demux = Optical_Demultiplex( ...
"fs_in", muxOut.fs, ...
"fs_out", muxOut.fs, ...
"lambda_center", lambdaCenterNm, ...
"attenuation", 0, ...
"filtype", 3, ...
"B", 200e9);
out = demux.process(muxOut);
testCase.verifyClass(out, 'cell');
testCase.verifyNumElements(out, 2);
testCase.verifyClass(out{1}, 'Opticalsignal');
testCase.verifyClass(out{2}, 'Opticalsignal');
testCase.verifyEqual(size(out{1}.signal), size(muxOut.signal));
testCase.verifyEqual(size(out{2}.signal), size(muxOut.signal));
testCase.verifyEqual(out{1}.fs, fs);
testCase.verifyEqual(out{2}.fs, fs);
testCase.verifyEqual(out{1}.lambda, muxOut.lambda(1), 'AbsTol', 1e-12);
testCase.verifyEqual(out{2}.lambda, muxOut.lambda(2), 'AbsTol', 1e-12);
end
function roundTripThroughMuxAndDemuxPreservesChannelMagnitudes(testCase)
fs = 16e9;
lambdaCenterNm = 1310;
sigA = makeOpticalSignal(ones(8, 2), fs, 1309.5e-9, 0.0);
mux = Optical_Multiplex( ...
"fs_in", fs, ...
"fs_out", fs, ...
"lambda_center", lambdaCenterNm, ...
"delta_f", 0, ...
"random_key", 0, ...
"attenuation", 0, ...
"filtype", 3, ...
"B", 200e9);
muxOut = mux.process({sigA});
demux = Optical_Demultiplex( ...
"fs_in", muxOut.fs, ...
"fs_out", muxOut.fs, ...
"lambda_center", lambdaCenterNm, ...
"attenuation", 0, ...
"filtype", 3, ...
"B", 200e9);
out = demux.process(muxOut);
testCase.verifyNumElements(out, 1);
testCase.verifyEqual(size(out{1}.signal), size(sigA.signal));
testCase.verifyEqual(out{1}.fs, fs);
testCase.verifyEqual(out{1}.lambda, muxOut.lambda(1), 'AbsTol', 1e-12);
testCase.verifyEqual(mean(abs(out{1}.signal), 'all'), mean(abs(sigA.signal), 'all'), 'AbsTol', 1e-6);
end end
end end
end end
function sig = makeOpticalSignal(signal, fs, lambda, polrot)
base = Signal(signal, "fs", fs);
sig = Opticalsignal( ...
signal, ...
"fs", fs, ...
"logbook", base.logbook, ...
"lambda", lambda, ...
"nase", 0, ...
"polrot", polrot);
end

View File

@@ -1,10 +1,101 @@
classdef Optical_Multiplex_test < IMDDTestCase classdef Optical_Multiplex_test < IMDDTestCase
% Auto-generated placeholder for Optical_Multiplex. methods (Test, TestTags = {'unit', 'fast', 'optical', 'mux'})
% Target: C:/Users/Silas/Documents/MATLAB/imdd_simulation/Classes/02_optical/Optical_Multiplex.m function processCombinesChannelsAndPreservesMetadata(testCase)
fs = 32e9;
lambdaCenter = 1550;
lambdaA = 1550e-9;
lambdaB = 1550e-9;
methods (Test, TestTags = {'placeholder', 'todo'}) sigA = makeOpticalSignal( ...
function testNotImplemented(testCase) [1 2; 1 2; 1 2], ...
testCase.assumeFail("Tests for Optical_Multiplex are not implemented yet."); fs, ...
lambdaA, ...
0.10);
sigB = makeOpticalSignal( ...
[3 4; 3 4; 3 4], ...
fs, ...
lambdaB, ...
0.90);
mux = Optical_Multiplex( ...
"fs_in", fs, ...
"fs_out", fs, ...
"lambda_center", lambdaCenter, ...
"delta_f", 0, ...
"random_key", 0, ...
"attenuation", 0, ...
"filtype", 3);
out = mux.process({sigA, sigB});
expected = combineSingleChannelMuxOutputs({sigA, sigB}, mux);
testCase.verifyClass(out, 'Opticalsignal');
testCase.verifyEqual(size(out.signal), [3 2]);
testCase.verifyEqual(out.signal, expected, 'AbsTol', 1e-12);
testCase.verifyEqual(out.fs, fs);
testCase.verifyEqual(out.lambda, [lambdaA, lambdaB], 'AbsTol', 1e-9);
testCase.verifyEqual(out.polrot, [0.10, 0.90], 'AbsTol', 1e-12);
testCase.verifyEqual(height(out.logbook), 2);
testCase.verifyEqual(string(out.logbook.Description(end)), "Opt. Mux. ");
end
function processSupportsMoreThanOneInputChannel(testCase)
fs = 16e9;
lambdaCenter = 1310;
sig1 = makeOpticalSignal(ones(4, 2), fs, lambdaCenter * 1e-9, 0.0);
sig2 = makeOpticalSignal(2 * ones(4, 2), fs, lambdaCenter * 1e-9, 0.5);
sig3 = makeOpticalSignal(3 * ones(4, 2), fs, lambdaCenter * 1e-9, 1.0);
mux = Optical_Multiplex( ...
"fs_in", fs, ...
"fs_out", fs, ...
"lambda_center", lambdaCenter, ...
"delta_f", 0, ...
"random_key", 0, ...
"attenuation", 0, ...
"filtype", 3);
out = mux.process({sig1, sig2, sig3});
expected = combineSingleChannelMuxOutputs({sig1, sig2, sig3}, mux);
testCase.verifySize(out.signal, [4, 2]);
testCase.verifyEqual(out.signal, expected, 'AbsTol', 1e-12);
testCase.verifyEqual(numel(out.lambda), 3);
testCase.verifyEqual(numel(out.polrot), 3);
end end
end end
end end
function sig = makeOpticalSignal(signal, fs, lambda, polrot)
base = Signal(signal, "fs", fs);
sig = Opticalsignal( ...
signal, ...
"fs", fs, ...
"logbook", base.logbook, ...
"lambda", lambda, ...
"nase", 0, ...
"polrot", polrot);
end
function expected = combineSingleChannelMuxOutputs(signals, muxPrototype)
expected = [];
for idx = 1:numel(signals)
mux = Optical_Multiplex( ...
"fs_in", muxPrototype.fs_in, ...
"fs_out", muxPrototype.fs_out, ...
"lambda_center", muxPrototype.lambda_center, ...
"delta_f", muxPrototype.delta_f, ...
"random_key", muxPrototype.random_key, ...
"attenuation", muxPrototype.attenuation, ...
"filtype", muxPrototype.filtype);
singleOut = mux.process({signals{idx}});
if isempty(expected)
expected = zeros(size(singleOut.signal));
end
expected = expected + singleOut.signal;
end
end

View File

@@ -0,0 +1,103 @@
classdef IMDD_base_system_impairment_monotonicity_integration_test < IMDDTestCase
% Integration test that compares a clean baseline IM/DD workflow
% against a deliberately impaired variant.
%
% The aim here is monotonic behavior, not exact waveform matching.
% If the channel is made worse, the recovered performance should not
% improve, and the intermediate physical metrics should reflect that.
properties
baseline
impaired
end
methods (TestClassSetup)
function runScenarioPairOnce(testCase)
% Build both scenarios once and share them across the test
% methods. The chain itself is deterministic, so the two
% results are directly comparable.
testCase.baseline = buildReducedImddWorkflow("baseline");
testCase.impaired = buildReducedImddWorkflow("impaired");
end
end
methods (Test, TestTags = {'integration', 'slow', 'imdd'})
function impairedScenarioDoesNotOutperformBaseline(testCase)
base = testCase.baseline;
bad = testCase.impaired;
% The impaired configuration should not beat the baseline.
testCase.verifyLessThanOrEqual( ...
base.ffe_results.metrics.BER, ...
bad.ffe_results.metrics.BER + 1e-12);
testCase.verifyLessThanOrEqual( ...
base.mlse_results.metrics.BER, ...
bad.mlse_results.metrics.BER + 1e-12);
end
function bothScenariosStayFiniteAndExposeTheImpairment(testCase)
base = testCase.baseline;
bad = testCase.impaired;
finiteSignals = {
base.Digi_sig.signal
base.El_sig.signal
base.Opt_sig_tx.signal
base.Opt_sig.signal
base.Rx_sig_after_pd.signal
base.Rx_sig_filtered.signal
base.Scpe_sig_pre_mf.signal
base.Scpe_sig.signal
base.Synced_sig_centered.signal
base.Synced_sig.signal
bad.Digi_sig.signal
bad.El_sig.signal
bad.Opt_sig_tx.signal
bad.Opt_sig.signal
bad.Rx_sig_after_pd.signal
bad.Rx_sig_filtered.signal
bad.Scpe_sig_pre_mf.signal
bad.Scpe_sig.signal
bad.Synced_sig_centered.signal
bad.Synced_sig.signal
};
for idx = 1:numel(finiteSignals)
sig = finiteSignals{idx};
testCase.verifyFalse(any(isnan(sig), 'all'));
testCase.verifyFalse(any(isinf(sig), 'all'));
end
% The impaired scenario is defined by a lower received power and
% a tighter electrical receiver bandwidth.
testCase.verifyLessThan(bad.params.ropDbm, base.params.ropDbm);
testCase.verifyLessThan(bad.params.rxElectricalBandwidthHz, base.params.rxElectricalBandwidthHz);
testCase.verifyLessThan(bad.params.scopeBandwidthHz, base.params.scopeBandwidthHz);
basePdPower = signalPower(base.Rx_sig_after_pd.signal);
badPdPower = signalPower(bad.Rx_sig_after_pd.signal);
baseRxPower = signalPower(base.Rx_sig_filtered.signal);
badRxPower = signalPower(bad.Rx_sig_filtered.signal);
baseScopePower = signalPower(base.Scpe_sig_pre_mf.signal);
badScopePower = signalPower(bad.Scpe_sig_pre_mf.signal);
testCase.verifyGreaterThan(basePdPower, badPdPower);
testCase.verifyGreaterThan(baseRxPower, badRxPower);
testCase.verifyGreaterThan(baseScopePower, badScopePower);
% Both scenarios must still produce valid metrics.
testCase.verifyGreaterThanOrEqual(base.ffe_results.metrics.BER, 0);
testCase.verifyLessThanOrEqual(base.ffe_results.metrics.BER, 1);
testCase.verifyGreaterThanOrEqual(bad.ffe_results.metrics.BER, 0);
testCase.verifyLessThanOrEqual(bad.ffe_results.metrics.BER, 1);
testCase.verifyGreaterThanOrEqual(base.mlse_results.metrics.BER, 0);
testCase.verifyLessThanOrEqual(base.mlse_results.metrics.BER, 1);
testCase.verifyGreaterThanOrEqual(bad.mlse_results.metrics.BER, 0);
testCase.verifyLessThanOrEqual(bad.mlse_results.metrics.BER, 1);
end
end
end
function p = signalPower(signal)
p = mean(abs(signal(:)).^2);
end

View File

@@ -6,6 +6,7 @@ classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
% - the reduced end-to-end chain runs without errors % - the reduced end-to-end chain runs without errors
% - key signal objects have the expected lengths and sampling rates % - key signal objects have the expected lengths and sampling rates
% - the DSP outputs return finite, bounded metrics % - 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 % - MLSE does not regress relative to the preceding FFE stage
% %
% Thresholds are intentionally provisional in this first iteration. % Thresholds are intentionally provisional in this first iteration.
@@ -19,7 +20,7 @@ classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
function runReducedImddWorkflowOnce(testCase) function runReducedImddWorkflowOnce(testCase)
% Run the deterministic reduced workflow once and share the % Run the deterministic reduced workflow once and share the
% resulting signals/metrics across all test methods. % resulting signals/metrics across all test methods.
testCase.workflow = runReducedWorkflow(); testCase.workflow = buildReducedImddWorkflow("minimal");
end end
end end
@@ -31,18 +32,30 @@ classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
testCase.verifyClass(wf.Symbols, 'Informationsignal'); testCase.verifyClass(wf.Symbols, 'Informationsignal');
testCase.verifyClass(wf.Digi_sig, 'Informationsignal'); testCase.verifyClass(wf.Digi_sig, 'Informationsignal');
testCase.verifyClass(wf.El_sig, 'Electricalsignal'); testCase.verifyClass(wf.El_sig, 'Electricalsignal');
testCase.verifyClass(wf.Opt_sig_tx, 'Opticalsignal');
testCase.verifyClass(wf.Opt_sig, 'Opticalsignal'); testCase.verifyClass(wf.Opt_sig, 'Opticalsignal');
testCase.verifyClass(wf.Rx_sig_after_pd, 'Electricalsignal'); 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.Scpe_sig, 'Informationsignal');
testCase.verifyClass(wf.Synced_sig_centered, 'Informationsignal');
testCase.verifyClass(wf.Synced_sig, 'Informationsignal'); testCase.verifyClass(wf.Synced_sig, 'Informationsignal');
testCase.verifyGreaterThan(length(wf.Tx_bits.signal), 0); testCase.verifyGreaterThan(length(wf.Tx_bits.signal), 0);
testCase.verifyGreaterThan(length(wf.Symbols.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.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 % After the matched filter, the signal is intentionally reduced
% to 2 samples per symbol for the downstream DSP chain. % to 2 samples per symbol for the downstream DSP chain.
testCase.verifyEqual(wf.Scpe_sig.fs, 2 * wf.params.fsym); 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); testCase.verifyEqual(wf.Synced_sig.fs, 2 * wf.params.fsym);
% The synchronized signal is explicitly cropped to 2 samples per % The synchronized signal is explicitly cropped to 2 samples per
@@ -56,9 +69,12 @@ classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
finiteSignals = { finiteSignals = {
wf.Digi_sig.signal wf.Digi_sig.signal
wf.El_sig.signal wf.El_sig.signal
wf.Opt_sig_tx.signal
wf.Opt_sig.signal wf.Opt_sig.signal
wf.Rx_sig_after_pd.signal wf.Rx_sig_after_pd.signal
wf.Scpe_sig_pre_mf.signal
wf.Scpe_sig.signal wf.Scpe_sig.signal
wf.Synced_sig_centered.signal
wf.Synced_sig.signal wf.Synced_sig.signal
}; };
@@ -68,6 +84,20 @@ classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
testCase.verifyFalse(any(isinf(sig), 'all')); testCase.verifyFalse(any(isinf(sig), 'all'));
end 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.verifyGreaterThanOrEqual(wf.ffe_results.metrics.BER, 0);
testCase.verifyLessThanOrEqual(wf.ffe_results.metrics.BER, 1); testCase.verifyLessThanOrEqual(wf.ffe_results.metrics.BER, 1);
testCase.verifyGreaterThanOrEqual(wf.mlse_results.metrics.BER, 0); testCase.verifyGreaterThanOrEqual(wf.mlse_results.metrics.BER, 0);
@@ -105,217 +135,11 @@ classdef IMDD_base_system_minimal_integration_test < IMDDTestCase
end end
end end
end end
function p = signalPower(signal)
function workflow = runReducedWorkflow() p = mean(abs(signal(:)).^2);
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;
end end
function params = reducedWorkflowParameters() function v = signalVariance(signal)
params = struct(); centered = signal(:) - mean(signal(:));
v = mean(abs(centered).^2);
% 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;
end end

View File

@@ -0,0 +1,109 @@
classdef IMDD_base_system_no_impairment_integration_test < IMDDTestCase
% Integration test for a reduced IM/DD workflow with no added channel
% impairments.
%
% The purpose of this test is to guard the near-ideal system behavior:
% the full chain still runs, the signal objects stay well-formed, and
% the DSP stages achieve very low error rates when fiber and receiver
% impairments are neutralized as far as practical.
properties
workflow
end
methods (TestClassSetup)
function runNoImpairmentWorkflowOnce(testCase)
% Run the deterministic workflow once and reuse the result for
% all test methods.
testCase.workflow = buildReducedImddWorkflow("no-impairment");
end
end
methods (Test, TestTags = {'integration', 'slow', 'imdd'})
function noImpairmentWorkflowBuildsExpectedSignalStages(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.Symbols.fs, wf.params.fsym);
testCase.verifyEqual(wf.Digi_sig.fs, wf.params.fdac);
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);
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);
testCase.verifyEqual(length(wf.Synced_sig.signal), 2 * length(wf.Symbols.signal));
end
function noImpairmentWorkflowProducesFiniteSignalsAndMetrics(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
testCase.verifyEqual(wf.params.linkLengthKm, 0);
testCase.verifyEqual(wf.params.fiberAlphaDbPerKm, 0);
testCase.verifyEqual(wf.params.rxFilterActive, false);
testCase.verifyEqual(wf.params.scopeLpfActive, false);
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 noImpairmentWorkflowMeetsPerformanceChecks(testCase)
wf = testCase.workflow;
% This is a no-added-impairment baseline, not a mathematical
% idealization of the full chain. The BER is therefore bounded
% rather than expected to be near zero.
maxFfeBer = 3e-1;
maxMlseBer = 5e-2;
testCase.verifyLessThanOrEqual(wf.ffe_results.metrics.BER, maxFfeBer);
testCase.verifyLessThanOrEqual(wf.mlse_results.metrics.BER, maxMlseBer);
% MLSE should never perform worse than the direct FFE path in
% this no-impairment regime.
testCase.verifyLessThanOrEqual( ...
wf.mlse_results.metrics.BER, ...
wf.ffe_results.metrics.BER + 1e-12);
end
end
end

View File

@@ -0,0 +1,222 @@
classdef WDM_end_to_end_two_channel_receiver_integration_test < IMDDTestCase
% End-to-end WDM receiver integration test.
%
% The fixture is intentionally small, deterministic, and aligned with
% the repo's WDM workflow:
% electrical drive -> EML -> polarization control -> optical mux
% -> launch amplifier -> optical demux -> photodiode
%
% The receiver-side check is based on a matched projection score and a
% normalized correlation check. That makes the test useful for
% regression while staying far away from a brittle golden waveform.
properties
workflow
end
methods (TestClassSetup)
function buildWorkflowOnce(testCase)
testCase.workflow = buildReceiverWorkflow();
end
end
methods (Test, TestTags = {'integration', 'slow', 'optical', 'wdm', 'receiver'})
function receiverChainHasExpectedStagesAndMetadata(testCase)
wf = testCase.workflow;
testCase.verifyClass(wf.muxOut, 'Opticalsignal');
testCase.verifyClass(wf.launchOut, 'Opticalsignal');
testCase.verifyClass(wf.demuxOut, 'cell');
testCase.verifyClass(wf.rxOut, 'cell');
testCase.verifyNumElements(wf.demuxOut, wf.params.numChannels);
testCase.verifyNumElements(wf.rxOut, wf.params.numChannels);
testCase.verifyEqual(wf.muxOut.fs, wf.params.fsMux);
testCase.verifyEqual(wf.launchOut.fs, wf.params.fsMux);
testCase.verifyEqual(wf.demuxOut{1}.fs, wf.params.fsBase);
testCase.verifyEqual(wf.demuxOut{2}.fs, wf.params.fsBase);
testCase.verifyEqual(wf.rxOut{1}.fs, wf.params.fsBase);
testCase.verifyEqual(wf.rxOut{2}.fs, wf.params.fsBase);
testCase.verifyEqual(numel(wf.muxOut.lambda), wf.params.numChannels);
testCase.verifyEqual(wf.muxOut.lambda, wf.channelPlanM, "AbsTol", 1e-12);
testCase.verifyEqual(wf.demuxOut{1}.lambda, wf.channelPlanM(1), "AbsTol", 1e-12);
testCase.verifyEqual(wf.demuxOut{2}.lambda, wf.channelPlanM(2), "AbsTol", 1e-12);
testCase.verifyTrue(all(isfinite(wf.muxOut.signal), "all"));
testCase.verifyTrue(all(isfinite(wf.launchOut.signal), "all"));
testCase.verifyTrue(all(isfinite(wf.demuxOut{1}.signal), "all"));
testCase.verifyTrue(all(isfinite(wf.demuxOut{2}.signal), "all"));
testCase.verifyTrue(all(isfinite(wf.rxOut{1}.signal), "all"));
testCase.verifyTrue(all(isfinite(wf.rxOut{2}.signal), "all"));
testCase.verifyGreaterThan(height(wf.muxOut.logbook), 0);
testCase.verifyGreaterThan(height(wf.demuxOut{1}.logbook), 0);
testCase.verifyGreaterThan(height(wf.rxOut{1}.logbook), 0);
end
function intendedBranchBeatsWrongBranchAfterDetection(testCase)
wf = testCase.workflow;
rx1 = centeredSignal(wf.rxOut{1}.signal);
rx2 = centeredSignal(wf.rxOut{2}.signal);
drive1 = centeredSignal(wf.driveRefs{1}.signal);
drive2 = centeredSignal(wf.driveRefs{2}.signal);
selfCorr1 = normalizedCorrelation(rx1, drive1);
selfCorr2 = normalizedCorrelation(rx2, drive2);
crossCorr1 = normalizedCorrelation(rx2, drive1);
crossCorr2 = normalizedCorrelation(rx1, drive2);
selfScore1 = matchedProjectionScore(rx1, drive1);
selfScore2 = matchedProjectionScore(rx2, drive2);
crossScore1 = matchedProjectionScore(rx2, drive1);
crossScore2 = matchedProjectionScore(rx1, drive2);
testCase.verifyGreaterThan(selfCorr1, crossCorr1);
testCase.verifyGreaterThan(selfCorr2, crossCorr2);
testCase.verifyGreaterThan(selfScore1, crossScore1);
testCase.verifyGreaterThan(selfScore2, crossScore2);
% Loose first-pass guardrails. The exact values can be tightened
% later once the baseline is reviewed across repeated runs.
testCase.verifyGreaterThan(selfCorr1, 0.05);
testCase.verifyGreaterThan(selfCorr2, 0.05);
testCase.verifyGreaterThan(selfScore1, 0);
testCase.verifyGreaterThan(selfScore2, 0);
end
end
end
function workflow = buildReceiverWorkflow()
params = receiverIntegrationParameters();
channelPlanNm = calcWavelengthPlan(params.numChannels, params.channelSpacingHz, params.lambdaCenterNm);
channelPlanM = channelPlanNm * 1e-9;
driveRefs = cell(1, params.numChannels);
opticalRefs = cell(1, params.numChannels);
for ch = 1:params.numChannels
driveRefs{ch} = makeElectricalSignal(params.driveSignals{ch}, params.fsBase);
optical = EML( ...
"mode", eml_mode.im_cosinus, ...
"fsimu", params.fsBase, ...
"lambda", channelPlanNm(ch), ...
"power", params.laserPowerDbm, ...
"linewidth", 0, ...
"alpha", 0, ...
"ampl_imbal", 0, ...
"pha_imbal", 0, ...
"bias", params.vbias, ...
"u_pi", params.uPi, ...
"randomkey", params.randomKey + ch).process(driveRefs{ch});
% The WDM mux expects dual-polarization optical signals.
opticalRefs{ch} = Polarization_Controller( ...
"mode", "rot_power", ...
"desired_power", 50).process(optical);
end
mux = Optical_Multiplex( ...
"fs_in", params.fsBase, ...
"fs_out", params.fsMux, ...
"lambda_center", params.lambdaCenterNm, ...
"delta_f", 0, ...
"random_key", 0, ...
"attenuation", 0, ...
"filtype", 1, ...
"B", 200e9);
muxOut = mux.process(opticalRefs);
launchOut = Amplifier( ...
"amp_mode", amp_mode.ideal_no_noise, ...
"gain_mode", gain_mode.output_power, ...
"nase_mode", nase_mode.pass_ase, ...
"amplification_db", params.launchPowerDbm).process(muxOut);
demux = Optical_Demultiplex( ...
"fs_in", launchOut.fs, ...
"fs_out", params.fsBase, ...
"lambda_center", params.lambdaCenterNm, ...
"attenuation", 0, ...
"filtype", 1, ...
"B", 200e9);
demuxOut = demux.process(launchOut);
rxOut = cell(1, params.numChannels);
for ch = 1:params.numChannels
rxOut{ch} = Photodiode( ...
"fsimu", params.fsBase, ...
"responsivity", 1, ...
"dark_current", 0, ...
"temperature", 20, ...
"nep", 0, ...
"randomkey", params.randomKey + 100 + ch).process(demuxOut{ch});
end
workflow = struct();
workflow.params = params;
workflow.channelPlanNm = channelPlanNm;
workflow.channelPlanM = channelPlanM;
workflow.driveRefs = driveRefs;
workflow.opticalRefs = opticalRefs;
workflow.muxOut = muxOut;
workflow.launchOut = launchOut;
workflow.demuxOut = demuxOut;
workflow.rxOut = rxOut;
end
function params = receiverIntegrationParameters()
params = struct();
% Small fixture, but still close to the WDM project conventions.
params.numChannels = 2;
params.channelSpacingHz = 200e9;
params.lambdaCenterNm = 1310;
params.fsBase = 64e9;
params.fsMux = 4 * params.fsBase;
params.laserPowerDbm = 3;
params.launchPowerDbm = params.laserPowerDbm + 10 * log10(params.numChannels);
params.uPi = 4.6;
params.vbias = -0.5 * params.uPi;
params.randomKey = 21;
n = (0:63).';
params.driveSignals = {
0.18 * sin(2*pi*n/16) + 0.03 * cos(2*pi*n/8)
0.17 * cos(2*pi*n/11 + pi/5) - 0.04 * sin(2*pi*n/5)
};
end
function sig = makeElectricalSignal(values, fs)
base = Signal(values, "fs", fs);
sig = Electricalsignal( ...
values, ...
"fs", fs, ...
"logbook", base.logbook);
end
function centered = centeredSignal(signal)
centered = signal(:) - mean(signal(:));
end
function corrVal = normalizedCorrelation(candidate, reference)
denom = sqrt(sum(abs(candidate).^2) * sum(abs(reference).^2));
if denom == 0
corrVal = 0;
return
end
corrVal = abs(sum(conj(candidate) .* reference)) / denom;
end
function score = matchedProjectionScore(candidate, reference)
denom = sum(abs(reference).^2);
if denom == 0
score = 0;
return
end
score = abs(sum(conj(candidate) .* reference)).^2 / denom;
end

View File

@@ -0,0 +1,264 @@
classdef WDM_mux_demux_optical_chain_integration_test < IMDDTestCase
% Integration test for the WDM optical subchain.
%
% The goal is to verify the mux/demux behavior in a way that matches
% the repo's WDM workflows:
% electrical drive -> EML -> polarization control -> optical mux
% -> launch amplifier -> optical demux
%
% The electrical stimuli are intentionally small and deterministic so
% the test isolates the optical chain rather than the TX waveform
% generation stack.
properties
workflow
end
methods (TestClassSetup)
function buildWorkflowOnce(testCase)
testCase.workflow = buildWdmWorkflow();
end
end
methods (Test, TestTags = {'integration', 'slow', 'optical', 'wdm'})
function muxAndDemuxPreserveMetadataAndSamplingRates(testCase)
wf = testCase.workflow;
numChannels = wf.params.numChannels;
testCase.verifyClass(wf.muxOut, 'Opticalsignal');
testCase.verifyClass(wf.launchOut, 'Opticalsignal');
testCase.verifyClass(wf.demuxOut, 'cell');
testCase.verifyNumElements(wf.demuxOut, numChannels);
testCase.verifyEqual(wf.muxOut.fs, wf.params.fsMux);
testCase.verifyEqual(wf.launchOut.fs, wf.params.fsMux);
for ch = 1:numChannels
testCase.verifyEqual(wf.demuxOut{ch}.fs, wf.params.fsBase);
end
testCase.verifyEqual(numel(wf.muxOut.lambda), numChannels);
testCase.verifyEqual(wf.muxOut.lambda, wf.channelPlanM, "AbsTol", 5e-9);
for ch = 1:numChannels
testCase.verifyEqual(wf.demuxOut{ch}.lambda, wf.channelPlanM(ch), "AbsTol", 5e-9);
end
testCase.verifyTrue(all(isfinite(wf.muxOut.signal), "all"));
testCase.verifyTrue(all(isfinite(wf.launchOut.signal), "all"));
for ch = 1:numChannels
testCase.verifyTrue(all(isfinite(wf.demuxOut{ch}.signal), "all"));
end
testCase.verifyGreaterThan(height(wf.muxOut.logbook), 0);
for ch = 1:numChannels
testCase.verifyGreaterThan(height(wf.demuxOut{ch}.logbook), 0);
end
end
function intendedChannelIsRecoveredBetterThanCrossTalk(testCase)
wf = testCase.workflow;
numChannels = wf.params.numChannels;
projectionPower = zeros(numChannels);
branchPower = zeros(numChannels, 1);
branchLambdaIdx = zeros(numChannels, 1);
for outCh = 1:numChannels
branchSignal = wf.demuxOut{outCh}.signal;
branchPower(outCh) = mean(abs(branchSignal).^2, 'all');
branchLambdaIdx(outCh) = nearestWavelengthIndex( ...
wf.demuxOut{outCh}.lambda, ...
wf.channelPlanM);
for refCh = 1:numChannels
projectionPower(outCh, refCh) = projectedPower( ...
branchSignal, ...
wf.channelRefs{refCh}.signal);
end
end
for ch = 1:numChannels
matchedRef = branchLambdaIdx(ch);
otherIdx = setdiff(1:numChannels, matchedRef);
bestProjection = projectionPower(ch, matchedRef);
offProjection = max(projectionPower(ch, otherIdx));
% The recovered branch should keep non-trivial optical power
% after demux, even when compared against the leaked branches.
testCase.verifyGreaterThan(branchPower(ch), 0);
testCase.verifyGreaterThan(branchPower(ch), 0.7 * mean(branchPower));
% The intended branch should dominate its row of the
% projection matrix even if the demux ordering changes.
testCase.verifyGreaterThan(bestProjection, offProjection * 1.0001);
testCase.verifyGreaterThan( ...
bestProjection / sum(projectionPower(ch, :)), ...
0.5 / numChannels);
% Correlation is kept as a secondary diagnostic metric.
testCase.verifyTrue(isfinite(normalizedCorrelation( ...
centredPowerEnvelope(wf.demuxOut{ch}.signal), ...
centredPowerEnvelope(wf.channelRefs{matchedRef}.signal))));
end
end
end
end
function workflow = buildWdmWorkflow()
params = wdmIntegrationParameters();
channelPlanNm = calcWavelengthPlan(params.numChannels, params.channelSpacingHz, params.lambdaCenterNm);
channelPlanM = channelPlanNm * 1e-9;
% Build the two WDM channels separately so the test can later compare
% the recovered branch against the original reference branch.
channelRefs = cell(1, params.numChannels);
for ch = 1:params.numChannels
drive = makeElectricalSignal(params.driveSignals{ch}, params.fsBase);
optical = EML( ...
"mode", eml_mode.im_cosinus, ...
"fsimu", params.fsBase, ...
"lambda", channelPlanNm(ch), ...
"power", params.laserPowerDbm, ...
"linewidth", 0, ...
"alpha", 0, ...
"ampl_imbal", 0, ...
"pha_imbal", 0, ...
"bias", params.vbias, ...
"u_pi", params.uPi, ...
"randomkey", params.randomKey + ch).process(drive);
% Create a dual-polarization optical channel, mirroring the WDM
% scripts where the mux receives DP optical signals.
channelRefs{ch} = Polarization_Controller( ...
"mode", "rot_power", ...
"desired_power", 50).process(optical);
end
mux = Optical_Multiplex( ...
"fs_in", params.fsBase, ...
"fs_out", params.fsMux, ...
"lambda_center", params.lambdaCenterNm, ...
"delta_f", 0, ...
"random_key", 0, ...
"attenuation", 0, ...
"filtype", 1, ...
"B", 200e9);
muxOut = mux.process(channelRefs);
% A very small deterministic propagation hop keeps the fixture closer
% to the WDM workflows without turning this into a full channel study.
wdmFiberOut = DP_Fiber( ...
"L", 0, ...
"dz", 1, ...
"lambda", params.lambdaCenterNm, ...
"rng", 0, ...
"gamma", 0, ...
"fa", muxOut.fs, ...
"X_alpha", 0, ...
"D", 0, ...
"Ds", 0, ...
"Dpmd", 0, ...
"beat_len", 1e9, ...
"corr_len", 1e9, ...
"manakov", 0, ...
"SS_dphimax", 1e-2, ...
"SS_dzmax", 1, ...
"SS_dzmin", 1, ...
"n_waveplates", 1, ...
"useGPU", false, ...
"useSingle", false).process(muxOut);
% The WDM project scripts normally apply a launch amplifier after the
% mux. We keep that stage here, but with deterministic no-noise settings.
launchOut = Amplifier( ...
"amp_mode", amp_mode.ideal_no_noise, ...
"gain_mode", gain_mode.output_power, ...
"nase_mode", nase_mode.pass_ase, ...
"amplification_db", params.launchPowerDbm).process(wdmFiberOut);
demux = Optical_Demultiplex( ...
"fs_in", launchOut.fs, ...
"fs_out", params.fsBase, ...
"lambda_center", params.lambdaCenterNm, ...
"attenuation", 0, ...
"filtype", 1, ...
"B", 200e9);
demuxOut = demux.process(launchOut);
workflow = struct();
workflow.params = params;
workflow.channelPlanNm = channelPlanNm;
workflow.channelPlanM = channelPlanM;
workflow.channelRefs = channelRefs;
workflow.muxOut = muxOut;
workflow.wdmFiberOut = wdmFiberOut;
workflow.launchOut = launchOut;
workflow.demuxOut = demuxOut;
end
function params = wdmIntegrationParameters()
params = struct();
% Keep the fixture small, deterministic, and close to the WDM scripts.
params.numChannels = 4;
params.channelSpacingHz = 200e9;
params.lambdaCenterNm = 1310;
params.fsBase = 64e9;
params.fsMux = 4 * params.fsBase;
params.laserPowerDbm = 3;
params.launchPowerDbm = params.laserPowerDbm + 10*log10(params.numChannels);
params.uPi = 4.6;
params.vbias = -0.5 * params.uPi;
params.randomKey = 11;
n = (0:63).';
params.driveSignals = {
0.18 * sin(2*pi*n/16) + 0.03 * cos(2*pi*n/8)
0.17 * cos(2*pi*n/11 + pi/5) - 0.04 * sin(2*pi*n/5)
0.16 * sin(2*pi*n/7 + pi/7) + 0.02 * cos(2*pi*n/4)
0.14 * cos(2*pi*n/9) - 0.05 * sin(2*pi*n/6 + pi/8)
};
end
function sig = makeElectricalSignal(values, fs)
base = Signal(values, "fs", fs);
sig = Electricalsignal( ...
values, ...
"fs", fs, ...
"logbook", base.logbook);
end
function corrVal = normalizedCorrelation(refSignal, candidateSignal)
ref = refSignal(:);
cand = candidateSignal(:);
denom = sqrt(sum(abs(ref).^2) * sum(abs(cand).^2));
if denom == 0
corrVal = 0;
return
end
corrVal = abs(sum(conj(ref) .* cand)) / denom;
end
function idx = nearestWavelengthIndex(value, plan)
[~, idx] = min(abs(plan(:) - value));
end
function pwr = projectedPower(refSignal, candidateSignal)
ref = refSignal(:);
cand = candidateSignal(:);
denom = sum(abs(ref).^2);
if denom == 0
pwr = 0;
return
end
pwr = abs(sum(conj(ref) .* cand)).^2 / denom;
end
function centeredEnvelope = centredPowerEnvelope(signal)
powerEnvelope = abs(signal(:)).^2;
centeredEnvelope = powerEnvelope - mean(powerEnvelope);
end

View File

@@ -0,0 +1,290 @@
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

View File

@@ -1,4 +0,0 @@