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);
149 // In other cases, means the mode is a non OpenROADM specific Operational Mode
150 InstanceIdentifier<SpecificOperationalMode> omCatalogIid = InstanceIdentifier
151 .builder(OperationalModeCatalog.class)
152 .child(SpecificOperationalModes.class)
153 .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId))
157 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
158 if (somOptional.isEmpty()) {
159 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
162 SpecificOperationalMode speTspOM = somOptional.get();
163 maxRollOff = speTspOM.getMaxRollOff() == null ? 0 : speTspOM.getMaxRollOff().doubleValue();
164 baudRate = speTspOM.getBaudRate().doubleValue();
165 } catch (InterruptedException | ExecutionException e) {
166 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
167 throw new RuntimeException("Operational mode not populated in Catalog : " + omCatalogIid + " :" + e);
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);
237 // In other cases, means the mode is a non OpenROADM specific Operational Mode
238 InstanceIdentifier<SpecificOperationalMode> omCatalogIid = InstanceIdentifier
239 .builder(OperationalModeCatalog.class)
240 .child(SpecificOperationalModes.class)
241 .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId))
245 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
246 if (somOptional.isEmpty()) {
247 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
250 speTspOM = somOptional.get();
251 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", speTspOM);
252 TXOOBOsnrKey key = new TXOOBOsnrKey(addDropMuxOperationalModeId);
253 if (speTspOM.getMinTXOsnr() != null) {
254 txOnsrLin = 1.0 / Math.pow(10.0, speTspOM.getMinTXOsnr().getValue().doubleValue() / 10.0);
256 if (speTspOM.nonnullTXOOBOsnr().get(key) == null) {
259 minOOBOsnrSingleChannelValue = speTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrSingleChannelValue();
260 minOOBOsnrMultiChannelValue = speTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrMultiChannelValue();
261 } catch (InterruptedException | ExecutionException e) {
262 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
263 throw new RuntimeException(
264 "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :"
268 if (minOOBOsnrSingleChannelValue != null) {
269 txOnsrLin += 1.0 / Math.pow(10.0, minOOBOsnrSingleChannelValue.getValue().doubleValue() / 10.0);
271 if (minOOBOsnrMultiChannelValue != null) {
272 txOnsrLin += 1.0 / Math.pow(10.0, minOOBOsnrMultiChannelValue.getValue().doubleValue() / 10.0);
278 * This method retrieves performance parameters associated with a Xponder RX.
279 * It calls getRxTspPenalty to evaluate the penalty associated with CD/PMD/PDL
280 * It compares expected OSNR with the OSNR resulting from the line degradation,
281 * and finally calculates and return the resulting margin.
283 * @param operationalModeId
284 * operational-mode-Id of the Xponder (OR or Specific)
286 * accumulated chromatic dispersion across the line
288 * accumulated Polarization mode dispersion across the line
290 * accumulated Polarization Dependant Loss across the line
292 * Optical Signal to Noise Ratio (dB)resulting from the transmission
293 * on the line, that shall include the Non Linear contribution
295 * @return the margin on the service path
296 * @throws RuntimeException
297 * if operationalModeId is not described in the catalog
299 public double getPceRxTspParameters(String operationalModeId, double calcCd, double calcPmd,
300 double calcPdl, double calcOsnrdB) {
301 double rxOsnrdB = 0.0;
302 XponderPluggableOpenroadmOperationalMode orTspOM = null;
303 SpecificOperationalMode speTspOM = null;
304 Map<PenaltiesKey, Penalties> penaltiesMap = null;
305 if (operationalModeId.startsWith("OR")) {
306 var omCatalogIid = InstanceIdentifier
307 .builder(OperationalModeCatalog.class)
308 .child(OpenroadmOperationalModes.class)
309 .child(XpondersPluggables.class)
310 .child(XponderPluggableOpenroadmOperationalMode.class,
311 new XponderPluggableOpenroadmOperationalModeKey(operationalModeId))
314 Optional<XponderPluggableOpenroadmOperationalMode> omOptional = networkTransactionService
315 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
316 if (omOptional.isPresent()) {
317 orTspOM = omOptional.get();
318 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orTspOM);
319 if (orTspOM.getMinRXOsnrTolerance() != null) {
320 rxOsnrdB = orTspOM.getMinRXOsnrTolerance().getValue().doubleValue();
322 penaltiesMap = orTspOM.getPenalties();
324 } catch (InterruptedException | ExecutionException e) {
325 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
326 throw new RuntimeException(
327 "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :"
331 // In other cases, means the mode is a non OpenROADM specific Operational Mode
332 // InstanceIdentifier<SpecificOperationalMode> omCatalogIid = InstanceIdentifier
333 var omCatalogIid = InstanceIdentifier
334 .builder(OperationalModeCatalog.class)
335 .child(SpecificOperationalModes.class)
336 .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId))
339 Optional<SpecificOperationalMode> somOptional = networkTransactionService
340 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
341 if (somOptional.isPresent()) {
342 speTspOM = somOptional.get();
343 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", speTspOM);
344 if (speTspOM.getMinRXOsnrTolerance() != null) {
345 rxOsnrdB = speTspOM.getMinRXOsnrTolerance().getValue().doubleValue();
347 penaltiesMap = speTspOM.getPenalties();
349 } catch (InterruptedException | ExecutionException e) {
350 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
351 throw new RuntimeException(
352 "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :"
356 if (penaltiesMap == null) {
357 LOG.error("Unable to calculate margin as penaltyMap can not be retrieved : Operational mode not populated");
360 HashMap<String, Double> impairments = new HashMap<>();
361 double penalty = getRxTspPenalty(calcCd, ImpairmentType.CDPsNm, penaltiesMap);
362 impairments.put("CDpenalty", penalty);
363 double totalPenalty = penalty;
364 penalty = getRxTspPenalty(calcPmd, ImpairmentType.PMDPs, penaltiesMap);
365 impairments.put("PMD Penalty", penalty);
366 totalPenalty += penalty;
367 // Calculation according to OpenROADM specification
368 // penalty = getRxTspPenalty(calcPdl, ImpairmentType.PDLDB, penaltiesMap);
369 // Calculation modified according to Julia's Tool
370 penalty = calcPdl / 2;
371 impairments.put("PDL penalty", penalty);
372 totalPenalty += penalty;
373 // TODO for Future work since at that time we have no way to calculate the following
374 // parameters,even if penalties are defined in the OpenROADM specifications
376 // impairments.put("Colorless Drop Adjacent Xtalk Penalty", getRxTspPenalty(TBD,
377 // ImpairmentType.ColorlessDropAdjacentChannelCrosstalkGHz, penalitiesMap));
378 // impairments.put("XTalk total Power Penalty", getRxTspPenalty(TBD,
379 // ImpairmentType.CrossTalkTotalPowerDB, penalitiesMap));
380 // impairments.put("Power penalty", getRxTspPenalty(TBD,
381 // ImpairmentType.PowerDBm, penalitiesMap));
382 LOG.info("Penalty resulting from CD, PMD and PDL is {} dB with following contributions {}",
383 totalPenalty, impairments);
384 double margin = calcOsnrdB - totalPenalty - rxOsnrdB;
385 LOG.info("According to RX TSP Specification and calculated impairments Margin is {} dB ", margin);
387 LOG.warn("Negative margin shall result in PCE rejecting the analyzed path");
393 * This generic method is called from getPceRxTspParameters to provide the
394 * Penalties associated with CD, PMD and DGD for Xponder. It scans a penalty
395 * list that includes penalty values corresponding to an interval between an
396 * upper and a lower boundary for each of the above parameters.
398 * @param impairmentType
399 * : the type of impairment (CD/PMD/DGD)
400 * @param calculatedParameter
401 * calculated accumulated value on the line for the impairment
402 * @param penaltiesMap
403 * the global map of penalties retrieved by getPceRxTspParameters
404 * from the Xponder operational mode
406 * @return the penalty associated with accumulated impairment if it is in the
407 * range specified in the table, a value that will lead to reject the
408 * path if this is not the case
411 private double getRxTspPenalty(double calculatedParameter, ImpairmentType impairmentType,
412 Map<PenaltiesKey, Penalties> penalitiesMap) {
413 Penalties penalty = penalitiesMap.values().stream()
414 // We only keep penalties corresponding to the calculated Parameter
415 .filter(val -> val.getParameterAndUnit().getName().equals(impairmentType.getName()))
416 // we sort it according to the comparator (based on up-to-boundary)
417 .sorted(penaltiesComparator)
418 // keep only items for which up to boundary is greater than calculatedParameter
419 .filter(val -> val.getUpToBoundary().doubleValue() >= calculatedParameter)
420 // takes the immediate greater or equal value
421 .findFirst().orElse(null);
422 if (penalty == null) {
423 //means a boundary that is greater than calculatedParameter couldn't be found
424 // Out of specification!
427 // In spec, return penalty associated with calculatedParameter
428 LOG.info("Penalty for {} is {} dB", impairmentType, penalty.getPenaltyValue().getValue().doubleValue());
429 return penalty.getPenaltyValue().getValue().doubleValue();
433 * This method retrieves performance parameters associated with ROADMs and
434 * Amplifiers. It calculates the contribution of the node to the degradation of
435 * the signal for CD, DGD, PDL, and OSNR which is calculated through a
436 * polynomial fit described in the catalog. It finally corrects the accumulated
437 * values for these parameters and return them.
439 * @param catalogNodeType
440 * crossed node path type (ADD/DROP/EXPRESS/AMP)
441 * @param operationalModeId
442 * operational-mode-Id of the Node (OpenROADM only)
444 * accumulated chromatic dispersion across the line
446 * Square of accumulated Group velocity dispersion across the line
448 * Square of the accumulated Polarization Dependant Loss across the
451 * Input power required to calculate OSNR contribution of the node =
454 * Linear Optical Noise to Signal Ratio resulting from the
455 * transmission on the line, that shall include the Non Linear
458 * Interchannel spacing used for correction to calculate OSNR
459 * contribution of the node
461 * @return Impairment, a map that provides corrected values for all calculated
462 * parameters which includes the contribution of the node
463 * (CD/DGD2/PDL2/ONSRLin)
464 * @throws RuntimeException
465 * if operationalModeId is not described in the catalog
468 public Map<String, Double> getPceRoadmAmpParameters(CatalogConstant.CatalogNodeType catalogNodeType,
469 String operationalModeId, double pwrIn, double cd, double dgd2, double pdl2,
470 double onsrLin, double spacing) {
471 double maxIntroducedCd;
472 double maxIntroducedPdl;
473 double maxIntroducedDgd;
474 List<Double> osnrPolynomialFits;
475 switch (catalogNodeType) {
477 var omCatalogIid = InstanceIdentifier
478 .builder(OperationalModeCatalog.class)
479 .child(OpenroadmOperationalModes.class)
482 .child(AddOpenroadmOperationalMode.class, new AddOpenroadmOperationalModeKey(operationalModeId))
486 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
487 if (omOptional.isEmpty()) {
488 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
489 return new HashMap<>();
491 var orAddOM = omOptional.get();
492 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orAddOM);
493 maxIntroducedCd = orAddOM.getMaxIntroducedCd().doubleValue();
494 // As per current OpenROADM Spec
495 //maxIntroducedPdl = orAddOM.getMaxIntroducedPdl().getValue().doubleValue();
496 // Applying calculation as provided in Julia's tool
497 maxIntroducedPdl = Math.sqrt(0.2 * 0.2 + 0.4 * 0.4);
498 maxIntroducedDgd = orAddOM.getMaxIntroducedDgd().doubleValue();
499 osnrPolynomialFits = List.of(orAddOM.getIncrementalOsnr().getValue().doubleValue());
500 } catch (InterruptedException | ExecutionException e) {
502 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
504 throw new RuntimeException(
505 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
506 + omCatalogIid + " :" + e);
511 var omCatalogIid1 = InstanceIdentifier
512 .builder(OperationalModeCatalog.class)
513 .child(OpenroadmOperationalModes.class)
516 .child(OpenroadmOperationalMode.class, new OpenroadmOperationalModeKey(operationalModeId))
520 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid1).get();
521 if (omOptional.isEmpty()) {
522 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
523 return new HashMap<>();
525 var orDropOM = omOptional.get();
526 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orDropOM);
527 maxIntroducedCd = orDropOM.getMaxIntroducedCd().doubleValue();
528 // As per current OpenROADM Spec
529 // maxIntroducedPdl = orDropOM.getMaxIntroducedPdl().getValue().doubleValue();
530 // Applying calculation as provided in Julia's tool
531 maxIntroducedPdl = Math.sqrt(0.2 * 0.2 + 0.4 * 0.4);
532 maxIntroducedDgd = orDropOM.getMaxIntroducedDgd().doubleValue();
533 osnrPolynomialFits = List.of(
534 orDropOM.getOsnrPolynomialFit().getD().doubleValue(),
535 orDropOM.getOsnrPolynomialFit().getC().doubleValue(),
536 orDropOM.getOsnrPolynomialFit().getB().doubleValue(),
537 orDropOM.getOsnrPolynomialFit().getA().doubleValue());
538 } catch (InterruptedException | ExecutionException e) {
539 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
541 throw new RuntimeException(
542 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
543 + omCatalogIid1 + " :" + e);
548 var omCatalogIid2 = InstanceIdentifier
549 .builder(OperationalModeCatalog.class)
550 .child(OpenroadmOperationalModes.class)
552 .child(Express.class)
554 org.opendaylight.yang.gen.v1.http
555 .org.openroadm.operational.mode.catalog.rev211210
556 .operational.mode.roadm.express.parameters.express.OpenroadmOperationalMode.class,
557 new org.opendaylight.yang.gen.v1.http
558 .org.openroadm.operational.mode.catalog.rev211210
559 .operational.mode.roadm.express.parameters.express.OpenroadmOperationalModeKey(
563 var omOptional = networkTransactionService
564 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid2)
566 if (omOptional.isEmpty()) {
567 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
568 return new HashMap<>();
570 var orExpressOM = omOptional.get();
571 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orExpressOM);
572 maxIntroducedCd = orExpressOM.getMaxIntroducedCd().doubleValue();
573 // As per current OpenROADM Spec
574 // maxIntroducedPdl = orExpressOM.getMaxIntroducedPdl().getValue().doubleValue();
575 // Applying calculation as provided in Julia's tool
576 maxIntroducedPdl = Math.sqrt(2 * 0.2 * 0.2 + 2 * 0.4 * 0.4);
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);
593 var omCatalogIid3 = InstanceIdentifier
594 .builder(OperationalModeCatalog.class)
595 .child(OpenroadmOperationalModes.class)
596 .child(Amplifiers.class)
597 .child(Amplifier.class)
598 .child(OpenroadmOperationalMode.class, new OpenroadmOperationalModeKey(operationalModeId))
601 var omOptional = networkTransactionService
602 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid3)
604 if (omOptional.isEmpty()) {
605 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
606 return new HashMap<>();
608 var orAmpOM = omOptional.get();
609 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orAmpOM);
610 maxIntroducedCd = orAmpOM.getMaxIntroducedCd().doubleValue();
611 // As per current OpenROADM Spec
612 // maxIntroducedPdl = orAmpOM.getMaxIntroducedPdl().getValue().doubleValue();
613 // Applying calculation as provided in Julia's tool
614 maxIntroducedPdl = 0.2;
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);
630 LOG.error("Unsupported catalogNodeType {}", catalogNodeType);
631 return new HashMap<>();
633 cd += maxIntroducedCd;
634 pdl2 += Math.pow(maxIntroducedPdl, 2.0);
635 dgd2 += Math.pow(maxIntroducedDgd, 2.0);
638 // We correct PwrIn to the value corresponding to a 50 GHz Bandwidth, because OpenROADM spec (polynomial fit)
639 // is based on power in 50GHz Bandwidth
640 pwrIn -= 10 * Math.log10(spacing / 50.0);
641 if (catalogNodeType != CatalogNodeType.ADD) {
642 // For add, incremental OSNR is defined for Noiseless input, BW Correction (contrib) does not apply
643 contrib = 10 * Math.log10(spacing / 50.0);
645 for (double fit : osnrPolynomialFits) {
646 contrib += pwrFact * fit;
648 // Using a for loop with multiplication instead of Math.pow optimizes the computation.
650 // Double is not strictly spoken a Mathematics commutative group because
651 // computers design limits their bits representation size.
652 // As a result, the order of arithmetic operation matters.
653 // In a sum, smallest numbers should be introduced first for a maximum of
654 // precision. In other words, the sum
655 // 10 * Math.log10(spacing / 50.0)
656 // + osnrPolynomialFits.get(0)
657 // + osnrPolynomialFits.get(1) * pwrIn
658 // + osnrPolynomialFits.get(2) * Math.pow(pwrIn, 2)
659 // + osnrPolynomialFits.get(3) * Math.pow(pwrIn, 3)
660 // is not equal to its reverse form
661 // osnrPolynomialFits.get(3) * Math.pow(pwrIn, 3)
662 // + osnrPolynomialFits.get(2) * Math.pow(pwrIn, 2)
663 // + osnrPolynomialFits.get(1) * pwrIn
664 // + osnrPolynomialFits.get(0)
665 // + 10 * Math.log10(spacing / 50.0)
666 // and the more precise first form should be preferred here.
667 onsrLin += Math.pow(10, -contrib / 10);
668 Map<String, Double> impairments = new HashMap<>();
669 impairments.put("CD", cd);
670 impairments.put("DGD2", dgd2);
671 impairments.put("PDL2", pdl2);
672 impairments.put("ONSRLIN", onsrLin);
673 LOG.info("Accumulated CD is {} ps, DGD is {} ps and PDL is {} dB", cd, Math.sqrt(dgd2), Math.sqrt(pdl2));
674 LOG.info("Resulting OSNR is {} dB", 10 * Math.log10(1 / onsrLin));
679 * This method calculates power that shall be applied at the output of ROADMs and
680 * Amplifiers. It retrieves the mask-power-vs-Pin and calculates target output
681 * power from the span loss
683 * @param catalogNodeType
684 * crossed node path type (ADD/EXPRESS/AMP)
685 * @param operationalModeId
686 * operational-mode-Id of the Node (OpenROADM only)
688 * spanLoss at the output of the ROADM
689 * @param powerCorrection
690 * correction to be applied to the calculated power according to fiber type
692 * Interchannel spacing used for correction to calculate output power
693 * @return outputPower
694 * Corrected output power calculated according to channel spacing
695 * @throws RuntimeException
696 * if operationalModeId is not described in the catalog
698 public double getPceRoadmAmpOutputPower(CatalogConstant.CatalogNodeType catalogNodeType,
699 String operationalModeId, double spanLoss, double spacing, double powerCorrection) {
700 double pout = 99999.0;
701 switch (catalogNodeType) {
703 var omCatalogIid = InstanceIdentifier
704 .builder(OperationalModeCatalog.class)
705 .child(OpenroadmOperationalModes.class)
708 .child(AddOpenroadmOperationalMode.class, new AddOpenroadmOperationalModeKey(operationalModeId))
712 networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
713 if (omOptional.isEmpty()) {
714 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
717 var orAddOM = omOptional.get();
718 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orAddOM);
719 var mask = orAddOM.getMaskPowerVsPin();
720 for (Map.Entry<MaskPowerVsPinKey, MaskPowerVsPin> pw : mask.entrySet()) {
721 if (spanLoss >= pw.getKey().getLowerBoundary().doubleValue()
722 && spanLoss <= pw.getKey().getUpperBoundary().doubleValue()) {
723 pout = pw.getValue().getC().doubleValue() * spanLoss + pw.getValue().getD().doubleValue()
724 + powerCorrection + 10 * Math.log10(spacing / 50.0);
725 LOG.info("Calculated target Output power is {} dB in {} Bandwidth", pout, spacing);
729 LOG.info("Did not succeed in calculating target Output power, SpanLoss {} dB is out of range",
731 } catch (InterruptedException | ExecutionException e) {
732 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
734 throw new RuntimeException(
735 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
736 + omCatalogIid + " :" + e);
741 var omCatalogIid2 = InstanceIdentifier
742 .builder(OperationalModeCatalog.class)
743 .child(OpenroadmOperationalModes.class)
745 .child(Express.class)
747 org.opendaylight.yang.gen.v1.http
748 .org.openroadm.operational.mode.catalog.rev211210
749 .operational.mode.roadm.express.parameters.express.OpenroadmOperationalMode.class,
750 new org.opendaylight.yang.gen.v1.http
751 .org.openroadm.operational.mode.catalog.rev211210
752 .operational.mode.roadm.express.parameters.express.OpenroadmOperationalModeKey(
756 var omOptional = networkTransactionService
757 .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid2)
759 if (omOptional.isEmpty()) {
760 LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
763 var orExpressOM = omOptional.get();
764 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orExpressOM);
765 var mask = orExpressOM.getMaskPowerVsPin();
766 for (Map.Entry<MaskPowerVsPinKey, MaskPowerVsPin> pw : mask.entrySet()) {
767 if (spanLoss >= pw.getKey().getLowerBoundary().doubleValue()
768 && spanLoss <= pw.getKey().getUpperBoundary().doubleValue()) {
769 pout = pw.getValue().getC().doubleValue() * spanLoss + pw.getValue().getD().doubleValue()
770 + powerCorrection + 10 * Math.log10(spacing / 50.0);
771 LOG.info("Calculated target Output power is {} dB in {} Bandwidth", pout, spacing);
775 LOG.info("Did not succeed in calculating target Output power, SpanLoss {} dB is out of range",
777 } catch (InterruptedException | ExecutionException e) {
778 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
780 throw new RuntimeException(
781 "readMdSal: Error reading from operational store, Operational Mode Catalog : "
782 + omCatalogIid2 + " :" + e);
787 LOG.error("Unsupported catalogNodeType {}", catalogNodeType);
793 * Non linear contribution computation.
794 * Public method calculating non linear contribution among the path from
795 * launched power and span length Formula comes from
796 * OpenROADM_OSNR_Calculation_20220610 Tool The resulting contribution shall be
797 * calculated for each fiber span and summed up
798 * @param launchedPowerdB
799 * The power launched in the span (shall account for Optical Distribution
802 * Length of the span in km
804 * OpenROADM power and osnr contribution calculations are based on
805 * spacing between channels : the Media Channel (MC) width
807 * @return nonLinearOnsrContributionLin
808 * The inverse of the NL OSNR contribution converted from dB to linear value
810 public double calculateNLonsrContribution(double launchedPowerdB, double spanLength, double spacing) {
811 double constanteC0 = 0 ;
812 if (spacing > 162.5) {
813 constanteC0 = CatalogConstant.NLCONSTANTC0GT1625;
814 } else if (spacing > 112.5) {
815 constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1625;
816 } else if (spacing > 100.0) {
817 constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1125;
818 } else if (spacing > 87.5) {
819 constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1000;
821 constanteC0 = CatalogConstant.NLCONSTANTC0UPTO875;
823 double nonLinearOnsrContributionLinDb = launchedPowerdB * CatalogConstant.NLCONSTANTC1
824 + constanteC0 + CatalogConstant.NLCONSTANTCE * Math.exp(CatalogConstant.NLCONSTANTEX * spanLength);
825 LOG.info(" OSNR Non Linear contribution is {} dB", nonLinearOnsrContributionLinDb);
826 return Math.pow(10.0, -nonLinearOnsrContributionLinDb / 10);
829 public boolean isCatalogFilled() {
830 var omCatalogIid = InstanceIdentifier
831 .builder(OperationalModeCatalog.class)
832 .child(OpenroadmOperationalModes.class)
835 .child(AddOpenroadmOperationalMode.class, new AddOpenroadmOperationalModeKey(CatalogConstant.MWWRCORE))
838 if (networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get().isEmpty()) {
839 LOG.error("Operational Mode catalog is not filled");
843 } catch (InterruptedException | ExecutionException e) {
844 LOG.error("readMdSal: Error reading Operational Mode Catalog, catalog not filled");
845 throw new RuntimeException(
846 "readMdSal: Error reading from operational store, Operational Mode Catalog not filled" + e);