001/*******************************************************************************
002 * Copyright (c) 2016 Pablo Pavon Mariņo.
003 * All rights reserved. This program and the accompanying materials
004 * are made available under the terms of the GNU Lesser Public License v2.1
005 * which accompanies this distribution, and is available at
006 * http://www.gnu.org/licenses/lgpl.html
007 ******************************************************************************/
008
009
010
011
012 
013
014
015
016
017
018package com.net2plan.examples.general.reports;
019
020import java.text.DecimalFormat;
021import java.util.Iterator;
022import java.util.LinkedHashMap;
023import java.util.LinkedList;
024import java.util.List;
025import java.util.Map;
026
027import com.net2plan.interfaces.networkDesign.IReport;
028import com.net2plan.interfaces.networkDesign.Link;
029import com.net2plan.interfaces.networkDesign.Net2PlanException;
030import com.net2plan.interfaces.networkDesign.NetPlan;
031import com.net2plan.interfaces.networkDesign.Route;
032import com.net2plan.utils.Constants.OrderingType;
033import com.net2plan.utils.DoubleUtils;
034import com.net2plan.utils.Pair;
035import com.net2plan.utils.StringUtils;
036import com.net2plan.utils.Triple;
037
038/**
039 * <p>
040 * This report shows line engineering information for WDM links in a multilayer
041 * optical network. The impairment calculations are inspired in the procedures
042 * described in the 2009 ITU-T WDM manual "Optical fibres, cabbles and
043 * systems".</p>
044 *
045 * <p>
046 * The report assumes that the WDM network follows the scheme:</p>
047 * <ul>
048 * <li>In the net2plan object, nodes are OADMs, links are fiber links, and
049 * routes are lightpaths: WDM channels optically switched at intermediate nodes.
050 * </li>
051 * <li>Nodes are connected by unidirectional fiber links. Fiber link distance is
052 * given by the link length. Other specifications are given by fiber_XXX input
053 * parameters. The fiber can be split into spans if optical amplifers (EDFAs)
054 * and/or dispersion compensating modules (DCMs) are placed along the
055 * fiber.</li>
056 * <li>Optical line amplifiers (EDFAs) can be located in none, one or more
057 * positions in the fiber link, separating them in different spans. EDFAs are
058 * supposed to operate in the automatic gain control mode. Thus, the gain is the
059 * same, whatever the number of input WDM channels. EDFA positions (as distance
060 * in km from the link start to the EDFA location) and EDFA gains (assumed in
061 * dB) are read from the "edfaPositions_km" and "edfaGains_dB" attributes of the
062 * links. The format of both attributes are the same: a string of numbers
063 * separated by spaces. The <i>i</i>-th number corresponding to the
064 * position/gain of the
065 * <i>i</i>-th EDFA. If the attributes do not exist, it is assumed that no EDFAs
066 * are placed in this link. EDFA specifications are given by "edfa_XXX"
067 * parameters</li>
068 * <li>Dispersion compensating modules (DCMs) can be located in none, one or
069 * more positions in the fiber link, separating them in different spans. If a
070 * DCM and a EDFA have the same location, it is assumed that the DCM is placed
071 * first, to reduce the non-linear effects. DCM positions (as distance in km
072 * from the link start to the DCM location) are read from the "dcmPositions_km"
073 * attribute of the link, and the same format as with "edfaPositions_km"
074 * attribute is expected. If the attribute does not exist, it is assumed that no
075 * DCMs are placed in this link. DCM specifications are given by "dcm_XXX"
076 * parameters</li>
077 * <li>Fiber links start and end in OADM modules, that permit adding, dropping
078 * and optically switch individual WDM channels. OADMs have a pre-amplifier
079 * (traversed by drop and express channels) and a boost amplifier (traversed by
080 * add and express channels). They are supposed to equalize the channel power at
081 * their outputs, to a fixed value (added and express channels will thus have
082 * the same power in the fibers). Also, OADMs attenuate appropriately the
083 * optical signal coming from the pre-amplifier, in the drop channels, so that
084 * they fall within the receiver sensitivity range. OADM noise figures for add,
085 * drop and express channels are given as input parameters. PMD values for add,
086 * drop and express channels are computed assumming that: (i) add channel
087 * traverse a multiplexer and the booster, (ii) drop channels travese the
088 * pre-amplifier and a demultiplexer, (iii) express channels traverse the two
089 * amplifiers. The required parameters are provided in oadm_XXX parameters.
090 * </li>
091 * <li>Each channel ends in a receiver, with specifications given by "tp_XXX"
092 * parameters.</li>
093 * </ul>
094 *
095 * <p>
096 * The basic checks performed are:</p>
097 * <ul>
098 * <li>For each link, signal power levels are within operating ranges at the
099 * oadm/edfas/dcms, both when the link has one single active channel, or when
100 * all the "channels_maxNumChannels" are active</li>
101 * <li>For each link, chromatic dispersion is within the limits set per
102 * link</li>
103 * <li>For each route (lightpath), chromatic dispersion is within the limits of
104 * the receiver.</li>
105 * <li>For each route (lightpath), OSNR (Optical Signal to Noise Ration) is
106 * within the operating range at the receiver. A set of margins are considered
107 * to account to several not directly considered impairments. </li>
108 * <li>For each route (lightpath), PMD (Polarization mode dispersion) is within
109 * the operating range at the receiver</li>
110 * </ul>
111 * @net2plan.keywords WDM, Multilayer
112 * @author Pablo Pavon-Marino, Jose-Luis Izquierdo-Zaragoza
113 * @version 1.1, May 2015
114 */
115public class Report_WDM_lineEngineering implements IReport
116{
117        /* Constants */
118        private final static double constant_c = 299792458; /* speed of light in m/s */
119        private final static double constant_h = 6.626E-34; /* Plank constant m^2 kg/sec */
120
121        /* Input parameters */
122        private NetPlan netPlan;
123        private Map<String, String> reportParameters;
124
125        /* Usable wavelengths */
126        private double channels_minChannelLambda_nm;
127        private double channels_channelSpacing_GHz;
128        private int channels_maxNumChannels;
129        private double channels_maxChannelLambda_nm;
130
131        /* Fiber specifications  */
132        private double fiber_attenuation_dB_per_km;
133        private double fiber_worseChromaticDispersion_ps_per_nm_per_km;
134        private double fiber_PMD_ps_per_sqroot_km;
135
136        /* Transponder specifications */
137        private double tp_maxChromaticDispersionTolerance_ps_per_nm;
138        private double tp_minOSNR_dB;
139        private double tp_minWavelength_nm;
140        private double tp_maxWavelength_nm;
141        private double tp_pmdTolerance_ps;
142        private double tp_inputPowerSensitivityMin_dBm;
143        private double tp_inputPowerSensitivityMax_dBm;
144
145        /* OADM specs */
146        private double oadm_perChannelOutputPower_dBm;
147        private double oadm_perChannelMinInputPower_dBm;
148        private double oadm_perChannelMaxInputPower_dBm;
149        private double oadm_muxDemuxPMD_ps;
150        private double oadm_preAmplifierPMD_ps;
151        private double oadm_boosterPMD_ps;
152        private double oadm_addChannelNoiseFactor_dB;
153        private double oadm_dropChannelNoiseFactor_dB;
154        private double oadm_expressChannelNoiseFactor_dB;
155        private double oadm_noiseFactorReferenceBandwidth_nm;
156        private double oadm_maxAbsoluteChromaticDispersionAtInput_ps_per_nm;
157
158        /* Optical line amplifier specifications */
159        private double edfa_minWavelength_nm;
160        private double edfa_maxWavelength_nm;
161        private double edfa_minInputPower_dBm;
162        private double edfa_maxInputPower_dBm;
163        private double edfa_minOutputPower_dBm;
164        private double edfa_maxOutputPower_dBm;
165        private double edfa_minGain_dB;
166        private double edfa_maxGain_dB;
167        private double edfa_PMD_ps;
168        private double edfa_noiseFactorMaximumGain_dB;
169        private double edfa_noiseFactorMinimumGain_dB;
170        private double edfa_noiseFactorReferenceBandwidth_nm;
171
172        /* Dispersion compensation modules specifications */
173        private double dcm_channelDispersionMax_ps_per_nm;
174        private double dcm_channelDispersionMin_ps_per_nm;
175        private double dcm_PMD_ps;
176        private double dcm_insertionLoss_dB;
177
178        /* OSNR penalties */
179        private double osnrPenalty_CD_dB;
180        private double osnrPenalty_nonLinear_dB;
181        private double osnrPenalty_PMD_dB;
182        private double osnrPenalty_PDL_dB;
183        private double osnrPenalty_transmitterChirp_dB;
184        private double osnrPenalty_OADMCrosstalk_dB;
185        private double osnrPenalty_unassignedMargin_dB;
186        private double osnrPenalty_SUM_dB;
187
188        @Override
189        public String executeReport(NetPlan netPlan, Map<String, String> reportParameters, Map<String, String> net2planParameters)
190        {
191                /* Input parameters */
192                this.netPlan = netPlan;
193                this.reportParameters = reportParameters;
194
195                /* Usable wavelengths */
196                channels_minChannelLambda_nm = Double.parseDouble(reportParameters.get("channels_minChannelLambda_nm"));
197                channels_channelSpacing_GHz = Double.parseDouble(reportParameters.get("channels_channelSpacing_GHz"));
198                channels_maxNumChannels = Integer.parseInt(reportParameters.get("channels_maxNumChannels"));
199                channels_maxChannelLambda_nm = constant_c / ((constant_c / channels_minChannelLambda_nm) - (channels_maxNumChannels - 1) * channels_channelSpacing_GHz);
200
201                /* Fiber specifications  */
202                fiber_attenuation_dB_per_km = Double.parseDouble(reportParameters.get("fiber_attenuation_dB_per_km"));
203                fiber_worseChromaticDispersion_ps_per_nm_per_km = Double.parseDouble(reportParameters.get("fiber_worseChromaticDispersion_ps_per_nm_per_km"));
204                fiber_PMD_ps_per_sqroot_km = Double.parseDouble(reportParameters.get("fiber_PMD_ps_per_sqroot_km"));
205
206                /* Transponder specifications */
207                tp_maxChromaticDispersionTolerance_ps_per_nm = Double.parseDouble(reportParameters.get("tp_maxChromaticDispersionTolerance_ps_per_nm"));
208                tp_minOSNR_dB = Double.parseDouble(reportParameters.get("tp_minOSNR_dB"));
209                tp_minWavelength_nm = Double.parseDouble(reportParameters.get("tp_minWavelength_nm"));
210                tp_maxWavelength_nm = Double.parseDouble(reportParameters.get("tp_maxWavelength_nm"));
211                tp_pmdTolerance_ps = Double.parseDouble(reportParameters.get("tp_pmdTolerance_ps"));
212                tp_inputPowerSensitivityMin_dBm = Double.parseDouble(reportParameters.get("tp_inputPowerSensitivityMin_dBm"));
213                tp_inputPowerSensitivityMax_dBm = Double.parseDouble(reportParameters.get("tp_inputPowerSensitivityMax_dBm"));
214
215                /* OADM specs */
216                oadm_perChannelOutputPower_dBm = Double.parseDouble(reportParameters.get("oadm_perChannelOutputPower_dBm"));
217                oadm_perChannelMinInputPower_dBm = Double.parseDouble(reportParameters.get("oadm_perChannelMinInputPower_dBm"));
218                oadm_perChannelMaxInputPower_dBm = Double.parseDouble(reportParameters.get("oadm_perChannelMaxInputPower_dBm"));
219                oadm_muxDemuxPMD_ps = Double.parseDouble(reportParameters.get("oadm_muxDemuxPMD_ps"));
220                oadm_preAmplifierPMD_ps = Double.parseDouble(reportParameters.get("oadm_preAmplifierPMD_ps"));
221                oadm_boosterPMD_ps = Double.parseDouble(reportParameters.get("oadm_boosterPMD_ps"));
222                oadm_addChannelNoiseFactor_dB = Double.parseDouble(reportParameters.get("oadm_addChannelNoiseFactor_dB"));
223                oadm_dropChannelNoiseFactor_dB = Double.parseDouble(reportParameters.get("oadm_dropChannelNoiseFactor_dB"));
224                oadm_expressChannelNoiseFactor_dB = Double.parseDouble(reportParameters.get("oadm_expressChannelNoiseFactor_dB"));
225                oadm_noiseFactorReferenceBandwidth_nm = Double.parseDouble(reportParameters.get("oadm_noiseFactorReferenceBandwidth_nm"));
226                oadm_maxAbsoluteChromaticDispersionAtInput_ps_per_nm = Double.parseDouble(reportParameters.get("oadm_maxAbsoluteChromaticDispersionAtInput_ps_per_nm"));
227
228                /* Optical line amplifier specifications */
229                edfa_minWavelength_nm = Double.parseDouble(reportParameters.get("edfa_minWavelength_nm"));
230                edfa_maxWavelength_nm = Double.parseDouble(reportParameters.get("edfa_maxWavelength_nm"));
231                edfa_minInputPower_dBm = Double.parseDouble(reportParameters.get("edfa_minInputPower_dBm"));
232                edfa_maxInputPower_dBm = Double.parseDouble(reportParameters.get("edfa_maxInputPower_dBm"));
233                edfa_minOutputPower_dBm = Double.parseDouble(reportParameters.get("edfa_minOutputPower_dBm"));
234                edfa_maxOutputPower_dBm = Double.parseDouble(reportParameters.get("edfa_maxOutputPower_dBm"));
235                edfa_minGain_dB = Double.parseDouble(reportParameters.get("edfa_minGain_dB"));
236                edfa_maxGain_dB = Double.parseDouble(reportParameters.get("edfa_maxGain_dB"));
237                edfa_PMD_ps = Double.parseDouble(reportParameters.get("edfa_PMD_ps"));
238                edfa_noiseFactorMaximumGain_dB = Double.parseDouble(reportParameters.get("edfa_noiseFactorMaximumGain_dB"));
239                edfa_noiseFactorMinimumGain_dB = Double.parseDouble(reportParameters.get("edfa_noiseFactorMinimumGain_dB"));
240                edfa_noiseFactorReferenceBandwidth_nm = Double.parseDouble(reportParameters.get("edfa_noiseFactorReferenceBandwidth_nm"));
241
242                /* Dispersion compensation modules specifications */
243                dcm_channelDispersionMax_ps_per_nm = Double.parseDouble(reportParameters.get("dcm_channelDispersionMax_ps_per_nm"));
244                dcm_channelDispersionMin_ps_per_nm = Double.parseDouble(reportParameters.get("dcm_channelDispersionMin_ps_per_nm"));
245                dcm_PMD_ps = Double.parseDouble(reportParameters.get("dcm_PMD_ps"));
246                dcm_insertionLoss_dB = Double.parseDouble(reportParameters.get("dcm_insertionLoss_dB"));
247
248                /* OSNR penalties */
249                osnrPenalty_CD_dB = Double.parseDouble(reportParameters.get("osnrPenalty_CD_dB"));
250                osnrPenalty_nonLinear_dB = Double.parseDouble(reportParameters.get("osnrPenalty_nonLinear_dB"));
251                osnrPenalty_PMD_dB = Double.parseDouble(reportParameters.get("osnrPenalty_PMD_dB"));
252                osnrPenalty_PDL_dB = Double.parseDouble(reportParameters.get("osnrPenalty_PDL_dB"));
253                osnrPenalty_transmitterChirp_dB = Double.parseDouble(reportParameters.get("osnrPenalty_transmitterChirp_dB"));
254                osnrPenalty_OADMCrosstalk_dB = Double.parseDouble(reportParameters.get("osnrPenalty_OADMCrosstalk_dB"));
255                osnrPenalty_unassignedMargin_dB = Double.parseDouble(reportParameters.get("osnrPenalty_unassignedMargin_dB"));
256                osnrPenalty_SUM_dB = osnrPenalty_CD_dB + osnrPenalty_nonLinear_dB + osnrPenalty_PMD_dB + osnrPenalty_PDL_dB + osnrPenalty_transmitterChirp_dB + osnrPenalty_OADMCrosstalk_dB + osnrPenalty_unassignedMargin_dB;
257
258                Map<Link, LinkedList<Triple<Double, String, Double>>> elements_e = new LinkedHashMap<Link, LinkedList<Triple<Double, String, Double>>>();
259                Map<Link, LinkedList<Pair<double[], double[]>>> impairments_e = new LinkedHashMap<Link, LinkedList<Pair<double[], double[]>>>();
260                Map<Link, LinkedList<String>> warnings_e = new LinkedHashMap<Link, LinkedList<String>>();
261
262                for (Link link : netPlan.getLinks())
263                {
264                        final List<Link> seqLinks = new LinkedList<Link>(); seqLinks.add (link);
265                        final LinkedList<Triple<Double, String, Double>> elementPositions = getElementPositionsList(seqLinks);
266                        final LinkedList<Pair<double[], double[]>> impairmentsAtInputAndOutputs = computeImpairments(elementPositions);
267                        final LinkedList<String> warningMessages = computeWarningMessages(elementPositions, impairmentsAtInputAndOutputs);
268
269                        elements_e.put(link, elementPositions);
270                        impairments_e.put(link, impairmentsAtInputAndOutputs);
271                        warnings_e.put(link, warningMessages);
272                }
273
274                Map<Route, LinkedList<Triple<Double, String, Double>>> elements_r = new LinkedHashMap<Route, LinkedList<Triple<Double, String, Double>>>();
275                Map<Route, LinkedList<Pair<double[], double[]>>> impairments_r = new LinkedHashMap<Route, LinkedList<Pair<double[], double[]>>>();
276                Map<Route, LinkedList<String>> warnings_r = new LinkedHashMap<Route, LinkedList<String>>();
277                for (Route r : netPlan.getRoutes())
278                {
279                        final List<Link> seqLinks = r.getSeqLinksRealPath();
280                        final LinkedList<Triple<Double, String, Double>> elementPositions = getElementPositionsList(seqLinks);
281                        final LinkedList<Pair<double[], double[]>> impairmentsAtInputAndOutputs = computeImpairments(elementPositions);
282                        final LinkedList<String> warningMessages = computeWarningMessages(elementPositions, impairmentsAtInputAndOutputs);
283
284                        elements_r.put(r, elementPositions);
285                        impairments_r.put(r, impairmentsAtInputAndOutputs);
286                        warnings_r.put(r, warningMessages);
287                }
288
289                return printReport(elements_e, impairments_e, warnings_e, elements_r, impairments_r, warnings_r);
290        }
291
292        @Override
293        public String getDescription()
294        {
295                return "This report shows line engineering information for WDM links in the network. Further description in the HTML generated.";
296        }
297
298        @Override
299        public List<Triple<String, String, String>> getParameters()
300        {
301                List<Triple<String, String, String>> parameters = new LinkedList<Triple<String, String, String>>();
302
303                /* Usable wavelengths */
304                parameters.add(Triple.of("channels_minChannelLambda_nm", "1530.33", "Channel minimum wavelength in nm"));
305                parameters.add(Triple.of("channels_channelSpacing_GHz", "100", "Channel spacing in GHz"));
306                parameters.add(Triple.of("channels_maxNumChannels", "16", "Maximum number of WDM channels that will be used"));
307
308                /* Fiber specifications  */
309                parameters.add(Triple.of("fiber_attenuation_dB_per_km", "0.25", "Fiber attenuation in dB/km"));
310                parameters.add(Triple.of("fiber_worseChromaticDispersion_ps_per_nm_per_km", "6", "Chromatic dispersion of the fiber in ps/nm/km"));
311                parameters.add(Triple.of("fiber_PMD_ps_per_sqroot_km", "0.4", "Polarization mode dispersion per km^0.5 of fiber (PMD_Q link factor)"));
312
313                /* Transponder specifications */
314                parameters.add(Triple.of("tp_maxChromaticDispersionTolerance_ps_per_nm", "800", "Maximum chromatic dispersion tolerance in ps/nm at the receiver"));
315                parameters.add(Triple.of("tp_minOSNR_dB", "7", "Minimum OSNR needed at the receiver"));
316                parameters.add(Triple.of("tp_inputPowerSensitivityMin_dBm", "-20", "Minimum input power at the receiver in dBm"));
317                parameters.add(Triple.of("tp_inputPowerSensitivityMax_dBm", "-8", "Maximum input power at the receiver in dBm"));
318                parameters.add(Triple.of("tp_minWavelength_nm", "1529.55", "Minimum wavelength usable by the transponder"));
319                parameters.add(Triple.of("tp_maxWavelength_nm", "1561.84", "Maximum wavelength usable by the transponder"));
320                parameters.add(Triple.of("tp_pmdTolerance_ps", "10", "Maximum tolarance of polarizarion mode dispersion (mean of differential group delay) in ps at the receiver"));
321
322                /* Optical amplifier specifications */
323                parameters.add(Triple.of("edfa_minWavelength_nm", "1530", "Minimum wavelength usable by the EDFA"));
324                parameters.add(Triple.of("edfa_maxWavelength_nm", "1563", "Maximum wavelength usable by the EDFA"));
325                parameters.add(Triple.of("edfa_minInputPower_dBm", "-29", "Minimum input power at the EDFA"));
326                parameters.add(Triple.of("edfa_maxInputPower_dBm", "2", "Maximum input power at the EDFA"));
327                parameters.add(Triple.of("edfa_minOutputPower_dBm", "-6", "Minimum output power at the EDFA"));
328                parameters.add(Triple.of("edfa_maxOutputPower_dBm", "19", "Maximum output power at the EDFA"));
329                parameters.add(Triple.of("edfa_minGain_dB", "17", "Minimum gain at the EDFA"));
330                parameters.add(Triple.of("edfa_maxGain_dB", "23", "Maximum gain at the EDFA"));
331                parameters.add(Triple.of("edfa_PMD_ps", "0.5", "Polarization mode dispersion in ps added by the EDFA"));
332                parameters.add(Triple.of("edfa_noiseFactorMaximumGain_dB", "5", "Noise factor at the EDFA when the gain is in its upper limit (linear interpolation is used to estimate the noise figura at other gains)"));
333                parameters.add(Triple.of("edfa_noiseFactorMinimumGain_dB", "5", "Noise factor at the EDFA when the gain is in its lower limit (linear interpolation is used to estimate the noise figura at other gains)"));
334                parameters.add(Triple.of("edfa_noiseFactorReferenceBandwidth_nm", "0.5", "Reference bandwidth that measures the noise factor at the EDFA"));
335
336                /* OADM specs */
337                parameters.add(Triple.of("oadm_perChannelOutputPower_dBm", "6", "Output power per channel at the OADM in dBm"));
338                parameters.add(Triple.of("oadm_perChannelMinInputPower_dBm", "-19", "Minimum power needed at the OADM input"));
339                parameters.add(Triple.of("oadm_perChannelMaxInputPower_dBm", "1000", "Maximum power admitted at the OADM input"));
340                parameters.add(Triple.of("oadm_maxAbsoluteChromaticDispersionAtInput_ps_per_nm", "150", "Maximum chromatic dispersion tolerance in ps/nm at te end of each link (absolute value)"));
341                parameters.add(Triple.of("oadm_muxDemuxPMD_ps", "0.5", "PMD of the mux/demux inside the OADMs. Does not affect express lightpaths"));
342                parameters.add(Triple.of("oadm_preAmplifierPMD_ps", "0.5", "PMD off OADM preamplifier"));
343                parameters.add(Triple.of("oadm_boosterPMD_ps", "0.5", "PMD off OADM booster amplifier"));
344                parameters.add(Triple.of("oadm_addChannelNoiseFactor_dB", "6", "Noise factor observed by add channels"));
345                parameters.add(Triple.of("oadm_dropChannelNoiseFactor_dB", "6", "Noise factor observed by drop channels"));
346                parameters.add(Triple.of("oadm_expressChannelNoiseFactor_dB", "10", "Noise factor observed by express channels"));
347                parameters.add(Triple.of("oadm_noiseFactorReferenceBandwidth_nm", "0.5", "Reference bandwidth that measures the noise factor at the OADM amplifiers"));
348
349                /* Dispersion compensation modules specifications */
350                parameters.add(Triple.of("dcm_channelDispersionMax_ps_per_nm", "-827", "Max (in absolute value) dispersion compensation a DCM can place (ps/nm). It is assumed to be the same for all wavelengths"));
351                parameters.add(Triple.of("dcm_channelDispersionMin_ps_per_nm", "-276", "Min (in absolute value) dispersion compensation a DCM can place (ps/nm). It is assumed to be the same for all wavelengths"));
352                parameters.add(Triple.of("dcm_PMD_ps", "0.7", "Polarization mode dispersion in ps added by the DCM"));
353                parameters.add(Triple.of("dcm_insertionLoss_dB", "3.5", "Maximum insertion loss added by the DCM"));
354
355                /* OSNR penalties */
356                parameters.add(Triple.of("osnrPenalty_CD_dB", "1", "OSNR penalty caused by residual chromatic dispersion (assumed within limits)"));
357                parameters.add(Triple.of("osnrPenalty_nonLinear_dB", "2", "OSNR penalty caused by the non-linear effects SPM, XPM, FWM and Brillouin / Raman scattering"));
358                parameters.add(Triple.of("osnrPenalty_PMD_dB", "0.5", "OSNR penalty caused by the polarization mode dispersion (assumed within limits)"));
359                parameters.add(Triple.of("osnrPenalty_PDL_dB", "0.3", "OSNR penalty caused by polarization dispersion losses"));
360                parameters.add(Triple.of("osnrPenalty_transmitterChirp_dB", "0.5", "OSNR penalty caused by transmitter chirp "));
361                parameters.add(Triple.of("osnrPenalty_OADMCrosstalk_dB", "0.8", "OSNR penalty caused by the crosstalk at the OADMs"));
362                parameters.add(Triple.of("osnrPenalty_unassignedMargin_dB", "3", "OSNR penalty caused by not assigned margins (e.g. random effects, aging, ...)"));
363
364                return parameters;
365        }
366
367        @Override
368        public String getTitle()
369        {
370                return "WDM line engineering";
371        }
372
373        private LinkedList<Pair<double[], double[]>> computeImpairments(LinkedList<Triple<Double, String, Double>> elementPositions)
374        {
375                LinkedList<Pair<double[], double[]>> res = new LinkedList<Pair<double[], double[]>>();
376
377                /* In the transmitter */
378                double current_powerPerChannel_dBm = oadm_perChannelOutputPower_dBm;
379                double current_CD_ps_per_nm = 0;
380                double current_PMDSquared_ps2 = 0;
381                double current_OSNR_linear = Double.MAX_VALUE; /* no noise */
382
383                if (!elementPositions.getFirst().getSecond().equalsIgnoreCase("OADM-ADD")) throw new RuntimeException("The route should start in a OADM-ADD element");
384                if (!elementPositions.getLast().getSecond().equalsIgnoreCase("OADM-DROP")) throw new RuntimeException("The route should end in a OADM-DROP element");
385
386                for (Triple<Double, String, Double> element : elementPositions)
387                {
388                        final String name = element.getSecond();
389                        final double elementData = element.getThird();
390
391                        double[] prevState = DoubleUtils.arrayOf(current_powerPerChannel_dBm, current_CD_ps_per_nm, current_PMDSquared_ps2, current_OSNR_linear);
392                        if (name.equalsIgnoreCase("OADM-ADD"))
393                        {
394                                current_OSNR_linear = updateOSNRAfterEDFA(Double.MAX_VALUE, oadm_addChannelNoiseFactor_dB, oadm_noiseFactorReferenceBandwidth_nm, current_powerPerChannel_dBm);
395                                current_powerPerChannel_dBm = oadm_perChannelOutputPower_dBm;
396                                current_CD_ps_per_nm = 0;
397                                current_PMDSquared_ps2 = Math.pow(oadm_muxDemuxPMD_ps, 2) + Math.pow(oadm_boosterPMD_ps, 2);
398
399                        }
400                        else if (name.equalsIgnoreCase("OADM-EXPRESS"))
401                        {
402                                current_OSNR_linear = updateOSNRAfterEDFA(current_OSNR_linear, oadm_expressChannelNoiseFactor_dB, oadm_noiseFactorReferenceBandwidth_nm, current_powerPerChannel_dBm);
403                                current_powerPerChannel_dBm = oadm_perChannelOutputPower_dBm;
404                                current_PMDSquared_ps2 += Math.pow(oadm_preAmplifierPMD_ps, 2) + Math.pow(oadm_boosterPMD_ps, 2);
405                        }
406                        else if (name.equalsIgnoreCase("OADM-DROP"))
407                        {
408                                current_OSNR_linear = updateOSNRAfterEDFA(current_OSNR_linear, oadm_dropChannelNoiseFactor_dB, oadm_noiseFactorReferenceBandwidth_nm, current_powerPerChannel_dBm);
409                                current_powerPerChannel_dBm = (tp_inputPowerSensitivityMin_dBm + tp_inputPowerSensitivityMax_dBm) / 2;
410                                current_PMDSquared_ps2 += Math.pow(oadm_preAmplifierPMD_ps, 2);
411                        }
412                        else if (name.equalsIgnoreCase("SPAN"))
413                        {
414                                final double spanLength_km = elementData;
415                                current_powerPerChannel_dBm -= fiber_attenuation_dB_per_km * spanLength_km;
416                                current_CD_ps_per_nm += fiber_worseChromaticDispersion_ps_per_nm_per_km * spanLength_km;
417                                current_PMDSquared_ps2 += spanLength_km * Math.pow(fiber_PMD_ps_per_sqroot_km, 2);
418                        }
419                        else if (name.equalsIgnoreCase("EDFA"))
420                        {
421                                final double edfaGain_dB = elementData;
422                                final double edfa_noiseFactorThisGain_dB = edfa_noiseFactorMinimumGain_dB + (edfaGain_dB - edfa_minGain_dB) * (edfa_noiseFactorMaximumGain_dB - edfa_noiseFactorMinimumGain_dB) / (edfa_maxGain_dB - edfa_minGain_dB);
423                                if ((edfa_noiseFactorThisGain_dB < Math.min(edfa_noiseFactorMinimumGain_dB, edfa_noiseFactorMaximumGain_dB)) || (edfa_noiseFactorThisGain_dB > Math.max(edfa_noiseFactorMinimumGain_dB, edfa_noiseFactorMaximumGain_dB)))
424                                        throw new RuntimeException("Bad");
425
426                                current_OSNR_linear = updateOSNRAfterEDFA(current_OSNR_linear, edfa_noiseFactorThisGain_dB, edfa_noiseFactorReferenceBandwidth_nm, current_powerPerChannel_dBm);
427                                current_powerPerChannel_dBm += edfaGain_dB;
428                                current_PMDSquared_ps2 += Math.pow(edfa_PMD_ps, 2);
429                        }
430                        else if (name.equalsIgnoreCase("DCM"))
431                        {
432                                final double cdCompensated_ps_per_nm = elementData;
433                                current_powerPerChannel_dBm -= dcm_insertionLoss_dB;
434                                current_CD_ps_per_nm += cdCompensated_ps_per_nm;
435                                current_PMDSquared_ps2 += Math.pow(dcm_PMD_ps, 2);
436                        }
437                        else
438                        {
439                                throw new RuntimeException("Unknown element type");
440                        }
441
442                        double[] postState = DoubleUtils.arrayOf(current_powerPerChannel_dBm, current_CD_ps_per_nm, current_PMDSquared_ps2, current_OSNR_linear);
443                        res.add(Pair.of(prevState, postState));
444                }
445                return res;
446        }
447
448        private LinkedList<String> computeWarningMessages(LinkedList<Triple<Double, String, Double>> elementPositions, LinkedList<Pair<double[], double[]>> impairmentsAtInputAndOutputs)
449        {
450                final double numChannels_dB = linear2dB((double) channels_maxNumChannels);
451                LinkedList<String> res = new LinkedList<String>();
452
453                Iterator<Triple<Double, String, Double>> it_elementPositions = elementPositions.iterator();
454                Iterator<Pair<double[], double[]>> it_impairments = impairmentsAtInputAndOutputs.iterator();
455
456                int numberOfExpressOADMs = 0;
457                while (it_elementPositions.hasNext())
458                {
459                        String st = "";
460
461                        final Triple<Double, String, Double> thisElement = it_elementPositions.next();
462                        final Pair<double[], double[]> thisImpairments = it_impairments.next();
463
464                        final double initialPosition_km = thisElement.getFirst();
465                        final String name = thisElement.getSecond();
466                        final double elementData = thisElement.getThird();
467
468                        final double[] prevImp = thisImpairments.getFirst();
469                        final double[] postImp = thisImpairments.getSecond();
470
471                        final double pre_powerPerChannel_dBm = prevImp[0];
472                        final double pre_CD_ps_per_nm = prevImp[1];
473
474                        final double post_powerPerChannel_dBm = postImp[0];
475                        final double post_CD_ps_per_nm = postImp[1];
476                        final double post_PMDSquared_ps2 = postImp[2];
477                        final double post_OSNR_linear = postImp[3];
478
479                        if (name.equalsIgnoreCase("OADM-ADD"))
480                        {
481                                /* Wavelengths in use within transponder range  */
482                                if (channels_minChannelLambda_nm < tp_minWavelength_nm) st += "Wavelength " + channels_minChannelLambda_nm + " nm is outside the transponder range [" + tp_minWavelength_nm + " nm, " + tp_maxWavelength_nm + " nm]";
483                                if (channels_maxChannelLambda_nm > tp_maxWavelength_nm) st += "Wavelength " + channels_maxChannelLambda_nm + " nm is outside the transponder range [" + tp_minWavelength_nm + " nm, " + tp_maxWavelength_nm + " nm]";
484
485                                /* Output power within limits */
486                                if (Math.abs(post_powerPerChannel_dBm - oadm_perChannelOutputPower_dBm) > 1E-3) st += "At " + initialPosition_km + "km: Power at the OADM-ADD output is " + post_powerPerChannel_dBm + " dBm. It should be: " + oadm_perChannelOutputPower_dBm;
487                        }
488                        else if (name.equalsIgnoreCase("OADM-EXPRESS"))
489                        {
490                                /* Input power within limits */
491                                if (pre_powerPerChannel_dBm < oadm_perChannelMinInputPower_dBm - 1E-3) st += "At " + initialPosition_km + "km: Power at the OADM-EXPRESS input is " + pre_powerPerChannel_dBm + " dBm. It should be between [" + oadm_perChannelMinInputPower_dBm + ", " + oadm_perChannelMaxInputPower_dBm + "] dBm";
492                                if (pre_powerPerChannel_dBm > oadm_perChannelMaxInputPower_dBm + 1E-3) st += "At " + initialPosition_km + "km: Power at the OADM-EXPRESS input is " + pre_powerPerChannel_dBm + " dBm. It should be between [" + oadm_perChannelMinInputPower_dBm + ", " + oadm_perChannelMaxInputPower_dBm + "] dBm";
493
494                                /* Output power within limits */
495                                if (Math.abs(post_powerPerChannel_dBm - oadm_perChannelOutputPower_dBm) > 1E-3) st += "At " + initialPosition_km + "km: Power at the OADM-EXPRESS output is " + post_powerPerChannel_dBm + " dBm. It should be: " + oadm_perChannelOutputPower_dBm;
496
497                                numberOfExpressOADMs++;
498                        }
499                        else if (name.equalsIgnoreCase("OADM-DROP"))
500                        {
501                                final double cdLimit = numberOfExpressOADMs == 0 ? oadm_maxAbsoluteChromaticDispersionAtInput_ps_per_nm : tp_maxChromaticDispersionTolerance_ps_per_nm;
502                                
503                                /* CD at input */
504                                if (Math.abs(pre_CD_ps_per_nm) > cdLimit + 1E-3) st += "At " + initialPosition_km + "km: CD at the input of OADM-DROP is " + pre_CD_ps_per_nm + " dBm. It should be in absolute value below: " + cdLimit;
505
506                                /* Input power within limits */
507                                if (pre_powerPerChannel_dBm < oadm_perChannelMinInputPower_dBm - 1E-3) st += "At " + initialPosition_km + "km: Power at the OADM-DROP input is " + pre_powerPerChannel_dBm + " dBm. It should be between [" + oadm_perChannelMinInputPower_dBm + "," + oadm_perChannelMaxInputPower_dBm + "] dBm";
508                                if (pre_powerPerChannel_dBm > oadm_perChannelMaxInputPower_dBm + 1E-3) st += "At " + initialPosition_km + "km: Power at the OADM-DROP input is " + pre_powerPerChannel_dBm + " dBm. It should be between [" + oadm_perChannelMinInputPower_dBm + "," + oadm_perChannelMaxInputPower_dBm + "] dBm";
509
510                                /* Output power within limits */
511                                if (post_powerPerChannel_dBm < tp_inputPowerSensitivityMin_dBm - 1E-3) st += "At " + initialPosition_km + "km: Power at the OADM-DROP output is " + post_powerPerChannel_dBm + ". It should be between [" + tp_inputPowerSensitivityMin_dBm + "," + tp_inputPowerSensitivityMax_dBm + "] dBm";
512                                if (post_powerPerChannel_dBm > tp_inputPowerSensitivityMax_dBm + 1E-3) st += "At " + initialPosition_km + "km: Power at the OADM-DROP output is " + post_powerPerChannel_dBm + ". It should be between [" + tp_inputPowerSensitivityMin_dBm + "," + tp_inputPowerSensitivityMax_dBm + "] dBm";
513
514                                /* Chromatic dispersion in the receiver */
515                                if (post_CD_ps_per_nm > tp_maxChromaticDispersionTolerance_ps_per_nm) st += "At " + initialPosition_km + "km: Chromatic dispersion at the RECEIVER is " + post_CD_ps_per_nm + " ps/nm. It should be within +- " + tp_maxChromaticDispersionTolerance_ps_per_nm + " ps/nm";
516                                if (post_CD_ps_per_nm < -tp_maxChromaticDispersionTolerance_ps_per_nm) st += "At " + initialPosition_km + "km: Chromatic dispersion at the RECEIVER is " + post_CD_ps_per_nm + " ps/nm. It should be within +- " + tp_maxChromaticDispersionTolerance_ps_per_nm + " ps/nm";
517
518                                /* OSNR within limits */
519                                if (linear2dB(post_OSNR_linear) < tp_minOSNR_dB + osnrPenalty_SUM_dB) st += "At " + initialPosition_km + "km: OSNR at the RECEIVER is " + linear2dB(post_OSNR_linear) + " dB. It is below the tolerance plus margin " + tp_minOSNR_dB + " dB " + osnrPenalty_SUM_dB + " dB = " + (tp_minOSNR_dB + osnrPenalty_SUM_dB) + " dB)";
520
521                                /* PMD tolerance at the receiver */
522                                final double pmdAtReceiver = Math.sqrt(post_PMDSquared_ps2);
523                                if (pmdAtReceiver > tp_pmdTolerance_ps) st += "At " + initialPosition_km + "km: PMD at the RECEIVER is " + pmdAtReceiver + " ps. It is above the maximum PMD tolerance (" + tp_pmdTolerance_ps + " ps)";
524                        }
525                        else if (name.equalsIgnoreCase("SPAN"))
526                        {
527                        }
528                        else if (name.equalsIgnoreCase("EDFA"))
529                        {
530                                final double edfaGain_dB = elementData;
531                                
532                                /* Wavelengths within limits */
533                                if (channels_minChannelLambda_nm < edfa_minWavelength_nm) st += "Wavelength " + channels_minChannelLambda_nm + " nm is outside the transponder range [" + edfa_minWavelength_nm + " nm, " + edfa_maxWavelength_nm + " nm]";
534                                if (channels_maxChannelLambda_nm > edfa_maxWavelength_nm) st += "Wavelength " + channels_maxChannelLambda_nm + " nm is outside the transponder range [" + edfa_minWavelength_nm + " nm, " + edfa_maxWavelength_nm + " nm]";
535
536                                /* Gain within limits */
537                                if (edfaGain_dB < edfa_minGain_dB - 1E-3) st += "At " + initialPosition_km + "km: EDFA gain is " + edfaGain_dB + " dB. It should be between [" + edfa_minGain_dB + ", " + edfa_maxGain_dB + "] dB";
538                                if (edfaGain_dB > edfa_maxGain_dB + 1E-3) st += "At " + initialPosition_km + "km: EDFA gain is " + edfaGain_dB + " dB. It should be between [" + edfa_minGain_dB + ", " + edfa_maxGain_dB + "] dB";
539
540                                /* Input power within limits */
541                                if (pre_powerPerChannel_dBm < edfa_minInputPower_dBm - 1E-3) st += "At " + initialPosition_km + "km: Power at the EDFA input is (is one WDM channel) " + pre_powerPerChannel_dBm + " dBm. It should be between [" + edfa_minInputPower_dBm + ", " + edfa_maxInputPower_dBm + "] dBm";
542                                if (pre_powerPerChannel_dBm + numChannels_dB > edfa_maxInputPower_dBm + 1E-3) st += "At " + initialPosition_km + "km: Power at the EDFA input is (if all WDM channels are active) " + (pre_powerPerChannel_dBm + numChannels_dB) + " dBm. It should be between [" + edfa_minInputPower_dBm + "," + edfa_maxInputPower_dBm + "] dBm";
543
544                                /* Output power within limits */
545                                if (post_powerPerChannel_dBm < edfa_minOutputPower_dBm - 1E-3) st += "At " + initialPosition_km + "km: Power at the EDFA output is (is one WDM channel) " + post_powerPerChannel_dBm + " dBm. It should be between [" + edfa_minOutputPower_dBm + ", " + edfa_maxOutputPower_dBm + "] dBm";
546                                if (post_powerPerChannel_dBm + numChannels_dB > edfa_maxOutputPower_dBm + 1E-3) st += "At " + initialPosition_km + "km: Power at the EDFA output is (if all WDM channels are active) " + (post_powerPerChannel_dBm + numChannels_dB) + " dBm. It should be between [" + edfa_minOutputPower_dBm + ", " + edfa_maxOutputPower_dBm + "] dBm";
547                        }
548                        else if (name.equalsIgnoreCase("DCM"))
549                        {
550                                final double dcmCompensation_ps_per_nm = elementData;
551                                if ((dcmCompensation_ps_per_nm < dcm_channelDispersionMax_ps_per_nm) || (dcmCompensation_ps_per_nm > dcm_channelDispersionMin_ps_per_nm)) st += "At " + initialPosition_km + "km: DCM compensation is " + dcmCompensation_ps_per_nm + " ps/nm. It should be between [" + dcm_channelDispersionMax_ps_per_nm + ", " + dcm_channelDispersionMin_ps_per_nm + "] ps/nm";
552                        }
553                        else
554                        {
555                                throw new RuntimeException("Unknown element type");
556                        }
557
558                        res.add(st);
559                }
560
561                return res;
562        }
563
564        private LinkedList<Triple<Double, String, Double>> getElementPositionsList(List<Link> seqLinks)
565        {
566                LinkedList<Triple<Double, String, Double>> res = new LinkedList<Triple<Double, String, Double>>();
567                double currentDistanceFromRouteInit_km = 0;
568
569                res.add(Triple.of(currentDistanceFromRouteInit_km, "OADM-ADD", (double) seqLinks.get(0).getOriginNode().getId ()));
570                for (Link e : seqLinks)
571                {
572                        final double d_e = e.getLengthInKm();
573                        final String st_edfaPositions_km = e.getAttribute("edfaPositions_km") == null ? "" : e.getAttribute("edfaPositions_km");
574                        final String st_edfaGains_dB = e.getAttribute("edfaGains_dB") == null ? "" : e.getAttribute("edfaGains_dB");
575                        final String st_dcmPositions_km = e.getAttribute("dcmPositions_km") == null ? "" : e.getAttribute("dcmPositions_km");
576                        final String st_dcmCDCompensation_ps_per_nm = e.getAttribute("dcmCDCompensation_ps_per_nm") == null ? "" : e.getAttribute("dcmCDCompensation_ps_per_nm");
577                        final double[] edfaPositions_km = StringUtils.toDoubleArray(StringUtils.split(st_edfaPositions_km));
578                        final double[] edfaGains_dB = StringUtils.toDoubleArray(StringUtils.split(st_edfaGains_dB));
579                        final double[] dcmPositions_km = StringUtils.toDoubleArray(StringUtils.split(st_dcmPositions_km));
580                        final double[] dcmCDCompensation_ps_per_nm = StringUtils.toDoubleArray(StringUtils.split(st_dcmCDCompensation_ps_per_nm));
581                        if (edfaPositions_km.length != edfaGains_dB.length) throw new Net2PlanException("Link: " + e + ". Number of elements in edfaPositions_km is not equal to the number of elements in edfaGains_dB");
582                        if (dcmPositions_km.length != dcmCDCompensation_ps_per_nm.length) throw new Net2PlanException("Link: " + e + ". Number of elements in dcmPositions_km is not equal to the number of elements in dcmCDCompensation_ps_per_nm");
583
584                        for (double edfaPosition : edfaPositions_km)
585                                if ((edfaPosition < 0) || (edfaPosition > d_e))
586                                        throw new Net2PlanException("Link: " + e + ". Wrong OA position: " + edfaPosition + ", link length = " + d_e);
587
588                        for (double dcmPosition : dcmPositions_km)
589                                if ((dcmPosition < 0) || (dcmPosition > d_e))
590                                        throw new Net2PlanException("Link: " + e + ". Wrong DCM position: " + ", link length = " + d_e);
591
592                        /* Place in a sorted form the spans, dcms and EDFAS. If DCM and EDFA colocated, DCM goes first */
593                        double[] dcmAndEDFAPositions_km = DoubleUtils.concatenate(dcmPositions_km, edfaPositions_km);
594                        int[] sortedDCMAndEDFAPositionsIndexes = dcmAndEDFAPositions_km.length == 0 ? new int[0] : DoubleUtils.sortIndexes(dcmAndEDFAPositions_km, OrderingType.ASCENDING);
595                        double posKmLastElementThisLink_km = 0;
596                        for (int cont = 0; cont < sortedDCMAndEDFAPositionsIndexes.length; cont++)
597                        {
598                                final int indexInCommonArray = sortedDCMAndEDFAPositionsIndexes[cont];
599                                final boolean isDCM = (indexInCommonArray < dcmPositions_km.length);
600                                final double posFromLinkInit_km = dcmAndEDFAPositions_km[indexInCommonArray];
601                                final double previousSpanLength = (Math.abs(posFromLinkInit_km - posKmLastElementThisLink_km) < 1E-3) ? 0 : posFromLinkInit_km - posKmLastElementThisLink_km;
602
603                                if (previousSpanLength < 0) throw new RuntimeException("Bad");
604
605                                if (previousSpanLength > 0)
606                                {
607                                        res.add(Triple.of(currentDistanceFromRouteInit_km, "SPAN", previousSpanLength));
608                                        currentDistanceFromRouteInit_km += previousSpanLength;
609                                        posKmLastElementThisLink_km += previousSpanLength;
610                                }
611                                
612                                if (isDCM) res.add(Triple.of(currentDistanceFromRouteInit_km, "DCM", dcmCDCompensation_ps_per_nm[indexInCommonArray]));
613                                else res.add(Triple.of(currentDistanceFromRouteInit_km, "EDFA", edfaGains_dB[indexInCommonArray - dcmPositions_km.length]));
614                        }
615
616                        /* Last span of the link before the OADM */
617                        final double lastSpanOfLink_km = (Math.abs(d_e - posKmLastElementThisLink_km) < 1E-3) ? 0 : d_e - posKmLastElementThisLink_km;
618
619                        if (lastSpanOfLink_km < 0) throw new RuntimeException("Bad");
620
621                        if (lastSpanOfLink_km > 0)
622                        {
623                                res.add(Triple.of(currentDistanceFromRouteInit_km, "SPAN", lastSpanOfLink_km));
624                                currentDistanceFromRouteInit_km += lastSpanOfLink_km;
625                                posKmLastElementThisLink_km += lastSpanOfLink_km;
626                        }
627
628                        /* OADM at the end of the link */
629                        final long endNodeLink = e.getDestinationNode().getId ();
630                        final long lastLink = seqLinks.get(seqLinks.size() - 1).getId ();
631                        if (e.getId () == lastLink) res.add(Triple.of(currentDistanceFromRouteInit_km, "OADM-DROP", (double) endNodeLink));
632                        else res.add(Triple.of(currentDistanceFromRouteInit_km, "OADM-EXPRESS", (double) endNodeLink));
633                }
634
635                /* Check current distance equals the sum of the traversed links */
636                double sumLinks = 0;
637                for (Link e : seqLinks) sumLinks += e.getLengthInKm();
638
639                if (Math.abs(sumLinks - currentDistanceFromRouteInit_km) > 1E-3) throw new RuntimeException("Bad");
640                return res;
641        }
642
643        private static double dB2Linear(double dB)
644        {
645                return Math.pow(10, dB / 10);
646        }
647
648        private static double linear2dB(double num)
649        {
650                return 10 * Math.log10(num);
651        }
652
653        private String printReport(Map<Link, LinkedList<Triple<Double, String, Double>>> elements_e, Map<Link, LinkedList<Pair<double[], double[]>>> impairments_e, Map<Link, LinkedList<String>> warnings_e, Map<Route, LinkedList<Triple<Double, String, Double>>> elements_r, Map<Route, LinkedList<Pair<double[], double[]>>> impairments_r, Map<Route, LinkedList<String>> warnings_r)
654        {
655                StringBuilder out = new StringBuilder();
656                DecimalFormat df_2 = new DecimalFormat("###.##");
657
658                out.append("<html><body>");
659                out.append("<head><title>WDM line engineering in multilayer (lightpath based) networks</title></head>");
660                out.append("<h1>WDM line engineering report for lighptath-based networks</h1>");
661
662                out.append("<p>This report shows line engineering information for WDM links in a multilayer optical network. The impairment calculations are inspired in the procedures described in the 2009 ITU-T WDM manual  \"Optical fibres, cabbles and systems\".</p>");
663                out.append("<p>The report assumes that the WDM network follows the scheme:</p>");
664                out.append("<ul>");
665                out.append("<li>In the net2plan object, nodes are OADMs, links are fiber links, and routes are lightpaths: WDM channels optically switched at intermediate nodes. </li>");
666                out.append("<li>Nodes are connected by unidirectional fiber links. Fiber link distance is given by the link length.");
667                out.append("Other specifications are given by fiber_XXX input parameters. The fiber can be split into spans if optical amplifers (EDFAs)");
668                out.append("and/or dispersion compensating modules (DCMs) are placed along the fiber.</li>");
669                out.append("<li>Optical line amplifiers (EDFAs) can be located in none, one or more positions in the");
670                out.append("fiber link, separating them in different spans. EDFAs are supposed to operate in the ");
671                out.append("automatic gain control mode. Thus, the gain is the same, whatever the number of input");
672                out.append("WDM channels. EDFA positions (as distance in km from the link start to the EDFA location)");
673                out.append("and EDFA gains (assumed in dB) are read from the \"edfaPositions_km\" and \"edfaGains_dB\" ");
674                out.append("attributes of the links. The format of both attributes are the same: a string of numbers ");
675                out.append("separated by spaces. The <i>i</i>-th number corresponding to the position/gain of the ");
676                out.append("<i>i</i>-th EDFA. If the attributes do not exist, it is assumed that no EDFAs are placed ");
677                out.append("in this link. EDFA specifications are given by \"edfa_XXX\" parameters</li>");
678                out.append("<li>Dispersion compensating modules (DCMs) can be located in none, one or more positions");
679                out.append("in the fiber link, separating them in different spans. If a DCM and a EDFA have the same ");
680                out.append("location, it is assumed that the DCM is placed first, to reduce the non-linear effects. DCM ");
681                out.append("positions (as distance in km from the link start to the DCM location) are read from the ");
682                out.append("\"dcmPositions_km\" attribute of the link, and the same format as with \"edfaPositions_km\" ");
683                out.append("attribute is expected. If the attribute does not exist, it is assumed that no DCMs are ");
684                out.append("placed in this link. DCM specifications are given by \"dcm_XXX\" parameters</li>");
685                out.append("<li>Fiber links start and end in OADM modules, that permit adding, dropping and optically switch");
686                out.append("individual WDM channels. OADMs have a pre-amplifier (traversed by drop and express channels) and ");
687                out.append("a boost amplifier (traversed by add and express channels). They are supposed to equalize the ");
688                out.append("channel power at their outputs, to a fixed value (added and express channels will thus have the same power in the fibers).");
689                out.append("Also, OADMs attenuate appropriately the optical signal coming from the pre-amplifier, in the drop channels,");
690                out.append("so that they fall within the receiver sensitivity range. OADM noise figures for add, drop and express channels");
691                out.append("are given as input parameters. PMD values for add, drop and express channels are computed assumming that: (i) ");
692                out.append("add channel traverse a multiplexer and the booster, (ii) drop channels travese the pre-amplifier and a demultiplexer,");
693                out.append("(iii) express channels traverse the two amplifiers. The required parameters are provided in oadm_XXX parameters. </li>");
694                out.append("<li>Each channel ends in a receiver, with specifications given by \"tp_XXX\" parameters.</li>");
695                out.append("</ul></p>");
696                out.append("<p>The basic checks performed are:</p>");
697                out.append("<ul>");
698                out.append("<li>For each link, signal power levels are within operating ranges at the oadm/edfas/dcms, both when the link has one single active channel, or when all the");
699                out.append("\"channels_maxNumChannels\" are active</li>");
700                out.append("<li>For each link, chromatic dispersion is within the limits set per link</li>");
701                out.append("<li>For each route (lightpath), chromatic dispersion is within the limits of the receiver.</li>");
702                out.append("<li>For each route (lightpath), OSNR (Optical Signal to Noise Ration) is within the operating range at the receiver.");
703                out.append("A set of margins are considered to account to several not directly considered impairments. </li>");
704                out.append("<li>For each route (lightpath), PMD (Polarization mode dispersion) is within the operating range at the receiver</li>");
705                out.append("</ul></p>");
706
707                out.append("<h2>Input Parameters</h2>");
708                out.append("<table border='1'>");
709                out.append("<tr><th><b>Name</b></th><th><b>Value</b></th><th><b>Description</b></th>");
710
711                for (Triple<String, String, String> paramDef : getParameters())
712                {
713                        String name = paramDef.getFirst();
714                        String description = paramDef.getThird();
715                        String value = reportParameters.get(name);
716                        out.append("<tr><td>").append(name).append("</td><td>").append(value).append("</td><td>").append(description).append("</td></tr>");
717                }
718                out.append("</table>");
719
720                out.append("<h2>PER LINK INFORMATION SUMMARY - Signal metrics at the input of end OADM</h2>");
721                out.append("<table border='1'>");
722                out.append("<tr><th><b>Link #</b></th><th><b>Length (km)</b></th><th><b># EDFAs</b></th><th><b># DCMs</b></th><th><b>Chromatic Dispersion (ps/nm)</b></th><th><b>OSNR (dB)</b></th><th><b>Power per WDM channel (dBm)</b></th><th><b>Polarization Mode Dispersion (ps)</b></th><th><b>Warnings</b></th></tr>");
723                for (Link e : netPlan.getLinks())
724                {
725                        final double d_e = e.getLengthInKm();
726                        final String st_a_e = e.getOriginNode().getName();
727                        final String st_b_e = e.getDestinationNode().getName();
728                        LinkedList<Triple<Double, String, Double>> el = elements_e.get(e);
729                        LinkedList<Pair<double[], double[]>> imp = impairments_e.get(e);
730                        LinkedList<String> w = warnings_e.get(e);
731
732                        int numEDFAs = 0;
733                        for (Triple<Double, String, Double> t : el)
734                                if (t.getSecond().equalsIgnoreCase("EDFA"))
735                                        numEDFAs++;
736
737                        int numDCMs = 0;
738                        for (Triple<Double, String, Double> t : el)
739                                if (t.getSecond().equalsIgnoreCase("DCM"))
740                                        numDCMs++;
741
742                        final double[] impInfoInputOADM = imp.getLast().getFirst();
743                        StringBuilder warnings = new StringBuilder();
744                        for (String s : w) warnings.append("<p>").append(s).append("</p>");
745
746                        out.append("<tr><td>").append(e).append(" (").append(st_a_e).append(" --> ").append(st_b_e).append(") </td><td>").append(df_2.format(d_e)).append("</td><td>").append(numEDFAs).append("</td><td>").append(numDCMs).append("</td><td>").append(df_2.format(impInfoInputOADM[1])).append("</td><td>").append(df_2.format(linear2dB(impInfoInputOADM[3]))).append("</td><td>").append(df_2.format(impInfoInputOADM[0])).append("</td><td>").append(df_2.format(Math.sqrt(impInfoInputOADM[2]))).append("</td><td>").append(warnings).append("</td></tr>");
747                }
748                out.append("</table>");
749
750                out.append("<h2>PER ROUTE INFORMATION SUMMARY - Signal metrics at the transponder</h2>");
751                out.append("<table border='1'>");
752                out.append("<tr><th><b>Route #</b></th><th><b>Length (km)</b></th><th><b># EDFAs</b></th><th><b># DCMs</b></th><th><b>Chromatic Dispersion (ps/nm)</b></th><th><b>OSNR (dB)</b></th><th><b>Power per WDM channel (dBm)</b></th><th><b>Polarization Mode Dispersion (ps)</b></th><th><b>Warnings</b></th></tr>");
753                for (Route r : netPlan.getRoutes())
754                {
755                        final double d_r = r.getLengthInKm();
756                        final String st_a_r = r.getIngressNode().getName ();
757                        final String st_b_r = r.getEgressNode().getName ();
758                        LinkedList<Triple<Double, String, Double>> el = elements_r.get(r);
759                        LinkedList<Pair<double[], double[]>> imp = impairments_r.get(r);
760                        LinkedList<String> w = warnings_r.get(r);
761
762                        int numEDFAs = 0;
763                        for (Triple<Double, String, Double> t : el)
764                                if (t.getSecond().equalsIgnoreCase("EDFA"))
765                                        numEDFAs++;
766
767                        int numDCMs = 0;
768                        for (Triple<Double, String, Double> t : el)
769                                if (t.getSecond().equalsIgnoreCase("DCM"))
770                                        numDCMs++;
771
772                        final double[] impInfoInputOADM = imp.getLast().getFirst();
773                        StringBuilder warnings = new StringBuilder();
774                        for (String s : w) warnings.append("<p>").append(s).append("</p>");
775
776                        out.append("<tr><td>").append(r).append(" (").append(st_a_r).append(" --> ").append(st_b_r).append(") </td><td>").append(df_2.format(d_r)).append("</td><td>").append(numEDFAs).append("</td><td>").append(numDCMs).append("</td><td>").append(df_2.format(impInfoInputOADM[1])).append("</td><td>").append(df_2.format(linear2dB(impInfoInputOADM[3]))).append("</td><td>").append(df_2.format(impInfoInputOADM[0])).append("</td><td>").append(df_2.format(Math.sqrt(impInfoInputOADM[2]))).append("</td><td>").append(warnings.toString()).append("</td>" + "</tr>");
777                }
778                out.append("</table>");
779
780                out.append("<h2>PER-LINK DETAILED INFORMATION </h2>");
781                out.append("<p>Number of links: ").append(netPlan.getNumberOfLinks()).append("</p>");
782
783                for (Link e : netPlan.getLinks())
784                {
785                        final double d_e = e.getLengthInKm();
786                        final String st_a_e = e.getOriginNode().getName ();
787                        final String st_b_e = e.getDestinationNode().getName ();
788                        LinkedList<Triple<Double, String, Double>> el = elements_e.get(e);
789                        LinkedList<Pair<double[], double[]>> imp = impairments_e.get(e);
790                        LinkedList<String> w = warnings_e.get(e);
791                        final String st_edfaPositions_km = e.getAttribute("edfaPositions_km") == null ? "" : e.getAttribute("edfaPositions_km");
792                        final String st_edfaGains_dB = e.getAttribute("edfaGains_dB") == null ? "" : e.getAttribute("edfaGains_dB");
793                        final String st_dcmPositions_km = e.getAttribute("dcmPositions_km") == null ? "" : e.getAttribute("dcmPositions_km");
794
795                        out.append("<h3>LINK # ").append(e).append(" (").append(st_a_e).append(" --> ").append(st_b_e).append(")</h3>");
796                        out.append("<table border=\"1\">");
797                        out.append("<caption>Link input information</caption>");
798                        out.append("<tr><td>Link length (km)</td><td>").append(d_e).append("</td></tr>");
799                        out.append("<tr><td>EDFA positions (km)</td><td>").append(st_edfaPositions_km).append("</td></tr>");
800                        out.append("<tr><td>EDFA gains (dB)</td><td>").append(st_edfaGains_dB).append("</td></tr>");
801                        out.append("<tr><td>DCM positions (km)</td><td>").append(st_dcmPositions_km).append("</td></tr>");
802                        out.append("</table>");
803
804                        out.append("<table border=\"1\">");
805                        out.append("<caption>Signal metrics evolution</caption>");
806                        out.append("<tr><th><b>Position (km)</b></th><th><b>Position (description)</b></th><th><b>Chromatic Dispersion (ps/nm)</b></th><th><b>OSNR (dB)</b></th><th><b>Power per WDM channel (dBm)</b></th><th><b>Polarization Mode Dispersion (ps)</b></th><th><b>Warnings</b></th></tr>");
807                        Iterator<Triple<Double, String, Double>> it_el = el.iterator();
808                        Iterator<Pair<double[], double[]>> it_imp = imp.iterator();
809                        Iterator<String> it_w = w.iterator();
810                        while (it_el.hasNext())
811                        {
812                                final Triple<Double, String, Double> this_el = it_el.next();
813                                final Pair<double[], double[]> this_imp = it_imp.next();
814                                final String this_warnings = it_w.next();
815
816                                final double pos_km = this_el.getFirst();
817                                String elementType = this_el.getSecond();
818                                final double elementAuxData = this_el.getThird();
819                                final double[] prevInfo = this_imp.getFirst();
820
821                                if (elementType.equalsIgnoreCase("EDFA")) elementType += " (gain " + elementAuxData + " dB)";
822                                else if (elementType.equalsIgnoreCase("SPAN")) elementType += " (" + elementAuxData + " km)";
823                                else if (elementType.equalsIgnoreCase("DCM")) elementType += " (comp " + elementAuxData + " ps/nm)";
824
825                                out.append("<tr><td>").append(df_2.format(pos_km)).append("</td><td>" + "Input of ").append(elementType).append("</td><td>").append(df_2.format(prevInfo[1])).append("</td><td>").append(df_2.format(linear2dB(prevInfo[3]))).append("</td><td>").append(df_2.format(prevInfo[0])).append("</td><td>").append(df_2.format(Math.sqrt(prevInfo[2]))).append("</td><td>").append(this_warnings).append("</td>" + "</tr>");
826
827                        }
828                        out.append("</table>");
829                }
830
831                out.append("<h2>PER-LIGHTPATH DETAILED INFORMATION</h2>");
832                out.append("<p>Number of lightpaths: ").append(netPlan.getNumberOfRoutes()).append("</p>");
833
834                for (Route r : netPlan.getRoutes())
835                {
836                        final double d_r = r.getLengthInKm();
837                        final String st_a_r = r.getIngressNode().getName();
838                        final String st_b_r = r.getEgressNode().getName();
839                        LinkedList<Triple<Double, String, Double>> el = elements_r.get(r);
840                        LinkedList<Pair<double[], double[]>> imp = impairments_r.get(r);
841                        LinkedList<String> w = warnings_r.get(r);
842
843                        out.append("<h3>ROUTE # ").append(r).append(" (").append(st_a_r).append(" --> ").append(st_b_r).append("), Length: ").append(d_r).append(" km</h3>");
844                        out.append("<table border=\"1\">");
845                        out.append("<caption>Signal metrics evolution</caption>");
846                        out.append("<tr><th><b>Position (km)</b></th><th><b>Position (description)</b></th><th><b>Chromatic Dispersion (ps/nm)</b></th><th><b>OSNR (dB)</b></th><th><b>Power per WDM channel (dBm)</b></th><th><b>Polarization Mode Dispersion (ps)</b></th><th><b>Warnings</b></th></tr>");
847                        Iterator<Triple<Double, String, Double>> it_el = el.iterator();
848                        Iterator<Pair<double[], double[]>> it_imp = imp.iterator();
849                        Iterator<String> it_w = w.iterator();
850                        while (it_el.hasNext())
851                        {
852                                final Triple<Double, String, Double> this_el = it_el.next();
853                                final Pair<double[], double[]> this_imp = it_imp.next();
854                                final String this_warnings = it_w.next();
855
856                                final double pos_km = this_el.getFirst();
857                                String elementType = this_el.getSecond();
858                                final double elementAuxData = this_el.getThird();
859                                final double[] prevInfo = this_imp.getFirst();
860                                if (elementType.equalsIgnoreCase("EDFA")) elementType += " (gain " + elementAuxData + " dB)";
861                                else if (elementType.equalsIgnoreCase("SPAN")) elementType += " (" + elementAuxData + " km)";
862                                else if (elementType.equalsIgnoreCase("DCM")) elementType += " (comp " + elementAuxData + " ps/nm)";
863
864                                out.append("<tr><td>").append(df_2.format(pos_km)).append("</td><td>" + "Input of ").append(elementType).append("</td><td>").append(df_2.format(prevInfo[1])).append("</td><td>").append(df_2.format(linear2dB(prevInfo[3]))).append("</td><td>").append(df_2.format(prevInfo[0])).append("</td><td>").append(df_2.format(Math.sqrt(prevInfo[2]))).append("</td><td>").append(this_warnings).append("</td>" + "</tr>");
865
866                        }
867                        final Triple<Double, String, Double> this_el = el.getLast();
868                        final Pair<double[], double[]> this_imp = imp.getLast();
869                        final String this_warnings = w.getLast();
870                        final double pos_km = this_el.getFirst();
871                        final double[] postInfo = this_imp.getSecond();
872                        out.append("<tr><td>").append(df_2.format(pos_km)).append("</td><td>" + "Receiver" + "</td><td>").append(df_2.format(postInfo[1])).append("</td><td>").append(df_2.format(linear2dB(postInfo[3]))).append("</td><td>").append(df_2.format(postInfo[0])).append("</td><td>").append(df_2.format(Math.sqrt(postInfo[2]))).append("</td><td>").append(this_warnings).append("</td>" + "</tr>");
873
874                        out.append("</table>");
875                }
876
877                out.append("</body></html>");
878                return out.toString();
879        }
880
881        private double updateOSNRAfterEDFA(double currentOSNR_linear, double noiseFactor_dB, double noiseFactorReferenceBandwidth_nm, double inputPowerPerChannel_dBm)
882        {
883                final double edfa_NF_linear = dB2Linear(noiseFactor_dB);
884                final double highestFrequencyChannel_Hz = constant_c / (channels_minChannelLambda_nm * 1e-9);
885                final double referenceBandwidthAtHighestFrequency_Hz = -highestFrequencyChannel_Hz + constant_c / ((channels_minChannelLambda_nm - noiseFactorReferenceBandwidth_nm) * 1e-9);
886                final double inputPower_linear = dB2Linear(inputPowerPerChannel_dBm) * 1E-3;
887                final double thisEDFAAddedNoise_linear = edfa_NF_linear * constant_h * highestFrequencyChannel_Hz * referenceBandwidthAtHighestFrequency_Hz;
888                final double addedOSNRThisOA_linear = inputPower_linear / thisEDFAAddedNoise_linear;
889                final double new_OSNR_linear = (currentOSNR_linear == Double.MAX_VALUE) ? addedOSNRThisOA_linear : 1 / (1 / currentOSNR_linear + 1 / addedOSNRThisOA_linear);
890                return new_OSNR_linear;
891        }
892}