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}