3737a33867c0bf8359096772aa49f7a378cf8e17
[transportpce.git] / common / src / main / java / org / opendaylight / transportpce / common / catalog / CatalogUtils.java
1 /*
2  * Copyright © 2022 Orange, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.transportpce.common.catalog;
9
10 import java.util.HashMap;
11 import java.util.List;
12 import java.util.Map;
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;
44
45 /**
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.
51  *
52  */
53 public class CatalogUtils {
54
55     private static final Logger LOG = LoggerFactory.getLogger(CatalogUtils.class);
56
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);
72
73     private final PenaltiesComparator penaltiesComparator = new PenaltiesComparator();
74     private NetworkTransactionService networkTransactionService;
75
76     public CatalogUtils(NetworkTransactionService networkTransactionService) {
77         this.networkTransactionService = networkTransactionService;
78     }
79
80     /**
81      * Following method returns default OperationalModeId for devices that do not expose them.
82      *
83      * @param catalogNodeType
84      *            identifies type of nodes in the catalog
85      * @param serviceType
86      *            allows for Xponder selecting default mode according to the rate
87      *
88      * @return a default operational mode that corresponds to initial specifications
89      *
90      */
91     public String getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType catalogNodeType,
92             String serviceType) {
93         if (CATALOGNODETYPE_OPERATIONMODEID_MAP.containsKey(catalogNodeType)) {
94             return CATALOGNODETYPE_OPERATIONMODEID_MAP.get(catalogNodeType);
95         }
96         if (!catalogNodeType.equals(CatalogConstant.CatalogNodeType.TSP)) {
97             LOG.warn("Unsupported catalogNodeType {}", catalogNodeType);
98             return "";
99         }
100         if (!TSP_DEFAULT_OM_MAP.containsKey(serviceType)) {
101             LOG.warn("Unsupported serviceType {} for TSP catalogNodeType", serviceType);
102             return "";
103         }
104         return TSP_DEFAULT_OM_MAP.get(serviceType);
105     }
106
107     /**
108      * This method retrieves channel-spacing associated with a Xponder TX.
109      *
110      * @param operationalModeId
111      *            operational-mode-Id of the Xponder (OR or Specific)
112      *
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
117      */
118
119     public double getPceTxTspChannelSpacing(String operationalModeId) {
120         double baudRate;
121         double maxRollOff;
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))
129                 .build();
130             try {
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);
135                     return 0.0;
136                 }
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);
143             } finally {
144                 networkTransactionService.close();
145             }
146         } else {
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))
152                 .build();
153             try {
154                 var somOptional =
155                     networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
156                 if (somOptional.isEmpty()) {
157                     LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
158                     return 0.0;
159                 }
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);
166             } finally {
167                 networkTransactionService.close();
168             }
169         }
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);
174                 return 50.0;
175             }
176             LOG.warn("Missing rolloff factor (mandatory in Catalog) from Operational Mode {}: use default=0.2",
177                 operationalModeId);
178             maxRollOff = 0.2;
179         }
180         double spacing = 12.5 * Math.ceil(baudRate * (1 + maxRollOff) / 12.5);
181         LOG.info("Operational Mode {} associated channel spacing is {}", operationalModeId, spacing);
182         return spacing;
183     }
184
185     /**
186      * This method retrieves performance parameters associated with a Xponder TX.
187      *
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)
193      *
194      * @return the linear Optical Noise to signal Ratio
195      * @throws RuntimeException
196      *             if operationalModeId is not described in the catalog
197      */
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))
211                 .build();
212             try {
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);
217                     return 0.0;
218                 }
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);
224                 }
225                 if (orTspOM.nonnullTXOOBOsnr().get(key) == null) {
226                     return txOnsrLin;
227                 }
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);
235             } finally {
236                 networkTransactionService.close();
237             }
238         } else {
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))
244                 .build();
245             try {
246                 var somOptional =
247                     networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
248                 if (somOptional.isEmpty()) {
249                     LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
250                     return 0.0;
251                 }
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);
257                 }
258                 if (speTspOM.nonnullTXOOBOsnr().get(key) == null) {
259                     return txOnsrLin;
260                 }
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 + " :"
267                         + e);
268             } finally {
269                 networkTransactionService.close();
270             }
271         }
272         if (minOOBOsnrSingleChannelValue != null) {
273             txOnsrLin += 1.0 / Math.pow(10.0, minOOBOsnrSingleChannelValue.getValue().doubleValue() / 10.0);
274         }
275         if (minOOBOsnrMultiChannelValue != null) {
276             txOnsrLin += 1.0 / Math.pow(10.0, minOOBOsnrMultiChannelValue.getValue().doubleValue() / 10.0);
277         }
278         return txOnsrLin;
279     }
280
281     /**
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.
286      *
287      * @param operationalModeId
288      *            operational-mode-Id of the Xponder (OR or Specific)
289      * @param calcCd
290      *            accumulated chromatic dispersion across the line
291      * @param calcPmd
292      *            accumulated Polarization mode dispersion across the line
293      * @param calcPdl
294      *            accumulated Polarization Dependant Loss across the line
295      * @param calcOsnrdB
296      *            Optical Signal to Noise Ratio (dB)resulting from the transmission
297      *            on the line, that shall include the Non Linear contribution
298      *
299      * @return the margin on the service path
300      * @throws RuntimeException
301      *             if operationalModeId is not described in the catalog
302      */
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))
316                 .build();
317             try {
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();
325                     }
326                     penaltiesMap = orTspOM.getPenalties();
327                 }
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 + " :"
332                         + e);
333             } finally {
334                 networkTransactionService.close();
335             }
336         } else {
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))
343                 .build();
344             try {
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();
352                     }
353                     penaltiesMap = speTspOM.getPenalties();
354                 }
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 + " :"
359                         + e);
360             } finally {
361                 networkTransactionService.close();
362             }
363         }
364         if (penaltiesMap == null) {
365             LOG.error("Unable to calculate margin as penaltyMap can not be retrieved : Operational mode not populated");
366             return -9999.9;
367         }
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
380         //
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);
391         if (margin < 0) {
392             LOG.warn("Negative margin shall result in PCE rejecting the analyzed path");
393         }
394         return margin;
395     }
396
397     /**
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.
402      *
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
410      *
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
414      */
415
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!
430                 ? 9999.9
431             // In spec, return penalty associated with calculatedParameter
432                 : penalty.getPenaltyValue().getValue().doubleValue();
433     }
434
435     /**
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.
441      *
442      * @param catalogNodeType
443      *            crossed node path type (ADD/DROP/EXPRESS/AMP)
444      * @param operationalModeId
445      *            operational-mode-Id of the Node (OpenROADM only)
446      * @param cd
447      *            accumulated chromatic dispersion across the line
448      * @param dgd2
449      *            Square of accumulated Group velocity dispersion across the line
450      * @param pdl2
451      *            Square of the accumulated Polarization Dependant Loss across the
452      *            line
453      * @param pwrIn
454      *            Input power required to calculate OSNR contribution of the node =
455      *            f(pwrIn)
456      * @param onsrLin
457      *            Linear Optical Noise to Signal Ratio resulting from the
458      *            transmission on the line, that shall include the Non Linear
459      *            contribution
460      * @param spacing
461      *            Interchannel spacing used for correction to calculate OSNR
462      *            contribution of the node
463      *
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
469      */
470
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) {
479             case ADD:
480                 var omCatalogIid = InstanceIdentifier
481                     .builder(OperationalModeCatalog.class)
482                     .child(OpenroadmOperationalModes.class)
483                     .child(Roadms.class)
484                     .child(Add.class)
485                     .child(AddOpenroadmOperationalMode.class, new AddOpenroadmOperationalModeKey(operationalModeId))
486                     .build();
487                 try {
488                     var omOptional =
489                         networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
490                     if (omOptional.isEmpty()) {
491                         LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
492                         return new HashMap<>();
493                     }
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) {
502                     onsrLin = 1;
503                     LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
504                         omCatalogIid);
505                     throw new RuntimeException(
506                         "readMdSal: Error reading from operational store, Operational Mode Catalog : "
507                             + omCatalogIid + " :" + e);
508                 } finally {
509                     networkTransactionService.close();
510                 }
511                 break;
512
513             case DROP:
514                 var omCatalogIid1 = InstanceIdentifier
515                     .builder(OperationalModeCatalog.class)
516                     .child(OpenroadmOperationalModes.class)
517                     .child(Roadms.class)
518                     .child(Drop.class)
519                     .child(OpenroadmOperationalMode.class, new OpenroadmOperationalModeKey(operationalModeId))
520                     .build();
521                 try {
522                     var omOptional =
523                         networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid1).get();
524                     if (omOptional.isEmpty()) {
525                         LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
526                         return new HashMap<>();
527                     }
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",
541                         omCatalogIid1);
542                     throw new RuntimeException(
543                         "readMdSal: Error reading from operational store, Operational Mode Catalog : "
544                             + omCatalogIid1 + " :" + e);
545                 } finally {
546                     networkTransactionService.close();
547                 }
548                 break;
549
550             case EXPRESS:
551                 var omCatalogIid2 = InstanceIdentifier
552                     .builder(OperationalModeCatalog.class)
553                     .child(OpenroadmOperationalModes.class)
554                     .child(Roadms.class)
555                     .child(Express.class)
556                     .child(
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(
563                                 operationalModeId))
564                     .build();
565                 try {
566                     var omOptional = networkTransactionService
567                         .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid2)
568                         .get();
569                     if (omOptional.isEmpty()) {
570                         LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
571                         return new HashMap<>();
572                     }
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",
585                         omCatalogIid2);
586                     throw new RuntimeException(
587                         "readMdSal: Error reading from operational store, Operational Mode Catalog : "
588                             + omCatalogIid2 + " :" + e);
589                 } finally {
590                     networkTransactionService.close();
591                 }
592                 break;
593
594             case AMP:
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))
601                     .build();
602                 try {
603                     var omOptional = networkTransactionService
604                         .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid3)
605                         .get();
606                     if (omOptional.isEmpty()) {
607                         LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
608                         return new HashMap<>();
609                     }
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",
623                         omCatalogIid3);
624                     throw new RuntimeException(
625                         "readMdSal: Error reading from operational store, Operational Mode Catalog : "
626                             + omCatalogIid3 + " :" + e);
627                 } finally {
628                     networkTransactionService.close();
629                 }
630                 break;
631             default:
632                 LOG.error("Unsupported catalogNodeType {}", catalogNodeType);
633                 return new HashMap<>();
634         }
635         cd += maxIntroducedCd;
636         pdl2 += Math.pow(maxIntroducedPdl, 2.0);
637         dgd2 += Math.pow(maxIntroducedDgd, 2.0);
638         double pwrFact = 1;
639         double contrib = 10 * Math.log10(spacing / 50.0);
640         for (double fit : osnrPolynomialFits) {
641             contrib += pwrFact * fit;
642             pwrFact *= pwrIn;
643             // Using a for loop with multiplication instead of Math.pow optimizes the computation.
644         }
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));
670         return impairments;
671     }
672
673     /**
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
681      *            Frame loss)
682      * @param spanLength
683      *            Length of the span in km
684      * @param spacing
685      *            OpenROADM power and osnr contribution calculations are based on
686      *            spacing between channels : the Media Channel (MC) width
687      *
688      * @return nonLinearOnsrContributionLin
689      *         The inverse of the NL OSNR contribution converted from dB to linear value
690      */
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;
701         } else {
702             constanteC0 = CatalogConstant.NLCONSTANTC0UPTO875;
703         }
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);
708     }
709
710 }