From c77327d39f3328da64dcb6f6bb0624c8a6ce9a49 Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Thu, 13 Aug 2026 10:22:01 +0200 Subject: [PATCH] =?UTF-8?q?w=C3=A4hrend=20Diss,=20400G=20plots?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Classes/00_signals/Signal.m | 2 +- Classes/04_DSP/Equalizer/ML_MLSE.m | 2 +- Functions/EQ_blocks/ffe.m | 4 +- Functions/EQ_recipes/dsp_400g_recipe.m | 18 +- .../partial_response/duobinary_back2back.m | 4 +- .../calcFWM/analytical_calculation_paper.m | 3 +- .../final/FIGURE_NGMI_THESIS_FINAL.m | 458 ++++++++++++++++++ .../Auswertung_JLT/final/FIGURE_NGMI_v2.m | 1 + ...HECK_BEST_PREEMPHASIS_PRECODE_2KM_1310NM.m | 241 +++++++++ .../FIGURE_EQ_NOISE_VS_BAUDRATE.m | 93 ++-- ...LOT_AIR_BEST_CURVES_OVER_WAVELENGTH_10KM.m | 407 ++++++++++++++++ ..._BER_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m | 14 +- .../PLOT_DUOBINARY_DETECTION_BER_VS_BITRATE.m | 2 +- ...ATES_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m | 189 +++++--- ..._ROP_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m | 4 +- ...NGTH_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m | 2 +- projects/Diss/400G_revisit/RX_sprectra.m | 175 +++++-- .../TABLE_BEST_DATARATES_2KM_10KM_1310NM.m | 261 ++++++++++ projects/Diss/400G_revisit/TX_spectra.m | 23 +- .../investigate_400g_algorithms.m | 13 +- .../minimal_example_huawei/ml_mlse_pam.m | 4 +- .../theoretic_channel_evaluation.m | 72 +-- projects/WDM/WDM_auswertung.m | 4 +- projects/WDM/WDM_model.m | 21 +- tmp/pdfs/paper-2.png | Bin 0 -> 701089 bytes tmp/pdfs/paper-3.png | Bin 0 -> 491898 bytes workerError.mat | Bin 1201 -> 1175 bytes 27 files changed, 1769 insertions(+), 248 deletions(-) create mode 100644 projects/Advanced_DSP_for_400G_IMDD_experiments/Auswertung_JLT/final/FIGURE_NGMI_THESIS_FINAL.m create mode 100644 projects/Diss/400G_revisit/CHECK_BEST_PREEMPHASIS_PRECODE_2KM_1310NM.m create mode 100644 projects/Diss/400G_revisit/PLOT_AIR_BEST_CURVES_OVER_WAVELENGTH_10KM.m create mode 100644 projects/Diss/400G_revisit/TABLE_BEST_DATARATES_2KM_10KM_1310NM.m create mode 100644 tmp/pdfs/paper-2.png create mode 100644 tmp/pdfs/paper-3.png diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index 608510f..f0852e8 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -403,7 +403,7 @@ classdef Signal % Divide by 2*pi for the f/fs axis where Nyquist is 0.5. end - p_lin = movmean(p_lin,20); + p_lin = movmean(p_lin,5); if options.normalizeTo0dB p_lin = p_lin ./ max(p_lin); diff --git a/Classes/04_DSP/Equalizer/ML_MLSE.m b/Classes/04_DSP/Equalizer/ML_MLSE.m index a27712b..ee90526 100644 --- a/Classes/04_DSP/Equalizer/ML_MLSE.m +++ b/Classes/04_DSP/Equalizer/ML_MLSE.m @@ -280,7 +280,7 @@ classdef ML_MLSE < handle if sym_idx>=obj.L if obj.adaptive_mu mu_eff=CE_smooth(symbol); - mu_eff=max(min(mu_eff,0.2),1e-4); + mu_eff=max(min(mu_eff,0.2),1e-5); else mu_eff=mu; end diff --git a/Functions/EQ_blocks/ffe.m b/Functions/EQ_blocks/ffe.m index 07ea93b..5860b15 100644 --- a/Functions/EQ_blocks/ffe.m +++ b/Functions/EQ_blocks/ffe.m @@ -50,10 +50,10 @@ eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); %% Calculate performance metrics [snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal); % [gmi] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000); -[gmi] = calc_ngmi(eq_signal_sd,tx_symbols); +[gmi,ngmi] = calc_ngmi(eq_signal_sd,tx_symbols); gmi = max(gmi,0); -air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ log2(double(M)); +air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* ngmi; [evm_total, evm_lvl] = calc_evm(eq_signal_sd, tx_symbols); [std_total, std_lvl] = calc_std(eq_signal_sd, tx_symbols); [std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols); diff --git a/Functions/EQ_recipes/dsp_400g_recipe.m b/Functions/EQ_recipes/dsp_400g_recipe.m index 5fe580e..b672867 100644 --- a/Functions/EQ_recipes/dsp_400g_recipe.m +++ b/Functions/EQ_recipes/dsp_400g_recipe.m @@ -86,7 +86,7 @@ if options.duob_mode ~= db_mode.db_encoded [vnle_results, equalized_signal] = runFfe(eq_vnle, "VNLE", ... Scpe_sig, Symbols, Tx_bits, options); vnle_results.config.equalizer_structure = equalizer_structure.ffe; - ffe_results.recipe_config = collectRecipeConfig("vnle", eq_ffe, p, options); + ffe_results.recipe_config = collectRecipeConfig("vnle", eq_vnle, p, options); output.vnle_package = vnle_results; if p.plot_output_signals @@ -273,14 +273,14 @@ end function p = defaultRecipeParameters() p = struct(); -p.run_ffe = false; -p.run_vnle = false; -p.run_dfe = false; -p.run_vnle_mlse = true; -p.run_dbtgt = true; -p.run_ml_mlse = false; % non-encoded and precoded branches -p.run_ml_mlse_db = false; % db_encoded: ML-based MLSE -p.run_mlse_db = true; % db_encoded: conventional MLSE +p.run_ffe = 0; +p.run_vnle = 0; +p.run_dfe = 0; +p.run_vnle_mlse = 1; +p.run_dbtgt = 0; +p.run_ml_mlse = 0; % non-encoded and precoded branches +p.run_ml_mlse_db = 0; % db_encoded: ML-based MLSE +p.run_mlse_db = 1; % db_encoded: conventional MLSE p.preprocess_mode = "auto"; diff --git a/Theory/Mapping_Coding/partial_response/duobinary_back2back.m b/Theory/Mapping_Coding/partial_response/duobinary_back2back.m index 3fccde2..6e23652 100644 --- a/Theory/Mapping_Coding/partial_response/duobinary_back2back.m +++ b/Theory/Mapping_Coding/partial_response/duobinary_back2back.m @@ -1,4 +1,4 @@ -for M = [4] +for M = [6] bits = Signalgenerator("form", signalform.prms,"M", M,"order", 18).process(); mapper = PAMmapper(M,0); symbols = mapper.map(bits); @@ -13,7 +13,7 @@ for M = [4] symbols_db_ = awgn_channel(symbols_db,"snr_dB",15); figure(1003042); showLevelHistogram(symbols_db_,symbols_db,"ref_symbol_uncoded",symbols); - showLevelHistogram(symbols_db_,symbols_db); + % showLevelHistogram(symbols_db_,symbols_db); symbols_rx = db.decode(symbols_db); bits_rx = PAMmapper(M,0).demap(symbols_rx); diff --git a/Theory/Optical/calcFWM/analytical_calculation_paper.m b/Theory/Optical/calcFWM/analytical_calculation_paper.m index d088424..030b34a 100644 --- a/Theory/Optical/calcFWM/analytical_calculation_paper.m +++ b/Theory/Optical/calcFWM/analytical_calculation_paper.m @@ -16,13 +16,14 @@ for i = 1:length(N_) [Mndg,Mdg] = getProducts(N_(i)); total(i) = sum(Mndg) + sum(Mdg); subplot(1,length(N_),i) + fprintf('%d Channel: Non degenerate: %d; Degenerate: %d \n', N_(i),sum(Mndg),sum(Mdg)); xline(calcWavelengthPlan(N_(i), df_hz, center_nm)); hold on stem(channelplan_nm,(Mndg+Mdg),'filled','LineWidth',1,'Marker','o','MarkerSize',2) stem(channelplan_nm,(Mdg),'filled','LineWidth',1,'Marker','none'); ylim([0,100]) xlim([1260, 1365]); - grid off + % grid off xlabel('O-band wavelength region in nm'); ylabel('Number of FWM products'); title([num2str(N_(i)),' ch.']) diff --git a/projects/Advanced_DSP_for_400G_IMDD_experiments/Auswertung_JLT/final/FIGURE_NGMI_THESIS_FINAL.m b/projects/Advanced_DSP_for_400G_IMDD_experiments/Auswertung_JLT/final/FIGURE_NGMI_THESIS_FINAL.m new file mode 100644 index 0000000..96bfe9e --- /dev/null +++ b/projects/Advanced_DSP_for_400G_IMDD_experiments/Auswertung_JLT/final/FIGURE_NGMI_THESIS_FINAL.m @@ -0,0 +1,458 @@ +%% Final thesis figure: best EQ curves for NGMI, AIR and FEC rates +% Four-panel thesis view based on FIGURE_NGMI.m: +% a) NGMI +% b) AIR +% c) SD+HD FEC NDR +% d) O-FEC and KP4+Hamming NDR +% +% The normal EQ classes use the 2 km data set. DBS + VNLE + MLSE uses the +% post-2026 10 km data set because that is the available DBS measurement set. +% Within each selected EQ/PAM curve, the best valid result is retained for +% every symbol rate. The PAM mapping follows the reference thesis figure: +% VNLE -> PAM-8 +% VNLE + PF + MLSE -> PAM-6 +% VNLE DBt. + MLSE -> PAM-4 +% ML pre-EQ + Viterbi -> PAM-4 +% DBS + VNLE + MLSE -> PAM-4 and PAM-6 + +clear; + +%% 1) Configuration + +normalFiberLengthKm = 2; +duobinaryFiberLengthKm = 2; +selectedWavelengthNm = 1310; +selectedRopAttenuation = 0; +selectedIsMpi = 0; +duobinaryDateCutoff = datetime("2026-01-01 00:00:00"); + +% Thesis colors and curvet /PAM mapping. +curves = struct; +curves(1).name = "VNLE + PF + MLSE"; +curves(1).algorithm_key = "vnle_pf_mlse"; +curves(1).pam = 8; +curves(1).color = clr.Paired.red; +curves(1).marker = "o"; +curves(1).source = "normal"; +curves(1).usePrecodedBer = false; + +curves(2).name = "VNLE + PF + MLSE"; +curves(2).algorithm_key = "vnle_pf_mlse"; +curves(2).pam = 6; +curves(2).color = clr.Paired.green; +curves(2).marker = "square"; +curves(2).source = "normal"; +curves(2).usePrecodedBer = false; + +curves(3).name = "VNLE DBt. + MLSE"; +curves(3).algorithm_key = "vnle_db_mlse"; +curves(3).pam = 4; +curves(3).color = clr.Paired.blue; +curves(3).marker = "diamond"; +curves(3).source = "normal"; +curves(3).usePrecodedBer = true; + +curves(4).name = "ML pre-EQ + Viterbi"; +curves(4).algorithm_key = "ml_mlse"; +curves(4).pam = 4; +curves(4).color = clr.Paired.purple; +curves(4).marker = "^"; +curves(4).source = "normal"; +curves(4).usePrecodedBer = true; + +curves(5).name = "DBS + VNLE + MLSE (PAM-4)"; +curves(5).algorithm_key = "db_encoded"; +curves(5).pam = 4; +curves(5).color = clr.Paired.orange; +curves(5).marker = "v"; +curves(5).source = "duobinary"; +curves(5).usePrecodedBer = false; + +curves(6).name = "DBS + VNLE + MLSE (PAM-6)"; +curves(6).algorithm_key = "db_encoded"; +curves(6).pam = 6; +curves(6).color = clr.Paired.orange; +curves(6).marker = "v"; +curves(6).source = "duobinary"; +curves(6).usePrecodedBer = false; + +%% 2) Query normal 2 km and duobinary 10 km data + +db = DBHandler( ... + "dataBase", "labor_highspeed", ... + "type", "mysql", ... + "server", "192.168.178.192", ... + "user", "silas", ... + "password", "silas"); +db.refresh(); + +selectedFields = db.getTableFieldNames('dashboard_ungrouped_alltime'); + +normalRows = queryMeasurementRows(db, selectedFields, normalFiberLengthKm,selectedWavelengthNm); +normalRows = cleanMeasurementRows(normalRows); +normalRows = normalRows(ismember(normalRows.db_mode, ... + [double(db_mode.no_db), double(db_mode.db_precoded)]), :); +normalRows.algorithm_key = lower(string(normalRows.equalizer_structure)); + +duobinaryRows = queryMeasurementRows(db, selectedFields, duobinaryFiberLengthKm,selectedWavelengthNm); +duobinaryRows = cleanMeasurementRows(duobinaryRows); +duobinaryRows = duobinaryRows(duobinaryRows.db_mode == double(db_mode.db_encoded), :); +duobinaryRows = duobinaryRows( ... + equalizerMask(duobinaryRows.equalizer_structure, ... + equalizer_structure.db_encoded), :); + +if ismember("date_of_processing", string(duobinaryRows.Properties.VariableNames)) + duobinaryRows.date_of_processing = datetime(string(duobinaryRows.date_of_processing)); + duobinaryRows = duobinaryRows(datetime(duobinaryRows.date_of_processing)>duobinaryDateCutoff,:); + duobinaryRows.date_of_processing = string(duobinaryRows.date_of_processing); +else + warning("figure_ngmi_thesis:NoProcessingDate", ... + "date_of_processing was not returned; no DBS date filter was applied."); +end +% DBS PAM-8 is intentionally excluded from the final thesis figure. +duobinaryRows = duobinaryRows(ismember(duobinaryRows.pam_level, [4 6]), :); +duobinaryRows.algorithm_key = repmat("db_encoded", height(duobinaryRows), 1); + +fprintf("Normal %g km rows: %d\n", normalFiberLengthKm, height(normalRows)); +fprintf("DBS %g km rows after date filter: %d\n", ... + duobinaryFiberLengthKm, height(duobinaryRows)); + +%% 3) Extract the best curves and calculate FEC rates + +tp = TransmissionPerformance; +results = repmat(emptyResult(), numel(curves), 1); + +for curveIdx = 1:numel(curves) + curve = curves(curveIdx); + + if curve.source == "normal" + curveRows = normalRows; + else + curveRows = duobinaryRows; + end + + curveRows = curveRows(curveRows.pam_level == curve.pam & ... + curveRows.algorithm_key == curve.algorithm_key, :); + + if isempty(curveRows) + warning("figure_ngmi_thesis:NoCurveRows", ... + "No rows found for %s, PAM-%d.", curve.name, curve.pam); + continue + end + + curveRows.BER_plot = curveRows.BER; + curveRows.precode = zeros(height(curveRows), 1); + if curve.usePrecodedBer && ismember("BER_precoded", ... + string(curveRows.Properties.VariableNames)) + usePrecoded = isfinite(curveRows.BER_precoded); + curveRows.BER_plot(usePrecoded) = curveRows.BER_precoded(usePrecoded); + curveRows.precode(usePrecoded) = 1; + end + + curveRows = addDerivedMetrics(curveRows); + + % Each metric is optimized independently. In particular, the BER + % winner does not have to be the NGMI or AIR winner for a given baud + % rate because these quantities are stored/calculated separately. + berSeries = bestSeries(curveRows, "BER_plot"); + ngmiSeries = bestSeries(curveRows, "NGMI"); + airSeries = bestSeries(curveRows, "AIR_Gbps"); + + grossRate = double(curveRows.grossrate); + measuredNgmi = double(curveRows.NGMI); + measuredBer = double(curveRows.BER_plot); + measuredNgmi(~isfinite(measuredNgmi) | measuredNgmi < 0 | ... + measuredNgmi > 1.05) = NaN; + measuredBer(~isfinite(measuredBer) | measuredBer <= 0 | ... + measuredBer > 0.5) = NaN; + + ndr = tp.calculateNetRate(grossRate, ... + "NGMI", measuredNgmi, ... + "BER", measuredBer); + + curveRows.NDR_SDHD = columnVector(ndr.SDHD.NetRate) .* 1e-9; + curveRows.NDR_O_FEC = columnVector(ndr.O_FEC.NetRate) .* 1e-9; + curveRows.NDR_KP4_HAMMING = columnVector(ndr.KP4_hamming.NetRate) .* 1e-9; + + results(curveIdx).name = curve.name; + results(curveIdx).color = curve.color; + results(curveIdx).marker = curve.marker; + results(curveIdx).pam = curve.pam; + results(curveIdx).ber = berSeries; + results(curveIdx).ngmi = ngmiSeries; + results(curveIdx).air = airSeries; + results(curveIdx).sdhd = bestSeries(curveRows, "NDR_SDHD"); + results(curveIdx).ofec = bestSeries(curveRows, "NDR_O_FEC"); + results(curveIdx).kp4Hamming = bestSeries(curveRows, "NDR_KP4_HAMMING"); + + fprintf("%s, PAM-%d: BER=%d, NGMI=%d, AIR=%d, SD+HD=%d, O-FEC=%d, KP4+Hamming=%d points\n", ... + curve.name, curve.pam, ... + numel(results(curveIdx).ber.x), ... + numel(results(curveIdx).ngmi.x), ... + numel(results(curveIdx).air.x), ... + numel(results(curveIdx).sdhd.x), ... + numel(results(curveIdx).ofec.x), ... + numel(results(curveIdx).kp4Hamming.x)); +end + +%% 4) Four-panel thesis figure + +fig = figure(72+normalFiberLengthKm); clf; +t = tiledlayout(fig, 1, 4, ... + "TileSpacing", "compact", ... + "Padding", "compact"); + +axNgmi = nexttile(t, 1); +hold(axNgmi, "on"); +for curveIdx = 1:numel(results) + if isempty(results(curveIdx).ngmi.x) + continue + end + plotSeries(axNgmi, results(curveIdx).ngmi, ... + results(curveIdx), "-", results(curveIdx).marker, results(curveIdx).name); +end +formatAxis(axNgmi, "NGMI", [0.90 1.00], [100 220]); +title(axNgmi, "a) NGMI"); + +axAir = nexttile(t, 2); +hold(axAir, "on"); +for curveIdx = 1:numel(results) + if isempty(results(curveIdx).air.x) + continue + end + plotSeries(axAir, results(curveIdx).air, results(curveIdx), ... + "-", results(curveIdx).marker, results(curveIdx).name); +end +formatAxis(axAir, "AIR [Gb/s]", [280 440], [100 220]); +yline(axAir, 400, "--", "Color", [0.25 0.25 0.25], ... + "HandleVisibility", "off"); +title(axAir, "b) AIR"); + +axSdhd = nexttile(t, 3); +hold(axSdhd, "on"); +for curveIdx = 1:numel(results) + if isempty(results(curveIdx).sdhd.x) + continue + end + plotSeries(axSdhd, results(curveIdx).sdhd, results(curveIdx), ... + "-", results(curveIdx).marker, results(curveIdx).name); +end +formatAxis(axSdhd, "NDR [Gb/s]", [280 430], [100 220]); +yline(axSdhd, 400, "--", "Color", [0.25 0.25 0.25], ... + "HandleVisibility", "off"); +title(axSdhd, "c) SD+HD FEC"); + +axHd = nexttile(t, 4); +hold(axHd, "on"); +hdHandles = gobjects(numel(curves), 1); +for curveIdx = 1:numel(results) + curve = results(curveIdx); + if ~isempty(curve.ofec.x) + hdHandles(curveIdx) = plotSeries(axHd, curve.ofec, curve, ":", curve.marker, ... + curve.name + " — O-FEC"); + end + if ~isempty(curve.kp4Hamming.x) + plotSeries(axHd, curve.kp4Hamming, curve, "--", "diamond", ... + curve.name + " — KP4+Hamming"); + end +end +formatAxis(axHd, "NDR [Gb/s]", [280 430], [100 220]); +yline(axHd, 400, "--", "Color", [0.25 0.25 0.25], ... + "HandleVisibility", "off"); +title(axHd, "d) O-FEC; KP4+Hamming"); + +validLegendHandles = hdHandles(isgraphics(hdHandles)); +if ~isempty(validLegendHandles) + legend(axHd, validLegendHandles, ... + {results(isgraphics(hdHandles)).name}, ... + "Location", "southoutside", ... + "NumColumns", min(3, numel(validLegendHandles)), ... + "Interpreter", "none"); +end + +sgtitle(t, sprintf("Best EQ-class results, lambda=%g nm", selectedWavelengthNm)); +set(fig, "Position", 1e3 .* [0.18 0.55 1.52 0.31]); + +%% Local helpers + +function T = queryMeasurementRows(db, fields, fiberLengthKm,selectedWavelengthNm) +fp = QueryFilter(); +fp.where("Runs", "fiber_length", "EQUALS", fiberLengthKm); +% fp.where("Runs", "wavelength", "EQUALS", selectedWavelengthNm); +fp.where("Runs", "rop_attenuation", "EQUALS", 0); +fp.where("Runs", "is_mpi", "EQUALS", 0); +[T, query] = db.queryDB(fp, fields); +disp(query); +end + +function T = cleanMeasurementRows(T) +numericFields = ["result_id", "run_id", "eq_id", "bitrate", "grossrate", ... + "symbolrate", "pam_level", "wavelength", "fiber_length", "db_mode", ... + "rop_attenuation", "numBits", "numBitErr", "BER", ... + "numBitErr_precoded", "BER_precoded", "GMI", "AIR", "NGMI"]; +for fieldIdx = 1:numel(numericFields) + fieldName = numericFields(fieldIdx); + if ismember(fieldName, string(T.Properties.VariableNames)) + T.(char(fieldName)) = numericColumn(T.(char(fieldName))); + end +end +T.equalizer_structure = lower(string(T.equalizer_structure)); +end + +function result = emptyResult() +result = struct( ... + "name", "", ... + "color", [0 0 0], ... + "marker", "o", ... + "pam", NaN, ... + "ber", emptySeries(), ... + "ngmi", emptySeries(), ... + "air", emptySeries(), ... + "sdhd", emptySeries(), ... + "ofec", emptySeries(), ... + "kp4Hamming", emptySeries()); +end + +function series = emptySeries() +series = struct( ... + "x", [], ... + "y", [], ... + "ber", [], ... + "ngmi", [], ... + "air", [], ... + "wavelength", [], ... + "precode", [], ... + "dbMode", []); +end + +function series = bestSeries(T, fieldName) +series = emptySeries(); +if ~ismember(fieldName, string(T.Properties.VariableNames)) + return +end + +values = numericColumn(T.(char(fieldName))); +valid = isfinite(values); +if strcmp(fieldName, "BER_plot") + % BER is minimized; zero is omitted because the axis is logarithmic. + valid = valid & values > 0 & values <= 0.5; +elseif strcmp(fieldName, "NGMI") + valid = valid & values >= 0 & values <= 1.05; +else + valid = valid & values >= 0; +end + +candidate = T(valid, :); +values = values(valid); +if isempty(candidate) + return +end + +[groupId, ~] = findgroups(candidate.symbolrate_GBd); +keepIndex = zeros(max(groupId), 1); +for groupIdx = 1:max(groupId) + rowIndex = find(groupId == groupIdx); + if strcmp(fieldName, "BER_plot") + [~, localIndex] = min(values(rowIndex)); + else + [~, localIndex] = max(values(rowIndex)); + end + keepIndex(groupIdx) = rowIndex(localIndex(1)); +end + +candidate = candidate(keepIndex, :); +series.x = double(candidate.symbolrate_GBd); +series.y = values(keepIndex); +[series.x, order] = sort(series.x); +series.y = series.y(order); + +% Preserve the complete selected database row as data-tip metadata. The +% metadata therefore belongs to the plotted point, even when BER, NGMI, +% AIR, and NDR select different rows at the same baud rate. +series.ber = numericOrNaN(candidate, "BER_plot"); +series.ngmi = numericOrNaN(candidate, "NGMI"); +series.air = numericOrNaN(candidate, "AIR_Gbps"); +series.wavelength = numericOrNaN(candidate, "wavelength"); +series.precode = numericOrNaN(candidate, "precode"); +series.dbMode = numericOrNaN(candidate, "db_mode"); +series.ber = series.ber(order); +series.ngmi = series.ngmi(order); +series.air = series.air(order); +series.wavelength = series.wavelength(order); +series.precode = series.precode(order); +series.dbMode = series.dbMode(order); +end + +function T = addDerivedMetrics(T) +T.symbolrate_GBd = double(T.symbolrate) .* 1e-9; + +T.AIR_Gbps = numericOrNaN(T, "AIR") .* 1e-9; +gmi = numericOrNaN(T, "GMI"); +fallbackAir = gmi .* double(T.symbolrate) .* 1e-9; +grossRate = double(T.grossrate); +useFallback = ~isfinite(T.AIR_Gbps) | T.AIR_Gbps < 0 | ... + (isfinite(grossRate) & T.AIR_Gbps > grossRate .* 1.05e-9); +T.AIR_Gbps(useFallback) = fallbackAir(useFallback); +T.NGMI = numericOrNaN(T, "NGMI"); +end + +function h = plotSeries(ax, series, curve, lineStyle, marker, displayName) +h = plot(ax, series.x, series.y, ... + "LineStyle", lineStyle, ... + "Marker", marker, ... + "MarkerSize", 4, ... + "LineWidth", 1.35, ... + "Color", curve.color, ... + "MarkerFaceColor", curve.color, ... + "MarkerEdgeColor", curve.color, ... + "DisplayName", displayName); +h.DataTipTemplate.DataTipRows = [ ... + dataTipTextRow("X", series.x); ... + dataTipTextRow("Y", series.y); ... + dataTipTextRow("symbol rate [GBd]", series.x); ... + dataTipTextRow("BER", series.ber); ... + dataTipTextRow("NGMI", series.ngmi); ... + dataTipTextRow("AIR [Gb/s]", series.air); ... + dataTipTextRow("wavelength [nm]", series.wavelength); ... + dataTipTextRow("precoding", series.precode); ... + dataTipTextRow("db_mode", series.dbMode)]; +end + +function formatAxis(ax, yLabel, yLimits, xLimits) +set(ax, "FontSize", 8, "TickLabelInterpreter", "none"); +xlabel(ax, "Baud rate [GBd]"); +ylabel(ax, yLabel); +xlim(ax, xLimits); +xticks(ax, 100:15:220); +ylim(ax, yLimits); +grid(ax, "on"); +grid(ax, "minor"); +box(ax, "on"); +end + +function values = numericColumn(values) +if iscell(values) + values = string(values); +end +if isstring(values) || ischar(values) + values = str2double(values); +end +values = double(values); +end + +function values = numericOrNaN(T, fieldName) +if ismember(fieldName, string(T.Properties.VariableNames)) + values = numericColumn(T.(char(fieldName))); +else + values = NaN(height(T), 1); +end +values = values(:); +end + +function values = columnVector(values) +values = double(values(:)); +end + +function mask = equalizerMask(equalizerColumn, eqValue) +mask = lower(string(equalizerColumn)) == lower(string(eqValue)); +end diff --git a/projects/Advanced_DSP_for_400G_IMDD_experiments/Auswertung_JLT/final/FIGURE_NGMI_v2.m b/projects/Advanced_DSP_for_400G_IMDD_experiments/Auswertung_JLT/final/FIGURE_NGMI_v2.m index a690207..314f00e 100644 --- a/projects/Advanced_DSP_for_400G_IMDD_experiments/Auswertung_JLT/final/FIGURE_NGMI_v2.m +++ b/projects/Advanced_DSP_for_400G_IMDD_experiments/Auswertung_JLT/final/FIGURE_NGMI_v2.m @@ -161,6 +161,7 @@ for ti = 1:3 grid minor; box on; beautifyBERplot; yline(400,'HandleVisibility','off'); + legend end % === FIX FIGURE SIZE FOR TIKZ ========================================== diff --git a/projects/Diss/400G_revisit/CHECK_BEST_PREEMPHASIS_PRECODE_2KM_1310NM.m b/projects/Diss/400G_revisit/CHECK_BEST_PREEMPHASIS_PRECODE_2KM_1310NM.m new file mode 100644 index 0000000..97bedc7 --- /dev/null +++ b/projects/Diss/400G_revisit/CHECK_BEST_PREEMPHASIS_PRECODE_2KM_1310NM.m @@ -0,0 +1,241 @@ +%% Best pre-emphasis and precoding settings at 2 km / 1310 nm +% The four requested technique labels map to the stored equalizer classes: +% VNLE -> vnle +% MLSE -> VNLE + PF + MLSE (vnle_pf_mlse) +% DB tgt. -> VNLE DBt. + MLSE (vnle_db_mlse) +% ML based -> ML pre-EQ + Viterbi (ml_mlse) +% +% For every technique and PAM format, the globally lowest valid BER is +% selected across the available gross rates, db_mode 0/1 variants, and +% BER/BER_precoded variants. The resulting table reports binary settings: +% preemph = 0/1 and precode = 0/1. + +clear; clc; + +%% 1) Query the 2 km / 1310 nm normal-link data + +selectedFiberLengthKm = 2; +selectedWavelengthNm = 1310; +selectedRopAttenuation = 0; +selectedIsMpi = 0; +selectedPamLevels = [4 6 8]; +normalDbModes = [double(db_mode.no_db), double(db_mode.db_precoded)]; + +techniques = table( ... + ["VNLE"; "MLSE"; "DB tgt."; "ML based"], ... + ["vnle"; "vnle_pf_mlse"; "vnle_db_mlse"; "ml_mlse"], ... + 'VariableNames', ["technique", "algorithm_key"]); + +db = DBHandler( ... + "dataBase", "labor_highspeed", ... + "type", "mysql", ... + "server", "192.168.178.192", ... + "user", "silas", ... + "password", "silas"); +db.refresh(); + +fp = QueryFilter(); +fp.where("Runs", "fiber_length", "EQUALS", selectedFiberLengthKm); +fp.where("Runs", "wavelength", "EQUALS", selectedWavelengthNm); +fp.where("Runs", "rop_attenuation", "EQUALS", selectedRopAttenuation); +fp.where("Runs", "is_mpi", "EQUALS", selectedIsMpi); + +fields = db.getTableFieldNames('dashboard_ungrouped_alltime'); +fields = appendMissingFields(fields, {'Runs.precomp_amp'; 'Runs.is_mpi'}); +[rawData, query] = db.queryDB(fp, fields); +disp(query); + +data = cleanRows(rawData); +data = data(ismember(data.db_mode, normalDbModes) & ... + ismember(data.pam_level, selectedPamLevels), :); + +if ~ismember("precomp_amp", string(data.Properties.VariableNames)) + warning("check_best_settings:NoPrecompAmp", ... + "Runs.precomp_amp was not returned; using db_mode == 0 as preemph = 1."); + data.pre_emphasis = data.db_mode == double(db_mode.no_db); +else + data.pre_emphasis = derivePreEmphasis(data.precomp_amp, data.db_mode); +end + +%% 2) Build BER and BER_precoded candidates + +baseRows = data(isfinite(data.BER) & data.BER > 0, :); +baseRows.precode = zeros(height(baseRows), 1); +baseRows.BER_plot = baseRows.BER; +baseRows.algorithm_key = algorithmKeyFromEqualizer(baseRows.equalizer_structure); +baseRows = baseRows(baseRows.algorithm_key ~= "", :); + +if ismember("BER_precoded", string(data.Properties.VariableNames)) + precodedRows = data(isfinite(data.BER_precoded) & data.BER_precoded > 0, :); + precodedRows.precode = ones(height(precodedRows), 1); + precodedRows.BER_plot = precodedRows.BER_precoded; + precodedRows.algorithm_key = algorithmKeyFromEqualizer( ... + precodedRows.equalizer_structure); + precodedRows = precodedRows(precodedRows.algorithm_key ~= "", :); + metricRows = [baseRows; precodedRows]; +else + warning("check_best_settings:NoPrecodedBer", ... + "BER_precoded was not returned; only precode = 0 is available."); + metricRows = baseRows; +end + +fprintf("Candidate rows: %d\n", height(metricRows)); + +%% 3) Find the lowest BER and its binary settings + +numResults = height(techniques) * numel(selectedPamLevels); +best = repmat(struct( ... + "technique", "", ... + "algorithm_key", "", ... + "pam", NaN, ... + "BER", NaN, ... + "preemph", NaN, ... + "precode", NaN, ... + "db_mode", NaN, ... + "grossrate_Gbps", NaN, ... + "symbolrate_GBd", NaN), numResults, 1); + +resultIdx = 0; +for techniqueIdx = 1:height(techniques) + for pamIdx = 1:numel(selectedPamLevels) + resultIdx = resultIdx + 1; + algorithmKey = techniques.algorithm_key(techniqueIdx); + pamLevel = selectedPamLevels(pamIdx); + candidates = metricRows( ... + metricRows.algorithm_key == algorithmKey & ... + metricRows.pam_level == pamLevel, :); + + best(resultIdx).technique = techniques.technique(techniqueIdx); + best(resultIdx).algorithm_key = algorithmKey; + best(resultIdx).pam = pamLevel; + if isempty(candidates) + continue + end + + [~, bestRowIdx] = min(candidates.BER_plot); + row = candidates(bestRowIdx, :); + best(resultIdx).BER = row.BER_plot; + best(resultIdx).preemph = double(row.pre_emphasis); + best(resultIdx).precode = double(row.precode); + best(resultIdx).db_mode = double(row.db_mode); + best(resultIdx).grossrate_Gbps = double(row.grossrate) .* 1e-9; + best(resultIdx).symbolrate_GBd = double(row.symbolrate) .* 1e-9; + end +end + +bestRows = struct2table(best); +disp(bestRows); + +%% 4) Compact tables for direct use in the thesis configuration + +binaryTable = table(techniques.technique, ... + NaN(height(techniques), 1), NaN(height(techniques), 1), ... + NaN(height(techniques), 1), NaN(height(techniques), 1), ... + NaN(height(techniques), 1), NaN(height(techniques), 1), ... + 'VariableNames', ["technique", ... + "PAM4_preemph", "PAM4_precode", ... + "PAM6_preemph", "PAM6_precode", ... + "PAM8_preemph", "PAM8_precode"]); + +labelTable = table(techniques.technique, strings(height(techniques), 1), ... + strings(height(techniques), 1), strings(height(techniques), 1), ... + 'VariableNames', ["technique", "PAM4", "PAM6", "PAM8"]); + +for rowIdx = 1:height(bestRows) + techniqueIdx = find(techniques.algorithm_key == bestRows.algorithm_key(rowIdx), 1); + pamField = sprintf("PAM%d", bestRows.pam(rowIdx)); + binaryTable.(sprintf("%s_preemph", pamField))(techniqueIdx) = ... + bestRows.preemph(rowIdx); + binaryTable.(sprintf("%s_precode", pamField))(techniqueIdx) = ... + bestRows.precode(rowIdx); + + if isfinite(bestRows.BER(rowIdx)) + labelTable.(pamField)(techniqueIdx) = sprintf( ... + "preemph=%d, precode=%d (BER=%.3g)", ... + bestRows.preemph(rowIdx), bestRows.precode(rowIdx), ... + bestRows.BER(rowIdx)); + else + labelTable.(pamField)(techniqueIdx) = "no valid BER"; + end +end + +disp("Binary setting table:"); +disp(binaryTable); +disp("Settings with winning BER:"); +disp(labelTable); + +%% Local helpers + +function fields = appendMissingFields(fields, extraFields) +fields = cellstr(fields); +extraFields = cellstr(extraFields); +for idx = 1:numel(extraFields) + if ~any(strcmp(fields, extraFields{idx})) + fields{end+1, 1} = extraFields{idx}; %#ok + end +end +end + +function T = cleanRows(T) +numericFields = ["result_id", "run_id", "eq_id", "bitrate", ... + "grossrate", "symbolrate", "pam_level", "wavelength", ... + "fiber_length", "db_mode", "rop_attenuation", "precomp_amp", ... + "numBits", "numBitErr", "BER", "numBitErr_precoded", ... + "BER_precoded", "GMI", "AIR", "NGMI"]; +for fieldIdx = 1:numel(numericFields) + fieldName = numericFields(fieldIdx); + if ismember(fieldName, string(T.Properties.VariableNames)) + T.(char(fieldName)) = numericColumn(T.(char(fieldName))); + end +end +end + +function preEmphasis = derivePreEmphasis(precompAmp, dbMode) +preEmphasis = false(size(dbMode)); +validPrecomp = isfinite(precompAmp); +preEmphasis(validPrecomp) = precompAmp(validPrecomp) > -45; +missingPrecomp = ~validPrecomp; +preEmphasis(missingPrecomp) = ... + dbMode(missingPrecomp) == double(db_mode.no_db); +end + +function algorithmKey = algorithmKeyFromEqualizer(equalizerColumn) +eqNumeric = equalizerNumeric(equalizerColumn); +algorithmKey = strings(size(eqNumeric)); +algorithmKey(eqNumeric == double(equalizer_structure.vnle)) = "vnle"; +algorithmKey(eqNumeric == double(equalizer_structure.vnle_pf_mlse)) = ... + "vnle_pf_mlse"; +algorithmKey(eqNumeric == double(equalizer_structure.vnle_db_mlse)) = ... + "vnle_db_mlse"; +algorithmKey(eqNumeric == double(equalizer_structure.ml_mlse)) = "ml_mlse"; +end + +function eqNumeric = equalizerNumeric(equalizerColumn) +if isa(equalizerColumn, "equalizer_structure") || isnumeric(equalizerColumn) + eqNumeric = double(equalizerColumn); + return +end + +equalizerString = string(equalizerColumn); +eqNumeric = str2double(equalizerString); +enumNames = ["vnle", "vnle_pf_mlse", "vnle_db_mlse", "ml_mlse"]; +enumValues = [double(equalizer_structure.vnle), ... + double(equalizer_structure.vnle_pf_mlse), ... + double(equalizer_structure.vnle_db_mlse), ... + double(equalizer_structure.ml_mlse)]; +for idx = 1:numel(enumNames) + missingNumeric = isnan(eqNumeric); + eqNumeric(missingNumeric & equalizerString == enumNames(idx)) = ... + enumValues(idx); +end +end + +function values = numericColumn(values) +if iscell(values) + values = string(values); +end +if isstring(values) || ischar(values) + values = str2double(values); +end +values = double(values); +end diff --git a/projects/Diss/400G_revisit/FIGURE_EQ_NOISE_VS_BAUDRATE.m b/projects/Diss/400G_revisit/FIGURE_EQ_NOISE_VS_BAUDRATE.m index 54d0d88..9bc827b 100644 --- a/projects/Diss/400G_revisit/FIGURE_EQ_NOISE_VS_BAUDRATE.m +++ b/projects/Diss/400G_revisit/FIGURE_EQ_NOISE_VS_BAUDRATE.m @@ -35,10 +35,6 @@ fp.where('Runs', 'wavelength', SqlOperator.EQUALS, wavelength); fp.where('Runs', 'is_mpi', SqlOperator.EQUALS, is_mpi); fp.where('Runs', 'db_mode', 'LESS_THAN', 2); fp.where('Runs', 'rop_attenuation', SqlOperator.EQUALS, rop_attenuation); -% fp.where('Runs', 'symbolrate','EQUALS', 180e9); -% if ~isempty(baudrate_GBd) -% fp.where('Runs', 'symbolrate', SqlOperator.IN, baudrate_GBd*1e9); -% end fields = [db.getTableFieldNames('dashboard_ungrouped_alltime')]; fields = unique(fields, 'stable'); @@ -65,6 +61,12 @@ for runIndex = 1:numel(run_ids) runData = queryRunid(run_ids(runIndex), db); fsym = double(runData.symbolrate(1)); bitrate = double(runData.bitrate(1)); + symbolrate = double(runData.symbolrate(1)); + if double(runData.db_mode) == 0 || double(runData.db_mode) == 2 + ispreemph = 1; + elseif double(runData.db_mode) == 1 + ispreemph = 0; + end dsp_options.max_occurences = 20; dsp_options.start_occurence = 2; @@ -94,53 +96,54 @@ for runIndex = 1:numel(run_ids) "plotfinal", 0, ... "ideal_dfe", 1); - % eq_ = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",len_tr, ... - % "mu_dd",1e-1,"mu_tr",0.4,"order",50, ... - % "sps",2,"decide",0,"optmize_mus",1,"dd_mode",1, ... - % "adaption_technique","nlms","dc_tracking_mu",1.021e-05); - - - [equalized_signal, eq_noise] = eq_vnle.process(Scpe_sig, Symbols); - - - postfilter_order = 3; - pf = Postfilter("ncoeff", postfilter_order, "useBurg", 1); - [mlse_sig_sd,whitened_noise] = pf.process(equalized_signal, eq_noise); - - % mlse = MLSE( ... - % "DIR", [0 0], ... - % "duobinary_output", 0, ... - % "M", M, ... - % "trellis_states", PAMmapper(M, 0).levels); - % - % [results, ~] = vnle_postfilter_mlse( ... - % eq_vnle, pf, mlse, M, Scpe_sig, Symbols, Tx_bits, ... - % "precode_mode", db_mode.no_db, ... - % "showAnalysis", 0, ... - % "postFFE", [], ... - % "eth_style_symbol_mapping", 0); + % + dbt = 1; + if dbt + Symbols_ = Duobinary().encode(Symbols); + else + Symbols_ = Symbols; + end + [equalized_signal, eq_noise] = eq_vnle.process(Scpe_sig, Symbols_); eq_noise = eq_noise - mean(eq_noise.signal); - fig = figure(220+runIndex); - showEQNoisePSD(eq_noise, ... - "fignum", fig.Number, ... - "displayname", sprintf('%.0f Gbps: VNLE Noise', bitrate*1e-9), ... - "postfilter_taps", pf.coefficients, ... - "colormode", "qualitative"); - whitened_noise = whitened_noise - mean(whitened_noise.signal); - whitened_noise.spectrum("displayname", 'Whitened Noise', "fignum", fig.Number, "normalizeTo0dB", 0,"fft_length",4096,"normalizeToDC",0); + if ~dbt + postfilter_order = 3; + pf = Postfilter("ncoeff", postfilter_order, "useBurg", 1); + [mlse_sig_sd,whitened_noise] = pf.process(equalized_signal, eq_noise); + end + + fig = figure(220); + if ~dbt + showEQNoisePSD(eq_noise, ... + "fignum", fig.Number, ... + "displayname", sprintf('EEN: %.0f GBd; preemph: %d', symbolrate*1e-9,ispreemph), ... + "postfilter_taps", pf.coefficients, ... + "colormode", "qualitative","color",clr.Paired.lblue); + whitened_noise = whitened_noise - mean(whitened_noise.signal); + whitened_noise.spectrum("displayname", 'Whitened Noise', "fignum", fig.Number, "normalizeTo0dB", 0,"fft_length",4096,"normalizeToDC",0,"color",clr.Paired.dblue); + else + showEQNoisePSD(eq_noise, ... + "fignum", fig.Number, ... + "displayname", sprintf('EEN: %.0f GBd; preemph: %d', symbolrate*1e-9,ispreemph), ... + "colormode", "qualitative","color",clr.Paired.lblue); + end + + xlabel('Frequency in GHz'); + + ylabel('normalized to 0 dB'); + % title('Noise of soft decision signal (not MLSE)'); + grid on; + grid minor; + legend('show', 'Interpreter', 'none', 'Location', 'best'); + + % ylim([-70 -50]); max_fs = max(max_fs, eq_noise.fs); + xlim([-max_fs/2 max_fs/2]*1e-9); end -xlabel('Frequency in GHz'); -ylabel('normalized to 0 dB'); -title('Noise of soft decision signal (not MLSE)'); -grid on; -grid minor; -legend('show', 'Interpreter', 'none', 'Location', 'best'); -xlim([-max_fs/2 max_fs/2]*1e-9); -ylim([-20 0]); + + function signalOut = averageScopeSignals(scopeCell, averageSignals, gain) signalOut = scopeCell{1}; diff --git a/projects/Diss/400G_revisit/PLOT_AIR_BEST_CURVES_OVER_WAVELENGTH_10KM.m b/projects/Diss/400G_revisit/PLOT_AIR_BEST_CURVES_OVER_WAVELENGTH_10KM.m new file mode 100644 index 0000000..984131b --- /dev/null +++ b/projects/Diss/400G_revisit/PLOT_AIR_BEST_CURVES_OVER_WAVELENGTH_10KM.m @@ -0,0 +1,407 @@ +%% Maximum AIR over wavelength at 10 km +% One curve is shown for each selected EQ/PAM contender. For every +% wavelength, the row with the maximum AIR is retained across the available +% symbol rates and normal db_mode 0/1 variants. +% DBS uses db_mode = 2, equalizer_structure = db_encoded, and only results +% processed after 2026-01-01. + +clear; clc; + +%% 1) Configuration + +normalFiberLengthKm = 2; +duobinaryFiberLengthKm = 10; +selectedRopAttenuation = 0; +selectedIsMpi = 0; +duobinaryDateCutoff = datetime("2026-01-01 00:00:00"); + +% Thesis colors and curve/PAM mapping. +curves = struct; + +curves(1).name = "DBS + VNLE + MLSE (PAM-4)"; +curves(1).algorithm_key = "db_encoded"; +curves(1).pam = 4; +curves(1).color = clr.Paired.orange; +curves(1).marker = "square"; +curves(1).source = "duobinary"; +curves(1).usePrecodedBer = false; + +curves(2).name = "VNLE DBt. + MLSE"; +curves(2).algorithm_key = "vnle_db_mlse"; +curves(2).pam = 4; +curves(2).color = clr.Paired.blue; +curves(2).marker = "square"; +curves(2).source = "normal"; +curves(2).usePrecodedBer = true; + +% curves(3).name = "ML pre-EQ + Viterbi"; +% curves(3).algorithm_key = "ml_mlse"; +% curves(3).pam = 4; +% curves(3).color = clr.Paired.purple; +% curves(3).marker = "square"; +% curves(3).source = "normal"; +% curves(3).usePrecodedBer = true; + +curves(3).name = "VNLE + PF + MLSE"; +curves(3).algorithm_key = "vnle_pf_mlse"; +curves(3).pam = 6; +curves(3).color = clr.Paired.green; +curves(3).marker = "v"; +curves(3).source = "normal"; +curves(3).usePrecodedBer = false; + +% curves(5).name = "ML pre-EQ + Viterbi"; +% curves(5).algorithm_key = "ml_mlse"; +% curves(5).pam = 6; +% curves(5).color = clr.Paired.purple; +% curves(5).marker = "v"; +% curves(5).source = "normal"; +% curves(5).usePrecodedBer = true; + +curves(4).name = "DBS + VNLE + MLSE (PAM-6)"; +curves(4).algorithm_key = "db_encoded"; +curves(4).pam = 6; +curves(4).color = clr.Paired.orange; +curves(4).marker = "v"; +curves(4).source = "duobinary"; +curves(4).usePrecodedBer = false; + +curves(5).name = "VNLE + PF + MLSE"; +curves(5).algorithm_key = "vnle_pf_mlse"; +curves(5).pam = 8; +curves(5).color = clr.Paired.red; +curves(5).marker = "o"; +curves(5).source = "normal"; +curves(5).usePrecodedBer = false; + + + +%% 2) Query normal and DBS rows over all wavelengths + +db = DBHandler( ... + "dataBase", "labor_highspeed", ... + "type", "mysql", ... + "server", "192.168.178.192", ... + "user", "silas", ... + "password", "silas"); +db.refresh(); + +selectedFields = db.getTableFieldNames('dashboard_ungrouped_alltime'); + +normalRows = queryRows(db, selectedFields, normalFiberLengthKm, ... + selectedRopAttenuation, selectedIsMpi); +normalRows = cleanRows(normalRows); +normalRows = normalRows(ismember(normalRows.db_mode, ... + [double(db_mode.no_db), double(db_mode.db_precoded)]), :); +normalRows.algorithm_key = lower(string(normalRows.equalizer_structure)); + +duobinaryRows = queryRows(db, selectedFields, duobinaryFiberLengthKm, ... + selectedRopAttenuation, selectedIsMpi); +duobinaryRows = cleanRows(duobinaryRows); +duobinaryRows = duobinaryRows( ... + duobinaryRows.db_mode == double(db_mode.db_encoded), :); +duobinaryRows = duobinaryRows( ... + equalizerMask(duobinaryRows.equalizer_structure, ... + equalizer_structure.db_encoded), :); + +if ismember("date_of_processing", string(duobinaryRows.Properties.VariableNames)) + duobinaryRows.date_of_processing = ... + datetime(string(duobinaryRows.date_of_processing)); + duobinaryRows = duobinaryRows( ... + datetime(duobinaryRows.date_of_processing) > duobinaryDateCutoff, :); +else + warning("plot_air_wavelength:NoProcessingDate", ... + "date_of_processing was not returned; no DBS date filter was applied."); +end +duobinaryRows.algorithm_key = repmat("db_encoded", height(duobinaryRows), 1); + +normalRows = addAirMetric(normalRows); +duobinaryRows = addAirMetric(duobinaryRows); + +fprintf("Normal %g km rows: %d\n", normalFiberLengthKm, height(normalRows)); +fprintf("DBS %g km rows after date filter: %d\n", ... + duobinaryFiberLengthKm, height(duobinaryRows)); + +%% 3) Select maximum AIR per wavelength for every configured curve + +tp = TransmissionPerformance; +results = repmat(emptyResult(), numel(curves), 1); +for curveIdx = 1:numel(curves) + curve = curves(curveIdx); + if curve.source == "normal" + sourceRows = normalRows; + else + sourceRows = duobinaryRows; + end + + curveRows = sourceRows(sourceRows.pam_level == curve.pam & ... + sourceRows.algorithm_key == curve.algorithm_key, :); + curveRows.BER_plot = curveRows.BER; + if curve.usePrecodedBer && ismember("BER_precoded", ... + string(curveRows.Properties.VariableNames)) + usePrecoded = isfinite(curveRows.BER_precoded); + curveRows.BER_plot(usePrecoded) = curveRows.BER_precoded(usePrecoded); + end + + measuredNgmi = numericOrNaN(curveRows, "NGMI"); + measuredBer = numericOrNaN(curveRows, "BER_plot"); + grossRate = numericOrNaN(curveRows, "grossrate"); + measuredNgmi(~isfinite(measuredNgmi) | measuredNgmi < 0 | ... + measuredNgmi > 1.05) = NaN; + measuredBer(~isfinite(measuredBer) | measuredBer <= 0 | ... + measuredBer > 0.5) = NaN; + + ndr = tp.calculateNetRate(grossRate, ... + "NGMI", measuredNgmi, ... + "BER", measuredBer); + curveRows.NDR_O_FEC = columnVector(ndr.O_FEC.NetRate) .* 1e-9; + + bestRows = bestMetricByWavelength(curveRows, "AIR_Gbps"); + bestOfecRows = bestMetricByWavelength(curveRows, "NDR_O_FEC"); + + results(curveIdx).name = curve.name; + results(curveIdx).color = curve.color; + results(curveIdx).marker = curve.marker; + results(curveIdx).pam = curve.pam; + results(curveIdx).air = makeMetricSeries( ... + bestRows, "AIR_Gbps", curve.usePrecodedBer); + results(curveIdx).ofec = makeMetricSeries( ... + bestOfecRows, "NDR_O_FEC", curve.usePrecodedBer); + + fprintf("%s, PAM-%d: AIR=%d, O-FEC=%d wavelength points\n", ... + curve.name, curve.pam, ... + numel(results(curveIdx).air.x), ... + numel(results(curveIdx).ofec.x)); +end + +%% 4) Plot maximum AIR and O-FEC NDR over wavelength + +fig = figure(435); clf; +t = tiledlayout(fig, 1, 2, ... + "TileSpacing", "compact", ... + "Padding", "compact"); + +axAir = nexttile(t, 1); +airHandles = plotMetricPanel(axAir, results, "air", ... + "AIR [Gb/s]", "a) Maximum AIR"); + +axOfec = nexttile(t, 2); +ofecHandles = plotMetricPanel(axOfec, results, "ofec", ... + "O-FEC NDR [Gb/s]", "b) O-FEC"); + +allWavelengthCells = cell(numel(results), 1); +for curveIdx = 1:numel(results) + allWavelengthCells{curveIdx} = [ ... + results(curveIdx).air.x(:); ... + results(curveIdx).ofec.x(:)]; +end +allWavelengths = vertcat(allWavelengthCells{:}); +allWavelengths = allWavelengths(isfinite(allWavelengths)); +if ~isempty(allWavelengths) + xLimits = [min(allWavelengths) - 1, max(allWavelengths) + 1]; + for ax = [axAir, axOfec] + xlim(ax, xLimits); + xticks(ax, unique(allWavelengths)); + end +end + +validOfecMask = isgraphics(ofecHandles); +if any(validOfecMask) + legend(axOfec, ofecHandles(validOfecMask), ... + makeLegendNames(results(validOfecMask)), ... + "Location", "southoutside", ... + "NumColumns", min(3, nnz(validOfecMask)), ... + "Interpreter", "none"); +elseif any(isgraphics(airHandles)) + validAirMask = isgraphics(airHandles); + legend(axAir, airHandles(validAirMask), ... + makeLegendNames(results(validAirMask)), ... + "Location", "southoutside", ... + "NumColumns", min(3, nnz(validAirMask)), ... + "Interpreter", "none"); +end + +sgtitle(t, sprintf("Best AIR and O-FEC results over wavelength, %.0f km", ... + normalFiberLengthKm)); +set(fig, "Position", 1e3 .* [0.08 0.42 1.75 0.48]); + +%% Local helpers + +function T = queryRows(db, fields, fiberLengthKm, ropAttenuation, isMpi) +fp = QueryFilter(); +fp.where("Runs", "fiber_length", "EQUALS", fiberLengthKm); +fp.where("Runs", "rop_attenuation", "EQUALS", ropAttenuation); +fp.where("Runs", "is_mpi", "EQUALS", isMpi); +[T, query] = db.queryDB(fp, fields); +disp(query); +end + +function T = cleanRows(T) +numericFields = ["result_id", "run_id", "eq_id", "bitrate", ... + "grossrate", "symbolrate", "pam_level", "wavelength", ... + "fiber_length", "db_mode", "rop_attenuation", "numBits", ... + "numBitErr", "BER", "numBitErr_precoded", "BER_precoded", ... + "GMI", "AIR", "NGMI"]; +for fieldIdx = 1:numel(numericFields) + fieldName = numericFields(fieldIdx); + if ismember(fieldName, string(T.Properties.VariableNames)) + T.(char(fieldName)) = numericColumn(T.(char(fieldName))); + end +end +T.equalizer_structure = lower(string(T.equalizer_structure)); +end + +function T = addAirMetric(T) +T.AIR_Gbps = numericOrNaN(T, "AIR") .* 1e-9; +gmi = numericOrNaN(T, "GMI"); +symbolrate = numericOrNaN(T, "symbolrate"); +grossrate = numericOrNaN(T, "grossrate"); +fallbackAir = gmi .* symbolrate .* 1e-9; +useFallback = ~isfinite(T.AIR_Gbps) | T.AIR_Gbps < 0 | ... + (isfinite(grossrate) & T.AIR_Gbps > grossrate .* 1.05e-9); +T.AIR_Gbps(useFallback) = fallbackAir(useFallback); +end + +function bestRows = bestMetricByWavelength(T, metricField) +if isempty(T) + bestRows = T; + return +end + +values = numericOrNaN(T, metricField); +valid = isfinite(T.wavelength) & isfinite(values) & values >= 0; +candidate = T(valid, :); +values = values(valid); +if isempty(candidate) + bestRows = candidate; + return +end + +[groupId, ~] = findgroups(candidate.wavelength); +keepIdx = zeros(max(groupId), 1); +for groupIdx = 1:max(groupId) + rowIdx = find(groupId == groupIdx); + [~, localIdx] = max(values(rowIdx)); + keepIdx(groupIdx) = rowIdx(localIdx(1)); +end +bestRows = sortrows(candidate(keepIdx, :), "wavelength"); +end + +function series = makeMetricSeries(T, metricField, usePrecodedBer) +series = emptyMetricSeries(); +if isempty(T) + return +end + +series.x = numericOrNaN(T, "wavelength"); +series.y = numericOrNaN(T, metricField); +series.air = numericOrNaN(T, "AIR_Gbps"); +series.ngmi = numericOrNaN(T, "NGMI"); +series.ber = numericOrNaN(T, "BER_plot"); +series.symbolrate = numericOrNaN(T, "symbolrate") .* 1e-9; +series.grossrate = numericOrNaN(T, "grossrate") .* 1e-9; +series.precoding = repmat(double(usePrecodedBer), height(T), 1); +series.dbMode = numericOrNaN(T, "db_mode"); +end + +function series = emptyMetricSeries() +series = struct( ... + "x", [], ... + "y", [], ... + "air", [], ... + "ngmi", [], ... + "ber", [], ... + "symbolrate", [], ... + "grossrate", [], ... + "precoding", [], ... + "dbMode", []); +end + +function handles = plotMetricPanel(ax, results, seriesField, yLabel, panelTitle) +hold(ax, "on"); +handles = gobjects(numel(results), 1); +for curveIdx = 1:numel(results) + result = results(curveIdx); + series = result.(seriesField); + if isempty(series.x) + continue + end + + handles(curveIdx) = plot(ax, series.x, series.y, ... + "LineStyle", "-", ... + "Marker", result.marker, ... + "MarkerSize", 5, ... + "LineWidth", 1.35, ... + "Color", result.color, ... + "MarkerFaceColor", result.color, ... + "MarkerEdgeColor", result.color, ... + "DisplayName", result.name); + + handles(curveIdx).DataTipTemplate.DataTipRows = [ ... + dataTipTextRow(yLabel, series.y); ... + dataTipTextRow("AIR [Gb/s]", series.air); ... + dataTipTextRow("NGMI", series.ngmi); ... + dataTipTextRow("BER", series.ber); ... + dataTipTextRow("Symbol rate [GBd]", series.symbolrate); ... + dataTipTextRow("Gross rate [Gb/s]", series.grossrate); ... + dataTipTextRow("precoding", series.precoding); ... + dataTipTextRow("db_mode", series.dbMode)]; +end + +set(ax, "FontSize", 9, "TickLabelInterpreter", "none"); +xlabel(ax, "Wavelength [nm]"); +ylabel(ax, yLabel); +title(ax, panelTitle); +grid(ax, "on"); +grid(ax, "minor"); +box(ax, "on"); +end + +function names = makeLegendNames(results) +names = strings(numel(results), 1); +for idx = 1:numel(results) + if contains(results(idx).name, "PAM-") + names(idx) = results(idx).name; + else + names(idx) = results(idx).name + ... + " (PAM-" + string(results(idx).pam) + ")"; + end +end +end + +function result = emptyResult() +result = struct( ... + "name", "", ... + "color", [0 0 0], ... + "marker", "o", ... + "pam", NaN, ... + "air", emptyMetricSeries(), ... + "ofec", emptyMetricSeries()); +end + +function values = numericColumn(values) +if iscell(values) + values = string(values); +end +if isstring(values) || ischar(values) + values = str2double(values); +end +values = double(values); +end + +function values = numericOrNaN(T, fieldName) +if ismember(fieldName, string(T.Properties.VariableNames)) + values = numericColumn(T.(char(fieldName))); +else + values = NaN(height(T), 1); +end +values = values(:); +end + +function values = columnVector(values) +values = double(values(:)); +end + +function mask = equalizerMask(equalizerColumn, eqValue) +mask = lower(string(equalizerColumn)) == lower(string(eqValue)); +end diff --git a/projects/Diss/400G_revisit/PLOT_BER_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m b/projects/Diss/400G_revisit/PLOT_BER_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m index dc57fc4..02e0b13 100644 --- a/projects/Diss/400G_revisit/PLOT_BER_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m +++ b/projects/Diss/400G_revisit/PLOT_BER_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m @@ -33,7 +33,7 @@ db.refresh(); fp = QueryFilter(); fp.where('Runs', 'fiber_length', 'EQUALS', selectedFiberLengthKm); -fp.where('Runs', 'wavelength', 'EQUALS', selectedWavelengthNm); +% fp.where('Runs', 'wavelength', 'EQUALS', selectedWavelengthNm); if ~isempty(selectedRopAttenuation) fp.where('Runs', 'rop_attenuation', 'EQUALS', selectedRopAttenuation); end @@ -141,7 +141,7 @@ for pamIdx = 1:numel(selectedPamLevels) algoData = sortrows(bestPlotData(rowMask, :), "grossrate_Gbps"); if showRawEntries - scatter(ax, algoData.grossrate_Gbps, algoData.BER_plot, ... + rawHandle = scatter(ax, algoData.grossrate_Gbps, algoData.BER_plot, ... 9, ... "Marker", ".", ... "MarkerEdgeColor", style.color, ... @@ -149,10 +149,14 @@ for pamIdx = 1:numel(selectedPamLevels) "MarkerEdgeAlpha", 0.25, ... "MarkerFaceAlpha", 0.25, ... "HandleVisibility", "off"); + rawHandle.DataTipTemplate.DataTipRows = [ ... + dataTipTextRow("Gross rate [Gb/s]", algoData.grossrate_Gbps); ... + dataTipTextRow("BER", algoData.BER_plot); ... + dataTipTextRow("db_mode", algoData.db_mode)]; end if showBestLine - plot(ax, algoData.grossrate_Gbps, algoData.BER_plot, ... + bestHandle = plot(ax, algoData.grossrate_Gbps, algoData.BER_plot, ... "LineStyle", style.lineStyle, ... "Marker", style.marker, ... "MarkerSize", 5, ... @@ -161,6 +165,10 @@ for pamIdx = 1:numel(selectedPamLevels) "MarkerFaceColor", style.markerFaceColor, ... "MarkerEdgeColor", style.color, ... "DisplayName", style.name); + bestHandle.DataTipTemplate.DataTipRows = [ ... + dataTipTextRow("Gross rate [Gb/s]", algoData.grossrate_Gbps); ... + dataTipTextRow("BER", algoData.BER_plot); ... + dataTipTextRow("db_mode", algoData.db_mode)]; end end diff --git a/projects/Diss/400G_revisit/PLOT_DUOBINARY_DETECTION_BER_VS_BITRATE.m b/projects/Diss/400G_revisit/PLOT_DUOBINARY_DETECTION_BER_VS_BITRATE.m index f2a2890..6c20474 100644 --- a/projects/Diss/400G_revisit/PLOT_DUOBINARY_DETECTION_BER_VS_BITRATE.m +++ b/projects/Diss/400G_revisit/PLOT_DUOBINARY_DETECTION_BER_VS_BITRATE.m @@ -81,7 +81,7 @@ if isempty(plotData) return end -plotData.bitrate_Gbps = plotData.bitrate .* 1e-9; +plotData.bitrate_Gbps = plotData.grossrate .* 1e-9; plotData = sortrows(plotData, ... ["pam_level", "wavelength", "detection_type", "bitrate_Gbps", "run_id"]); diff --git a/projects/Diss/400G_revisit/PLOT_NGMI_AIR_GMI_RATES_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m b/projects/Diss/400G_revisit/PLOT_NGMI_AIR_GMI_RATES_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m index e387475..32d7832 100644 --- a/projects/Diss/400G_revisit/PLOT_NGMI_AIR_GMI_RATES_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m +++ b/projects/Diss/400G_revisit/PLOT_NGMI_AIR_GMI_RATES_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m @@ -4,7 +4,7 @@ % algorithm and is restricted to the post-2026 processing results. % % The figure has one column per PAM format and one row per metric: -% BER, NGMI, GMI, AIR, NGMI-based SD+HD NDR, and the best BER-based NDR. +% BER, NGMI, GMI, AIR, and all requested FEC NDR curves. % NDR values are calculated from the measured BER/NGMI using % TransmissionPerformance. @@ -13,7 +13,7 @@ clear; clc; %% 1) Query data selectedPamLevels = [4, 6, 8]; -selectedFiberLengthKm = 10; +selectedFiberLengthKm = 2; selectedWavelengthNm = 1310; selectedRopAttenuation = 0; selectedIsMpi = 0; @@ -21,7 +21,7 @@ selectedIsMpi = 0; normalDbModes = [double(db_mode.no_db), double(db_mode.db_precoded)]; duobinaryDbMode = double(db_mode.db_encoded); -showRawEntries = false; +showRawEntries = true; % set true to overlay all candidate rows maxBerForPlot = 0.5; algoStyles = defaultAlgorithmStyles(); @@ -76,6 +76,9 @@ end if ismember("date_of_processing", string(duobinaryRows.Properties.VariableNames)) duobinaryRows.date_of_processing = datetime(string(duobinaryRows.date_of_processing)); duobinaryRows = duobinaryRows(datetime(duobinaryRows.date_of_processing)>datetime("2026-01-01 00:00:00"),:); + % Keep the table variable type compatible with the normal rows before + % concatenating both result sets. + duobinaryRows.date_of_processing = string(duobinaryRows.date_of_processing); else warning("plot_best_metrics:NoProcessingDate", ... "date_of_processing was not returned; no duobinary date filtering was applied."); @@ -94,7 +97,8 @@ plotData = plotData(plotData.symbolrate_GBd > 0 & ... isfinite(plotData.symbolrate_GBd), :); fprintf("Remaining metric rows: %d\n", height(plotData)); -disp(groupcounts(plotData, ["algorithm_key", "pam_level", "precode"])); +disp(groupcounts(plotData, ... + ["algorithm_key", "pam_level", "pre_emphasis", "precode"])); %% 3) Calculate BER-/NGMI-dependent net rates @@ -108,30 +112,48 @@ ber(~isfinite(ber) | ber <= 0 | ber > maxBerForPlot) = NaN; ndr = tp.calculateNetRate(grossRate, "NGMI", ngmi, "BER", ber); plotData.NDR_SDHD = columnVector(ndr.SDHD.NetRate) .* 1e-9; - -berBasedRates = [ ... - columnVector(ndr.STAIR.NetRate); ... - columnVector(ndr.HD.NetRate); ... - columnVector(ndr.KP4.NetRate); ... - columnVector(ndr.KP4_hamming.NetRate); ... - columnVector(ndr.O_FEC.NetRate)]; -plotData.NDR_BER_BEST = max(reshape(berBasedRates, height(plotData), []), [], 2, "omitnan") .* 1e-9; +plotData.NDR_STAIR = columnVector(ndr.STAIR.NetRate) .* 1e-9; +plotData.NDR_HD = columnVector(ndr.HD.NetRate) .* 1e-9; +plotData.NDR_KP4 = columnVector(ndr.KP4.NetRate) .* 1e-9; +plotData.NDR_KP4_HAMMING = columnVector(ndr.KP4_hamming.NetRate) .* 1e-9; +plotData.NDR_O_FEC = columnVector(ndr.O_FEC.NetRate) .* 1e-9; %% 4) Keep the best row for every plotted metric and group -metricDefinitions = struct( ... - "field", {"BER_plot", "NGMI", "GMI", "AIR_Gbps", "NDR_SDHD", "NDR_BER_BEST"}, ... - "label", {"BER", "NGMI", "GMI [bit/sym]", "AIR [Gb/s]", ... - "SD+HD NDR [Gb/s]", "Best BER-FEC NDR [Gb/s]"}, ... - "scale", {"log", "linear", "linear", "linear", "linear", "linear"}); +metricDefinitions = struct; +metricDefinitions(1).fields = "BER_plot"; +metricDefinitions(1).label = "BER"; +metricDefinitions(1).seriesLabels = "BER"; +metricDefinitions(1).seriesLineStyles = "-"; +metricDefinitions(2).fields = "NGMI"; +metricDefinitions(2).label = "NGMI"; +metricDefinitions(2).seriesLabels = "NGMI"; +metricDefinitions(2).seriesLineStyles = "-"; +metricDefinitions(3).fields = "GMI"; +metricDefinitions(3).label = "GMI [bit/sym]"; +metricDefinitions(3).seriesLabels = "GMI"; +metricDefinitions(3).seriesLineStyles = "-"; +metricDefinitions(4).fields = "AIR_Gbps"; +metricDefinitions(4).label = "AIR [Gb/s]"; +metricDefinitions(4).seriesLabels = "AIR"; +metricDefinitions(4).seriesLineStyles = "-"; +metricDefinitions(5).fields = ["NDR_SDHD", "NDR_STAIR", "NDR_HD", ... + "NDR_KP4", "NDR_KP4_HAMMING", "NDR_O_FEC"]; +metricDefinitions(5).label = "NDR [Gb/s]"; +metricDefinitions(5).seriesLabels = ["SD+HD", "Staircase", "HD-FEC", ... + "KP4", "KP4 + Hamming", "O-FEC"]; +metricDefinitions(5).seriesLineStyles = ["-"; "--"; ":"; "-."; "-"; "--"]; bestMetricData = cell(numel(metricDefinitions), 1); for metricIdx = 1:numel(metricDefinitions) - bestMetricData{metricIdx} = bestMetricRows(plotData, ... - metricDefinitions(metricIdx).field); + bestMetricData{metricIdx} = cell(numel(metricDefinitions(metricIdx).fields), 1); + for seriesIdx = 1:numel(metricDefinitions(metricIdx).fields) + bestMetricData{metricIdx}{seriesIdx} = bestMetricRows(plotData, ... + metricDefinitions(metricIdx).fields(seriesIdx)); + end end -availableStyles = algoStyles(hasAlgorithmRows(plotData, algoStyles), :); +availableStyles = algoStyles(hasPlotStyleRows(plotData, algoStyles), :); if isempty(availableStyles) warning("plot_best_metrics:NoSelectedAlgorithms", ... "None of the configured algorithm styles match the queried rows."); @@ -147,53 +169,60 @@ t = tiledlayout(fig, numel(metricDefinitions), numel(selectedPamLevels), ... for metricIdx = 1:numel(metricDefinitions) metric = metricDefinitions(metricIdx); - metricData = bestMetricData{metricIdx}; for pamIdx = 1:numel(selectedPamLevels) selectedPamLevel = selectedPamLevels(pamIdx); ax = nexttile(t); hold(ax, "on"); - pamMask = metricData.pam_level == selectedPamLevel; + for seriesIdx = 1:numel(metric.fields) + metricData = bestMetricData{metricIdx}{seriesIdx}; + metricField = metric.fields(seriesIdx); + pamMask = metricData.pam_level == selectedPamLevel; - for styleIdx = 1:height(availableStyles) - style = availableStyles(styleIdx, :); - rowMask = pamMask & metricData.algorithm_key == style.algorithm_key; - if ~any(rowMask) - continue - end + for styleIdx = 1:height(availableStyles) + style = availableStyles(styleIdx, :); + rowMask = pamMask & ... + metricData.algorithm_key == style.algorithm_key & ... + metricData.pre_emphasis == style.pre_emphasis; + if ~any(rowMask) + continue + end - algoData = sortrows(metricData(rowMask, :), "symbolrate_GBd"); - y = algoData.(metric.field); - valid = isfinite(y); - if ~any(valid) - continue - end + algoData = sortrows(metricData(rowMask, :), "symbolrate_GBd"); + y = algoData.(char(metricField)); + valid = isfinite(y); + if ~any(valid) + continue + end - if showRawEntries - scatter(ax, algoData.symbolrate_GBd(valid), y(valid), ... - 9, ... - "Marker", ".", ... + if isequal(showRawEntries, true) + scatter(ax, algoData.symbolrate_GBd(valid), y(valid), ... + 9, ... + "Marker", ".", ... + "MarkerEdgeColor", style.color, ... + "MarkerFaceColor", style.color, ... + "MarkerEdgeAlpha", 0.25, ... + "MarkerFaceAlpha", 0.25, ... + "HandleVisibility", "off"); + end + + plot(ax, algoData.symbolrate_GBd(valid), y(valid), ... + "LineStyle", metric.seriesLineStyles(seriesIdx), ... + "Marker", style.marker, ... + "MarkerSize", 4, ... + "LineWidth", 1.35, ... + "Color", style.color, ... + "MarkerFaceColor", style.markerFaceColor, ... "MarkerEdgeColor", style.color, ... - "MarkerFaceColor", style.color, ... - "MarkerEdgeAlpha", 0.25, ... - "MarkerFaceAlpha", 0.25, ... - "HandleVisibility", "off"); + "DisplayName", style.displayName); end - - plot(ax, algoData.symbolrate_GBd(valid), y(valid), ... - "LineStyle", style.lineStyle, ... - "Marker", style.marker, ... - "MarkerSize", 4, ... - "LineWidth", 1.35, ... - "Color", style.color, ... - "MarkerFaceColor", style.markerFaceColor, ... - "MarkerEdgeColor", style.color, ... - "DisplayName", style.name); end formatMetricAxis(ax, metric, selectedPamLevel); if metricIdx == 1 && pamIdx == 1 legend(ax, "Location", "southwest", "Interpreter", "none"); + elseif metricIdx == numel(metricDefinitions) && pamIdx == 1 + addNdrLegend(ax, metric.seriesLabels, metric.seriesLineStyles); end end end @@ -229,17 +258,27 @@ values = double(values(:)); end function styles = defaultAlgorithmStyles() -styles = table( ... - ["vnle"; "vnle_pf_mlse"; "vnle_db_mlse"; "ml_mlse"; "db_encoded"], ... - ["VNLE"; "VNLE + PF + MLSE"; "VNLE DBt. + MLSE"; ... - "ML pre-EQ + Viterbi"; "DBS + VNLE + MLSE"], ... - ["o"; "square"; "diamond"; "^"; "v"], ... - ["-"; "-"; "-"; "-"; "-"], ... - ["w"; "w"; "w"; "w"; "w"], ... - [clr.Paired.red; clr.Paired.green; clr.Paired.blue; ... - clr.Paired.purple; clr.Paired.orange], ... - 'VariableNames', ["algorithm_key", "name", "marker", ... - "lineStyle", "markerFaceColor", "color"]); +algorithmKey = repelem(["vnle"; "vnle_pf_mlse"; "vnle_db_mlse"; ... + "ml_mlse"; "db_encoded"], 2, 1); +name = repelem(["VNLE"; "VNLE + PF + MLSE"; "VNLE DBt. + MLSE"; ... + "ML pre-EQ + Viterbi"; "DBS + VNLE + MLSE"], 2, 1); +preEmphasis = repmat([false; true], 5, 1); +preEmphasisLabel = repmat(["w/o pre-emph."; "w/ pre-emph."], 5, 1); +preEmphasisLabel(algorithmKey == "db_encoded") = "duobinary"; + +styles = table(algorithmKey, name, preEmphasis, preEmphasisLabel, ... + repelem(["o"; "square"; "diamond"; "^"; "v"], 2, 1), ... + repmat("-", 10, 1), ... + repmat("w", 10, 1), ... + [clr.Paired.lred; clr.Paired.dred; ... + clr.Paired.lgreen; clr.Paired.dgreen; ... + clr.Paired.lblue; clr.Paired.dblue; ... + clr.Paired.llila; clr.Paired.dlila; ... + clr.Paired.lorange; clr.Paired.dorange], ... + 'VariableNames', ["algorithm_key", "name", "pre_emphasis", ... + "preEmphasisLabel", "marker", "lineStyle", ... + "markerFaceColor", "color"]); +styles.displayName = styles.name + "; " + styles.preEmphasisLabel; end function plotData = buildNormalMetricRows(data) @@ -247,6 +286,7 @@ baseRows = data(isfinite(data.BER), :); baseRows.precode = false(height(baseRows), 1); baseRows.BER_plot = baseRows.BER; baseRows.algorithm_key = algorithmKeyFromEqualizer(baseRows.equalizer_structure); +baseRows.pre_emphasis = baseRows.db_mode == double(db_mode.db_precoded); baseRows = baseRows(baseRows.algorithm_key ~= "", :); if ismember("BER_precoded", string(data.Properties.VariableNames)) @@ -255,6 +295,7 @@ if ismember("BER_precoded", string(data.Properties.VariableNames)) precodedRows.BER_plot = precodedRows.BER_precoded; precodedRows.algorithm_key = algorithmKeyFromEqualizer( ... precodedRows.equalizer_structure); + precodedRows.pre_emphasis = precodedRows.db_mode == double(db_mode.db_precoded); precodedRows = precodedRows(precodedRows.algorithm_key ~= "", :); plotData = [baseRows; precodedRows]; else @@ -269,6 +310,7 @@ plotData = data(isfinite(data.BER), :); plotData.precode = false(height(plotData), 1); plotData.BER_plot = plotData.BER; plotData.algorithm_key = repmat("db_encoded", height(plotData), 1); +plotData.pre_emphasis = false(height(plotData), 1); end function plotData = addDerivedMetrics(plotData) @@ -343,7 +385,7 @@ if isempty(candidateData) return end -groupVars = ["pam_level", "algorithm_key", "symbolrate_GBd"]; +groupVars = ["pam_level", "algorithm_key", "pre_emphasis", "symbolrate_GBd"]; groupId = findgroups(candidateData(:, groupVars)); keepIdx = zeros(max(groupId), 1); for curGroup = 1:max(groupId) @@ -359,10 +401,11 @@ end bestData = sortrows(candidateData(keepIdx, :), groupVars); end -function keep = hasAlgorithmRows(data, algoStyles) +function keep = hasPlotStyleRows(data, algoStyles) keep = false(height(algoStyles), 1); for idx = 1:height(algoStyles) - keep(idx) = any(data.algorithm_key == algoStyles.algorithm_key(idx)); + keep(idx) = any(data.algorithm_key == algoStyles.algorithm_key(idx) & ... + data.pre_emphasis == algoStyles.pre_emphasis(idx)); end end @@ -376,7 +419,7 @@ grid(ax, "on"); grid(ax, "minor"); box(ax, "on"); -switch metric.field +switch metric.fields(1) case "BER_plot" set(ax, "YScale", "log"); ylim(ax, [1e-5 0.2]); @@ -405,3 +448,15 @@ else title(ax, sprintf("PAM-%d", pamLevel)); end end + +function addNdrLegend(ax, seriesLabels, seriesLineStyles) +legendHandles = gobjects(numel(seriesLabels), 1); +for idx = 1:numel(seriesLabels) + legendHandles(idx) = plot(ax, NaN, NaN, ... + "Color", [0.2 0.2 0.2], ... + "LineStyle", seriesLineStyles(idx), ... + "LineWidth", 1.35, ... + "DisplayName", seriesLabels(idx)); +end +legend(ax, legendHandles, "Location", "southwest", "Interpreter", "none"); +end diff --git a/projects/Diss/400G_revisit/PLOT_ROP_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m b/projects/Diss/400G_revisit/PLOT_ROP_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m index 4f84fc1..58d743b 100644 --- a/projects/Diss/400G_revisit/PLOT_ROP_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m +++ b/projects/Diss/400G_revisit/PLOT_ROP_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m @@ -9,9 +9,9 @@ clear; clc; %% 1) Query data selectedPamLevels = [4, 6, 8]; -selectedFiberLengthKm = 1; +selectedFiberLengthKm = 10; selectedWavelengthNm = 1310; -selectedBitrateGbps = 360; +selectedBitrateGbps = 420; selectedIsMpi = 0; % set [] to use all entries maxPowerPdIn = []; % set a numeric limit to enable diff --git a/projects/Diss/400G_revisit/PLOT_WAVELENGTH_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m b/projects/Diss/400G_revisit/PLOT_WAVELENGTH_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m index a9657e2..78d05aa 100644 --- a/projects/Diss/400G_revisit/PLOT_WAVELENGTH_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m +++ b/projects/Diss/400G_revisit/PLOT_WAVELENGTH_BEST_ALGOS_WITH_DUOBINARY_SIGNALING.m @@ -9,7 +9,7 @@ clear; clc; %% 1) Query data selectedPamLevels = [4, 6, 8]; -selectedFiberLengthKm = 5; +selectedFiberLengthKm = 10; selectedBitratesGbps = 300:30:480; % set [] to use all available bitrates selectedRopAttenuation = 0; % set [] to use all ROP attenuation values selectedIsMpi = 0; % set [] to use all entries diff --git a/projects/Diss/400G_revisit/RX_sprectra.m b/projects/Diss/400G_revisit/RX_sprectra.m index bb39f77..0b42458 100644 --- a/projects/Diss/400G_revisit/RX_sprectra.m +++ b/projects/Diss/400G_revisit/RX_sprectra.m @@ -8,7 +8,7 @@ dsp_options.recipe = @dsp_400g_recipe; dsp_options.append_to_db = false; % dsp_options.append_mpi_reduction_db = false; dsp_options.start_occurence = 1; -dsp_options.max_occurences = 3; +dsp_options.max_occurences = 16; dsp_options.debug_plots = false; @@ -33,65 +33,148 @@ db = DBHandler("dataBase", [dsp_options.dataBase], ... "password", dsp_options.password); %% Load normal Signal w/o preemphasis -fp = QueryFilter(); -fp.where('Runs','fiber_length','EQUALS', 10); -fp.where('Runs','wavelength','EQUALS', 1310); -fp.where('Runs','bitrate','EQUALS', 420e9); -fp.where('Runs','pam_level','EQUALS', 4); -fp.where('Runs','rop_attenuation','EQUALS', 0); -fp.where('Runs','is_mpi','EQUALS', 0); -fp.where('Runs', 'db_mode','EQUALS', 0); -fields = db.getTableFieldNames('Runs'); -[dataTable, query] = db.queryDB(fp, fields); +cols = cbrewer2('Spectral',6); +cols = linspecer(2); +len = [1 2 3 5 6 8 10]; +idx = 1; +for lambda = 1302%[1297.5] + + fp = QueryFilter(); + len = 10; + fp.where('Runs','fiber_length','EQUALS', 10); % 1 2 3 5 6 8 10 + fp.where('Runs','wavelength','EQUALS', lambda); % + fp.where('Runs','bitrate','EQUALS', 450e9); + fp.where('Runs','pam_level','EQUALS', 4); + fp.where('Runs','rop_attenuation','EQUALS', 0); + fp.where('Runs','is_mpi','EQUALS', 0); + fp.where('Runs', 'db_mode','EQUALS', 0); + + fields = db.getTableFieldNames('Runs'); + [dataTable, query] = db.queryDB(fp, fields); + + [~, Symbols_preemph, S, ~] = loadAndSyncRunSignals(dataTable(1,:), dsp_options); + + average_signals = 1; + if average_signals + Scpe_sig_avg = S{1}; + scope_mean = zeros(size(S{1}.signal)); + for n=1:numel(S) + scope_mean = scope_mean + S{n}.signal; + end + scope_mean = scope_mean ./ n; + Scpe_sig_avg.signal = scope_mean; + ScopeSignal_preemph = preprocessSignal(Scpe_sig_avg, Symbols_preemph, Symbols_preemph.fs,"gaussian_cutoff_factor",0.8); + else + ScopeSignal = S{1}; + ScopeSignal_preemph = preprocessSignal(ScopeSignal, Symbols_preemph, Symbols_preemph.fs); + end + + fignum = 2; + dn = sprintf("Rx Spectrum; %d nm",floor(lambda)); + ScopeSignal_preemph.spectrum("displayname",dn,'fignum',fignum,'normalizeTo0dB',1,'fft_length',2^15,'show_onesided',1,'color',cols(idx,:)); -[~, Symbols_preemph, Scpe_cell_preemph, ~] = loadAndSyncRunSignals(dataTable(1,:), dsp_options); -ScopeSignal = Scpe_cell_preemph{1}; -ScopeSignal_preemph = preprocessSignal(ScopeSignal, Symbols_preemph, Symbols_preemph.fs); + if len == 10 + + fp.where('Runs', 'db_mode','EQUALS', 2); + fields = db.getTableFieldNames('Runs'); + [dataTable, query] = db.queryDB(fp, fields); + + [~, Symbols_db, S, found_sync] = loadAndSyncRunSignals(dataTable(1,:), dsp_options); + ScopeSignal = S{1}; + average_signals = 1; + if average_signals + Scpe_sig_avg = S{1}; + scope_mean = zeros(size(S{1}.signal)); + for n=1:numel(S) + scope_mean = scope_mean + S{n}.signal; + end + scope_mean = scope_mean ./ n; + Scpe_sig_avg.signal = scope_mean; + ScopeSignal = preprocessSignal(Scpe_sig_avg, Symbols_preemph, Symbols_preemph.fs,"gaussian_cutoff_factor",0.8); + else + ScopeSignal = S{1}; + ScopeSignal = preprocessSignal(ScopeSignal, Symbols_preemph, Symbols_preemph.fs); + end + + ScopeSignal_DB = preprocessSignal(ScopeSignal, Symbols_db, Symbols_db.fs); + end + + if len == 10 + fignum = 2; + dn = sprintf("DBS Rx Spectrum; %d nm",floor(lambda)); + ScopeSignal_DB.spectrum("displayname",dn,'fignum',fignum,'normalizeTo0dB',1,'fft_length',2^15,'show_onesided',1,'color',cols(idx+1,:)); + % Symbols_db.spectrum("displayname",dn,'fignum',fignum,'normalizeTo0dB',1,'fft_length',1024,'show_onesided',1,'color',cols(idx+1,:)); + end + + + + % Fiber and system parameters + lambda0 = 1314e-9; % zero-dispersion wavelength [m] + lambda = lambda*1e-9; % operating wavelength [m] + S0 = 0.092; % dispersion slope [ps/(nm²·km)] + L = 10e3; % fiber length [m] + c = physconst('lightspeed'); + + % Derived quantities + S0_si = S0 * 1e3; % → s/m³ + D_lambda = (S0/4) * (lambda*1e9 - (lambda0*1e9)^4/(lambda*1e9)^3); % ps/(nm·km) + D_si = D_lambda * 1e-6; % → s/m² + b2 = -D_si * lambda^2 / (2*pi*c); % s²/m + + Dacc = D_lambda * L; + fprintf('Accumulated Dispersion: %.2f ps/nm \n', Dacc / 1e3); + + % Frequency grid + f_max = 200e9; + f = linspace(0, f_max, 5000); % [Hz] + + % IM/DD transfer function (power fading) + phi = 2*pi^2 * b2 * f.^2 * L; + H = abs(cos(phi)); + + % Plot + plot(f/1e9, 10*log10(H), 'LineWidth', 1,'Color','black','DisplayName','CD Transfer function','HandleVisibility','on'); + grid on; box on; + xlabel('Frequency [GHz]'); + ylabel('Magnitude [dB]'); + % title(sprintf('IM/DD Power Fading: 10 km; 1275nm', lambda*1e9, L/1000),"Interpreter","latex"); + % ylim([-20 0]); + + idx = idx +1; + +end %% Load normal Signal w/o preemphasis -fp = QueryFilter(); -fp.where('Runs','fiber_length','EQUALS', 10); -fp.where('Runs','wavelength','EQUALS', 1310); -fp.where('Runs','bitrate','EQUALS', 420e9); -fp.where('Runs','pam_level','EQUALS', 4); -fp.where('Runs','rop_attenuation','EQUALS', 0); -fp.where('Runs','is_mpi','EQUALS', 0); -fp.where('Runs', 'db_mode','EQUALS', 1); +fp.where('Runs', 'db_mode','EQUALS', 0); fields = db.getTableFieldNames('Runs'); [dataTable, query] = db.queryDB(fp, fields); [~, Symbols, Scpe_cell, ~] = loadAndSyncRunSignals(dataTable(1,:), dsp_options); ScopeSignal = Scpe_cell{1}; - - - ScopeSignal_no_preemph = preprocessSignal(ScopeSignal, Symbols, Symbols.fs); +fignum = len; +ScopeSignal_preemph.spectrum("displayname",'Full Response w/ preemphasis','fignum',fignum,'normalizeTo0dB',0,'color',clr.Paired.dgreen,'fft_length',4096*6); %% Duobinary -fp = QueryFilter(); -fp.where('Runs','fiber_length','EQUALS', 10); -fp.where('Runs','wavelength','EQUALS', 1310); -fp.where('Runs','bitrate','EQUALS', 420e9); -fp.where('Runs','pam_level','EQUALS', 4); -fp.where('Runs','rop_attenuation','EQUALS', 0); -fp.where('Runs','is_mpi','EQUALS', 0); -fp.where('Runs', 'db_mode','EQUALS', 2); +if len == 10 + fp.where('Runs', 'db_mode','EQUALS', 2); + + fields = db.getTableFieldNames('Runs'); + [dataTable, query] = db.queryDB(fp, fields); + + [~, Symbols_db, Scpe_cell_db, found_sync] = loadAndSyncRunSignals(dataTable(1,:), dsp_options); + ScopeSignal = Scpe_cell_db{1}; + ScopeSignal_DB = preprocessSignal(ScopeSignal, Symbols_db, Symbols_db.fs); +end -fields = db.getTableFieldNames('Runs'); -[dataTable, query] = db.queryDB(fp, fields); +if len == 10 + fignum = 2; + dn = sprintf("DBS Rx Spectrum; %d nm",floor(lambda)); + ScopeSignal_DB.spectrum("displayname",dn,'fignum',fignum,'normalizeTo0dB',1,'fft_length',2^15,'show_onesided',1,'color',cols(idx,:)); +end -[~, Symbols_db, Scpe_cell_db, found_sync] = loadAndSyncRunSignals(dataTable(1,:), dsp_options); -ScopeSignal = Scpe_cell_db{1}; -ScopeSignal_DB = preprocessSignal(ScopeSignal, Symbols_db, Symbols_db.fs); - - -%% - - -ScopeSignal_no_preemph.spectrum("displayname",'Full Response w/o preemphasis','fignum',2,'normalizeTo0dB',0,'color',clr.Paired.dblue); -ScopeSignal_preemph.spectrum("displayname",'Full Response w/ preemphasis','fignum',2,'normalizeTo0dB',0,'color',clr.Paired.dgreen); -ScopeSignal_DB.spectrum("displayname",'DB Response w/ preemphasis','fignum',2,'normalizeTo0dB',0,'color',clr.Paired.dorange); \ No newline at end of file +%% \ No newline at end of file diff --git a/projects/Diss/400G_revisit/TABLE_BEST_DATARATES_2KM_10KM_1310NM.m b/projects/Diss/400G_revisit/TABLE_BEST_DATARATES_2KM_10KM_1310NM.m new file mode 100644 index 0000000..94328c3 --- /dev/null +++ b/projects/Diss/400G_revisit/TABLE_BEST_DATARATES_2KM_10KM_1310NM.m @@ -0,0 +1,261 @@ +%% Best AIR and FEC data rates over all wavelengths for 2 km and 10 km +% The table follows the contender mapping used in the thesis figures: +% PAM-4: DB target + MLSE, with DBS + VNLE + MLSE at 10 km +% PAM-6: VNLE + PF + MLSE, with DBS + VNLE + MLSE at 10 km +% PAM-8: VNLE + PF + MLSE +% +% For every configured contender, the maximum valid rate is selected across +% the available gross rates and symbol rates. BER_precoded is used for the +% DB-target curve, as in FIGURE_NGMI_THESIS_FINAL.m. + +clear; + +%% Configuration + +normalFiberLengthsKm = [2 10]; +duobinaryFiberLengthKm = 10; +selectedRopAttenuation = 0; +selectedIsMpi = 0; +duobinaryDateCutoff = datetime("2026-01-01 00:00:00"); + +curves = struct( ... + "name", {"DBt. + MLSE"; "DBS + VNLE + MLSE"; ... + "VNLE + PF + MLSE"; "DBS + VNLE + MLSE"; ... + "VNLE + PF + MLSE"}, ... + "algorithm_key", {"vnle_db_mlse"; "db_encoded"; ... + "vnle_pf_mlse"; "db_encoded"; ... + "vnle_pf_mlse"}, ... + "pam", {4; 4; 6; 6; 8}, ... + "source", {"normal"; "duobinary"; "normal"; "duobinary"; "normal"}, ... + "usePrecodedBer", {true; false; false; false; false}); + +%% Query the normal and DBS data + +db = DBHandler( ... + "dataBase", "labor_highspeed", ... + "type", "mysql", ... + "server", "192.168.178.192", ... + "user", "silas", ... + "password", "silas"); +db.refresh(); +selectedFields = db.getTableFieldNames('dashboard_ungrouped_alltime'); + +normalRows = cell(size(normalFiberLengthsKm)); +for lengthIdx = 1:numel(normalFiberLengthsKm) + normalRows{lengthIdx} = queryRows(db, selectedFields, ... + normalFiberLengthsKm(lengthIdx), ... + selectedRopAttenuation, selectedIsMpi); + normalRows{lengthIdx} = cleanRows(normalRows{lengthIdx}); + normalRows{lengthIdx} = normalRows{lengthIdx}(ismember( ... + normalRows{lengthIdx}.db_mode, ... + [double(db_mode.no_db), double(db_mode.db_precoded)]), :); + normalRows{lengthIdx}.algorithm_key = ... + lower(string(normalRows{lengthIdx}.equalizer_structure)); + fprintf("Normal %g km rows: %d\n", normalFiberLengthsKm(lengthIdx), ... + height(normalRows{lengthIdx})); +end + +duobinaryRows = queryRows(db, selectedFields, duobinaryFiberLengthKm, ... + selectedRopAttenuation, selectedIsMpi); +duobinaryRows = cleanRows(duobinaryRows); +duobinaryRows = duobinaryRows(duobinaryRows.db_mode == ... + double(db_mode.db_encoded), :); +duobinaryRows = duobinaryRows( ... + duobinaryRows.equalizer_structure == "db_encoded", :); +if ismember("date_of_processing", string(duobinaryRows.Properties.VariableNames)) + processedDate = datetime(string(duobinaryRows.date_of_processing)); + duobinaryRows = duobinaryRows(processedDate > duobinaryDateCutoff, :); +else + warning("table_best_rates:NoProcessingDate", ... + "date_of_processing was not returned; no DBS date filter was applied."); +end +duobinaryRows.algorithm_key = repmat("db_encoded", height(duobinaryRows), 1); +fprintf("DBS %g km rows after date filter: %d\n", ... + duobinaryFiberLengthKm, height(duobinaryRows)); + +%% Calculate maximum rates + +tp = TransmissionPerformance; +resultRows = repmat(emptyResultRow(), 0, 1); + +for lengthIdx = 1:numel(normalFiberLengthsKm) + for curveIdx = 1:numel(curves) + curve = curves(curveIdx); + if curve.source ~= "normal" + continue + end + resultRows(end+1) = evaluateCurve(normalRows{lengthIdx}, curve, ... + normalFiberLengthsKm(lengthIdx), tp); %#ok + end +end + +for curveIdx = 1:numel(curves) + curve = curves(curveIdx); + if curve.source ~= "duobinary" + continue + end + resultRows(end+1) = evaluateCurve(duobinaryRows, curve, ... + duobinaryFiberLengthKm, tp); %#ok +end + +ratesTable = struct2table(resultRows); +ratesTable = ratesTable(:, ... + ["pam", "fiber_length_km", "technique", "AIR_Gbps", ... + "SDHD_Gbps", "HD_Gbps", "AIR_grossrate_Gbps", ... + "SDHD_grossrate_Gbps", "HD_grossrate_Gbps", ... + "AIR_symbolrate_GBd", "SDHD_symbolrate_GBd", ... + "HD_symbolrate_GBd"]); + +disp("Best rates (Gb/s):"); +disp(ratesTable(:, ["pam", "fiber_length_km", "technique", ... + "AIR_Gbps", "SDHD_Gbps", "HD_Gbps"])); + +displayTable = table( ... + ratesTable.pam(:), ... + ratesTable.fiber_length_km(:), ... + string(ratesTable.technique(:)), ... + formatRateWithSymbol(ratesTable.AIR_Gbps(:), ratesTable.AIR_symbolrate_GBd(:)), ... + formatRateWithSymbol(ratesTable.SDHD_Gbps(:), ratesTable.SDHD_symbolrate_GBd(:)), ... + formatRateWithSymbol(ratesTable.HD_Gbps(:), ratesTable.HD_symbolrate_GBd(:)), ... + 'VariableNames', {'PAM', 'Distance_km', 'EQ_contender', ... + 'AIR', 'SDHD_FEC', 'HD_FEC'}); +disp("Best rates with the symbol rate used for each metric:"); +disp(displayTable); + +%% Local functions + +function T = queryRows(db, fields, fiberLengthKm, ropAttenuation, isMpi) +fp = QueryFilter(); +fp.where("Runs", "fiber_length", "EQUALS", fiberLengthKm); +fp.where("Runs", "rop_attenuation", "EQUALS", ropAttenuation); +fp.where("Runs", "is_mpi", "EQUALS", isMpi); +[T, query] = db.queryDB(fp, fields); +disp(query); +end + +function T = cleanRows(T) +numericFields = ["result_id", "run_id", "eq_id", "bitrate", "grossrate", ... + "symbolrate", "pam_level", "wavelength", "fiber_length", "db_mode", ... + "rop_attenuation", "numBits", "numBitErr", "BER", ... + "numBitErr_precoded", "BER_precoded", "GMI", "AIR", "NGMI"]; +for fieldIdx = 1:numel(numericFields) + fieldName = numericFields(fieldIdx); + if ismember(fieldName, string(T.Properties.VariableNames)) + T.(char(fieldName)) = numericColumn(T.(char(fieldName))); + end +end +T.equalizer_structure = lower(string(T.equalizer_structure)); +end + +function result = evaluateCurve(sourceRows, curve, fiberLengthKm, tp) +result = emptyResultRow(); +result.pam = curve.pam; +result.fiber_length_km = fiberLengthKm; +result.technique = curve.name; + +curveRows = sourceRows(sourceRows.pam_level == curve.pam & ... + sourceRows.algorithm_key == curve.algorithm_key, :); +if isempty(curveRows) + warning("table_best_rates:NoRows", ... + "No rows found for %s, PAM-%d, %g km.", ... + curve.name, curve.pam, fiberLengthKm); + return +end + +curveRows.BER_plot = numericOrNaN(curveRows, "BER"); +if curve.usePrecodedBer && ismember("BER_precoded", ... + string(curveRows.Properties.VariableNames)) + precodedBer = numericOrNaN(curveRows, "BER_precoded"); + usePrecoded = isfinite(precodedBer); + curveRows.BER_plot(usePrecoded) = precodedBer(usePrecoded); +end + +curveRows = addAirMetric(curveRows); +grossRate = numericOrNaN(curveRows, "grossrate"); +ngmi = numericOrNaN(curveRows, "NGMI"); +ber = numericOrNaN(curveRows, "BER_plot"); +ngmi(~isfinite(ngmi) | ngmi < 0 | ngmi > 1.05) = NaN; +ber(~isfinite(ber) | ber <= 0 | ber > 0.5) = NaN; + +ndr = tp.calculateNetRate(grossRate, "NGMI", ngmi, "BER", ber); +curveRows.SDHD_Gbps = columnVector(ndr.SDHD.NetRate) .* 1e-9; +curveRows.HD_Gbps = columnVector(ndr.O_FEC.NetRate) .* 1e-9; + +result = assignMaximum(result, curveRows, "AIR_Gbps"); +result = assignMaximum(result, curveRows, "SDHD_Gbps"); +result = assignMaximum(result, curveRows, "HD_Gbps"); +end + +function result = assignMaximum(result, T, fieldName) +values = numericOrNaN(T, fieldName); +valid = isfinite(values) & values >= 0; +if ~any(valid) + return +end +[maximum, localIndex] = max(values(valid)); +validRows = find(valid); +rowIndex = validRows(localIndex); +prefix = erase(fieldName, "_Gbps"); +result.(fieldName) = maximum; +result.(char(prefix + "_grossrate_Gbps")) = ... + numericOrNaN(T(rowIndex, :), "grossrate") .* 1e-9; +result.(char(prefix + "_symbolrate_GBd")) = ... + numericOrNaN(T(rowIndex, :), "symbolrate") .* 1e-9; +end + +function T = addAirMetric(T) +T.AIR_Gbps = numericOrNaN(T, "AIR") .* 1e-9; +gmi = numericOrNaN(T, "GMI"); +fallbackAir = gmi .* numericOrNaN(T, "symbolrate") .* 1e-9; +grossRate = numericOrNaN(T, "grossrate"); +useFallback = ~isfinite(T.AIR_Gbps) | T.AIR_Gbps < 0 | ... + (isfinite(grossRate) & T.AIR_Gbps > grossRate .* 1.05e-9); +T.AIR_Gbps(useFallback) = fallbackAir(useFallback); +end + +function result = emptyResultRow() +result = struct( ... + "pam", NaN, ... + "fiber_length_km", NaN, ... + "technique", "", ... + "AIR_Gbps", NaN, ... + "SDHD_Gbps", NaN, ... + "HD_Gbps", NaN, ... + "AIR_grossrate_Gbps", NaN, ... + "SDHD_grossrate_Gbps", NaN, ... + "HD_grossrate_Gbps", NaN, ... + "AIR_symbolrate_GBd", NaN, ... + "SDHD_symbolrate_GBd", NaN, ... + "HD_symbolrate_GBd", NaN); +end + +function values = numericColumn(values) +if iscell(values) + values = string(values); +end +if isstring(values) || ischar(values) + values = str2double(values); +end +values = double(values); +end + +function values = numericOrNaN(T, fieldName) +if ismember(fieldName, string(T.Properties.VariableNames)) + values = numericColumn(T.(char(fieldName))); +else + values = NaN(height(T), 1); +end +values = values(:); +end + +function values = columnVector(values) +values = double(values(:)); +end + +function text = formatRateWithSymbol(rateGbps, symbolrateGBd) +text = strings(size(rateGbps)); +valid = isfinite(rateGbps) & isfinite(symbolrateGBd); +text(valid) = compose("%.0f (%.0f GBd)", ... + rateGbps(valid), symbolrateGBd(valid)); +text(~valid) = "--"; +end diff --git a/projects/Diss/400G_revisit/TX_spectra.m b/projects/Diss/400G_revisit/TX_spectra.m index df869bd..70779ef 100644 --- a/projects/Diss/400G_revisit/TX_spectra.m +++ b/projects/Diss/400G_revisit/TX_spectra.m @@ -1,5 +1,5 @@ -rates = [420e9]; +rates = [400e9]; rcalpha = 0.05; fsym = rates/2; apply_pulsef = 1; @@ -56,8 +56,6 @@ Digi_sig_rx.spectrum(... - - Digi_sig_pre_rx.spectrum(... "displayname",'Rx w/ pre-emphasis',... "fignum",2,"normalizeTo0dB",0,"color",clr.Paired.dblue); @@ -80,6 +78,9 @@ Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"al "mrds_code",0,"mrds_blocklength",512).process(); Digi_sig_DB= Digi_sig_DB.normalize("mode","oneone"); +Digi_sig_DB_rx = precomp_est.apply(... + Digi_sig_DB,'maxampdb',0,'loadPath',precomp_path,'fileName',precomp_fn); + maxamp = -38; %optimized for DB! precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig_DB.fs); precomp_path = "W:\labdata\sioe_labor\precomp"; @@ -89,27 +90,33 @@ Digi_sig_DB_pre.signal = reshape(Digi_sig_DB_pre.signal,[],1); Digi_sig_DB_pre = Digi_sig_DB_pre.resample("fs_out",256e9); Digi_sig_DB_pre = Digi_sig_DB_pre.normalize("mode","oneone"); +Digi_sig_DB_pre_rx = precomp_est.apply(... + Digi_sig_DB_pre,'maxampdb',0,'loadPath',precomp_path,'fileName',precomp_fn); + %% AWG_ = M8199B("kover",4); % AWG_ = AWG("fdac",256e9,"f_cutoff",fsym,"lpf_active",0,"kover",4,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1); - El_sig = AWG_.process(Digi_sig); El_sig_preemph = AWG_.process(Digi_sig_pre); -El_sig_channel = AWG_.process(Digi_sig_channel); +% El_sig_channel = AWG_.process(Digi_sig_channel); El_sig.spectrum("displayname",'Full Response w/o preemphasis','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.lblue); El_sig_preemph.spectrum("displayname",'Full Response w/ preemphasis','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.dblue); -El_sig_channel.spectrum("displayname",'Full Response w/ measured channel','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.blue); +% El_sig_channel.spectrum("displayname",'Full Response w/ measured channel','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.blue); +%% El_sig_DB_preemph = AWG_.process(Digi_sig_DB_pre); El_sig_DB = AWG_.process(Digi_sig_DB); +El_sig_DB_rx = AWG_.process(Digi_sig_DB_pre_rx); +Digi_sig_DB.spectrum("displayname",'DB Response w/o preemphasis','fignum',1,'normalizeTo0dB',1,'color',clr.Paired.lorange); +Digi_sig_DB_pre.spectrum("displayname",'DB Response w/ preemphasis','fignum',1,'normalizeTo0dB',1,'color',clr.Paired.dorange); -El_sig_DB.spectrum("displayname",'DB Response w/o preemphasis','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.lorange); -El_sig_DB_preemph.spectrum("displayname",'DB Response w/ preemphasis','fignum',1,'normalizeTo0dB',0,'color',clr.Paired.dorange); +Digi_sig_DB_rx.spectrum("displayname",'DB Response w/o preemphasis at Rx','fignum',1,'normalizeTo0dB',1,'color',clr.Paired.lred); +Digi_sig_DB_pre_rx.spectrum("displayname",'DB Response w/ preemphasis at Rx','fignum',1,'normalizeTo0dB',1,'color',clr.Paired.dred); \ No newline at end of file diff --git a/projects/Diss/400G_revisit/investigate_400g_algorithms.m b/projects/Diss/400G_revisit/investigate_400g_algorithms.m index a8d0dc6..882ba74 100644 --- a/projects/Diss/400G_revisit/investigate_400g_algorithms.m +++ b/projects/Diss/400G_revisit/investigate_400g_algorithms.m @@ -5,7 +5,7 @@ dsp_options.recipe = @dsp_400g_recipe; dsp_options.append_to_db = false; % dsp_options.append_mpi_reduction_db = false; dsp_options.start_occurence = 1; -dsp_options.max_occurences = 3; +dsp_options.max_occurences = 1; dsp_options.debug_plots = false; @@ -36,11 +36,12 @@ maxRunIds = 1; % keep small until the recipe settings are sett fp = QueryFilter(); fp.where('Runs','fiber_length','EQUALS', 10); fp.where('Runs','wavelength','EQUALS', 1310); -% fp.where('Runs','bitrate','LESS_THAN', 480e9); -% fp.where('Runs','pam_level','EQUALS', 4); +% fp.where('Runs','bitrate','LESS_THAN', 330e9); +fp.where('Runs','symbolrate','EQUALS', 174e9); +fp.where('Runs','pam_level','EQUALS', 6); fp.where('Runs','rop_attenuation','EQUALS', 0); fp.where('Runs','is_mpi','EQUALS', 0); -% fp.where('Runs', 'db_mode','EQUALS', 2); +fp.where('Runs', 'db_mode','EQUALS', 1); fields = db.getTableFieldNames('Runs'); [dataTable, query] = db.queryDB(fp, fields); @@ -67,7 +68,7 @@ dsp_options.userParameters = struct(); % dsp_options.userParameters.len_tr = 4096*2; % dsp_options.userParameters.pf_ncoeffs = [1,2,3]; -dsp_options.userParameters.decoding_mode = [db_decoder.sequencedetection]; +% dsp_options.userParameters.decoding_mode = [db_decoder.sequencedetection]; % dsp_options.userParameters.pf_ncoeffs = [1, 2, 3]; % dsp_options.userParameters.mu_dc = [0, 1e-5, 1e-4]; % dsp_options.userParameters.run_ml_mlse_db = [false, true]; @@ -86,7 +87,7 @@ fprintf("-> [ %d run_id(s) x %d userParam combination(s) = %d job(s) ] x %d real %% Run -[results, wh] = submitJobs(run_ids, dsp_options, processingMode.parallel, ... +[results, wh] = submitJobs(run_ids, dsp_options, processingMode.serial, ... "wh", wh, ... "waitbar", true); diff --git a/projects/MLSE_ML_based/minimal_example_huawei/ml_mlse_pam.m b/projects/MLSE_ML_based/minimal_example_huawei/ml_mlse_pam.m index f33d40d..3de3cab 100644 --- a/projects/MLSE_ML_based/minimal_example_huawei/ml_mlse_pam.m +++ b/projects/MLSE_ML_based/minimal_example_huawei/ml_mlse_pam.m @@ -210,8 +210,8 @@ classdef ml_mlse_pam < handle % ============================================================== % ML-Based Branch Metric Estimation + Viterbi % ============================================================== - % debug = 1; - % showPlots = 1; + debug = 0; + showPlots = 0; nSymbols = ceil(N/obj.sps); diff --git a/projects/MLSE_ML_based/theoretic_channel_evaluation.m b/projects/MLSE_ML_based/theoretic_channel_evaluation.m index cdcdc81..a0ad445 100644 --- a/projects/MLSE_ML_based/theoretic_channel_evaluation.m +++ b/projects/MLSE_ML_based/theoretic_channel_evaluation.m @@ -47,7 +47,7 @@ ber_ml_mlse_l4 = zeros(size(SNR_dB)); epochs_training = 100; -parfor i = 1:numel(SNR_dB) +for i = 1:numel(SNR_dB) symbols_noi = symbols_filt; symbols_noi.signal = awgn(symbols_filt.signal, SNR_dB(i), 'measured'); % AWGN with given SNR @@ -73,46 +73,46 @@ parfor i = 1:numel(SNR_dB) [~, ~, ber_ffe(i), ~] = calc_ber(Eq_bits.signal, Bits.signal, "skip_front", 0, "skip_end", 0, "returnErrorLocation", 1); fprintf('FFE: %.2e \n',ber_ffe(i)); - % Postfilter - [y_white,~] = pf_.process(y_ffe, ffe_noise); - - % Sequence Est - mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',pf_.coefficients); - [y_mlse] = mlse_.process(y_white,Symbols); - mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse); - [~, errors, ber_nwf_mlse_l2(i), errpos] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); - fprintf('MLSE: %.2e \n',ber_nwf_mlse_l2(i)); - - % ML-base MLSE L=2 - adaptive_mu = 0; - mu_lms = 0.15; - ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^15,... - "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,... - "traceback_depth",128,"L",2,"delta",4,"adaptive_mu",adaptive_mu); - [y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols); - ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); - [~, errors, ber_ml_mlse_l2(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); - fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l2(i)); - - % ML-base MLSE L=3 - mu_lms = 0.15; - ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^16,... - "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,... - "traceback_depth",128,"L",3,"delta",4,"adaptive_mu",adaptive_mu); - [y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols); - ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); - [~, errors, ber_ml_mlse_l3(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); - fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l3(i)); - - % % ML-base MLSE L=5 + % % Postfilter + % [y_white,~] = pf_.process(y_ffe, ffe_noise); + % + % % Sequence Est + % mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels,'scale_mode',0,'trellis_exclusion',0,'trellis_state_mode',2,'debug',0,'DIR',pf_.coefficients); + % [y_mlse] = mlse_.process(y_white,Symbols); + % mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_mlse); + % [~, errors, ber_nwf_mlse_l2(i), errpos] = calc_ber(mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + % fprintf('MLSE: %.2e \n',ber_nwf_mlse_l2(i)); + % + % % ML-base MLSE L=2 + % adaptive_mu = 0; % mu_lms = 0.15; % ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^15,... % "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,... - % "traceback_depth",128,"L",5,"delta",4); + % "traceback_depth",128,"L",2,"delta",4,"adaptive_mu",adaptive_mu); % [y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols); % ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); - % [~, errors, ber_ml_mlse_l5(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); - % fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l5(i)); + % [~, errors, ber_ml_mlse_l2(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + % fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l2(i)); + % + % % ML-base MLSE L=3 + % mu_lms = 0.15; + % ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^16,... + % "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,... + % "traceback_depth",128,"L",3,"delta",4,"adaptive_mu",adaptive_mu); + % [y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols); + % ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); + % [~, errors, ber_ml_mlse_l3(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + % fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l3(i)); + + % ML-base MLSE L=5 + mu_lms = 0.15; + ml_mlse_equalizer = ML_MLSE("epochs_tr",epochs_training,"epochs_dd",1,"len_tr",2^15,... + "mu_dd",mu_lms,"mu_tr",mu_lms,"order",11,"sps",1,... + "traceback_depth",128,"L",5,"delta",4); + [y_ml_mlse,~] = ml_mlse_equalizer.process(symbols_noi,Symbols); + ml_mlse_bits = PAMmapper(M, 0, "eth_style", 0).demap(y_ml_mlse); + [~, errors, ber_ml_mlse_l5(i), errpos] = calc_ber(ml_mlse_bits.signal, Bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); + fprintf('ML MLSE BER: %.2e \n',ber_ml_mlse_l5(i)); end diff --git a/projects/WDM/WDM_auswertung.m b/projects/WDM/WDM_auswertung.m index 1435d1f..6456815 100644 --- a/projects/WDM/WDM_auswertung.m +++ b/projects/WDM/WDM_auswertung.m @@ -1,5 +1,5 @@ -base = "C:\Users\Silas\Nextcloud4\Cluster"; +base = "C:\Users\Silas\Nextcloud2\CAU\Cluster"; all_files = dir(fullfile(base, "**/*.mat")); schemes = ["co","pair","alt","seg"]; @@ -77,7 +77,7 @@ res_all = drop_empty_realizations(res_all); S = plot_BER_vs_ROP(res_all, 'fec', 3.8e-3); %% Routine B: violin plot (independent) -plot_FEC_violin(res_all, 'tech','VNLE', 'fec',3.8e-3, 'ylim',[-10 0],'eval_ptr',5); +plot_FEC_violin(res_all, 'tech','VNLE', 'fec',3.8e-3, 'ylim',[-10 0],'eval_ptr',10); %% diff --git a/projects/WDM/WDM_model.m b/projects/WDM/WDM_model.m index aba8593..193b0e9 100644 --- a/projects/WDM/WDM_model.m +++ b/projects/WDM/WDM_model.m @@ -6,10 +6,10 @@ function WDM_model(options) arguments options.num_channels = 16; options.channel_spacing = 400e9; - options.fiber_length_km = 0; + options.fiber_length_km = 1; options.rand_key = 1; options.num_realiz = 1; - options.fwm_mitigation_technique = "co"; + options.fwm_mitigation_technique = "pair"; end %% @@ -72,7 +72,6 @@ host = getenv('HOSTNAME'); if isempty(host), host = 'localhost'; end fname = sprintf('WDM_%s_%s_%s_%dkm_%dch_%dghz_%s.mat', char(t), host, jobid, options.fiber_length_km(end), options.num_channels, options.channel_spacing.*1e-9, options.fwm_mitigation_technique); -%% s.num_realiz = options.num_realiz; % s.wavelengthplan = calcWavelengthPlan(16,400e9,1310); s.wavelengthplan = calcWavelengthPlan(options.num_channels,options.channel_spacing,1310); @@ -162,7 +161,7 @@ end for realiz = 1:s.num_realiz - parfor l = 1:N + for l = 1:N [Digi_sig,Symbols{l},Tx_bits{l}] = PAMsource(... "fsym",fsym,"M",s.M,"order",18,"useprbs",0,... @@ -199,9 +198,9 @@ for realiz = 1:s.num_realiz Opt_sig_wdm = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",s.p_launch+10*log10(N)).process(Opt_sig_wdm); - % Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',0); + Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',0,'lambda0_nm',1310,'useWavelengthAxis',1); - % Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',1,'max_num_lines',2); + %% Opt_sig_wdm.spectrum("fignum",101,"displayname",'bla','normalizeTo0dB',1,'max_num_lines',2); %%%%%% Fiber %%%%%% Opt_sig_wdm_fib=Opt_sig_wdm; @@ -219,20 +218,17 @@ for realiz = 1:s.num_realiz "beat_len",10,"corr_len",100,"dz",1,"manakov",0,... "gamma",s.gamma,"lambda",zdw,"n_waveplates",10,"SS_dphimax",0.01,... "SS_dzmax",50,"SS_dzmin",10,"X_alpha",0.3,"X_beta",0,"rng",1).process(Opt_sig_wdm_fib); - propdist = segment_length; - - end - %%%%%% Demux after 2 km %%%%%% + %% %%%% Demux after 2 km %%%%%% Opt_sig_wdm_demux = Optical_Demultiplex("attenuation",0,"B",200e9,"filtype",1,"fs_out",fdac*kover,"fs_in",fdac*kover*upsample_pow,"lambda_center",1310).process(Opt_sig_wdm_fib); for ri = 1:length(s.rop) - parfor l = 1:N + for l = 1:N %%%%%% ROP %%%%%% Opt_sig_wdm_rx = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",s.rop(ri)).process(Opt_sig_wdm_demux{l}); % rop+10*log10(N) @@ -276,14 +272,13 @@ for realiz = 1:s.num_realiz output_ffe{l,ri,realiz} = ffe_results; - - %VNLE pf_ncoeffs = 1; ffe_order = [50, 5, 5]; eq_ = EQ("Ne",ffe_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1); pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); + useviterbi = 0; if useviterbi mlse_ = MLSE_viterbi("duobinary_output",0,'M',s.M,'trellis_states',PAMmapper(s.M,0).levels); diff --git a/tmp/pdfs/paper-2.png b/tmp/pdfs/paper-2.png new file mode 100644 index 0000000000000000000000000000000000000000..c235fd32ab2ed4df17c0185bf4453c447ab8ac94 GIT binary patch literal 701089 zcmeFZWmuK%)-`MkCSroLl!Bskql9!Q5`vV3G|~+Q3W$_62uMpeNE;{}f=CNWmr6?W zj!`Gp`0ZW04%qM7 zMSOv14}PNlS}Pd;B7P`!*LK$~s*i;K?Y3;U7v8mtW|y@1jXO?#vxCm}6sC4W{?1tH zoVcVZ-6-zROMES|!ROS)4f3$d`a|Ai&&^W|=*3y2zvnZ`mRv1R>3C8U(P5+J`0r2P z%s;-s#j1*m3V}g}$~aH^_zk$a%d(f?dvTG7;W zy2|xmR(AF?(VywcY0r;QW@Kh=bp4xd2w}nvi9J8k(Arw5_4FAmWr7Zrupjkt$h7jtkZDKFtlHa0fq z9Ie{A5*8LVHa2ErX4d^REZeJdU8td{X=L8X|2$7B_2I*ZySlnO-_O#M5ET>@MDti( zVPhL2>A2CDF;dZx;wCJ@#3U&z>qliHR(~WTBg3DZE2EV3+nd|hpS*bS;?*lsF0SGX zRc2=9_HW;^F6phgd8q8L{G`6YNV1-OGFQ7wBTx6kV1*+--}?yJub)4^CWsxRKaAV2 z52~u#TAXOZ-<&>udjFmqckf2MdiCn{>wEZ*>ok+yxkKAq8+-Tc5fu{)2?;sRW0{qb zBC(U7mBo7ba!YM3Ju|aU)rbC4tBSI+l(aO*>2K+8-@Z*wuKoD&S68N*?RfJwcJ_tE z#lytJSWVSix1t1{3$nAbtE)XkMMWPze5jdWF0#GAu&MAto3PuelCtt* za}>vAjUwHW__#PKa`NekiMo;bRE1c6y^mfT4_dvRJxfVWu5W5mjOJ`;Y;64Y?GE!P zD&B|ho0^(>dU$zw)Nb9HkI?aumX_`cvXlcn# zLgF*_UgPfFVymGz61r1|0;z;tR%Y>*Aq@?UmFcdTwZ(~_KYxlQJlxsd`t`7pSJB)KlST<>n?xn=IyiEFd7@f@FZf zwtIJu*7o+cp`oGvmzPmIRzGlQ#n-&I)Yac+W@ct&$aG)4bg8kq`L(e7`odV#$gf|m zu>y{`?ScY!tdWJqTw)@fme$~{q?;|T`INCiRaV5r#I3EZ2-m*8zJ`W|@$vDyckjk< zTd=dUm)lLfs`ofX&iPg?SKEt3LP7!;iQYR!$r|5w_RupNJM8;e=`TJ$KDcLUKAYqP zp{w56W8>pg0uHmaz9$(N7;vM#)$SENw@0Q3uk^%3cKhjX!51WdRJp2+PkjCME%b`# zzu&PjG5rWHd3pJ;&`=v28-B;%1F7;czkdBPYzmL%wf=>@%+JsFB9WDqJ%2t1Z}wbU z80*Q`r=_LE{bKt#XKqSL>Qp+-BiiuC%bFzyzu$9kaQON8Q471Bqo$@NCB5A_7((*& zE%%mW0L5Jk4}EUtQ-`pAKgY&W($h_Jb%Vmf5E4uMRyC9&9-CM}YisK?h1f%f4v`Y= z%ho84zN+`}B^gI(SlFL|vXc}PZX;igib|DQ4l>F`;=T6R_R-PND_Z5Rd99xun0h{olCxuqpu z(D~!Yd`}Y&G~v*QiVAyd){o31%e1t#cpsF*$zkmOpWz!}Ry>sUdZrk3@4j*mQ7I&Fcn-53p@!o^q zy1MM`?N6RODJvrr&Zc8&Xed#$g-bttm{(KdX=KD0SZDS<>#nnN+4JW&hpOFUU%$4q zvr9I&PIhbB=q-GpS!#J1S);qVt+7!?LBZkSL-iu#TVuniqohR-+NiMZe0+QegaoDf ziN({W2(QU%B(9x*L!P%&uUlGLN=f=MUln)<1;EJ*Q_BOV*=ebNzxjySmQ*?E5ij0n~c3w1^XpI$e zS<*dQBp<^qLbf@{r}dNXOQPa6aEH@Y}0t*MfB|B z!oosO5Q(j=ZFzJ}jRsiN z?8^MY0-YDDcBQ(3A_WCSqz>*4SH@xV3PKEbp0_FCGW%}cTkvd6>QB66AXN+N+DM=X zzuja|Y^=P9u)gV>FY zlT1)@!Wp{hPoAw+5gwbTr>93wPLA+Ppw_AJkbf#kr@1i^?@2_9Lf`xf30qVOrKu;= zeS6b+`2FMN;P1x&=!1l}QW~tV;Wb+;niURn+SP6i+rIN7btgq5Yoj<#NRK|r<59VF zDt3bPEprWI zh1VqPCLPJ-DubxY^j=5KbHDU(`#by?FBCl~85!FSgo_Lh0|}9gjLg8`;MnM>oV+}e z{N$lMk9EhJqYfN6fUMGd6ICKPJG;vGE8R-?aSGn+M7>2OCoRM`x3-W0HHuBcA|ut4 z&PcLbSy>?~k{?d(=~}AEH~#QF$>iqdw(@&$W^(cz1x2OZR7X};R+3~Oogq%n_2)=mmswfGT~tU! zN5{r2%*{Qv*B-0~J3nbz=~NW)C*yd&HaRy}h0}&ApsA_JaTu%cJy%D>b!GPTYl`yZ zyM;#gd6)}c*Nq@l|2kE^eakErMa2z!iBBP%^tPFsHmvs zGUYpW28zuh%F3KoHrB?PB95NtZjaNU!$o&C7uy=G=&L+;kh2#*fBsCz6w&|v-1_?Z zTz~1r;u>8Q#uU< z@we!DYZ+WHt@}@$&*?AC+zK;yw(-tA@}e-R%TH}=c6Jmhk)(eA{+*JNa`@~OgIvZI z>9n=*1NV_HVq;^|sNkhbm-qz)aKP7bX{76{Tdub(?yGhcTD^a?8zlg!2-O1s09mub z@lWbdcxWiAMv*aKz1_hBN6xjESZJ<~K27ZbfDgZ{c~i0d$^OGsA|7(KY_y|*4qNNX z`X0srT%5HpXPBeJ+~il*alnk*X~stqCH#nJBY&Pp@EVx?9w;NNHni-zG<571i)z*z zPlPb=MV9@{chu1I%08rAB!31gnOPfvlK@HqZ0!qi{@R>oR#rB)wx*^Ek1ZExXTHVk zXV3N@ee&(=57ij)Uu;QZqx2L3^)_N+Vh0a;M@B|Yb*2^mF{MvCz=i7~3sMTX6eTBn zRc(&9#8kN~-yfQlwxtQ{PWV%cKfSp7Dl80n%zAlRWyMrEO(EM<1jVJHS}&(kW9({X zTG|8>0_uKcWu>^~BComs)?DaCWjlNOs>(_LYKiK>8c&fb_6JO&TCZ`IB!%N^tVg~y z_S-#tsC&PzxGfQ&j)$H7S9lA(xM#B5j_G1QHgM10y*R!-AGl=h-gQQL@9piSm_DPX zre<%yjN>ch^!NIDxx{+LQ)?6*;4co2LcgRD^G*o8mO|pP5Ddf5m85Napr4XeXRb9P}pNP1x(>v!zM&D>WlXeNfcZs3B(QJBV z2022f-0mVPtK2#%y|X8}vyOMxBO0l@XbtLvXuX1fl)e{4pO%)(_Y$Su_&lAjUx(EQ zC%$kG#~tN=cJEYou8z9;Ijx^S=`E2 z`)j+a0|2Y@L^>95yAP6&5PBaHj#WSPW}!;;rTv_ZLqj!h-x@DXewFh)RDuud>*;ye zQ-y7O=Iw1}Vp3LKu3h0E-?(#IN$DJ?aXW$?H@W?LP{3#nfjMnw#lRFSM~qyvuXv^QrqpdVKYntT_DM!L-iO22B_#H1 z@mdb>8d1D$%qKP4cg4g;Us}2W2n}a&dsT`_}VY3;a9XY9PftOa)+p_7cQRP>hI{dJsWLzNBo#; zPqt<$(mRq=0JTV5wfM!O$5gy4DmHL%UdP6=T)GsQbKU-N_>H}LpPDk=ep{lQsAt$$ z_ZLk8${^0%{lx@iO56u`QI{y*upY<8(o$rY1xUiI#cDNuI_Ok{_xx2fq?9`*}BU& zZ{ECi?HalkuOO4o)GDX>`wOED=wr~+0S?-2?hE3p?VcV$pcfYx8``HI#HPOA9p8o{ zOL#RlmZN(wvz>^{HC8;B@5mn7)2)}RXJ8<1rNt;89U2rwGv*1vD5a)`gVOQsn_WvZ z7h0*M-=#VKR7FV}yk5MJQCC+7x+pd8B_$`Nq|6;g*)mmoH~$w5Z;>Guu}@vohCz=FA!8R5`tHO?7o(MS!%P-rh3gX>=Wl#4MTf zd0SgsXopQ`UXqB`U3!&(!fa}6JUduXO2BLH%(`=iJa?+7S}(LD;t;a8CGL6rD6SU} zA~Nz^{*-Er`4@k$O~$gKjJIze#zk~~`C^2;2n<6m;!!D8)0*+EXj)223czu)D>EWC zRtw#VyPI3`WFNDM>_ z`EG&1Y)}5bKSL2$ZO(kEtsTYDK^YW8D%SDb8XFlYH0!=HK2}@auSqOT&w_UF(BZ@9 z*$wJ}mR`PmNeJ695x~^|G7d&T!Oid4noI5sXn*o^%T9=HuT8|uMqD`|ibc!Osi}6F z|J67%i#8V9kIht3Q4z8kC0wbe@EWNoa8y4k8sHat+T#eYk6y%pp;lcPVR9WG`ujJ~ z4SNN}AzRLL|LI7P_4W0g>njdX;YXTjiQ;&Gti`18I+Dxm4DBemD14??g@d8vi>#zY z^-k7xfDk15{D`Y;S-ws?+d?ggq6t^8T?<~x3fAKuNzK?))z*%Si_=a@RxdR2m*Qd0 zw=%p9WaKjcOTyTAveJ3+Qs8IRyYg**-rfge(UYAVBfhm|~pZ8p>wP&UTEEE5AZi{k=rG`Ln^dCR*b~sLvB# zF+o9jmV@P>IMn*D$;)*7k&|x=mGcPwIWw~{+si=}NNB~py!HtR384>a)N)KQVKa@2 ztgfy`1g4#IArYNy`ZjBjpx<`-%o*e<9HbX_5}HfBNRSWE8KYN0KIi1(S_X^pjydB3 zn7M2m7f>iN9()uCo;NxEKq@NWkgcq&%zV$*XG6gF_WT@8|Lk|R zuC{h{byY5k{TZ3IWWkxs8eY$zYgf70qgO?r@EYI~U8m%~n%^tP3cd`^f1@+1=Q`8c zP;0<)7#SL>sI;BP4`;ngN<~#|HB?P2_8f(K-~Rn561f>%QNblcRjy~xol|$FJbU)6 zkWdvaZencwl2SmG{@qO=Vs#A-%ie;|pFWK&O?9GvRJm_(yB4uo0=noM8`pbH-@A7Y zO(vkg_V$Jqnb9g@%f-oQcAyO59VJL*g`Qc(UOQyXIm_e>VkuLqjOg&;q)xLJSFc{x zZHwXt4$mlQ;ESQa7cPu1Yp)AFy{UdFsY)P*)8wP}Y7<+{=Jv)PuvGCPo>YQPd09`)xcZ`^ z!)c;6nf38)5vGZr=o-xXicIiZNF>@d9`5q}etq;tH*Y>id+vWvnNypQgea21PLK=P z7WtYgux|Us7P|e)%7Gt0Jdj=RL!5mS<5i>B3JVKM5fm_E zUSQ*1N8cr`*XSS0HzX7l+wcvadwcs0B>Je_yC=7^ijyBN~QP zkorpeaj5;VZ(+@iIQKSKNqZ1ls7uIevnCYu=ef<*5qWKKLNTGC=%!K-5>ksLsR;=` zeNJ4s#HM!V4kvigl@$ef`E0kk*4Ix^kdRO|a9+^t37*2&?s@i6s=&151ch+%;>G;J!WaQZOB7^~w&3xmC*3EgDSnyR zk0Aq~MlUWeXJ?39!oI%m7ERMnTnzXc$4;l;T$rC$G;&i~`XVRi9YsZ}{t}C>0ODfT zf0pA``}aRPjg|mx1qmq`*8_OF1?7yV$I|sHkZ2;#>c3-@et> z)?(pbKR|L{?9E0)s)P4+bsw%TnP9^O1O$lLLcZHQI0kAI0MOw|O00GYHOpTVi;k?+tSpLi z=Rm?6TUoKmo8EEl=$#}QM!Gz+G57S~aS(X50i>4Z=CeP40?Dwt3n^!#gmRj6j3RWx zm{w8*#E1way}2yO#3|i#VBTkD4nO+{GSoKHh>w2!6@#qD&X!|v{vMO=R@7S#+1s~o zgEs~FIaLZWw>jy=y2ZcWgT+Nf`}REc_O6@!nlxnt;+Kb~6u<5#S96&C@v25_eOsF& zs7PB|{R2A9&CPBbE0=E{z~+(r{Q7m@(a}+~9x9L^f^7#G18Ff#&ClOIyF}lI6Ys)m zXQrk)ERM@Jn8k^B#zhsrO6spy93RB{P+|=b2Q>4VwE%NB#Zw69Po<5BfU`Slr&Mp!tAI)iPZ-1I-U$dr> zIRI-{>!ij7&;csgc|Gn-A;y}^TIGNXsc0e2^q-xY?JZ=VN;0stMEf2W5y8Zl%;4UI z&aevyZE|ulPwgCsksJbd@7}#=B{Bz7aK{LG*|HtfD^|^CSqxC1EMh_i5>W>b1er>byJnNM!D~pRWooUDY za~tyZbusRL3j)z|b2YebBq}=E#*)o6G_omzjaroQW&Zzo0eCDaI2`416hC<%C#FRk zqok%5=;NdN9jlJI%jqt9)qQ;l)CX>lo$o3)_xB$^;w*2_kmL66=_ zjW3PJ$DU#fic$Lji_Cvi_I~`BxT5wS2f+QWRJ-r%h$Ohv^#m3Z)4Bs`LV|)lO~B?*3iK^3T3C^P(%lq) zph^Av_hE2?=e|8RKyRv45!3`64IL$=t5!b*^I3d^fCtIH(v%ZD%2)e>P_%KN@{O!N3)2N?_;+^OhlyX?Q3?ov>CK< zlu>?0xkO`z81pGh>_cm-!cxgy1r!0)3WnLod#2)&AzE}dx3-p)mj3B2O!KOca3G`i zYK|4?22I@7W(A&;i+{@QU{4IGH844Xq4_mmNR`6Pn+Jp>XX7&~)eat~f=*y(V{^aG zj(+q7q&Q?*Dt^1S8L@Xk-65TFTMpE>9zpU$4N_57768;!R!CvJ(J^`(?2t{J^|nrFPxI@uV24*bX>Z8 z*{}qad1P|(Z9;L(2Tf@Dg03;}#J9~Bl&16Et4Wg<3(m!BzR^5cR9M=_cN=jB6(D zQ43vciQ(DV_)`N#yI3=mm51kFyyx~cZf=~bE_;>TnGw92r|Zb`G;5o7b8i3atTQu*QLo@{tj%y!g0iUQmy@@ z9XipQU!0KZQ70j=9XLwP>AAg$od7kbRc`0&AzR2m>0J$X11eHt?*i7SIrJ2oq~nJV zZ!Arn%%0(Qn4LuQMMTtXhXJ{uE2;L_(qKN7nvy~(=%j=MN78}Fc69s))!rl{0IC-V ze^OCMK)LtIKK}3kkCx4h5{}(PhZjh}_wmS}p}F}07#-lh600E~J8dzsq<8Ps%`+fO zX=-S6%ppm`ni1Xb-tJL06lo|n(zYEaP@Y@MYSGUSLf~pn=GUy^tPv;->=r=r-<1^z zI?iZUa57s?acbywl%9YNg)T5Eaeny2UeqfK}VoP{Aqvc%peCON`SuyGqj1aS5le|+>#@^g`3W#~| zDEa8fi1Mvlw1y!XNwueFS3XEXu54^-it6ithziOcJvsn>jmK8GR3WeFx3|!~2n>C# z&k1DzW;J|6oFc-)A3D@m<5~0cso2<^JyFBN&qVi;?M6y?Bpl)iM1r*f8ufqqGTC{* z@25|Lv$N*JTkBxyG>c7DR8`NMJQ)JH9P!LQu&marY4Ue%6TA?-2oQ>p`HfnlyC#P~ zTZ;b>4`ID_?bgC!`C>`M-CGxgg>mU>zzEJg3zV6Di|XKR&}hfS{YaKRsjshZlA8SX z!t9`*ukRWHQe1pr#iD{%Q){aSpjCVufbZp_^bPksJgRX(!EA}Wog|M&ya4fxj*m}I zPTEa>^Nku*DVd6ojC_aN2nq@^^`;5*_s5NOVL1oN3cZvWKat)!eo@KFkPk-?=7#%= zbI7tpw9lVE537NBVG|4u%pLM9JV8{y%&;nWi3BarbVI2RUK!sT6ozlwCwWAAw+Q2Y zdcv{+uLshIhz!-$J@`BPfhgy(IS%yzG6A|LsY9!J2|MKf{jK8(2tc?xB%LmhPT#$8 z9=-}xbR}gRYptk>eZ8?j4BvOMi;5GS2+z}wHaF4=AnRADdolgDm3(tlG0xx%3Dw) zfVNBRW6zvD8xbD9LiwQZxiVQbPzCBc^Qw*(&^#!YJNc*VasE=Rc zl~q-%?WS%6m`JF?nDWkcA^>S8G4cN0yJYcgj@H(9Q4;o+J*z-l2JteB8}=9v@LGEh z9)r6qmpM+*`PbYWp#7iW;fH_+r!qBuEv}&)qtQ7BLq%!v#E{)Q?$UE%R2t<$QDn=& z)HEYK{jSL8G}?H1h_hB!f8Z5#TVG0tn}j*0xCpHeG#&`S5p^T9T$h?!TB?xuLs{U; zasyn#ZGfJJ&Z;XeUJEEuQ^v16JGTz5u&SzxO}o+wpClkq@w}!DJqXYjjD(ipX;oEK z0pZX)fX-I$oFgzTfS-46-(Ej)>C=-ZPmWUy-?5}l0S78z_MH{t3NX%r$a^B8D0q-I z!Cq~H@f}if)i}HH)O<}PG$O*=+nX9)FjzR`X$tgJ9m%^-d^~B zR7W+hu$vOCfi}4DVq@*+r-PzWSn`>PiG6!Mpw|QqK|~8=e6m6TQWouGJSlAqKD7|0?8Pgnl_44!vJZCXvNFeSW2u5Jt6K8RrNe9Ia(7C1%__W5onvT z8;6L9j8_HjDJiuBtpLNqxdN(KLtP!FDHWBW8c&_1IbJ@#6QTq}a^cQ%7-!1M%e{g| zM-42aytMOywaAvgbW4oXLpa%Z-q*?BM{UrQdmnd)j_FO>0tkAkpac8oi>b8$4Txx= z5oK<6VZ%Z1z+SO>n!wk(dXYdNciH$1vAVnriUC#|>QIKMY_(-iaK6o$%;Cd+9$V}3 zD1sy%sq*Ke%n1xTnhCTlT{(OP^(3|388#a$7RPqLI}%!YfTk@?!BMNsRk_!^^X&;@ z&2>06XzocwL6^wL%FZOLa;hU<#ogpf#sK%q-@Q9R&gs)Oap9;0Y(WZQVk6jrX43Kf z4M6Jbhv8y@Z^GZ-f2s~VjHlu}*MCi2r%%^}t@fcIo~+ovqz{%AZkU1s8yG_TOE}DX zu0k0Gn?#+!{?gA6*=Dl8H1|5q@ZUd(eB5R=I?>Xw{FAWfDJ$0$YOv7K{sasBim{ZZ zXywlM#-hJb)1T`K3deq#9XwP+K*P;_r8;p6KoyyT_QPWE)gH{^1 zZ+Ad&UE{5>O{}+&3l|nlzb1YLl!J(@a~$z0 zS{DA7bO=q)yRAP+3P4{36~G{C^UDBEUX)#uR*6BFx9={Gq)vo1=EAJ?(gZLIIdc8!CbCJv~k!QeeN`E@2E^|2Yv_aJ zXvQm=Q-&IEpbo)RdWnUlkedE&db;(y`zT03U2h|XYRS+MuP1!bbDRJ58BK}D=z4a- zY15)VBCT42ZkK&>AJgM2B(hO?oCX(wlg5P>4z`7@&HKQ&l#J++qDWo^1w91y7Hkhi zp&dl$pXHai8pSW#CS*kl*q_gwL2_?_y3ef{$!BYH>(*Cjdo>A3+a)jBX1a5el9DEx zqb{5})Y~LrH+h|C?eEXM-cx@~HQdNqL(THRA*eO|cmQGp#5F;S7;9e)G{?~ew-8}t zh@R>XZ-fWS!LZ=q8$=0v^E`%>$iClHRs9Z!3R+&zQE_AAyF_=%%MMSIC&tnIz+yvc z)F?Y^?clJ2B$}9%bokJrBS(&W38dC3lYMye=9jNYk|6oupoPZ2h++%l9YKHOn(LhZ z`|}elz`+q>_yrNSRf1Uv%MwMsjB_$Mc?)$IIqRLnTr_A`9KuHJ|D;MYwn+kq3b6>9 zHCozi(^$ko9KZb~(Ci=~Nq-R`T6XW=jS>KR2P{v;$TTG-XG{(NWx(Z1F!78vorT(B zYrBX;>g%4TI`rhvZ+?cT*xhtTx?oSf`I z(=$#mxuLVb3+7h1%n+Ec?du_}LinH|_%ZYH@^H-PnV8^-c9{NV0xbbftF#>!5+VrS zs=3N00x^(=Oa%Qx3&I_IgQhN*W(r?>Bz>NM3EX*oX$l!s zg0v

