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