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.network.NetworkTransactionService;
18 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.link.types.rev191129.RatioDB;
19 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.ImpairmentType;
20 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.amplifier.parameters.Amplifier;
21 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.OpenroadmOperationalModes;
22 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.SpecificOperationalModes;
23 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.Amplifiers;
24 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.Roadms;
25 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.XpondersPluggables;
26 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.xponders.pluggables.XponderPluggableOpenroadmOperationalMode;
27 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.xponders.pluggables.XponderPluggableOpenroadmOperationalModeKey;
28 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.specific.operational.modes.SpecificOperationalMode;
29 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.specific.operational.modes.SpecificOperationalModeKey;
30 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.common.amplifier.drop.parameters.OpenroadmOperationalMode;
31 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.common.amplifier.drop.parameters.OpenroadmOperationalModeKey;
32 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.add.parameters.Add;
33 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.add.parameters.add.AddOpenroadmOperationalMode;
34 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.add.parameters.add.AddOpenroadmOperationalModeKey;
35 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.drop.parameters.Drop;
36 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.express.parameters.Express;
37 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.Penalties;
38 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.PenaltiesKey;
39 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.TXOOBOsnrKey;
40 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.OperationalModeCatalog;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
46 * Utility class for Catalog. Following methods are used to retrieve parameters
47 * from the specification catalog. They point to either openROADM or specific
48 * operational modes. They provide to the PCE, the OLM and the Renderer, the
49 * required parameters to calculate impairments and set output power levels
50 * according to the specifications.
53 public class CatalogUtils {
55 private static final Logger LOG = LoggerFactory.getLogger(CatalogUtils.class);
57 private static final String OPMODE_MISMATCH_MSG =
58 "Operational Mode {} passed to getPceRoadmAmpParameters does not correspond to an OpenROADM mode"
59 + "Parameters for amplifier and/or ROADMs can not be derived from specific-operational-modes.";
60 private static final Map<CatalogConstant.CatalogNodeType, String> CATALOGNODETYPE_OPERATIONMODEID_MAP = Map.of(
61 CatalogConstant.CatalogNodeType.ADD, CatalogConstant.MWWRCORE,
62 CatalogConstant.CatalogNodeType.DROP, CatalogConstant.MWWRCORE,
63 CatalogConstant.CatalogNodeType.EXPRESS, CatalogConstant.MWMWCORE,
64 CatalogConstant.CatalogNodeType.AMP, CatalogConstant.MWISTANDARD);
65 private static final Map<String, String> TSP_DEFAULT_OM_MAP = Map.of(
66 StringConstants.SERVICE_TYPE_100GE_T, CatalogConstant.ORW100GSC,
67 StringConstants.SERVICE_TYPE_OTU4, CatalogConstant.ORW100GSC,
68 StringConstants.SERVICE_TYPE_OTUC2, CatalogConstant.ORW200GOFEC316GBD,
69 StringConstants.SERVICE_TYPE_OTUC3, CatalogConstant.ORW300GOFEC631GBD,
70 StringConstants.SERVICE_TYPE_OTUC4, CatalogConstant.ORW400GOFEC631GBD,
71 StringConstants.SERVICE_TYPE_400GE, CatalogConstant.ORW400GOFEC631GBD);
73 private final PenaltiesComparator penaltiesComparator = new PenaltiesComparator();
74 private NetworkTransactionService networkTransactionService;
76 public CatalogUtils(NetworkTransactionService networkTransactionService) {
77 this.networkTransactionService = networkTransactionService;
81 * Following method returns default OperationalModeId for devices that do not expose them.
83 * @param catalogNodeType
84 * identifies type of nodes in the catalog
86 * allows for Xponder selecting default mode according to the rate
88 * @return a default operational mode that corresponds to initial specifications
91 public String getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType catalogNodeType,
93 if (CATALOGNODETYPE_OPERATIONMODEID_MAP.containsKey(catalogNodeType)) {
94 return CATALOGNODETYPE_OPERATIONMODEID_MAP.get(catalogNodeType);
96 if (!catalogNodeType.equals(CatalogConstant.CatalogNodeType.TSP)) {
97 LOG.warn("Unsupported catalogNodeType {}", catalogNodeType);
100 if (!TSP_DEFAULT_OM_MAP.containsKey(serviceType)) {
101 LOG.warn("Unsupported serviceType {} for TSP catalogNodeType", serviceType);
104 return TSP_DEFAULT_OM_MAP.get(serviceType);
108 * This method retrieves channel-spacing associated with a Xponder TX.
110 * @param operationalModeId
111 * operational-mode-Id of the Xponder (OR or Specific)
113 * @return the channel spacing used to correct OSNR contribution values from
114 * ROADMs and amplifiers
115 * @throws RuntimeException
116 * if operationalModeId is not described in the catalog
119 public double getPceTxTspChannelSpacing(String operationalModeId) {
122 if (operationalModeId.startsWith("OR")) {
123 InstanceIdentifier<XponderPluggableOpenroadmOperationalMode> omCatalogIid = InstanceIdentifier
124 .builder(OperationalModeCatalog.class)
125 .child(OpenroadmOperationalModes.class)
126 .child(XpondersPluggables.class)
127 .child(XponderPluggableOpenroadmOperationalMode.class,
128 new XponderPluggableOpenroadmOperationalModeKey(operationalModeId))
131 Optional<XponderPluggableOpenroadmOperationalMode> omOptional =
132 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
133 if (omOptional.isEmpty()) {
134 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
137 XponderPluggableOpenroadmOperationalMode orTspOM = omOptional.get();
138 maxRollOff = orTspOM.getMaxRollOff() == null ? 0 : orTspOM.getMaxRollOff().doubleValue();
139 baudRate = orTspOM.getBaudRate().doubleValue();
140 } catch (InterruptedException | ExecutionException e) {
141 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
142 throw new RuntimeException("Operational mode not populated in Catalog : " + omCatalogIid + " :" + e);
144 networkTransactionService.close();
147 // In other cases, means the mode is a non OpenROADM specific Operational Mode
148 InstanceIdentifier<SpecificOperationalMode> omCatalogIid = InstanceIdentifier
149 .builder(OperationalModeCatalog.class)
150 .child(SpecificOperationalModes.class)
151 .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId))
155 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
156 if (somOptional.isEmpty()) {
157 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
160 SpecificOperationalMode speTspOM = somOptional.get();
161 maxRollOff = speTspOM.getMaxRollOff() == null ? 0 : speTspOM.getMaxRollOff().doubleValue();
162 baudRate = speTspOM.getBaudRate().doubleValue();
163 } catch (InterruptedException | ExecutionException e) {
164 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
165 throw new RuntimeException("Operational mode not populated in Catalog : " + omCatalogIid + " :" + e);
167 networkTransactionService.close();
170 if (maxRollOff == 0) {
171 if (CatalogConstant.ORW100GSC.contentEquals(operationalModeId)) {
172 // OR 100G SCFEC is the only case where rolloff factor is not mandatory in the catalog
173 LOG.info("Operational Mode {} associated channel spacing is 50.0", operationalModeId);
176 LOG.warn("Missing rolloff factor (mandatory in Catalog) from Operational Mode {}: use default=0.2",
180 double spacing = 12.5 * Math.ceil(baudRate * (1 + maxRollOff) / 12.5);
181 LOG.info("Operational Mode {} associated channel spacing is {}", operationalModeId, spacing);
186 * This method retrieves performance parameters associated with a Xponder TX.
188 * @param operationalModeId
189 * operational-mode-Id of the Xponder (OR or Specific)
190 * @param addDropMuxOperationalModeId
191 * operational-mode-Id of the Add-Drop bloc the XponderTX is
192 * associated to (conditions TX-OOB OSNR value)
194 * @return the linear Optical Noise to signal Ratio
195 * @throws RuntimeException
196 * if operationalModeId is not described in the catalog
198 public double getPceTxTspParameters(String operationalModeId, String addDropMuxOperationalModeId) {
199 double txOnsrLin = 0.0;
200 XponderPluggableOpenroadmOperationalMode orTspOM = null;
201 SpecificOperationalMode speTspOM = null;
202 RatioDB minOOBOsnrSingleChannelValue;
203 RatioDB minOOBOsnrMultiChannelValue;
204 if (operationalModeId.startsWith("OR")) {
205 InstanceIdentifier<XponderPluggableOpenroadmOperationalMode> omCatalogIid = InstanceIdentifier
206 .builder(OperationalModeCatalog.class)
207 .child(OpenroadmOperationalModes.class)
208 .child(XpondersPluggables.class)
209 .child(XponderPluggableOpenroadmOperationalMode.class,
210 new XponderPluggableOpenroadmOperationalModeKey(operationalModeId))
213 Optional<XponderPluggableOpenroadmOperationalMode> omOptional =
214 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
215 if (omOptional.isEmpty()) {
216 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
219 orTspOM = omOptional.get();
220 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orTspOM);
221 TXOOBOsnrKey key = new TXOOBOsnrKey(addDropMuxOperationalModeId);
222 if (orTspOM.getMinTXOsnr() != null) {
223 txOnsrLin = 1.0 / Math.pow(10.0, orTspOM.getMinTXOsnr().getValue().doubleValue() / 10.0);
225 if (orTspOM.nonnullTXOOBOsnr().get(key) == null) {
228 minOOBOsnrSingleChannelValue = orTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrSingleChannelValue();
229 minOOBOsnrMultiChannelValue = orTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrMultiChannelValue();
230 } catch (InterruptedException | ExecutionException e) {
231 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
232 throw new RuntimeException(
233 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
234 + omCatalogIid + " :" + e);
236 networkTransactionService.close();
239 // In other cases, means the mode is a non OpenROADM specific Operational Mode
240 InstanceIdentifier<SpecificOperationalMode> omCatalogIid = InstanceIdentifier
241 .builder(OperationalModeCatalog.class)
242 .child(SpecificOperationalModes.class)
243 .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId))
247 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
248 if (somOptional.isEmpty()) {
249 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
252 speTspOM = somOptional.get();
253 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", speTspOM);
254 TXOOBOsnrKey key = new TXOOBOsnrKey(addDropMuxOperationalModeId);
255 if (speTspOM.getMinTXOsnr() != null) {
256 txOnsrLin = 1.0 / Math.pow(10.0, speTspOM.getMinTXOsnr().getValue().doubleValue() / 10.0);
258 if (speTspOM.nonnullTXOOBOsnr().get(key) == null) {
261 minOOBOsnrSingleChannelValue = speTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrSingleChannelValue();
262 minOOBOsnrMultiChannelValue = speTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrMultiChannelValue();
263 } catch (InterruptedException | ExecutionException e) {
264 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
265 throw new RuntimeException(
266 "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :"
269 networkTransactionService.close();
272 if (minOOBOsnrSingleChannelValue != null) {
273 txOnsrLin += 1.0 / Math.pow(10.0, minOOBOsnrSingleChannelValue.getValue().doubleValue() / 10.0);
275 if (minOOBOsnrMultiChannelValue != null) {
276 txOnsrLin += 1.0 / Math.pow(10.0, minOOBOsnrMultiChannelValue.getValue().doubleValue() / 10.0);
282 * This method retrieves performance parameters associated with a Xponder RX.
283 * It calls getRxTspPenalty to evaluate the penalty associated with CD/PMD/PDL
284 * It compares expected OSNR with the OSNR resulting from the line degradation,
285 * and finally calculates and return the resulting margin.
287 * @param operationalModeId
288 * operational-mode-Id of the Xponder (OR or Specific)
290 * accumulated chromatic dispersion across the line
292 * accumulated Polarization mode dispersion across the line
294 * accumulated Polarization Dependant Loss across the line
296 * Optical Signal to Noise Ratio (dB)resulting from the transmission
297 * on the line, that shall include the Non Linear contribution
299 * @return the margin on the service path
300 * @throws RuntimeException
301 * if operationalModeId is not described in the catalog
303 public double getPceRxTspParameters(String operationalModeId, double calcCd, double calcPmd,
304 double calcPdl, double calcOsnrdB) {
305 double rxOsnrdB = 0.0;
306 XponderPluggableOpenroadmOperationalMode orTspOM = null;
307 SpecificOperationalMode speTspOM = null;
308 Map<PenaltiesKey, Penalties> penaltiesMap = null;
309 if (operationalModeId.startsWith("OR")) {
310 var omCatalogIid = InstanceIdentifier
311 .builder(OperationalModeCatalog.class)
312 .child(OpenroadmOperationalModes.class)
313 .child(XpondersPluggables.class)
314 .child(XponderPluggableOpenroadmOperationalMode.class,
315 new XponderPluggableOpenroadmOperationalModeKey(operationalModeId))
318 Optional<XponderPluggableOpenroadmOperationalMode> omOptional = networkTransactionService
319 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
320 if (omOptional.isPresent()) {
321 orTspOM = omOptional.get();
322 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orTspOM);
323 if (orTspOM.getMinRXOsnrTolerance() != null) {
324 rxOsnrdB = orTspOM.getMinRXOsnrTolerance().getValue().doubleValue();
326 penaltiesMap = orTspOM.getPenalties();
328 } catch (InterruptedException | ExecutionException e) {
329 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
330 throw new RuntimeException(
331 "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :"
334 networkTransactionService.close();
337 // In other cases, means the mode is a non OpenROADM specific Operational Mode
338 // InstanceIdentifier<SpecificOperationalMode> omCatalogIid = InstanceIdentifier
339 var omCatalogIid = InstanceIdentifier
340 .builder(OperationalModeCatalog.class)
341 .child(SpecificOperationalModes.class)
342 .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId))
345 Optional<SpecificOperationalMode> somOptional = networkTransactionService
346 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
347 if (somOptional.isPresent()) {
348 speTspOM = somOptional.get();
349 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", speTspOM);
350 if (speTspOM.getMinRXOsnrTolerance() != null) {
351 rxOsnrdB = speTspOM.getMinRXOsnrTolerance().getValue().doubleValue();
353 penaltiesMap = speTspOM.getPenalties();
355 } catch (InterruptedException | ExecutionException e) {
356 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
357 throw new RuntimeException(
358 "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :"
361 networkTransactionService.close();
364 if (penaltiesMap == null) {
365 LOG.error("Unable to calculate margin as penaltyMap can not be retrieved : Operational mode not populated");
368 HashMap<String, Double> impairments = new HashMap<>();
369 double penalty = getRxTspPenalty(calcCd, ImpairmentType.CDPsNm, penaltiesMap);
370 impairments.put("CDpenalty", penalty);
371 double totalPenalty = penalty;
372 penalty = getRxTspPenalty(calcPmd, ImpairmentType.PMDPs, penaltiesMap);
373 impairments.put("PMD Penalty", penalty);
374 totalPenalty += penalty;
375 penalty = getRxTspPenalty(calcPdl, ImpairmentType.PDLDB, penaltiesMap);
376 impairments.put("PDL penalty", penalty);
377 totalPenalty += penalty;
378 // TODO for Future work since at that time we have no way to calculate the following
379 // parameters,even if penalties are defined in the OpenROADM specifications
381 // impairments.put("Colorless Drop Adjacent Xtalk Penalty", getRxTspPenalty(TBD,
382 // ImpairmentType.ColorlessDropAdjacentChannelCrosstalkGHz, penalitiesMap));
383 // impairments.put("XTalk total Power Penalty", getRxTspPenalty(TBD,
384 // ImpairmentType.CrossTalkTotalPowerDB, penalitiesMap));
385 // impairments.put("Power penalty", getRxTspPenalty(TBD,
386 // ImpairmentType.PowerDBm, penalitiesMap));
387 LOG.info("Penalty resulting from CD, PMD and PDL is {} dB with following contributions {}",
388 totalPenalty, impairments);
389 double margin = calcOsnrdB - totalPenalty - rxOsnrdB;
390 LOG.info("According to RX TSP Specification and calculated impairments Margin is {} dB ", margin);
392 LOG.warn("Negative margin shall result in PCE rejecting the analyzed path");
398 * This generic method is called from getPceRxTspParameters to provide the
399 * Penalties associated with CD, PMD and DGD for Xponder. It scans a penalty
400 * list that includes penalty values corresponding to an interval between an
401 * upper and a lower boundary for each of the above parameters.
403 * @param impairmentType
404 * : the type of impairment (CD/PMD/DGD)
405 * @param calculatedParameter
406 * calculated accumulated value on the line for the impairment
407 * @param penaltiesMap
408 * the global map of penalties retrieved by getPceRxTspParameters
409 * from the Xponder operational mode
411 * @return the penalty associated with accumulated impairment if it is in the
412 * range specified in the table, a value that will lead to reject the
413 * path if this is not the case
416 private double getRxTspPenalty(double calculatedParameter, ImpairmentType impairmentType,
417 Map<PenaltiesKey, Penalties> penalitiesMap) {
418 Penalties penalty = penalitiesMap.values().stream()
419 // We only keep penalties corresponding to the calculated Parameter
420 .filter(val -> val.getParameterAndUnit().getName().equals(impairmentType.getName()))
421 // we sort it according to the comparator (based on up-to-boundary)
422 .sorted(penaltiesComparator)
423 // keep only items for which up to boundary is greater than calculatedParameter
424 .filter(val -> val.getUpToBoundary().doubleValue() >= calculatedParameter)
425 // takes the immediate greater or equal value
426 .findFirst().orElse(null);
427 return penalty == null
428 //means a boundary that is greater than calculatedParameter couldn't be found
429 // Out of specification!
431 // In spec, return penalty associated with calculatedParameter
432 : penalty.getPenaltyValue().getValue().doubleValue();
436 * This method retrieves performance parameters associated with ROADMs and
437 * Amplifiers. It calculates the contribution of the node to the degradation of
438 * the signal for CD, DGD, PDL, and OSNR which is calculated through a
439 * polynomial fit described in the catalog. It finally corrects the accumulated
440 * values for these parameters and return them.
442 * @param catalogNodeType
443 * crossed node path type (ADD/DROP/EXPRESS/AMP)
444 * @param operationalModeId
445 * operational-mode-Id of the Node (OpenROADM only)
447 * accumulated chromatic dispersion across the line
449 * Square of accumulated Group velocity dispersion across the line
451 * Square of the accumulated Polarization Dependant Loss across the
454 * Input power required to calculate OSNR contribution of the node =
457 * Linear Optical Noise to Signal Ratio resulting from the
458 * transmission on the line, that shall include the Non Linear
461 * Interchannel spacing used for correction to calculate OSNR
462 * contribution of the node
464 * @return Impairment, a map that provides corrected values for all calculated
465 * parameters which includes the contribution of the node
466 * (CD/DGD2/PDL2/ONSRLin)
467 * @throws RuntimeException
468 * if operationalModeId is not described in the catalog
471 public Map<String, Double> getPceRoadmAmpParameters(CatalogConstant.CatalogNodeType catalogNodeType,
472 String operationalModeId, double pwrIn, double cd, double dgd2, double pdl2,
473 double onsrLin, double spacing) {
474 double maxIntroducedCd;
475 double maxIntroducedPdl;
476 double maxIntroducedDgd;
477 List<Double> osnrPolynomialFits;
478 switch (catalogNodeType) {
480 var omCatalogIid = InstanceIdentifier
481 .builder(OperationalModeCatalog.class)
482 .child(OpenroadmOperationalModes.class)
485 .child(AddOpenroadmOperationalMode.class, new AddOpenroadmOperationalModeKey(operationalModeId))
489 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
490 if (omOptional.isEmpty()) {
491 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
492 return new HashMap<>();
494 var orAddOM = omOptional.get();
495 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orAddOM);
496 networkTransactionService.close();
497 maxIntroducedCd = orAddOM.getMaxIntroducedCd().doubleValue();
498 maxIntroducedPdl = orAddOM.getMaxIntroducedPdl().getValue().doubleValue();
499 maxIntroducedDgd = orAddOM.getMaxIntroducedDgd().doubleValue();
500 osnrPolynomialFits = List.of(orAddOM.getIncrementalOsnr().getValue().doubleValue());
501 } catch (InterruptedException | ExecutionException e) {
503 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
505 throw new RuntimeException(
506 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
507 + omCatalogIid + " :" + e);
509 networkTransactionService.close();
514 var omCatalogIid1 = InstanceIdentifier
515 .builder(OperationalModeCatalog.class)
516 .child(OpenroadmOperationalModes.class)
519 .child(OpenroadmOperationalMode.class, new OpenroadmOperationalModeKey(operationalModeId))
523 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid1).get();
524 if (omOptional.isEmpty()) {
525 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
526 return new HashMap<>();
528 var orDropOM = omOptional.get();
529 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orDropOM);
530 networkTransactionService.close();
531 maxIntroducedCd = orDropOM.getMaxIntroducedCd().doubleValue();
532 maxIntroducedPdl = orDropOM.getMaxIntroducedPdl().getValue().doubleValue();
533 maxIntroducedDgd = orDropOM.getMaxIntroducedDgd().doubleValue();
534 osnrPolynomialFits = List.of(
535 orDropOM.getOsnrPolynomialFit().getD().doubleValue(),
536 orDropOM.getOsnrPolynomialFit().getC().doubleValue(),
537 orDropOM.getOsnrPolynomialFit().getB().doubleValue(),
538 orDropOM.getOsnrPolynomialFit().getA().doubleValue());
539 } catch (InterruptedException | ExecutionException e) {
540 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
542 throw new RuntimeException(
543 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
544 + omCatalogIid1 + " :" + e);
546 networkTransactionService.close();
551 var omCatalogIid2 = InstanceIdentifier
552 .builder(OperationalModeCatalog.class)
553 .child(OpenroadmOperationalModes.class)
555 .child(Express.class)
557 org.opendaylight.yang.gen.v1.http
558 .org.openroadm.operational.mode.catalog.rev211210
559 .operational.mode.roadm.express.parameters.express.OpenroadmOperationalMode.class,
560 new org.opendaylight.yang.gen.v1.http
561 .org.openroadm.operational.mode.catalog.rev211210
562 .operational.mode.roadm.express.parameters.express.OpenroadmOperationalModeKey(
566 var omOptional = networkTransactionService
567 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid2)
569 if (omOptional.isEmpty()) {
570 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
571 return new HashMap<>();
573 var orExpressOM = omOptional.get();
574 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orExpressOM);
575 maxIntroducedCd = orExpressOM.getMaxIntroducedCd().doubleValue();
576 maxIntroducedPdl = orExpressOM.getMaxIntroducedPdl().getValue().doubleValue();
577 maxIntroducedDgd = orExpressOM.getMaxIntroducedDgd().doubleValue();
578 osnrPolynomialFits = List.of(
579 orExpressOM.getOsnrPolynomialFit().getD().doubleValue(),
580 orExpressOM.getOsnrPolynomialFit().getC().doubleValue(),
581 orExpressOM.getOsnrPolynomialFit().getB().doubleValue(),
582 orExpressOM.getOsnrPolynomialFit().getA().doubleValue());
583 } catch (InterruptedException | ExecutionException e) {
584 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
586 throw new RuntimeException(
587 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
588 + omCatalogIid2 + " :" + e);
590 networkTransactionService.close();
595 var omCatalogIid3 = InstanceIdentifier
596 .builder(OperationalModeCatalog.class)
597 .child(OpenroadmOperationalModes.class)
598 .child(Amplifiers.class)
599 .child(Amplifier.class)
600 .child(OpenroadmOperationalMode.class, new OpenroadmOperationalModeKey(operationalModeId))
603 var omOptional = networkTransactionService
604 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid3)
606 if (omOptional.isEmpty()) {
607 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
608 return new HashMap<>();
610 var orAmpOM = omOptional.get();
611 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orAmpOM);
612 networkTransactionService.close();
613 maxIntroducedCd = orAmpOM.getMaxIntroducedCd().doubleValue();
614 maxIntroducedPdl = orAmpOM.getMaxIntroducedPdl().getValue().doubleValue();
615 maxIntroducedDgd = orAmpOM.getMaxIntroducedDgd().doubleValue();
616 osnrPolynomialFits = List.of(
617 orAmpOM.getOsnrPolynomialFit().getD().doubleValue(),
618 orAmpOM.getOsnrPolynomialFit().getC().doubleValue(),
619 orAmpOM.getOsnrPolynomialFit().getB().doubleValue(),
620 orAmpOM.getOsnrPolynomialFit().getA().doubleValue());
621 } catch (InterruptedException | ExecutionException e) {
622 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
624 throw new RuntimeException(
625 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
626 + omCatalogIid3 + " :" + e);
628 networkTransactionService.close();
632 LOG.error("Unsupported catalogNodeType {}", catalogNodeType);
633 return new HashMap<>();
635 cd += maxIntroducedCd;
636 pdl2 += Math.pow(maxIntroducedPdl, 2.0);
637 dgd2 += Math.pow(maxIntroducedDgd, 2.0);
639 double contrib = 10 * Math.log10(spacing / 50.0);
640 for (double fit : osnrPolynomialFits) {
641 contrib += pwrFact * fit;
643 // Using a for loop with multiplication instead of Math.pow optimizes the computation.
645 // Double is not strictly spoken a Mathematics commutative group because
646 // computers design limits their bits representation size.
647 // As a result, the order of arithmetic operation matters.
648 // In a sum, smallest numbers should be introduced first for a maximum of
649 // precision. In other words, the sum
650 // 10 * Math.log10(spacing / 50.0)
651 // + osnrPolynomialFits.get(0)
652 // + osnrPolynomialFits.get(1) * pwrIn
653 // + osnrPolynomialFits.get(2) * Math.pow(pwrIn, 2)
654 // + osnrPolynomialFits.get(3) * Math.pow(pwrIn, 3)
655 // is not equal to its reverse form
656 // osnrPolynomialFits.get(3) * Math.pow(pwrIn, 3)
657 // + osnrPolynomialFits.get(2) * Math.pow(pwrIn, 2)
658 // + osnrPolynomialFits.get(1) * pwrIn
659 // + osnrPolynomialFits.get(0)
660 // + 10 * Math.log10(spacing / 50.0)
661 // and the more precise first form should be preferred here.
662 onsrLin += Math.pow(10, -contrib / 10);
663 Map<String, Double> impairments = new HashMap<>();
664 impairments.put("CD", cd);
665 impairments.put("DGD2", dgd2);
666 impairments.put("PDL2", pdl2);
667 impairments.put("ONSRLIN", onsrLin);
668 LOG.info("Accumulated CD is {} ps, DGD2 is {} ps and PDL2 is {} dB", cd, Math.sqrt(dgd2), Math.sqrt(pdl2));
669 LOG.info("Resulting OSNR is {} dB", 10 * Math.log10(1 / onsrLin));
674 * Non linear contribution computation.
675 * Public method calculating non linear contribution among the path from
676 * launched power and span length Formula comes from
677 * OpenROADM_OSNR_Calculation_20220610 Tool The resulting contribution shall be
678 * calculated for each fiber span and summed up
679 * @param launchedPowerdB
680 * The power launched in the span (shall account for Optical Distribution
683 * Length of the span in km
685 * OpenROADM power and osnr contribution calculations are based on
686 * spacing between channels : the Media Channel (MC) width
688 * @return nonLinearOnsrContributionLin
689 * The inverse of the NL OSNR contribution converted from dB to linear value
691 public double calculateNLonsrContribution(double launchedPowerdB, double spanLength, double spacing) {
692 double constanteC0 = 0 ;
693 if (spacing > 162.5) {
694 constanteC0 = CatalogConstant.NLCONSTANTC0GT1625;
695 } else if (spacing > 112.5) {
696 constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1625;
697 } else if (spacing > 100.0) {
698 constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1125;
699 } else if (spacing > 87.5) {
700 constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1000;
702 constanteC0 = CatalogConstant.NLCONSTANTC0UPTO875;
704 double nonLinearOnsrContributionLinDb = launchedPowerdB * CatalogConstant.NLCONSTANTC1
705 + constanteC0 + CatalogConstant.NLCONSTANTCE * Math.exp(CatalogConstant.NLCONSTANTEX * spanLength);
706 LOG.info(" OSNR Non Linear contribution is {} dB", nonLinearOnsrContributionLinDb);
707 return Math.pow(10.0, -nonLinearOnsrContributionLinDb / 10);