*e;XPVIlBF9MaAE3s0!xqqU$%wSliIDdX-YAPx+vL{LMI1DlQRcmx~bPDmp zu*_y9Bz!Hk(gCd(y!cTshSb2w2p%EGOSsiMtQy?|`Sk)ZsCM7GyZ<3FAksU0;pFBX z&nrf)gjWh|8%|b-DnJSb2ZnJ=dNn;w!Y{CIKhR3%<+VdX$4#QF7^U?f)ZEeig5ql1<{P*14793fqS!`P6cf`b=ie=tuvu7PlfkI(mXovu{{Q2=2 zsf@A3LdlW98KghRTF7h3{=AnDdj;W;elO~4fF-)Qne*DUpA=~!0~EFufVZa$*DH&%HTIuWYF=G(MjvN6`sSbX_MK)>-XYC z;xefr2sq>RHvle-^z?aZD3Xa*#qqq>iEwUd&k~CLP5ClT&_=KjjEs(cLw;FWTEZUJ zxGXoew?Axn#junS*ET^7&pg&jG@-q(&jr*6ohCC__2p%ddsCp^QGK9yPYe&!I3IZo zZ~%U?Afl(gztf~{`pVc1H}^v#1#nA~STCNJi{Z{*$cu$D4sq}!Pwx{VCr7(VhD{!) z1*V0mLr~xIi;GieUqMwIsPT-y*oDjGOjU2q*HL?MJ)Qy~t%W7HcMq7SqsG(IGU<~H z4DMW}-dd1Ej{$Il+#@5gM&l!_(6;MCct(Y*$%{yqrXN*1nj4afDZwD3FVKQ9%BM}$7f@~L}2Uzma)*#Q;)Ad1V$#v z50|r1;PB7)`g)&+JFiMtmZp>$n~s8$JvNevsfXNd=zzWYM#|U&Zi1e_h(_QGYzjoQ z@M9oz<$?m0mDPl;8#Vyj#}cj_Cq&EbF)%PlbyfFKKtL;c4LIPzA;EQuwZ&r0CubA_kel6#L^vaKPX5 zkfL$$c-k2GwkjNI~*!@hmrM)`m(0>zeg6;D7Bm$9X&-x<_v!&xVPIT9UM_S(IE0r1MxXB z^78J>)2EMSwm_oya7Co+cy5IWKSqE0)^DY#&+OLJOc8%$N0(7khSb{@Q@qgxySmg4 zjI=mE0;~J#&rjayH$tw!gF@H^OaW=_5aj#+=vRCKoianAvUzZ+AT-yLz<9#K-}&{c z34w-l=Z08x@!}LP6zVZS{kBypw4G2`xrwm)4%0mnG$`sNbe>BcMferEvpGe%#2hsk z&jJ%ZLqrQ^m@sq%`wvIA>R^hXPZ{qGQkv?UM8H!nJ0i%Xh+FvX=;n)2hZmhHvHl#g zVB0B)4WUUC?Mh2cWn^Od2V1zqdsviV4*Cog64)DUs%g2^(oanpnL|~sr3D2q{QX;? z>U>SyPfO3wuTdg>f}C9DL3A2|c&gn9e7Ig+B>A{|u-q@RrC$VF?){orP7^SUhuo9J zByQg9P<>3P1FnMO>Q#b_u_5!UqVV&+LszD^f(tNP5uk`in?@_@`0x}$6f-;c{ zpXn`}u&UW|Kq-PuRaq$*%_q^?jnzT&?FsSeWJ!ZF6R2fuVggS3PNSD3>_AN5MhWg0 zqz_QL_f?IUP7KZw_H~N|#f{!8Z$!Q$?a0$(Nv-!}rPH^YrW}>rdQ2p!&0pM`{Fb^} z`}y-Vw!?}s+%2C!GhDtb&wT2@{{7cVYiBoJ!xe%IFg`YR?Xbz8%RvA2^-{MDrpft)~zjur)_Ys z3T+keNDhX8RsgTYJt5~qw^{%DQ*7nv<<38H%%{G5`NFP04V3Hb;({+3~rgTjsu z8(lvd^yXM%jwM>x7_{t(?(O0oO3|Tu?Y6Ze^QPn**WI!+R{KyZfe_?6Ie2*^uT;AJ zyE0K;N;TcHx#-wDnjZmq}&My4-7C(4zyrd(@m~QC5K8I z2bj4y)3_U|Uw{|^n8mf_3ULP8LqWxkL5bw#7q2>5B657FZj)z~4QF$ydl*u=kjv2&!U&pGohR8T`eH3JR$=EP_{~ zv-&zsb0t!;I;WXsWkW81&M1Mmy)B>>Ky~GNWOvW;`QMt%?>MSQdG2?;g`7Tr0qO3digDMUQnY;4#qE}+VJu1}>cEZFoieny&)tc&}Gv`<7R z^F{&dsIL@P>S}yfH5?qcg~CAHoIEc?>(v~?qiDc4m)aVTn~gb6^c2aCKO?W{PZsl5 zx%&gc@OinDPT6w@)`7;;@-JaLb%?OrM94E1E-w6qKhtvsi{5C2?oN( zxL@a9RFd;rAMJ9sF_R-6(EahFDE+GF!M*HUT-n^!jAQv%2_oJGP2PFehUuEu9@L+` z^TBe7N`c|RT^K&Cy#n6-zt06wcZ<~Bz41eZ`BVqf>)8o!PJw)@aOOVES_~L8h;&#f z6Q)#AmYW(HFh-$0`dkU;rLlkmfJC$&Sg|Mb>-H(IdAd5GvW3N%?ri6O>~8htrJv(O zE2WVh$SMqah#&K>{MuP>U{8d2LPXpUuG!0C`NYH)+d}PZi`$zZhp>lP7^~QO&ClQmykdT zxvQ>Dn6m~U#@=fK+=4DH@~1auh|J)5(57W&okdee=t*3ZmGQ6N;KJnOUV6he>KORL zgG6-w-ycQl3lA5EKRiXB12mePmIhZtaT#aeZzs@Agta7G8x`o5V5+pW@2i}-u5%<#O_HN za^%wA-?PvA9{|Bnph!2jjn-YK%H{J*UdER1M6`Ci$KgC&@p)|b6h|BsRnckUqen5k z1nzeVkrvs7q8NGPed~KKk`rky+D15D$KsHzGwYGQ;0!d{!ot;P z?mAxAuCt{LDbK^Fdl$_mbVz~~zmvFx{w%f8J2Inl!i;_xTzu}^azgYNVHPZ1@Le;C zDR^QGuI?b2blhj&;%0c~wzhzzg!;|MK101|x>u~RK+KPOv3hR>eMhco5tqwLaE8sH z!Ao6QnCHd~jGqIq20H?no?guhJ27gq(Kdrv0S4LdcAmh<$5xXuE?s9VE(S^@-#aM^ zty15_WNnU{4CBdm>en!r19=b`sq<8(CmcZ!Jim2!d(QvbZ!7&N;Wu<+ZYCz2{q2OU zk3CH=Xm%he0i0cvHH|Nw`Ss3p>f^J+c1wFjrF3;^0_#S;UyrCsW|!@!9ajN!w3(%wD?dYX2ugwCdu-iWw%mf2sJaACP1+c1KIrKtQJ!O z9{|%HruDFrXO#2X$+rj>CqWgJE;O$`ceaa;N0*dv zZ){hgbw)PA2g?yf){V>!)x;k=R!`L``;ki;6e0{Ehz1_ZfmM~;RNIPubi~!Ck?{)i z@^l;Fy!Zn{DZcW}Bn9^5w{L;ymDXzmtueqT>eKVM?cgzrbEv44lv=lMi^e$cU%DYL zdGM??L}5`Wa6&jfuuu1jv<$@8D`5yS>88K9Wkw8_88>=c9War{kCW59!{8#$s9E_u zBy-=Fxfi40BEX!n(1WYveWHM}qnSlToa-bpWK~aSZNdpK5l9q{;`~3}Q*$h~6JkUO zNu#IEo_%0s#Qq?}Bq7R-=3?p1jL6R~`ACd@UvfDGo`s~W@=f=8QN^K+H#fK1n$ zF`U4A^(rQTBNUPtH*0K_R2jGO?^GKkh|u~I;$MCq7&yPQw7att_?g^$;tc7FB(&~!jLqP# zxgS+A$9ScbXCcOb{jP*)BsU$Rk+^)G@?Es%pk}xIEa>hRets|@M$U(2TyiOH>$kVQ zR}9kT=0?$%x;jEbi!dA*_lYRr3tw2czTTL36Vu8c26Qokf(J?zz+V%_G!%4uO#f?p zdtQD%ABNu1!D0wgHRm25?5G5OBT^y(VcqxU59ggYqZUlUJD<6I)}N8q0DS_SK0y2& z@X_1YXh0Cj$@z^O$-orR+~1keSR87vL1}0*WgF0wlmj@Eg3AF3aMRO|!UQ7c*7E67 z5-3%`K?E3ye=hN}k0@&FT5G(VV*{_AsV0QwvcMC8wzMZIDC#$(kkA&GL1fF;=H{&F zXs-Yff*8{Hq1Z*%`MTR&4CjMZir_c)p!U(=a=dwD4r6?@kYodxlKP#vqpH<03|>V0 z_V7aw+NfA8Mwr5Vq#vgRnu&9E44qnmN26-@1!n#jPxbqjPiU!F@qxlvMj`19lb@msg? zXoRZp`wB&&njut!Z-D$HX}-*r;TkQ8AGxRLtVp@BjIf(sq1VN1-P07c%{W zmgtnMEH^Y6jcSXJu zI1V+1V8;Zm??bK$v5-F18yyg!aJdv*Wg$}J#ONsWr0+PM*l?JF#)}Vm+YGi>dhU3D z1EM=@g>;50&rh?|E%E0}q$qvAgE*tTxqOq>+@ix%Q-R1IoZuc)PP}!o3@~%PZR$1X zItHHEizLeNm$1#K4;p$r=|V>4MMy{|Qf`^!AMKTIU+jZb4Ehy(CA|0O{&3G}i-oxb z-9bMdgAu`|gAALGnNz|9_T9qgEB}I3%Ekxw$lN7RSliQ=?TdkXPLq<_On4yL@D+m+2NGf=N z>XG;%lGzq|5W{lU;6uhT+mDSwBFRnCne8uS+eo@j^QP1^tUFoJ11tlt_`l4L6N{kt zUi$lE&>YlMH)fD^-#saM|U62sjvt*)od~LiA1{i_b*QL@R<=!km;Y= z^qaMG#x!M&q!knbIAdP;`d(yV3Hke63#suKB|pTDJfv&bi!kh5P+9r9S`6AL3%jE$ zOkL>!X%8*+VX5TBQo+v$339N${%z`xc9Oo{uPM6DFS1H)V@nkmg|VD?zQGG0pW%V* z>!S0)R6_2gt-9bJ);C#&qBnx$@ywi6inPhYWLfGB2cwr~MV zaPbNVH09C17%c7E+(3tpJ*sw?>w~w&mgtp?Co4sxz1T@IvWqLH!yqj1c&%#8&HO0O z(Kjhe`jju*>G&qxxk9f5M&Z-AHfDQ6uO50B%UyS0@x@XY?ZKtuBm69xM}T$EKeb{7 z(aA!m+gY7QT}0AzmCzOowonbW5MVfSIlFmr(B(%I$XW$FtO5~z;|4)x0d4NKGD}A3 ztFC(YZTMx~VtWlCgQ<0^^ptBhqdWS4nnw}_whwyD_8z(@BO0D8VWNDg&(crkyjptE z+ks?v`=pqHqIV2~++jf-R3{{i^KUX6=)7|G|H2-*wq>n7G_kyCbdE#cXT5S~GK^z_ zug$r2oiBb7{~Rn)b=_ReFDoNB4iN#`!Zr9FULx_FK{E?kZ-D@U)(| z!#ArA-j|IkdqMnZ1HoDjKJDg}*CjcQP!i-v9!{~SJ z{CPjE4x4E5nBjdGoa>w8g30#&7H_PA4#-vPBgB5*u<^3ha2%N7ojlKb`Q95-xnJE0 zttSrTqer_6Y+62ic>FhM==1MSQ&;^+WTd2Yw@-HugN);qv4z<{S`PCaAr&h~;QT`S z8;pidoIL5eIPMQ#0E5{OKEZq$!LkcJi-JPV(n)L9cW(T$pddxXy3sJ&#@N*1iHTCc z^Z&XCUMr80rwx6SsQJyZrvVYN<>##&JY{LnUHM1JoVI`?zBQa-c!tkD zS2s+2{+gW6k*j09^epy69?Lox0_wnoj#e^IbXL|8T9lC2SYa4pq3>KHX8iD2fG>LbBXi-o^{2b`~s z254Pi;udjPGKSIN$j3u5>G=&=cMC~GL2)7f9udK~^&90B!cgRo9X)QM`y@Jf(tG%V z{H#7y1OWDX?d$DdpJHowhZ+RA5AL|V&r|fBoOmU({UrYBQG;f1(|GJ0Qi-X#)YO}z zqI+~mzOdvePNI*)6HUHr#k5q5C4EJp7C;38GmdE3*w`RQEs zw4?-nj_l&%uuWwT58>VqGLpJq%e0vOjcPX!wH{;R0Try}jam=lqnC0Nw~w+<5Q^y7h`AF>6l66XQ|$?i{iX3h#Zsg?;FmFyWDphWJ{$gJ<7B zDGRJ4`5rgyEFAA(EjRO~)0?!jA;1=qx}otNTE-H98jzNgU-5V$3;yPqo)gx)WvwY^T8m;cGgzI;|}*^fq2( zWc&i|r=h`D@0)Hs@KPRc8T2Kuw#utI*w^%`EzQ-i|E=$bTF40?@ck}QNpDgtOIF{B<5$CT-+gWgL z1mFo*@U5rubQDBsRBT1Bod+E@f1(h81k@Xekb3#bov1=H+M-+PC$#5-eflpNNts_djR6TD!-$d`nsP_y$ujt6gdco@8A@=*V zK6nVn-}!kce!;~Zvk`*tn+*BdncjRg1|Q3IO%ePcdaf)#lait$5}qc2zcBkz=*@23 zU0|?`ImwR^n4}XYh>skhudDrGio#Iixzj3sR_N*J4fORp&0@SuOL@-hLI3#0o(?_Jrv>Uo zMV!%h>BveLFJa>2TLTFNqua-o3FynHLm!Uf3379U2RPk~yz%q^+S2*OMOYCEa&nHP zJ!c|jbv;VP{(pxrPz<8+h@|4;Wz=fle+41ru?lq@b*L1+;nHcHc;eU%)-d1qNJA&X zWxydqMRqzo1MNc1kXEPMlID$ntRm-#(eVg%7IOVj#nJllY9u4)`r)?whWIBvZ2xD z#ayUqELgE8)P z_4P!OXwp<~-zLYS+u$UF`ik|1bI(g{WcHsptn3spOh*cy`T6-17;xYBcue3gpDUuq zNdHdSH0AWM3U~xeUCpm;sRPtpqC~JhKyZ8jiy(3{*fpXAX#F`F#nV7&tCxEyKK1mh zf;>k%a(_pY2)OgH&bPGvuke_|1s8qT=f!Quh=Ce-$IzHNo?>ESJ6<=xvSRdm&*(uu zN>^9im;S!KA6nj_r9nPKawK$dxL*EC9u!CzQo(WNl+OKLy}}>64TW}?2lEToOznV6 zcwh}_DFb-Dh2bx$Z|cYGnyU#Xvwpu*{1ld2YwB#z!F-&<;G?sS=8z+%ooMDBjm6a5-hmWvt!s27CiSZtWJs1324XC83sEB8y;Il9hMB@cN1|G^|y)t{3RcmCRyrP1`?%;FFj|vTY1@|qP zxxa+z9?zWtXBX_scou9k1;3q7rKhlYPafD>h*$YemlzoKw~1jU8`IPPVHiR3efg4} z4)m`Do|2Wf(caNfP+B@cXVu(T05d9dE|9@^fEKvlmzyWG_x~h*o^&==$W;|>5{?I^ zGVuR0GN2wZlIn3yFpX8&LKJ*92F;PzaClHhVA~DFa|6P{ASS}FYIptmuC#Qd1%l<{ z-n~jKEonVH+Hj_;sJhT7o!E@QmjJ+cQFQk7!0aZ?y%Y5xpVo>0!`PdL<=B4h-%W@H zO;i*z6iOsR15!zDlFXr~Op&2TWoRNrgb*2$OqrSurD!fgC{Za=8A3&bl7`>sbU)wc zdA9fWzT5U*f8g$}uIoI{<5shW|UCUE8Yq=$~gGGZYK2Sh=DYw+$;m<=* zoSi$x(ySIIuIR2&!gkxye*&s^+<2BO>HBBME-EkumgCW**RNi!xzTU?rO01DzZ4g# zj^BUeh+^EGgGotEpfr2+)&hC~ZxFHvRJd@d|f-bN{|wig!6ots2}Ij41? z?|7jZb*cCh`C^X76v)^FX)^2@OK|M_w=_sb$rH4?4q?>{TrMUi#xtS$JH`6ssZ-q_ zpT%I|*mRbVzg(YD4jC+fz2W#DB)|PwS9ioR(0WE-+QNYKJAFj`FI>I)nX(mDDrj{eiyS~Gc5 zwY$;DYDwD%7GJ{cCP&%O0(si#q4Nh-w&xF}KXVPGbeE=0Yw;yCS0=5V#fHJU5#K1c zwX2~H5czVhY1ACr4yM4Z+^t=nnJK!k2g?t-=5Di0<7OTqxQvU7i}8+@QPoRq7A)}K z#=@#2T&=K_!G+|m+uN?EhV*Qdo3!m~<2K138f+v|PYDSL&R~Hps~)!}IQS&jo?V^E z&L^%yTvb%)<+xS3Z{KT|FSn3rxxe#Nbx=fT$Jq)=`*526R82iS`LCP$s;KPQx6g9Q z6oE^8Xz~4{$Bs?4uz+^CcFh{S$N;ju%O_8kgHRCypl1(NnobuPQ&MrDu=n^0?z1gl*b>olgbHT8UIu+K;Xt$av=y5kZ-pcv_iS+iqi++%%C_Q@k zhAD^*KAhit{g3sA4Y8yt2M^Ao6++p;cQ0Q)Xt&uFSVvY?KN=gcD^z{i*LvYLr&QMV z5G^e!Ny(3&KP!}8NCc3w#$lC~!O{>u2+;?2?x-?DHU>Nsg+E4W-ZOaL>(}dX<+>kq z(68UF+fCC-^UJ7K;I07T&x8?@dVecy3q-jUcmJHzUZ3juM4d{5S^;c-W%8Ib<4bS9 zs7SwzM%ZL(>PU+!XU&48Mdjs-mn_j9H0TKV&Y3gT7v~7xxUthp)Z-0jy(leJ?$wLl zUBcJ;o&E^Nux_NB!~yC6%}m#4$N<=&9PBjEJ387^X?5VA`$u<>*VxGKYps@RnQ+w5 z`~V4nVU{B^&3^EhF*it0TBHpu&o3g(LrkSn(LJWWCmy>32S;O+hKAq^oV))11mzjY zoMIF3k(@a*BwU_#hqm7DtM7siREOOpGtqDH%2mZ*}QAUZ32RMcyiy!lx#6=}Iil`_$zUZwi%Ys|g4jjRuw(Uaqb zXlvK8iceR*!)00j>@xAz37m-n)8p4K?`ET0OLK{xT-S0(M^iiB2TiSPuM`#yTmTha zi*rTJoQRN`?D_|{b-5f9BS;T8(s6%_8f(`4$-HVw0Y_kvxAS32CF~|&(8qkx&%<{- zW+#9qlzmdKB}9B2Sw{|NehcrTY2eKVWESQlLA3Ppk1rrG9{qSPX^Z8 z{)Y>2)&V`IlhZje(t`(|ZqyD$Y_x>S*#5^e;1Jp4FT0Xg zuTGT}ald6}*j4c6tWS+=0K2Bwhi!(T`^Q2a zU(^b}%6|~vpiCkvHAaMqh`7tM5Jd_n(dhBqyt#rxbl3KcXXF$VkP)f(?TdVM_<#X8 zaKiVal~oRDtMgWurKo(V5J||&-Uoguw!Cuvy1S=mPgz;(S+k;ThGH&C;$e%AHaC7z zvSP=3z5^a{zNOf$&XDE6`RuNv(?fB0?B_%A`@ zf4<_{^Z)vm$?1X>#{axtiMRgG00iO7RsDoZ{NJx;Kc9b{&HwYoWJ|f=|NL8|YT<6{ z=pfR8Zr!-kr%s&`c4?5l&73(-gv7yv$87#!wS4(oBr5G6#tK$w98d%o9uMAzHeEK#W+F2Yap#>A!QFRo?$tbu3US3{;V-HV*)CQ$%dWKELSbCqR zBvPp#k!64jZl9Ar@zbdjS zBO@bsqluFy`H-I43{aQZN6V1MTKo3xqo+@6jUvGCaj9ImU;$f`1_oIa=9db$qJ``5 zo5?SBV~?;Ka1Khq0AT6TenCKG9DE;;OVh!CIB35^YUA8LT~;K4G|{gVzWMiejvWgd zxdY|yy^JDaqoQV$1Vltkq+MJR3Q~a*e29+DtDnu>@A({HG(QYYNak7CByR$MnQAUt zO7=lMOs~!ev3O+Uh~>xGQzZK#{Y}msJa};5_ysh#{~GiD+F?Is>QoFg<5jl;5WuTL z9VhfADBXa()aB)iP6gSB_3U|&9FG-Fo3ru4A6JQxsoN2r1GGW)!r>vo?d?>RW}kth z)`(7=0`LRw*}>X0I=qIe@&~mVgixYDZ1J}rKR6THX-!YUgpn!}a%dq4jE1rC zABw;Fc`3IzAxJ)-QX`a<+}bxXWZ%A1xRX)DCOOLY@Bao+9I7G|{cY(!A-OCSa*IoI zu0VTQJ~t&$ZZVp|$&>v6&?)9qJ}#i4bguXl%>#a7qMb51*1kP^$WgKC%Q0@}ah2u4 z|JlcVwT{MCqFN4y5cvstYD;UYKSCKK@L zN)kc~;@OvbN0XCVSiMgpj8_AaZ25Fq`%3$^#Fun=(PALgF4K?fJcUXAF1bEL$!I;; z#56*-ZrU~mx;110An1mU;kvp9ke4* zR*3Y7dPr5&UkF19L_`Xt`%OrG?Mm9l%X;qlB4=y`N`Z;D9xflLb85?MxtfnnzWvMe z!YCZ(FJFHC!i5#3%WhMS&@Ke{z41qT(z`wDwf_9DDSSQFjAT5Ilqe!v8O{X@G6UO=yUnYbs2`9>jq61B!bFd4Ax3sxc3Q0E!LAWXq8XbzD2JA& zbhgRJD=2(^_!hQ2()aE-Qdv;(eE$0wx2>DHstVc`XWoEQTc&%q*on*Ud3?Bb?7ucs?R_$q!`+94CH1~BY3Rt2 zmXa8>Ouj4Jm&psOo`iX&QoaKgO07uhr|Ti-?^pR!)3-kgACqy*4?x#OH*Zrld0Dq7 zL1NHJ9~KwuYiLB>ySEf&4*e4Fk5zk+9qU^Iv?0AQF8fg)fB8BzVwsXR|K9ZpV8 z5El_n0N0@GKvN*^&l$FR_f`%Hd@Y#p@#4i;T-1mcaIlBeuuN$qLTrBa=?7X02nEB3 z4t2kKi5h&vn|tbtisn%^bOaz%31GVK)!0~{@9(;sU5<~J(||7%^k^BZRKN(Gf1jg| z5xW2%kL=AGfEZZc%KiJB{0pt1E!NA&G##cplNzMV@N0YY*Fyb>R?~GpI&OWt%}y}) zfc;ewLIQ-5_q|Q;A0j@M_eVy>AtWs$b8*fUVGsn|cigy09^}nnjM;Li&`dgecEFk4 zOfx|3^myP{YpKDLCRLV}j@Q>0+UWG5%I4Hg!R`k)*SlC3cIKvM8EDMr&6^2( zDNm+4I8?!O0|!F-KnM54nKQyj1hrLh*RC~v`?mMMfnV76*8-vD6MZN?zY@r>gW^5u z`QeeB2uE?908&G0`N@gGnLxUT3@fa-i&rXH>Gtj0kC@Z?!)iaMv%jk9-qo`L3eiVn z0VZQ6c`#^jmFEZYCDO(Cq$C>K^i481???JcbuVAZERAhH*RFHBL!3k@Md5a?osklx zK2_mhMx?RHw-GJ9B1Z9oi)9v7R$i{KX$ig|hT^P8vPY|~-WZ|c&&ioGVZ*=x zZW$ABKDlpOvZO))jJq2H0?HkrVZi-#9Y~d>0*UkXZBJXll z;EgI>&3VY*gbq3d^t1DBl@M98(vf7d1%*gb=I9?AtJ~2;7EQ1p<3muZif+Ox#wooP z?K{hd!8@Uc4rOSN#pr5l56a&N7J}dg8yCU6=XVnfBs8Zh@&70xsr>EAL^=sCgpbvoU5p_L3ih(8_2o&PqB<_-E@Sje4 zU7uzV80v?z-6WPxxqR*%N+-qs{ju;rDAxz4SUL#X53b9T=V%yIa*Voj(8wjoYk*LC zZlWDX9+SO zHZn9^eJp0ql)06uH-5a2k6Pn#5qr%gA^D7d$pT7l7wddNKb&;qH1dDXsuZ;vb!+eE0lUfZe65)$;5-p8)% zHVG|4M>f5S0un(^#AKBoce2=ga%RvdozxphK~Rc%xVw)THHyGAF=Xo7k0V$Q#CFbN zvgUiBls%t!IVCqfCBbFdrG_|$Wf$KMu})QgOI`v+T*M)bDu;HY;J<*7dsX& z`1yis0w<(yLdDCMpFVyhe+fHwto8Tr>Rbl^Vww|=AK#3R&-kNCIWQ#&4}<|x-t-V@ z%p8;lw#~$gg=Kz2!0Z zawEBdSjFbwArpdILFLhGY84fY8xEOb125dXIhJyt{J3B=mzJ=9e*sL|`zcvpG=gOM z1{zP;ZHDeM|EjL2s6R?LGGP44IoGPH=kQ2$)iK3x%3KoAneF9Bhvm%x((-(t zIggp(cgApY*C4=sENZKI>LE6Qpcok$iOT)LKSzjEAKhE2nSjG-$5~(4tN)U}=_bYg zbm2C$EckPZpyYNNhi`@HfxXQWaZh}7NN4lkj3Q$eotlv&M#yaJ6Jwi@b^rcH$h4G9 zeC3ZcLb5^m@E3-%Y*_^I@B)F6MFI{12baFgSS2g;%)2QZ`Oat^l@@=+~Y`-PU1M> z3B8Qa_V%aaH_(Z)AK5sG>zTCJJgL{5R!P16_b8r!_kM_DblzE4PSF>Ih0g2OzyEmm z`pT>gBg{vW{g_L_mr8xTj1$lk{t^A-uM1bsb=Yv6pNIeOw)0@$@zx6$-aa2rd1<@s z<;?`O}47C>tkN`HVvAO-hefuk0vN z+b#+KI(!-Oui{TolCu1Tp-Ax#qWvnV?+Dd<4^CWl-* ze_mWc62xKYv$C?wFCT_KX{}=z{Ku5xHyb`F8dT!T~|X<=*}7xFzmVck`cm zzWVNU4z*G53gO^MYS?W&dE$iL1si+&5xTn8$8LXg-v%8tLHXfL!BJ)nh`Dr1?)b?^ zqE-KeIK1UaM%Q(iUmispZ`>)YLHfz#X)}1hfQ)!oYC1Z?rjkI2NRV8=dGrYx2g}^Y z$0u#+zzoY@;mR+{=ABMEO?EO_k^yCk8vHUrWE8WkZs^!wQiADeL7=s*atWm1tg835 z4_DdMmEIr5Ne>cUr6^846p%O{ZfZz3*>mO$Jmm`$&gs0yxoB4-CD(3~MhL`< z-fq2hY$sn&fv_NN#xkl;uYovTHi>RGK4S~)?eBK`ytB4-EXB$9Cz*h+@s%mX8uQqf z7>Avj!?yuKSG_)C~3#%Y1t z{04J0)b@mInrUSP{=YXIoDBCLKv&rwJ$R7U?PrgEqdwQpZvXA)jUEz)mq58ejrnwt z>6WDp3yCm*6Y%z~=6r0d$z_Os)K!+7e=5P zv0r~zT-+jOEKiVgkUyfan`7Q)=u!BGz8$}VB(KNC<#!6c51wO%K9{=sME3Wl#E-tp z>)axAgtF_0LZgleWSGQ1(Ku!f2-q{MU!5drtx8VUfI_Yv@&QQrCTcQv!{*jsDE=WC zLfHe%49zzF{wYuAnZvhiMDzLi-A-w675W9`^-|N+dP$l|UQ?0e{2^I_Cw$-9JD_?0 zy1cO~7EYloBhBrjze+k}s+6p3-l}ClAtY@r({z3-%9$zkAZZu81&9Nz-%#MxATKE#4m?HHMl0eY3aC?SzmOsadm{2$?%JVQ4(y>Y58%D5z>HO6G0!t0P!q?AwtWBNgNQ$b@)pAb`b zt%r-qUh16gs|QcN2v+iTri*oun66jrk6|SiiPQ9l4rRo`aof#cN7V&MoyW)%-fqq$ z_L9j5n4j7ZBXvgKDM#|UL_SWPYR&n9fnC3sz4+pH8V`A@CYA}w`P*j|IGuXF%*cK% zBsS72lanhzmrP_=>+M&ZQBQFin$0RGE-nV-dEhzZEh+ys`e=g!+ha#&F!V%l?-1-| zw{ItVRBHi8=5VAnKC5sfKo1)Arhqnil}*dz)(pWCK=>DXv#k?ky4 zxG?Eg#Dn@#KcP4fNBH>yb2>CWcRvrE()j0{ySDwBy^rpju4$EQn3K02&zq(%N}oTKD`r1^qk{KD}Y=q{=0$w(fR+ zdgneM??1e9WPn_U;)XH-Uu!jLc=ElhU0*LZNzNY6$O}NEL-r7JV}HD7wt-5%>9Asz%8L0^&Q-;FL|{ znLLG59@Qru4D5(#xZqorU^qZP19Q;LWK(Cy2c^v<7k3pv)Ym?99z4|8^UT#od-30fe< zZu<7-Clr7fcRgPHZGS8y@o>fLSE6L?0Unb!v$qhyTIC?mmQA@l=z#`2YN-TKjiV<| z26wC;mZ44SyMUY(w$hoxlfuzg+B5P;g;pWC|Dm79c9=5Hc?Kf*8 zZc$R#Hy*x&5lh#NF2M~qmH1i4}Dv_%Uo=~5G( zE%&QrF20$Qedpr>$s;>Gx(yx88<5t~m;;S8pl)IT5zy(2nBP)f-X9PcE-fTga8MR2 zY(=XGBR2Il>EXA|1f1{ELScv5Cna!W#5kTCHFrnLw?UGs?2@ zBx-AGzuw4qa#Xfz1@ALQ~hok|lGMT3I&6`dd#Gnslq5b!rVHXY=Hf*o`*DdPn zHd@&IQ>057zBJjj8c>7+y7PB!|E%#+(M-@qfAaYC>vr@K0N)gK_74na1@82~u>jvU zTQjqr_+U^(smdxRi9m~bG|u`PqXF=tW%OoUwr<+heM@uS_;yAdKAZ@d8*>>}9d*rH zZOP>2Akg%+S#V`Wa1}hDTI6gQI-_xa*WSHq?x}n^EYq`R{_XLjTse}R z3xwx3@*pGd#@!aUN*%ps$P(<{4qUmT>IVx{x|>H?w17#%9DGmbc4UXSB&UQxFj?jg z8_KEFtjuzfI4mPOoz6SvoS* z<~*^; zBVI;bz1riA!7l8geRyEB-sx-sEETlA{2%`K*2_+tk3}$}$80ErpCEcyM^lnj%^hJk zYI&YCi<2skVqb5bDd>*d#o5uzwg2=N6Jqh*1@3f49M8-#R%oefM1NKE>f5(swnV*T?yFa7%HN6Iso#w}ewaebV4V=EBU(>5%|xfLKtg}nicI@u zb*(lTt3vRfx|KSsaKqM3pLfU{2@C5*?tERzhXmzcUjVF7c9j2sRZ7Tzy-@jX5Av~^ zPU>lptbFfdsWYk>cKa=6q@)ILA!Y53^Da(K1ErE|`ei*4J3SVplP)6ZDATNHjYRI@$xLo-uFSDcx!^%+wq&D2agH@}?rO2@ijR*v?b zP8dTg=nHx+U0s$f2+dh}zw?HygN@&9PxP!!9V}Qo^lxp{8+)4#jVC_wJA#w(V4c^X zzpQ?FGcp9pC|ca#10L7Admshya%l&pLGGLJ^6Y&bSx&h@1v3^*a!IR+~$E_@XGK1RQ!0;meLlbV5H3ACg~baN z96Nn_0<$4YET0)UQEQhu_>xJyJMUPQs%m!m)~#bdt!ADtQYm=wO{PAe-hZ{@@UY;O zSc=*Xbo=nUY^2=v9zg5Rb>ZXX`;!{u9K!7kD2<8a1PCA=%(*fTT1=LAw~jU{(8u9= z;@7q`Msl;ckE5$)?w)_DwYI;;yM_j^^_h0hXs=DBlLXj7hP`TIbA8(JnJ41AJ(R0h zvE_m!_koN6geBiQp8>O(s}uMsGWn=wAC)|w=+_if8Ba_V(>P)|b*e(vZ{m~c6wea% zV7n)$WxKqdJ!l2@8rwz9S!W6yq+5W3Na$admnVAle$zkrYsmVwYeCZ2lu9j$-jdlA zCgyfidtaTgYl!>1)ge*KM)i!MGbP}%9h@_{F(>Pi1PS+m&J?`%zye4*vZ9uxW*dB4 zohlPoTynoiEbaUR2cil9c!1ot<}d0+_x-=@lx@qSiBz+^0$NDchM6DT?=XLQ&mKVl zZ-{HXhS@YkfOJ(Qa~}TTHXUA00n&eb4D?fV=Bff;8%z)&bC*5$>8G~z*$$5vE|;2$ zs_IMdJiaaT$d=8Uvkx)_O3@!+$99h)gAHUeCoRHma^A9K2hCxy;j{|YWHcfKB3)Yc zmGkGBy^X&l6CrPdHcpWLQkL+RKm|r;V$#1hpk>Txez#_x4jmvK@{Z>}2!!hv^f;pf zD<1-bsRX=$LmPVtYU0X?0%`sC%huLy6GTsihmRUuvEi;mgNE7ttSqrR!cuIU>+Ir^ z{pbv#D>VDtwzC9~dBejbe|-B^jd4Y7EfHmsY}A}9EUP*5<`qzu8TC}wwaZXE7Z{i) z#Q3BnG7d?TDgScSbD9{BDSm9CP%_qhBhe$$(E?J`pgoLr546icAKY}7qp}0z^PvZ9 zkoNky!Em|n3N8>f!1wD*<01nCr;?D541H{{J6uJF;))9k;SCLxN;ti0uQ${$w^=}6h2#`uZnTSb_PQh) z#gV1BX>{df<}(dWs6=&RE57^ZYdh&KcCg9iuMeslBZzF}%{V+(N^0p|dyJjf$CtRA zob8O*au-E3n|a#jYpcSqu>`J2?jg=XL8qld>;zMnD5}l^C9&pn>BO zE^Ghhnp_C9MY(q8`68e;eJD{~Vfj*~6ns0d^L=Th}t^_Pjz zgDCki^`L&^JVsz7aImHIuFU%k)-7iyNaGhQpeA-V613;vk)Z=|RH_V2{d+&JknM zkV58BcIm84%z!q#Po6g=3svgT<K7A56eWd4T zx|7eZ#$^aYKRlMbJ{D?la8r8x9=gR{Um?FZSc| zEld83a{gYJr%kpg802zd*9>jPA_OBT8hbR9)KR6JlYn!j$ap`x;q=1>lExY(bj$c4 z#=O^kqpX$W<$u@3rK4CrVX^{R9VNWPmGi09=XaTCibUb#0Bk|qlr$&tK%fJDl5{}8 zSxuO|0<|BWH3tG1_sdX2!86uRRJ<(x*gg0`d`hJr!t_Sgb55A;>1Y3MwBgAu{T+j_ z2_sG9&~^&vp7tH(AY!SkP~?0ArUxY`yg6JRwz!WlQvTm0=o;;H20K%h5><)m0FkZQ zDfYq!0HiN3C-lPA2ZV#lH}iVxooWA{L~OH!+!~eRl<|acs5X%C5oB<_g>TfL>m(ov zHx^I0K;qBVVJFw7Eqt)bu8A6vb{A|0;8x1*u-#|p5X!L1re$&Q;)|k&Ad*|)0qoPF zUOA6}wNi#*q;2W;=&ucvP8^yku)qndK*tKVHWko3<2LhqZVt-y0n-BhU9sVrG&3ej z(IHVqW@bk1TjUf;9?0>}Bqh20?YM&qg-KXng_M3h%Bo37C|UvXK0lW^#(~5xBq>ZC z@)iF%j{Gh84Q5d`GzJ1?`c6G6t-Pg3d0`K)$xr?tH0(uvpU49>Bv)eVLqP(s0s1y_UMHBI=Rxxe18Je711sc}##3#Dva)R=G& z9!dOx-Za;|R-~6GI~MzlKy{@t26F6yfrOt++Ceo@x<1#sc%c-;NObX%B~kNI`zF8n z_m}Z!mGXtUz#u+S+4ceYplW6RsG03Ci-NT?#nQ6dn}~r8iw@5sl}eA3h!@aqgx8xBom;m(XlDB8V@jvXvDTlm2C)fv6?f%aDEEBXW;( zts9uW)xIrDsxc!Y15<#2L$q)M0&s^O>O6aUSAQB2-KL8=WP&}ZtEo|<(wqnm4sM+I z*#4W!UskQ~^n22*n?N@jYz&Qos)~y1w#=Q(=;UCnXkP=#gWfH)Bxy-Wm9cA&rlzKn zf7d;ydd2viV;9lOx>c)wZ7D7PrC^Z%f=kn4^XS zSEQBrnz)R!`Zfb`Pz;LDYq7BiI&A*4qW=E<|Az$K4aD_-zcMC5#{bV3s`QAuAdfV5ZIt+8y7+(pPoL3%34CC0 z?WY)P5Ib!=B}iT?1h>kP0cpEwr87MksV;&!7L$ z_I3b`r4EUn1rO6zTAJa$4$s{&(T*#H^x6IBbUJ4>_iYuGvrCTFV{pJ|3mq1xmS<==@CzelD;TxF^aB*M zm@Od2_%-**g9oOPy_A(Zou(I5T5Z~t$Ouh#HrQvm@|&F%RvR|x&Kn})t}TR;zpsp0 z|3#}E(`S9=&MtOZ-y!}Z*8VYh16mA=i*w+qg1?{r!j{3cFoixN?8|^dw3s;XX&2Ft z@oG8N@0#6c+T^tQXdPyAQA8ma8hJ%;*-ZkLlRcLjnMxZ&tZZMf4SKsT9y}NZQ;4a; z2+Os}88{bKEQ zA`Gd-aLvf5{K=C8`|_c}s~&iZ7SIZ+8l9CXd$1KpA49G{okkx1(rVGaELK)i;D^#| zhasCr`tE~lWNIpEUPYUSX$m~|u3?fQuX})%DGycZF;4o6e||-IIYx!5Vt<0))J&@W zeX-lLnKK*eA}60Of*%jh&bOh-nCkZRwt1o-Z~nl(dfxD{`ud8)<*PI(xp4~k{9t_I znzcV3DPo->_ZcaKp$+M;jZOU51-etY0O4Pr8w3~wT#SVS4RZ~V@cc5<=wdP% zQWdU{Vw&=D8_Qu?T%wSXQiPEFf?j1A{Qgr|s(uaAC|E#(r5#{OV7Y{s1t=-0;ZQ&0loEOfF9!f8MD?W-@fLzr`L z&)fXfwwo)Oz7eI9Q&X9xe4R-%XCp}< zj9Kg+3r?HUD*_RM)&Lm-Iv!=+uC_XUo-D1gljbH3v{`Mwr~5;gw>_LpFhHuDbgLhC+u)lqImHL)iiaEPMDtw~rTlnxySx;$7Alhcx+i&)$&$%c zh!V-$$Nr&@=cA@EX4AWAfv%|X=^Itf4M)zz8h9rMC-kEdPzOg#z>QMnrcP|80tz`5 z_R1!rJ`Fg8?j&yEBG$HK6opN8KiSTTS9E}{I-<8j9BqZ+5-%9qNExfL= zq=5R0=(RU1Aq7Q~ZKfrRxNciVQP0A|VUHpRlRi|g0$W{MU;l{45i&2*TSh)n?BF|> zbo(~xYxY`$S2Dy%ZWGP(zqH<*UF<}f@i|{Z4jj;oX3Xlvvu7Pvt`vOanBzeMbWi|` z>46)c!4al88`;;llP>b|yJ1Yfr2!(J&m37xOD$d9*c&&VBM8ur=QJ@M@tfR~P)?G= zfC__=Bfmmm0{Y>-3GMas>=0`69&E`;jN;PqtueueFL!ngn_3;p@4kIAFk%Kg4u z#OIXFey{M9_~Qymo>`rIYH9(mh>a^&Oo_5_mGC$zYV0sSmrAH?fB!yxj3~THN)~*G zg!Ozv9EV{!Gt}r2mrkELJ%PpPUQNPm6a@h@ECJR07Hx-72+hB zTIHHZ@&re3$eH28hJgc1GMfvX6&^@#4oHybb@M@4TU@ByZ-(RqTSo)&M@;fQX_LY{ zD*2N*E_J{cGIP6h;Q}G(&8t`Yi^A z1__jHmCui|vv)-u^em46m=pNVPEI@1hfoNbFq;M8!GWG@GKq@gtuky^y?1HF*7TUCxu0VbfEgo%CYy0OG9Rdm4tV%|5kKe)-rd=) z>zeObMPoe1|}yG}b5Pb^`7{UH>_5u;%Bf@%lB^*Ct(EmVM^~UHyR< zVq#=QmibJ?F3>c+M3+340zbX6o0E3( z;@>M*`VG_7Jr)|e=!Ubrp+2p(+_guEJOeqVtl9chIJU0OKDh%iop#(bKFzgStm!H& zkCThi>A>h{YGN;_H&9}utlr?{9lx2*JgDX=A?A8{I8#?-X>C;l0>o|HylE3szOtgY z>cfIyyjeD&5U9!UY9@fzzyN`BYf)47P13NFW9%v7U_{HId*DmbHOtRlBuHQ|E(QODGI3m5vfxAxL@k!!@()vnyeYqXF*#85X0QZ z%Eqq^Apn@B2WGBvssC77_P`PHL6h@bavU2Qzd`zZ>C+VCMN_n%6%{SIZh zn3!BW{5-oTs*3pe#LRM9wJOgneQI>^-hDy6b?sV`MsFn>sEk{N4Lj`3QP~Rn3borm zJgz4mcrQ*5-2EIx?@d%9^Q@#=ZyuPwio!rxOv@uTyokGLZ2ev0dU(sQ)|+4c3_ZIK zU@P>eOE{<8vN)1cWUC-5k>i#yZ8Eg+7 zcHcvwF{Wfu9IUeGD9B^2A@3cL0hCSXjXU&;k_k4Zr(w}a7_2#aN=Xh7wlBS*qU;U}9 zTe^K_^WxGwC3V|=_E10ty;EE6^YPMnVT=v}uOLH@)HVAtVUK|-ldt3l#hJbFYfO8 zvQ*%}=G@C06*Qu}R3!evGpQ8&2xoBhLljju{{#g4?%Gu4YCycUcr(}e#JrNH2MN#Uh9Z4C z{<(VZQT~HvcU~)0*G8)9n(B$H*k!BJ+v^_@A|ZJrK-sABrf$cQaeGn&Ms43lHDwU) z`YB@XzI`KY<1^^#!H(fKg&sSGct!l$OL`5TJb!&GGRF<#k=D(9f@vrcAZkeWZ`;9| z!2qd`udfmYS&(kSyQr7iC5MJ4Yw!L(W2b|9YTQ8i1RdSry`LOV?5?^tH&<6CGz59~ zX_K{(L`&d)&+NqDFLiZP-BQO43h3N0GBRUT65z(~3&e__1W%%t!LYp;Yxf!BjOzO^ z>u-{M*<$?`Yh=uK0r;%m-8gFoy@G2bZGJRi?K{uUBPF9i|4W`fAFQc~jZM#GSmI>G z$abIwY3JM^Lppu*XuzOdfJXSAsYUjCOcJ?oA53?hiNi*@Ka(%-y!;<7z>%HhqUXnL z^nscKHBRWkz({_UeI0Wl4-#yvcb~(@=yTM-fMF)%R(vsk-1X3W?eFOMrEPmsw&A*% z$2YDin%0pU2Q*bp<`c-7K?S5ra~L;7PBQ1-uO*^2N`Ltc49j@d-~u0r&=?7l0&^8_ z#LL^AI!O6Jq-=ev)|>_ml-0KGG%7VmkPVlDKdB{Q&X?6(i`BoA8_o#R1T(EB}?YN*w@~ft1w{YQjEKDgan>2Hm#fE8Ey)&bk(5)s@Qec=Ls@B%gNrU8a z=1RuYfsItP5aXEJk-796B|WLUf!YBCOW<}sh7+gYSCEL$L|_hcfpkcW8CB%QDxEJJrB$6m(Lg zk>YywqT*dMI!0w5UX%2+wWWoY9$7du*h?AOXlS^|!eS+zUa#4i@$WltWPNVS7hFnt zPQNJ5X;q-qRzR0Me?G86UHs!Z@JX70{11S#F8`PYz(GWftknL5IJ5JdjM-4?taInY zI`V=`)m7T^na{(3sT~I)eu7j%>6@0m$N6C~DJJQI8O^FivZf^hh(~ zxR{ugPQ!P5_WpXR4%BkpxFyJPJRcikOGd3i#mb4nx%iqvZ6plbsi{*h<>nZRj2YEB zx>sRwadwE>rKLPmyWW$*=)64P@iVuhpn&8IXpl6?3yWsBn>`3+aodko6J8?kn2tc^SAPwD0%2+iS#{18V}ru zm77*m41!0JesM4Q&FaYQ5Q~x37=yzAda%}?d|nL#%e$&{ax5+Z_zo+KrXN27_OX6{ zlqwFs7!_rDyVTuOBttA85lyAxFv zat3-Sqdv(>r*5wOTIuA;K02#ZjybxNM=DuN+|eQzJAUj~%tk0_ zYD&9)jDJ==sPv)ShfPszZrC%s3T&4wS?<{i#G1KSssG7o*49hO3hO0+V%Pw)PK>6b zruuc5f1mrf-;vKXO5Fs;X0_YJwlsDKEq?E0%FwspH7E*8U%Yso?6w?kCf=4nFY;#i z`gD;g*L~h=!`z*%REM}h*xy{+Yw4z4g`=~@<<*^!tp9M;F3|1f=yYbCiWFyS$%O{fdJgQ{x657)Pb!bQkLbQi7B`dfQDqi)J0aAv6S+`hn6f$oIe9~6 z+CR$Tw9Hg6A*7>%l?<^os9PsT4lEZhiqFqfzC(jSU?ioab~z&NXv2k%<9GKIb_d7d z6Q=~`TlR0OTsK$kWQIgq$P|+zq|AW;^Vzy&LH> zu7_*@INZ4f0kw|H&x+TYS0~p1Z?_!po;JyKhD!(u?lYOS%)+cPn)8t8))fc~uM9#P z_-N*pF)}hCPa=D+JZv!3C?z?$aaMZn!`;9C*=HmIcE0OeMyS*&YDc8<9-` zNMD@whh2^X<-Fw~o>sz*?AiGmn2*q;daPIWs;*nW*Y98hBIMF{?f_wO-lNp+&>FAm zQEPps=gM&NNwwxchkHBTG)1x1AhwSxKL195jJ;*=FWEo2rL)_ZITGn7ykk;QRKH4+ zNX%9GNv%nHB6-^QL;h#>aMYEa_>Gu>l50Zd`i43s3#1$a0tVP9aWpM-4bAv-a zGpsJx#*b^xIC!z5wEVh~$PN=sJ^kY{QlzmT!(Ntvl@VXKYHh>%ytesASjI_7Nl`g+ zW_(s3^6xjkooqz9k2Cao7gWOO`+79g@j;^oU)0bL_4q1iioqT@2zZ7<{k*>%EN@qj zR+)eoOZsY&@X@bp^aX$gZ!=!C3@47!qei)Udir7lL+8(H$bbOso4-X=9S{Z_(1v^VR?2Whqi{2FVku@;XH7ePknXimsruvvbwxuV#Px9LdQ{ocz_M zpuuHS20J;t*(W?A(N-tT^ZTF%7x2%X=0BfuG+Uh zb=~dmcUz%kQQdxT5LIannjkm?KvNs3rKPN{{y6@+%D5z;CiFo}k*yxq!~9R5>@Tmj z^1f=f9^EU6wVqP%A|aL#*KXP1;$_w~D8A96aml5=_W}PMi_g^c>eahR7+Bej{}RxY%Xc`Guyi&}>)O>Xh(>s&jy# z`}a#9Pu2#ccyGqk-mayUG*JVOU+Ne#%hL9>2({6 zn0xN+pFI`@$ljFGI69rm!jzq zkHjRXGC3ngrlYDEFdm_^|47^sQ2i$

<+M5Gv+>4d%fk&WtKpX4__=lA{Q0ET->_TT+3*<7YGpSXFm@glLR{exD?*Xu4s?cdKc0T@~_qWV{@7A zpT((0@(FSBlG2W$iizp{y)-Qs_Ntz*@`)MccrKdVfBZPaCt}9LsGD(!DX3!_A11hc z8hhi{mYjt_y>1=-W)WUeHFt}M^ICFqd@ zo0lJzYM9)SImD6%mXDX9p^ROwx>)2^D&MuW7$5Zk)S(Rl+12n$l(qLy`{y4pO24H_ zBpw4A0+!v0DC+fLq$zgJ&CQ*^8kQ`aKQVjU%UCV4W^HN1poA?f>p;Yzq599*ShFYp++9AuC@tI7WvPURXdK2^WSxX)|9yN;&ls(OH1=t zuWh-#<3mGGdi=KhK88c}CyjM4;&CE`lOjBzIVl6F+rfP%-r+LVfY(D}|_ zst;V2yG`BlN)o$~sy%hsho6lVCDVh(fEB98Kl%L;`>G6zk5GCjab3&Zq5=9$;fqvL zwZ*u(ShzV?mTFi35$(M?IKdT$RhP0ThvuEu@Y>7-*L#PsjCLjK>~C4Cl2VoqO!mCt zp|#*(uHzd$QvUp}T@Eez`TcuV<->4+yTjQR=#;W6$3^gEuQg8a8EMR+1YwrfD#HKu-8~>2 z2Ie|ZAt5&kyf&6aQZ0E#+Ss7}G*#;_%KXfr_7fQwPSDKpR~oh;(6Lfo#bdS3k5@G< zllxCy9N;MXO;uC#ZcA$E+6`iZml5uD2|Nf3v`vP+u9>$kORnReXeqN1Ff zoV>i~B;$hL{We9#^wMb~0oDx{37;^Hp2byTWc(WQY;D7jA4lf@biOQ+fmDxI%h~Cx z-FN?1(lf(5s(4L(hrKJaEr zOAU92_9Bg|%wA&quhPka`?OH`K$Am37f=0Ug?{~HrLuT@>vYpWZIeZ+w%gRz6iRBy z1!?LlYF51;;vLcb&u;hP;ohI7r7JFV1u%%W{{bvZnBAfE)fkIU)mOL4SetO3J(fdY zH>jzGkD4*X2S;m;)Eywr;ysrZY9LvYPA zmLPp5UHy$0V|BW~(b|{ml=PB*7Je&%&v0?v;_A8wF8&>vAJa4?HAqEz@53kZk*`O< zDJ+0=`dtII@za_Btg>QK)4RkzqpUE3=}~DZu+cv}B7a-Gx)u_psr=0aDNr~i-bOJ0 z^7#6JqetWNBR@l?aZT3(f2HyrrFMX<%%~r+VfbCSFY({QIQ(}rOgK=3j&rvFqtGWFknEr%3m1SbK_F*tJ62JB)DW9 z(PM6(fI>ugoh@&Bs?~HqKD@KX;v3GPCr>V>yUD?kxpAL{)k)QNBvCS7FOi{{>KMd+ z$yoATIyH%Ao>huQYwxPMI;mMvJqL}LwA)dZ=FQpenTC7maS^v}99xxYG<#o+RH=G< z9cx4IevexxR-N8;Z~xX*fA7iC($X#IRq|%OZQp`!-SWWsQ8?j}FEaI>dtb5tQYi^) zUH^`K39(KXhSK9l*#XtqYqNBKG&OF*?MI$s)5X(g4-h$qWzliV2fm_wa=MMKlY%zE z_il}Sf9S-Cmo+OwH>}H=8`3|o`Th&IgkIUtW3gv@^hoMyNO}DRY3-FP5(;$kFIrT2 zp{t|JhcWlD^RaH6(=;1P%V_=Xc*UMEaLaoA+MD`8uKSJL#(V18C!YANw}O0NkhNH= zqUSvCQrVcS{+@z%R? zMR2O6_l)iW%I!PejMA*^iA3fCG{@{icsbO;KulOJZ9zi-4}(ol86}dh%IX&iRHoKj zSiA?3qrO9XHF4|r3(Vz%7fU&*F*pH0V8S+3ZisVS-Q77GmST^JYc#?TvMLe|_ z@>9v+Ah&PdraOy~9>x%AYHA2c+JWP#6G@9FD6fy%BrYxvB6Z~NsO)1r$*gvPcEF0I z1^1~!VaG{P^9)Tig~uWzXZ7@&8I)bk_(XhKLT4EPD8NZt00(W~m)ns3*DUnKDTZ<5 znH@<5f28^YPEc6TGotoDmyOss=IT{u*vx?v{Nrde~CfWGaJB#@q*!r zpcO6i~I&u3%vl`0Gub)xAPt&y%0DGn(@Bn@O7PBUFo@f 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�Yso;-PEjyU0G}>IE2BYO3KWP%2)5+t< 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⪚AX&5^45oFJC5 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%0Ah*2l0x%h7+~4Mm$u6x}9ztIa4y!BBZ73x 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