2 * Copyright © 2022 Orange, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.transportpce.common.catalog;
10 import java.util.HashMap;
11 import java.util.List;
13 import java.util.Optional;
14 import java.util.concurrent.ExecutionException;
15 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
16 import org.opendaylight.transportpce.common.StringConstants;
17 import org.opendaylight.transportpce.common.catalog.CatalogConstant.CatalogNodeType;
18 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
19 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.link.types.rev191129.RatioDB;
20 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.ImpairmentType;
21 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.amplifier.parameters.Amplifier;
22 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.OpenroadmOperationalModes;
23 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.SpecificOperationalModes;
24 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.Amplifiers;
25 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.Roadms;
26 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.XpondersPluggables;
27 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.xponders.pluggables.XponderPluggableOpenroadmOperationalMode;
28 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.xponders.pluggables.XponderPluggableOpenroadmOperationalModeKey;
29 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.specific.operational.modes.SpecificOperationalMode;
30 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.specific.operational.modes.SpecificOperationalModeKey;
31 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.common.amplifier.drop.parameters.OpenroadmOperationalMode;
32 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.common.amplifier.drop.parameters.OpenroadmOperationalModeKey;
33 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.add.parameters.Add;
34 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.add.parameters.add.AddOpenroadmOperationalMode;
35 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.add.parameters.add.AddOpenroadmOperationalModeKey;
36 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.drop.parameters.Drop;
37 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.express.parameters.Express;
38 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.Penalties;
39 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.PenaltiesKey;
40 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.TXOOBOsnrKey;
41 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.power.mask.MaskPowerVsPin;
42 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.power.mask.MaskPowerVsPinKey;
43 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.OperationalModeCatalog;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 * Utility class for Catalog. Following methods are used to retrieve parameters
50 * from the specification catalog. They point to either openROADM or specific
51 * operational modes. They provide to the PCE, the OLM and the Renderer, the
52 * required parameters to calculate impairments and set output power levels
53 * according to the specifications.
56 public class CatalogUtils {
58 private static final Logger LOG = LoggerFactory.getLogger(CatalogUtils.class);
60 private static final String OPMODE_MISMATCH_MSG =
61 "Operational Mode {} passed to getPceRoadmAmpParameters does not correspond to an OpenROADM mode"
62 + "Parameters for amplifier and/or ROADMs can not be derived from specific-operational-modes.";
63 private static final Map<CatalogConstant.CatalogNodeType, String> CATALOGNODETYPE_OPERATIONMODEID_MAP = Map.of(
64 CatalogConstant.CatalogNodeType.ADD, CatalogConstant.MWWRCORE,
65 CatalogConstant.CatalogNodeType.DROP, CatalogConstant.MWWRCORE,
66 CatalogConstant.CatalogNodeType.EXPRESS, CatalogConstant.MWMWCORE,
67 CatalogConstant.CatalogNodeType.AMP, CatalogConstant.MWISTANDARD);
68 private static final Map<String, String> TSP_DEFAULT_OM_MAP = Map.of(
69 StringConstants.SERVICE_TYPE_100GE_T, CatalogConstant.ORW100GSC,
70 StringConstants.SERVICE_TYPE_OTU4, CatalogConstant.ORW100GSC,
71 StringConstants.SERVICE_TYPE_OTUC2, CatalogConstant.ORW200GOFEC316GBD,
72 StringConstants.SERVICE_TYPE_OTUC3, CatalogConstant.ORW300GOFEC631GBD,
73 StringConstants.SERVICE_TYPE_OTUC4, CatalogConstant.ORW400GOFEC631GBD,
74 StringConstants.SERVICE_TYPE_400GE, CatalogConstant.ORW400GOFEC631GBD);
76 private final PenaltiesComparator penaltiesComparator = new PenaltiesComparator();
77 private NetworkTransactionService networkTransactionService;
79 public CatalogUtils(NetworkTransactionService networkTransactionService) {
80 this.networkTransactionService = networkTransactionService;
84 * Following method returns default OperationalModeId for devices that do not
87 * @param catalogNodeType
88 * identifies type of nodes in the catalog
90 * allows for Xponder selecting default mode according to the rate
92 * @return a default operational mode that corresponds to initial specifications
95 public String getPceOperationalModeFromServiceType(CatalogConstant.CatalogNodeType catalogNodeType,
97 if (CATALOGNODETYPE_OPERATIONMODEID_MAP.containsKey(catalogNodeType)) {
98 return CATALOGNODETYPE_OPERATIONMODEID_MAP.get(catalogNodeType);
100 if (!catalogNodeType.equals(CatalogConstant.CatalogNodeType.TSP)) {
101 LOG.warn("Unsupported catalogNodeType {}", catalogNodeType);
104 if (!TSP_DEFAULT_OM_MAP.containsKey(serviceType)) {
105 LOG.warn("Unsupported serviceType {} for TSP catalogNodeType", serviceType);
108 return TSP_DEFAULT_OM_MAP.get(serviceType);
112 * This method retrieves channel-spacing associated with a Xponder TX.
114 * @param operationalModeId
115 * operational-mode-Id of the Xponder (OR or Specific)
117 * @return the channel spacing used to correct OSNR contribution values from
118 * ROADMs and amplifiers
119 * @throws RuntimeException
120 * if operationalModeId is not described in the catalog
123 public double getPceTxTspChannelSpacing(String operationalModeId) {
126 if (operationalModeId.startsWith("OR")) {
127 InstanceIdentifier<XponderPluggableOpenroadmOperationalMode> omCatalogIid = InstanceIdentifier
128 .builder(OperationalModeCatalog.class)
129 .child(OpenroadmOperationalModes.class)
130 .child(XpondersPluggables.class)
131 .child(XponderPluggableOpenroadmOperationalMode.class,
132 new XponderPluggableOpenroadmOperationalModeKey(operationalModeId))
135 Optional<XponderPluggableOpenroadmOperationalMode> omOptional =
136 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
137 if (omOptional.isEmpty()) {
138 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
141 XponderPluggableOpenroadmOperationalMode orTspOM = omOptional.get();
142 maxRollOff = orTspOM.getMaxRollOff() == null ? 0 : orTspOM.getMaxRollOff().doubleValue();
143 baudRate = orTspOM.getBaudRate().doubleValue();
144 } catch (InterruptedException | ExecutionException e) {
145 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
146 throw new RuntimeException("Operational mode not populated in Catalog : " + omCatalogIid + " :" + e);
148 networkTransactionService.close();
151 // In other cases, means the mode is a non OpenROADM specific Operational Mode
152 InstanceIdentifier<SpecificOperationalMode> omCatalogIid = InstanceIdentifier
153 .builder(OperationalModeCatalog.class)
154 .child(SpecificOperationalModes.class)
155 .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId))
159 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
160 if (somOptional.isEmpty()) {
161 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
164 SpecificOperationalMode speTspOM = somOptional.get();
165 maxRollOff = speTspOM.getMaxRollOff() == null ? 0 : speTspOM.getMaxRollOff().doubleValue();
166 baudRate = speTspOM.getBaudRate().doubleValue();
167 } catch (InterruptedException | ExecutionException e) {
168 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
169 throw new RuntimeException("Operational mode not populated in Catalog : " + omCatalogIid + " :" + e);
171 networkTransactionService.close();
174 if (maxRollOff == 0) {
175 if (CatalogConstant.ORW100GSC.contentEquals(operationalModeId)) {
176 // OR 100G SCFEC is the only case where rolloff factor is not mandatory in the catalog
177 LOG.info("Operational Mode {} associated channel spacing is 50.0", operationalModeId);
180 LOG.warn("Missing rolloff factor (mandatory in Catalog) from Operational Mode {}: use default=0.2",
184 double spacing = 12.5 * Math.ceil(baudRate * (1 + maxRollOff) / 12.5);
185 LOG.info("Operational Mode {} associated channel spacing is {}", operationalModeId, spacing);
190 * This method retrieves performance parameters associated with a Xponder TX.
192 * @param operationalModeId
193 * operational-mode-Id of the Xponder (OR or Specific)
194 * @param addDropMuxOperationalModeId
195 * operational-mode-Id of the Add-Drop bloc the XponderTX is
196 * associated to (conditions TX-OOB OSNR value)
198 * @return the linear Optical Noise to signal Ratio
199 * @throws RuntimeException
200 * if operationalModeId is not described in the catalog
202 public double getPceTxTspParameters(String operationalModeId, String addDropMuxOperationalModeId) {
203 double txOnsrLin = 0.0;
204 XponderPluggableOpenroadmOperationalMode orTspOM = null;
205 SpecificOperationalMode speTspOM = null;
206 RatioDB minOOBOsnrSingleChannelValue;
207 RatioDB minOOBOsnrMultiChannelValue;
208 if (operationalModeId.startsWith("OR")) {
209 InstanceIdentifier<XponderPluggableOpenroadmOperationalMode> omCatalogIid = InstanceIdentifier
210 .builder(OperationalModeCatalog.class)
211 .child(OpenroadmOperationalModes.class)
212 .child(XpondersPluggables.class)
213 .child(XponderPluggableOpenroadmOperationalMode.class,
214 new XponderPluggableOpenroadmOperationalModeKey(operationalModeId))
217 Optional<XponderPluggableOpenroadmOperationalMode> omOptional =
218 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
219 if (omOptional.isEmpty()) {
220 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
223 orTspOM = omOptional.get();
224 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orTspOM);
225 TXOOBOsnrKey key = new TXOOBOsnrKey(addDropMuxOperationalModeId);
226 if (orTspOM.getMinTXOsnr() != null) {
227 txOnsrLin = 1.0 / Math.pow(10.0, orTspOM.getMinTXOsnr().getValue().doubleValue() / 10.0);
229 if (orTspOM.nonnullTXOOBOsnr().get(key) == null) {
232 minOOBOsnrSingleChannelValue = orTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrSingleChannelValue();
233 minOOBOsnrMultiChannelValue = orTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrMultiChannelValue();
234 } catch (InterruptedException | ExecutionException e) {
235 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
236 throw new RuntimeException(
237 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
238 + omCatalogIid + " :" + e);
240 networkTransactionService.close();
243 // In other cases, means the mode is a non OpenROADM specific Operational Mode
244 InstanceIdentifier<SpecificOperationalMode> omCatalogIid = InstanceIdentifier
245 .builder(OperationalModeCatalog.class)
246 .child(SpecificOperationalModes.class)
247 .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId))
251 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
252 if (somOptional.isEmpty()) {
253 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
256 speTspOM = somOptional.get();
257 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", speTspOM);
258 TXOOBOsnrKey key = new TXOOBOsnrKey(addDropMuxOperationalModeId);
259 if (speTspOM.getMinTXOsnr() != null) {
260 txOnsrLin = 1.0 / Math.pow(10.0, speTspOM.getMinTXOsnr().getValue().doubleValue() / 10.0);
262 if (speTspOM.nonnullTXOOBOsnr().get(key) == null) {
265 minOOBOsnrSingleChannelValue = speTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrSingleChannelValue();
266 minOOBOsnrMultiChannelValue = speTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrMultiChannelValue();
267 } catch (InterruptedException | ExecutionException e) {
268 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
269 throw new RuntimeException(
270 "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :"
273 networkTransactionService.close();
276 if (minOOBOsnrSingleChannelValue != null) {
277 txOnsrLin += 1.0 / Math.pow(10.0, minOOBOsnrSingleChannelValue.getValue().doubleValue() / 10.0);
279 if (minOOBOsnrMultiChannelValue != null) {
280 txOnsrLin += 1.0 / Math.pow(10.0, minOOBOsnrMultiChannelValue.getValue().doubleValue() / 10.0);
286 * This method retrieves performance parameters associated with a Xponder RX.
287 * It calls getRxTspPenalty to evaluate the penalty associated with CD/PMD/PDL
288 * It compares expected OSNR with the OSNR resulting from the line degradation,
289 * and finally calculates and return the resulting margin.
291 * @param operationalModeId
292 * operational-mode-Id of the Xponder (OR or Specific)
294 * accumulated chromatic dispersion across the line
296 * accumulated Polarization mode dispersion across the line
298 * accumulated Polarization Dependant Loss across the line
300 * Optical Signal to Noise Ratio (dB)resulting from the transmission
301 * on the line, that shall include the Non Linear contribution
303 * @return the margin on the service path
304 * @throws RuntimeException
305 * if operationalModeId is not described in the catalog
307 public double getPceRxTspParameters(String operationalModeId, double calcCd, double calcPmd,
308 double calcPdl, double calcOsnrdB) {
309 double rxOsnrdB = 0.0;
310 XponderPluggableOpenroadmOperationalMode orTspOM = null;
311 SpecificOperationalMode speTspOM = null;
312 Map<PenaltiesKey, Penalties> penaltiesMap = null;
313 if (operationalModeId.startsWith("OR")) {
314 var omCatalogIid = InstanceIdentifier
315 .builder(OperationalModeCatalog.class)
316 .child(OpenroadmOperationalModes.class)
317 .child(XpondersPluggables.class)
318 .child(XponderPluggableOpenroadmOperationalMode.class,
319 new XponderPluggableOpenroadmOperationalModeKey(operationalModeId))
322 Optional<XponderPluggableOpenroadmOperationalMode> omOptional = networkTransactionService
323 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
324 if (omOptional.isPresent()) {
325 orTspOM = omOptional.get();
326 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orTspOM);
327 if (orTspOM.getMinRXOsnrTolerance() != null) {
328 rxOsnrdB = orTspOM.getMinRXOsnrTolerance().getValue().doubleValue();
330 penaltiesMap = orTspOM.getPenalties();
332 } catch (InterruptedException | ExecutionException e) {
333 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
334 throw new RuntimeException(
335 "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :"
338 networkTransactionService.close();
341 // In other cases, means the mode is a non OpenROADM specific Operational Mode
342 // InstanceIdentifier<SpecificOperationalMode> omCatalogIid = InstanceIdentifier
343 var omCatalogIid = InstanceIdentifier
344 .builder(OperationalModeCatalog.class)
345 .child(SpecificOperationalModes.class)
346 .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId))
349 Optional<SpecificOperationalMode> somOptional = networkTransactionService
350 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
351 if (somOptional.isPresent()) {
352 speTspOM = somOptional.get();
353 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", speTspOM);
354 if (speTspOM.getMinRXOsnrTolerance() != null) {
355 rxOsnrdB = speTspOM.getMinRXOsnrTolerance().getValue().doubleValue();
357 penaltiesMap = speTspOM.getPenalties();
359 } catch (InterruptedException | ExecutionException e) {
360 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
361 throw new RuntimeException(
362 "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :"
365 networkTransactionService.close();
368 if (penaltiesMap == null) {
369 LOG.error("Unable to calculate margin as penaltyMap can not be retrieved : Operational mode not populated");
372 HashMap<String, Double> impairments = new HashMap<>();
373 double penalty = getRxTspPenalty(calcCd, ImpairmentType.CDPsNm, penaltiesMap);
374 impairments.put("CDpenalty", penalty);
375 double totalPenalty = penalty;
376 penalty = getRxTspPenalty(calcPmd, ImpairmentType.PMDPs, penaltiesMap);
377 impairments.put("PMD Penalty", penalty);
378 totalPenalty += penalty;
379 // Calculation according to OpenROADM specification
380 // penalty = getRxTspPenalty(calcPdl, ImpairmentType.PDLDB, penaltiesMap);
381 // Calculation modified according to Julia's Tool
382 penalty = calcPdl / 2;
383 impairments.put("PDL penalty", penalty);
384 totalPenalty += penalty;
385 // TODO for Future work since at that time we have no way to calculate the following
386 // parameters,even if penalties are defined in the OpenROADM specifications
388 // impairments.put("Colorless Drop Adjacent Xtalk Penalty", getRxTspPenalty(TBD,
389 // ImpairmentType.ColorlessDropAdjacentChannelCrosstalkGHz, penalitiesMap));
390 // impairments.put("XTalk total Power Penalty", getRxTspPenalty(TBD,
391 // ImpairmentType.CrossTalkTotalPowerDB, penalitiesMap));
392 // impairments.put("Power penalty", getRxTspPenalty(TBD,
393 // ImpairmentType.PowerDBm, penalitiesMap));
394 LOG.info("Penalty resulting from CD, PMD and PDL is {} dB with following contributions {}",
395 totalPenalty, impairments);
396 double margin = calcOsnrdB - totalPenalty - rxOsnrdB;
397 LOG.info("According to RX TSP Specification and calculated impairments Margin is {} dB ", margin);
399 LOG.warn("Negative margin shall result in PCE rejecting the analyzed path");
405 * This generic method is called from getPceRxTspParameters to provide the
406 * Penalties associated with CD, PMD and DGD for Xponder. It scans a penalty
407 * list that includes penalty values corresponding to an interval between an
408 * upper and a lower boundary for each of the above parameters.
410 * @param impairmentType
411 * : the type of impairment (CD/PMD/DGD)
412 * @param calculatedParameter
413 * calculated accumulated value on the line for the impairment
414 * @param penaltiesMap
415 * the global map of penalties retrieved by getPceRxTspParameters
416 * from the Xponder operational mode
418 * @return the penalty associated with accumulated impairment if it is in the
419 * range specified in the table, a value that will lead to reject the
420 * path if this is not the case
423 private double getRxTspPenalty(double calculatedParameter, ImpairmentType impairmentType,
424 Map<PenaltiesKey, Penalties> penalitiesMap) {
425 Penalties penalty = penalitiesMap.values().stream()
426 // We only keep penalties corresponding to the calculated Parameter
427 .filter(val -> val.getParameterAndUnit().getName().equals(impairmentType.getName()))
428 // we sort it according to the comparator (based on up-to-boundary)
429 .sorted(penaltiesComparator)
430 // keep only items for which up to boundary is greater than calculatedParameter
431 .filter(val -> val.getUpToBoundary().doubleValue() >= calculatedParameter)
432 // takes the immediate greater or equal value
433 .findFirst().orElse(null);
434 if (penalty == null) {
435 //means a boundary that is greater than calculatedParameter couldn't be found
436 // Out of specification!
439 // In spec, return penalty associated with calculatedParameter
440 LOG.info("Penalty for {} is {} dB", impairmentType, penalty.getPenaltyValue().getValue().doubleValue());
441 return penalty.getPenaltyValue().getValue().doubleValue();
445 * This method retrieves performance parameters associated with ROADMs and
446 * Amplifiers. It calculates the contribution of the node to the degradation of
447 * the signal for CD, DGD, PDL, and OSNR which is calculated through a
448 * polynomial fit described in the catalog. It finally corrects the accumulated
449 * values for these parameters and return them.
451 * @param catalogNodeType
452 * crossed node path type (ADD/DROP/EXPRESS/AMP)
453 * @param operationalModeId
454 * operational-mode-Id of the Node (OpenROADM only)
456 * accumulated chromatic dispersion across the line
458 * Square of accumulated Group velocity dispersion across the line
460 * Square of the accumulated Polarization Dependant Loss across the
463 * Input power required to calculate OSNR contribution of the node =
466 * Linear Optical Noise to Signal Ratio resulting from the
467 * transmission on the line, that shall include the Non Linear
470 * Interchannel spacing used for correction to calculate OSNR
471 * contribution of the node
473 * @return Impairment, a map that provides corrected values for all calculated
474 * parameters which includes the contribution of the node
475 * (CD/DGD2/PDL2/ONSRLin)
476 * @throws RuntimeException
477 * if operationalModeId is not described in the catalog
480 public Map<String, Double> getPceRoadmAmpParameters(CatalogConstant.CatalogNodeType catalogNodeType,
481 String operationalModeId, double pwrIn, double cd, double dgd2, double pdl2,
482 double onsrLin, double spacing) {
483 double maxIntroducedCd;
484 double maxIntroducedPdl;
485 double maxIntroducedDgd;
486 List<Double> osnrPolynomialFits;
487 switch (catalogNodeType) {
489 var omCatalogIid = InstanceIdentifier
490 .builder(OperationalModeCatalog.class)
491 .child(OpenroadmOperationalModes.class)
494 .child(AddOpenroadmOperationalMode.class, new AddOpenroadmOperationalModeKey(operationalModeId))
498 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
499 if (omOptional.isEmpty()) {
500 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
501 return new HashMap<>();
503 var orAddOM = omOptional.get();
504 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orAddOM);
505 networkTransactionService.close();
506 maxIntroducedCd = orAddOM.getMaxIntroducedCd().doubleValue();
507 // As per current OpenROADM Spec
508 //maxIntroducedPdl = orAddOM.getMaxIntroducedPdl().getValue().doubleValue();
509 // Applying calculation as provided in Julia's tool
510 maxIntroducedPdl = Math.sqrt(0.2 * 0.2 + 0.4 * 0.4);
511 maxIntroducedDgd = orAddOM.getMaxIntroducedDgd().doubleValue();
512 osnrPolynomialFits = List.of(orAddOM.getIncrementalOsnr().getValue().doubleValue());
513 } catch (InterruptedException | ExecutionException e) {
515 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
517 throw new RuntimeException(
518 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
519 + omCatalogIid + " :" + e);
521 networkTransactionService.close();
526 var omCatalogIid1 = InstanceIdentifier
527 .builder(OperationalModeCatalog.class)
528 .child(OpenroadmOperationalModes.class)
531 .child(OpenroadmOperationalMode.class, new OpenroadmOperationalModeKey(operationalModeId))
535 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid1).get();
536 if (omOptional.isEmpty()) {
537 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
538 return new HashMap<>();
540 var orDropOM = omOptional.get();
541 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orDropOM);
542 networkTransactionService.close();
543 maxIntroducedCd = orDropOM.getMaxIntroducedCd().doubleValue();
544 // As per current OpenROADM Spec
545 // maxIntroducedPdl = orDropOM.getMaxIntroducedPdl().getValue().doubleValue();
546 // Applying calculation as provided in Julia's tool
547 maxIntroducedPdl = Math.sqrt(0.2 * 0.2 + 0.4 * 0.4);
548 maxIntroducedDgd = orDropOM.getMaxIntroducedDgd().doubleValue();
549 osnrPolynomialFits = List.of(
550 orDropOM.getOsnrPolynomialFit().getD().doubleValue(),
551 orDropOM.getOsnrPolynomialFit().getC().doubleValue(),
552 orDropOM.getOsnrPolynomialFit().getB().doubleValue(),
553 orDropOM.getOsnrPolynomialFit().getA().doubleValue());
554 } catch (InterruptedException | ExecutionException e) {
555 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
557 throw new RuntimeException(
558 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
559 + omCatalogIid1 + " :" + e);
561 networkTransactionService.close();
566 var omCatalogIid2 = InstanceIdentifier
567 .builder(OperationalModeCatalog.class)
568 .child(OpenroadmOperationalModes.class)
570 .child(Express.class)
572 org.opendaylight.yang.gen.v1.http
573 .org.openroadm.operational.mode.catalog.rev211210
574 .operational.mode.roadm.express.parameters.express.OpenroadmOperationalMode.class,
575 new org.opendaylight.yang.gen.v1.http
576 .org.openroadm.operational.mode.catalog.rev211210
577 .operational.mode.roadm.express.parameters.express.OpenroadmOperationalModeKey(
581 var omOptional = networkTransactionService
582 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid2)
584 if (omOptional.isEmpty()) {
585 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
586 return new HashMap<>();
588 var orExpressOM = omOptional.get();
589 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orExpressOM);
590 maxIntroducedCd = orExpressOM.getMaxIntroducedCd().doubleValue();
591 // As per current OpenROADM Spec
592 // maxIntroducedPdl = orExpressOM.getMaxIntroducedPdl().getValue().doubleValue();
593 // Applying calculation as provided in Julia's tool
594 maxIntroducedPdl = Math.sqrt(2 * 0.2 * 0.2 + 2 * 0.4 * 0.4);
595 maxIntroducedDgd = orExpressOM.getMaxIntroducedDgd().doubleValue();
596 osnrPolynomialFits = List.of(
597 orExpressOM.getOsnrPolynomialFit().getD().doubleValue(),
598 orExpressOM.getOsnrPolynomialFit().getC().doubleValue(),
599 orExpressOM.getOsnrPolynomialFit().getB().doubleValue(),
600 orExpressOM.getOsnrPolynomialFit().getA().doubleValue());
601 } catch (InterruptedException | ExecutionException e) {
602 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
604 throw new RuntimeException(
605 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
606 + omCatalogIid2 + " :" + e);
608 networkTransactionService.close();
613 var omCatalogIid3 = InstanceIdentifier
614 .builder(OperationalModeCatalog.class)
615 .child(OpenroadmOperationalModes.class)
616 .child(Amplifiers.class)
617 .child(Amplifier.class)
618 .child(OpenroadmOperationalMode.class, new OpenroadmOperationalModeKey(operationalModeId))
621 var omOptional = networkTransactionService
622 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid3)
624 if (omOptional.isEmpty()) {
625 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
626 return new HashMap<>();
628 var orAmpOM = omOptional.get();
629 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orAmpOM);
630 networkTransactionService.close();
631 maxIntroducedCd = orAmpOM.getMaxIntroducedCd().doubleValue();
632 // As per current OpenROADM Spec
633 // maxIntroducedPdl = orAmpOM.getMaxIntroducedPdl().getValue().doubleValue();
634 // Applying calculation as provided in Julia's tool
635 maxIntroducedPdl = 0.2;
636 maxIntroducedDgd = orAmpOM.getMaxIntroducedDgd().doubleValue();
637 osnrPolynomialFits = List.of(
638 orAmpOM.getOsnrPolynomialFit().getD().doubleValue(),
639 orAmpOM.getOsnrPolynomialFit().getC().doubleValue(),
640 orAmpOM.getOsnrPolynomialFit().getB().doubleValue(),
641 orAmpOM.getOsnrPolynomialFit().getA().doubleValue());
642 } catch (InterruptedException | ExecutionException e) {
643 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
645 throw new RuntimeException(
646 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
647 + omCatalogIid3 + " :" + e);
649 networkTransactionService.close();
653 LOG.error("Unsupported catalogNodeType {}", catalogNodeType);
654 return new HashMap<>();
656 cd += maxIntroducedCd;
657 pdl2 += Math.pow(maxIntroducedPdl, 2.0);
658 dgd2 += Math.pow(maxIntroducedDgd, 2.0);
661 // We correct PwrIn to the value corresponding to a 50 GHz Bandwidth, because OpenROADM spec (polynomial fit)
662 // is based on power in 50GHz Bandwidth
663 pwrIn -= 10 * Math.log10(spacing / 50.0);
664 if (catalogNodeType != CatalogNodeType.ADD) {
665 // For add, incremental OSNR is defined for Noiseless input, BW Correction (contrib) does not apply
666 contrib = 10 * Math.log10(spacing / 50.0);
668 for (double fit : osnrPolynomialFits) {
669 contrib += pwrFact * fit;
671 // Using a for loop with multiplication instead of Math.pow optimizes the computation.
673 // Double is not strictly spoken a Mathematics commutative group because
674 // computers design limits their bits representation size.
675 // As a result, the order of arithmetic operation matters.
676 // In a sum, smallest numbers should be introduced first for a maximum of
677 // precision. In other words, the sum
678 // 10 * Math.log10(spacing / 50.0)
679 // + osnrPolynomialFits.get(0)
680 // + osnrPolynomialFits.get(1) * pwrIn
681 // + osnrPolynomialFits.get(2) * Math.pow(pwrIn, 2)
682 // + osnrPolynomialFits.get(3) * Math.pow(pwrIn, 3)
683 // is not equal to its reverse form
684 // osnrPolynomialFits.get(3) * Math.pow(pwrIn, 3)
685 // + osnrPolynomialFits.get(2) * Math.pow(pwrIn, 2)
686 // + osnrPolynomialFits.get(1) * pwrIn
687 // + osnrPolynomialFits.get(0)
688 // + 10 * Math.log10(spacing / 50.0)
689 // and the more precise first form should be preferred here.
690 onsrLin += Math.pow(10, -contrib / 10);
691 Map<String, Double> impairments = new HashMap<>();
692 impairments.put("CD", cd);
693 impairments.put("DGD2", dgd2);
694 impairments.put("PDL2", pdl2);
695 impairments.put("ONSRLIN", onsrLin);
696 LOG.info("Accumulated CD is {} ps, DGD is {} ps and PDL is {} dB", cd, Math.sqrt(dgd2), Math.sqrt(pdl2));
697 LOG.info("Resulting OSNR is {} dB", 10 * Math.log10(1 / onsrLin));
702 * This method calculates power that shall be applied at the output of ROADMs and
703 * Amplifiers. It retrieves the mask-power-vs-Pin and calculates target output
704 * power from the span loss
706 * @param catalogNodeType
707 * crossed node path type (ADD/EXPRESS/AMP)
708 * @param operationalModeId
709 * operational-mode-Id of the Node (OpenROADM only)
711 * spanLoss at the output of the ROADM
712 * @param powerCorrection
713 * correction to be applied to the calculated power according to fiber type
715 * Interchannel spacing used for correction to calculate output power
716 * @return outputPower
717 * Corrected output power calculated according to channel spacing
718 * @throws RuntimeException
719 * if operationalModeId is not described in the catalog
721 public double getPceRoadmAmpOutputPower(CatalogConstant.CatalogNodeType catalogNodeType,
722 String operationalModeId, double spanLoss, double spacing, double powerCorrection) {
723 double pout = 99999.0;
724 switch (catalogNodeType) {
726 var omCatalogIid = InstanceIdentifier
727 .builder(OperationalModeCatalog.class)
728 .child(OpenroadmOperationalModes.class)
731 .child(AddOpenroadmOperationalMode.class, new AddOpenroadmOperationalModeKey(operationalModeId))
735 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
736 if (omOptional.isEmpty()) {
737 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
740 var orAddOM = omOptional.get();
741 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orAddOM);
742 networkTransactionService.close();
743 var mask = orAddOM.getMaskPowerVsPin();
744 for (Map.Entry<MaskPowerVsPinKey, MaskPowerVsPin> pw : mask.entrySet()) {
745 if (spanLoss >= pw.getKey().getLowerBoundary().doubleValue()
746 && spanLoss <= pw.getKey().getUpperBoundary().doubleValue()) {
747 pout = pw.getValue().getC().doubleValue() * spanLoss + pw.getValue().getD().doubleValue()
748 + powerCorrection + 10 * Math.log10(spacing / 50.0);
749 LOG.info("Calculated target Output power is {} dB in {} Bandwidth", pout, spacing);
753 LOG.info("Did not succeed in calculating target Output power, SpanLoss {} dB is out of range",
755 } catch (InterruptedException | ExecutionException e) {
756 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
758 throw new RuntimeException(
759 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
760 + omCatalogIid + " :" + e);
762 networkTransactionService.close();
767 var omCatalogIid2 = InstanceIdentifier
768 .builder(OperationalModeCatalog.class)
769 .child(OpenroadmOperationalModes.class)
771 .child(Express.class)
773 org.opendaylight.yang.gen.v1.http
774 .org.openroadm.operational.mode.catalog.rev211210
775 .operational.mode.roadm.express.parameters.express.OpenroadmOperationalMode.class,
776 new org.opendaylight.yang.gen.v1.http
777 .org.openroadm.operational.mode.catalog.rev211210
778 .operational.mode.roadm.express.parameters.express.OpenroadmOperationalModeKey(
782 var omOptional = networkTransactionService
783 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid2)
785 if (omOptional.isEmpty()) {
786 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
789 var orExpressOM = omOptional.get();
790 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orExpressOM);
791 var mask = orExpressOM.getMaskPowerVsPin();
792 for (Map.Entry<MaskPowerVsPinKey, MaskPowerVsPin> pw : mask.entrySet()) {
793 if (spanLoss >= pw.getKey().getLowerBoundary().doubleValue()
794 && spanLoss <= pw.getKey().getUpperBoundary().doubleValue()) {
795 pout = pw.getValue().getC().doubleValue() * spanLoss + pw.getValue().getD().doubleValue()
796 + powerCorrection + 10 * Math.log10(spacing / 50.0);
797 LOG.info("Calculated target Output power is {} dB in {} Bandwidth", pout, spacing);
801 LOG.info("Did not succeed in calculating target Output power, SpanLoss {} dB is out of range",
803 } catch (InterruptedException | ExecutionException e) {
804 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
806 throw new RuntimeException(
807 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
808 + omCatalogIid2 + " :" + e);
810 networkTransactionService.close();
815 LOG.error("Unsupported catalogNodeType {}", catalogNodeType);
821 * Non linear contribution computation.
822 * Public method calculating non linear contribution among the path from
823 * launched power and span length Formula comes from
824 * OpenROADM_OSNR_Calculation_20220610 Tool The resulting contribution shall be
825 * calculated for each fiber span and summed up
826 * @param launchedPowerdB
827 * The power launched in the span (shall account for Optical Distribution
830 * Length of the span in km
832 * OpenROADM power and osnr contribution calculations are based on
833 * spacing between channels : the Media Channel (MC) width
835 * @return nonLinearOnsrContributionLin
836 * The inverse of the NL OSNR contribution converted from dB to linear value
838 public double calculateNLonsrContribution(double launchedPowerdB, double spanLength, double spacing) {
839 double constanteC0 = 0 ;
840 if (spacing > 162.5) {
841 constanteC0 = CatalogConstant.NLCONSTANTC0GT1625;
842 } else if (spacing > 112.5) {
843 constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1625;
844 } else if (spacing > 100.0) {
845 constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1125;
846 } else if (spacing > 87.5) {
847 constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1000;
849 constanteC0 = CatalogConstant.NLCONSTANTC0UPTO875;
851 double nonLinearOnsrContributionLinDb = launchedPowerdB * CatalogConstant.NLCONSTANTC1
852 + constanteC0 + CatalogConstant.NLCONSTANTCE * Math.exp(CatalogConstant.NLCONSTANTEX * spanLength);
853 LOG.info(" OSNR Non Linear contribution is {} dB", nonLinearOnsrContributionLinDb);
854 return Math.pow(10.0, -nonLinearOnsrContributionLinDb / 10);
857 public boolean isCatalogFilled() {
858 var omCatalogIid = InstanceIdentifier
859 .builder(OperationalModeCatalog.class)
860 .child(OpenroadmOperationalModes.class)
863 .child(AddOpenroadmOperationalMode.class, new AddOpenroadmOperationalModeKey(CatalogConstant.MWWRCORE))
866 if (networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get().isEmpty()) {
867 LOG.error("Operational Mode catalog is not filled");
870 networkTransactionService.close();
872 } catch (InterruptedException | ExecutionException e) {
873 LOG.error("readMdSal: Error reading Operational Mode Catalog, catalog not filled");
874 throw new RuntimeException(
875 "readMdSal: Error reading from operational store, Operational Mode Catalog not filled" + e);
877 networkTransactionService.close();