/* * Copyright © 2022 Orange, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.transportpce.common.catalog; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.ExecutionException; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.transportpce.common.StringConstants; import org.opendaylight.transportpce.common.network.NetworkTransactionService; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.ImpairmentType; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.amplifier.parameters.Amplifier; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.OpenroadmOperationalModes; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.SpecificOperationalModes; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.Amplifiers; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.Roadms; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.XpondersPluggables; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.xponders.pluggables.XponderPluggableOpenroadmOperationalMode; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.xponders.pluggables.XponderPluggableOpenroadmOperationalModeKey; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.specific.operational.modes.SpecificOperationalMode; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.specific.operational.modes.SpecificOperationalModeKey; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.common.amplifier.drop.parameters.OpenroadmOperationalMode; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.common.amplifier.drop.parameters.OpenroadmOperationalModeKey; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.add.parameters.Add; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.add.parameters.add.AddOpenroadmOperationalMode; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.add.parameters.add.AddOpenroadmOperationalModeKey; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.drop.parameters.Drop; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.express.parameters.Express; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.Penalties; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.PenaltiesKey; import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.TXOOBOsnrKey; import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.OperationalModeCatalog; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility class for Catalog. Following methods are used to retrieve parameters * from the specification catalog. They point to either openROADM or specific * operational modes. They provide to the PCE, the OLM and the Renderer, the * required parameters to calculate impairments and set output power levels * according to the specifications. * */ public class CatalogUtils { private static final Logger LOG = LoggerFactory.getLogger(CatalogUtils.class); private final PenaltiesComparator penaltiesComparator = new PenaltiesComparator(); private NetworkTransactionService networkTransactionService; public CatalogUtils(NetworkTransactionService networkTransactionService) { this.networkTransactionService = networkTransactionService; } /** * Following method returns default OperationalModeId for devices that do not expose them. * * @param catalogNodeType * identifies type of nodes in the catalog * @param serviceType * allows for Xponder selecting default mode according to the rate * * @return a default operational mode that corresponds to initial specifications * */ public String getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType catalogNodeType, String serviceType) { String operationalModeId = ""; switch (catalogNodeType) { case ADD: case DROP: operationalModeId = CatalogConstant.MWWRCORE; break; case EXPRESS: operationalModeId = CatalogConstant.MWMWCORE; break; case AMP: operationalModeId = CatalogConstant.MWISTANDARD; break; case TSP: if (StringConstants.SERVICE_TYPE_100GE_T.contentEquals(serviceType) || StringConstants.SERVICE_TYPE_OTU4.contentEquals(serviceType)) { operationalModeId = CatalogConstant.ORW100GSC; } if (StringConstants.SERVICE_TYPE_OTUC2.contentEquals(serviceType)) { operationalModeId = CatalogConstant.ORW200GOFEC316GBD; } if (StringConstants.SERVICE_TYPE_OTUC3.contentEquals(serviceType)) { operationalModeId = CatalogConstant.ORW300GOFEC631GBD; } if ((StringConstants.SERVICE_TYPE_OTUC4.contentEquals(serviceType)) || (StringConstants.SERVICE_TYPE_400GE.contentEquals(serviceType))) { operationalModeId = CatalogConstant.ORW400GOFEC631GBD; } break; default: LOG.warn("Unsupported catalogNodeType {}", catalogNodeType); break; } return operationalModeId; } /** * This method retrieves channel-spacing associated with a Xponder TX. * * @param operationalModeId * operational-mode-Id of the Xponder (OR or Specific) * * @return the channel spacing used to correct OSNR contribution values from * ROADMs and amplifiers * @throws RuntimeException * if operationalModeId is not described in the catalog */ public double getPceTxTspChannelSpacing(String operationalModeId) { double spacing = 0.0; double rolloff = 0.2; XponderPluggableOpenroadmOperationalMode orTspOM = null; SpecificOperationalMode speTspOM = null; if (operationalModeId.startsWith("OR")) { InstanceIdentifier omCatalogIid = InstanceIdentifier .builder(OperationalModeCatalog.class) .child(OpenroadmOperationalModes.class) .child(XpondersPluggables.class) .child(XponderPluggableOpenroadmOperationalMode.class, new XponderPluggableOpenroadmOperationalModeKey(operationalModeId)) .build(); try { Optional omOptional = networkTransactionService .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get(); if (omOptional.isPresent()) { orTspOM = omOptional.get(); if ((orTspOM.getMaxRollOff() == null) || (orTspOM.getMaxRollOff().doubleValue() == 0)) { if (CatalogConstant.ORW100GSC.contentEquals(operationalModeId)) { spacing = 50.0; LOG.info("Operational Mode {} associated channel spacing is {}", operationalModeId, spacing); } else { spacing = (Math.ceil( orTspOM.getBaudRate().doubleValue() * (1 + 0.2) / 12.5)) * 12.5; LOG.warn("Did not succeed in retrieving rolloff factor from Operational Mode {}," + " used default value of 0.2 --> Please check operational mode catalog," + " Rolloff factor is a mandatory parameter", operationalModeId); } } else { spacing = (Math.ceil( orTspOM.getBaudRate().doubleValue() * (1 + orTspOM.getMaxRollOff().doubleValue()) / 12.5)) * 12.5; } LOG.info("Operational Mode {} associated channel spacing is {}", operationalModeId, spacing); } } catch (InterruptedException | ExecutionException e) { LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid); throw new RuntimeException( "Operational mode not populated in Catalog : " + omCatalogIid + " :" + e); } finally { networkTransactionService.close(); } } else { // In other cases, means the mode is a non OpenROADM specific Operational Mode InstanceIdentifier omCatalogIid = InstanceIdentifier .builder(OperationalModeCatalog.class) .child(SpecificOperationalModes.class) .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId)) .build(); try { var somOptional = networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid) .get(); if (somOptional.isPresent()) { speTspOM = somOptional.get(); if ((speTspOM.getMaxRollOff() == null) || (speTspOM.getMaxRollOff().doubleValue() == 0)) { spacing = (Math.ceil(speTspOM.getBaudRate().doubleValue() * (1 + 0.2) / 12.5)) * 12.5; LOG.warn("Did not succeed in retrieving rolloff factor from Operational Mode {}," + " used default value of 0.2 --> Please check operational mode catalog," + " Rolloff factor is a mandatory parameter", operationalModeId); } else { spacing = (Math.ceil( speTspOM.getBaudRate().doubleValue() * (1 + speTspOM.getMaxRollOff().doubleValue()) / 12.5)) * 12.5; } LOG.info("Operational Mode {} associated channel spacing is {}", operationalModeId, spacing); } } catch (InterruptedException | ExecutionException e) { LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid); throw new RuntimeException( "Operational mode not populated in Catalog : " + omCatalogIid + " :" + e); } finally { networkTransactionService.close(); } } return spacing; } /** * This method retrieves performance parameters associated with a Xponder TX. * * @param operationalModeId * operational-mode-Id of the Xponder (OR or Specific) * @param addDropMuxOperationalModeId * operational-mode-Id of the Add-Drop bloc the XponderTX is * associated to (conditions TX-OOB OSNR value) * * @return the linear Optical Noise to signal Ratio * @throws RuntimeException * if operationalModeId is not described in the catalog */ public double getPceTxTspParameters(String operationalModeId, String addDropMuxOperationalModeId) { double txOnsrLin = 0.0; XponderPluggableOpenroadmOperationalMode orTspOM = null; SpecificOperationalMode speTspOM = null; if (operationalModeId.startsWith("OR")) { InstanceIdentifier omCatalogIid = InstanceIdentifier .builder(OperationalModeCatalog.class) .child(OpenroadmOperationalModes.class) .child(XpondersPluggables.class) .child(XponderPluggableOpenroadmOperationalMode.class, new XponderPluggableOpenroadmOperationalModeKey(operationalModeId)) .build(); try { Optional omOptional = networkTransactionService .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get(); if (omOptional.isPresent()) { orTspOM = omOptional.get(); LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orTspOM); TXOOBOsnrKey key = new TXOOBOsnrKey(addDropMuxOperationalModeId); if (orTspOM.getMinTXOsnr() != null) { txOnsrLin = 1.0 / (Math.pow(10.0, (orTspOM.getMinTXOsnr().getValue().doubleValue() / 10.0))); } if (orTspOM.nonnullTXOOBOsnr().get(key) != null && orTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrSingleChannelValue() != null) { // To 1/(Xponder Min TX OSNR lin) Add 1/(Xponder TX OOB OSNR Single channel lin) txOnsrLin = txOnsrLin + 1.0 / (Math.pow(10.0, (orTspOM.nonnullTXOOBOsnr().get(key) .getMinOOBOsnrSingleChannelValue().getValue().doubleValue() / 10.0))); } if (orTspOM.getTXOOBOsnr() != null && orTspOM.nonnullTXOOBOsnr().get(key) .getMinOOBOsnrMultiChannelValue() != null) { // To resulting 1/(OSNR lin) Add 1/(Xponder TX OOB OSNR Multi channel lin) // contribution txOnsrLin = txOnsrLin + 1.0 / (Math.pow(10.0, (orTspOM.nonnullTXOOBOsnr().get(key) .getMinOOBOsnrMultiChannelValue().getValue().doubleValue() / 10.0))); } } } catch (InterruptedException | ExecutionException e) { LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid); throw new RuntimeException( "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :" + e); } finally { networkTransactionService.close(); } } else { // In other cases, means the mode is a non OpenROADM specific Operational Mode InstanceIdentifier omCatalogIid = InstanceIdentifier .builder(OperationalModeCatalog.class) .child(SpecificOperationalModes.class) .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId)) .build(); try { var somOptional = networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid) .get(); if (somOptional.isPresent()) { speTspOM = somOptional.get(); LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", speTspOM); TXOOBOsnrKey key = new TXOOBOsnrKey(addDropMuxOperationalModeId); if (speTspOM.getMinTXOsnr() != null) { txOnsrLin = 1.0 / (Math.pow(10.0, (speTspOM.getMinTXOsnr().getValue().doubleValue() / 10.0))); } if (speTspOM.nonnullTXOOBOsnr().get(key) != null && speTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrSingleChannelValue() != null) { // Add to 1/(Transponder Min TX OSNR lin) 1/(Transponder TX OOB OSNR Single // channel lin) txOnsrLin = txOnsrLin + 1.0 / (Math.pow(10.0, (speTspOM.nonnullTXOOBOsnr().get(key) .getMinOOBOsnrSingleChannelValue().getValue().doubleValue() / 10.0))); } if (speTspOM.nonnullTXOOBOsnr().get(key) != null && speTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrMultiChannelValue() != null) { // Add to resulting 1/(OSNR lin) 1/(Transponder TX OOB OSNR Multi channel lin) // contribution txOnsrLin = txOnsrLin + 1.0 / (Math.pow(10.0, (speTspOM.nonnullTXOOBOsnr().get(key) .getMinOOBOsnrMultiChannelValue().getValue().doubleValue() / 10.0))); } } } catch (InterruptedException | ExecutionException e) { LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid); throw new RuntimeException( "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :" + e); } finally { networkTransactionService.close(); } } return txOnsrLin; } /** * This method retrieves performance parameters associated with a Xponder RX. * It calls getRxTspPenalty to evaluate the penalty associated with CD/PMD/PDL * It compares expected OSNR with the OSNR resulting from the line degradation, * and finally calculates and return the resulting margin. * * @param operationalModeId * operational-mode-Id of the Xponder (OR or Specific) * @param calcCd * accumulated chromatic dispersion across the line * @param calcPmd * accumulated Polarization mode dispersion across the line * @param calcPdl * accumulated Polarization Dependant Loss across the line * @param calcOsnrdB * Optical Signal to Noise Ratio (dB)resulting from the transmission * on the line, that shall include the Non Linear contribution * * @return the margin on the service path * @throws RuntimeException * if operationalModeId is not described in the catalog */ public double getPceRxTspParameters(String operationalModeId, double calcCd, double calcPmd, double calcPdl, double calcOsnrdB) { HashMap impairments = new HashMap<>(); double totalPenalty = 0.0; double penalty ; double rxOsnrdB = 0.0; double margin = -9999.9; XponderPluggableOpenroadmOperationalMode orTspOM = null; SpecificOperationalMode speTspOM = null; Map penaltiesMap = null; if (operationalModeId.startsWith("OR")) { var omCatalogIid = InstanceIdentifier .builder(OperationalModeCatalog.class) .child(OpenroadmOperationalModes.class) .child(XpondersPluggables.class) .child(XponderPluggableOpenroadmOperationalMode.class, new XponderPluggableOpenroadmOperationalModeKey(operationalModeId)) .build(); try { Optional omOptional = networkTransactionService .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get(); if (omOptional.isPresent()) { orTspOM = omOptional.get(); LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orTspOM); if (orTspOM.getMinRXOsnrTolerance() != null) { rxOsnrdB = orTspOM.getMinRXOsnrTolerance().getValue().doubleValue(); } penaltiesMap = orTspOM.getPenalties(); } } catch (InterruptedException | ExecutionException e) { LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid); throw new RuntimeException( "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :" + e); } finally { networkTransactionService.close(); } } else { // In other cases, means the mode is a non OpenROADM specific Operational Mode // InstanceIdentifier omCatalogIid = InstanceIdentifier var omCatalogIid = InstanceIdentifier .builder(OperationalModeCatalog.class) .child(SpecificOperationalModes.class) .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId)) .build(); try { Optional somOptional = networkTransactionService .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get(); if (somOptional.isPresent()) { speTspOM = somOptional.get(); LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", speTspOM); if (speTspOM.getMinRXOsnrTolerance() != null) { rxOsnrdB = speTspOM.getMinRXOsnrTolerance().getValue().doubleValue(); } penaltiesMap = speTspOM.getPenalties(); } } catch (InterruptedException | ExecutionException e) { LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid); throw new RuntimeException( "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :" + e); } finally { networkTransactionService.close(); } } if (penaltiesMap != null) { penalty = getRxTspPenalty(calcCd, ImpairmentType.CDPsNm, penaltiesMap); impairments.put("CDpenalty", penalty); totalPenalty = totalPenalty + penalty; penalty = getRxTspPenalty(calcPmd, ImpairmentType.PMDPs, penaltiesMap); impairments.put("PMD Penalty", penalty); totalPenalty = totalPenalty + penalty; penalty = getRxTspPenalty(calcPdl, ImpairmentType.PDLDB, penaltiesMap); impairments.put("PDL penalty", penalty); totalPenalty = totalPenalty + penalty; // For Future work since at that time we have no way to calculate the following // parameters,even if penalties are defined in the OpenROADM specifications // // impairments.put("Colorless Drop Adjacent Xtalk Penalty", getRxTspPenalty(TBD, // ImpairmentType.ColorlessDropAdjacentChannelCrosstalkGHz, penalitiesMap)); // impairments.put("XTalk total Power Penalty", getRxTspPenalty(TBD, // ImpairmentType.CrossTalkTotalPowerDB, penalitiesMap)); // impairments.put("Power penalty", getRxTspPenalty(TBD, // ImpairmentType.PowerDBm, penalitiesMap)); LOG.info("Penalty resulting from CD, PMD and PDL is {} dB with following contributions {}", totalPenalty, impairments); margin = calcOsnrdB - totalPenalty - rxOsnrdB; LOG.info("According to RX TSP Specification and calculated impairments Margin is {} dB ", margin); if (margin < 0) { LOG.info("Negative margin shall result in PCE rejecting the analyzed path"); } } else { LOG.info("Unable to calculate margin as penaltyMap can not be retrieved : Operational mode not populated"); } return margin; } /** * This generic method is called from getPceRxTspParameters to provide the * Penalties associated with CD, PMD and DGD for Xponder. It scans a penalty * list that includes penalty values corresponding to an interval between an * upper and a lower boundary for each of the above parameters. * * @param impairmentType * : the type of impairment (CD/PMD/DGD) * @param calculatedParameter * calculated accumulated value on the line for the impairment * @param penaltiesMap * the global map of penalties retrieved by getPceRxTspParameters * from the Xponder operational mode * * @return the penalty associated with accumulated impairment if it is in the * range specified in the table, a value that will lead to reject the * path if this is not the case */ private double getRxTspPenalty(double calculatedParameter, ImpairmentType impairmentType, Map penalitiesMap) { Penalties penalty = penalitiesMap.values().stream() // We only keep penalties corresponding to the calculated Parameter .filter(val -> val.getParameterAndUnit().getName().equals(impairmentType.getName())) // we sort it according to the comparator (based on up-to-boundary) .sorted(penaltiesComparator) // keep only items for which up to boundary is greater than calculatedParameter .filter(val -> val.getUpToBoundary().doubleValue() >= calculatedParameter) // takes the immediate greater or equal value .findFirst().orElse(null); if (penalty == null) { //means a boundary that is greater than calculatedParameter couldn't be found // Out of specification! return 9999.9; } // In spec, return penalty associated with calculatedParameter return penalty.getPenaltyValue().getValue().doubleValue(); } /** * This method retrieves performance parameters associated with ROADMs and * Amplifiers. It calculates the contribution of the node to the degradation of * the signal for CD, DGD, PDL, and OSNR which is calculated through a * polynomial fit described in the catalog. It finally corrects the accumulated * values for these parameters and return them. * * @param catalogNodeType * crossed node path type (ADD/DROP/EXPRESS/AMP) * @param operationalModeId * operational-mode-Id of the Node (OpenROADM only) * @param calcCd * accumulated chromatic dispersion across the line * @param calcDgd2 * Square of accumulated Group velocity dispersion across the line * @param calcPdl2 * Square of the accumulated Polarization Dependant Loss across the * line * @param pwrIn * Input power required to calculate OSNR contribution of the node = * f(pwrIn) * @param calcOnsrLin * Linear Optical Noise to Signal Ratio resulting from the * transmission on the line, that shall include the Non Linear * contribution * @param spacing * Interchannel spacing used for correction to calculate OSNR * contribution of the node * * @return Impairment, a map that provides corrected values for all calculated * parameters which includes the contribution of the node * (CD/DGD2/PDL2/ONSRLin) * @throws RuntimeException * if operationalModeId is not described in the catalog */ public Map getPceRoadmAmpParameters(CatalogConstant.CatalogNodeType catalogNodeType, String operationalModeId, double pwrIn, double calcCd, double calcDgd2, double calcPdl2, double calcOnsrLin, double spacing) { Map impairments = new HashMap<>(); double pdl2 = calcPdl2; double dgd2 = calcDgd2; double cd = calcCd; double onsrLin = calcOnsrLin; boolean supportedMode = true; switch (catalogNodeType) { case ADD: var omCatalogIid = InstanceIdentifier .builder(OperationalModeCatalog.class) .child(OpenroadmOperationalModes.class) .child(Roadms.class) .child(Add.class) .child(AddOpenroadmOperationalMode.class, new AddOpenroadmOperationalModeKey(operationalModeId)) .build(); try { var omOptional = networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get(); if (omOptional.isPresent()) { var orAddOM = omOptional.get(); LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orAddOM); networkTransactionService.close(); onsrLin = onsrLin + Math.pow(10, (-orAddOM.getIncrementalOsnr().getValue().doubleValue() - Math.log10(spacing / 50.0)) / 10.0); cd = cd + orAddOM.getMaxIntroducedCd().doubleValue(); pdl2 = pdl2 + Math.pow(orAddOM.getMaxIntroducedPdl().getValue().doubleValue(), 2.0); dgd2 = dgd2 + Math.pow(orAddOM.getMaxIntroducedDgd().doubleValue(), 2.0); } else { supportedMode = false; } } catch (InterruptedException | ExecutionException e) { onsrLin = 1; LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid); throw new RuntimeException( "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :" + e); } finally { networkTransactionService.close(); } break; case DROP: var omCatalogIid1 = InstanceIdentifier .builder(OperationalModeCatalog.class) .child(OpenroadmOperationalModes.class) .child(Roadms.class) .child(Drop.class) .child(OpenroadmOperationalMode.class, new OpenroadmOperationalModeKey(operationalModeId)) .build(); try { var omOptional = networkTransactionService .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid1) .get(); if (omOptional.isPresent()) { var orDropOM = omOptional.get(); LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orDropOM); networkTransactionService.close(); cd = cd + orDropOM.getMaxIntroducedCd().doubleValue(); pdl2 = pdl2 + Math.pow(orDropOM.getMaxIntroducedPdl().getValue().doubleValue(), 2.0); dgd2 = dgd2 + Math.pow(orDropOM.getMaxIntroducedDgd().doubleValue(), 2); onsrLin = onsrLin + Math.pow(10, -(orDropOM.getOsnrPolynomialFit().getA().doubleValue() * Math.pow(pwrIn, 3) + orDropOM.getOsnrPolynomialFit().getB().doubleValue() * Math.pow(pwrIn, 2) + orDropOM.getOsnrPolynomialFit().getC().doubleValue() * pwrIn + orDropOM.getOsnrPolynomialFit().getD().doubleValue() + 10 * Math.log10(spacing / 50.0)) / 10); } else { supportedMode = false; } } catch (InterruptedException | ExecutionException e) { onsrLin = 1; supportedMode = false; LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid1); throw new RuntimeException( "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid1 + " :" + e); } finally { networkTransactionService.close(); } break; case EXPRESS: var omCatalogIid2 = InstanceIdentifier .builder(OperationalModeCatalog.class) .child(OpenroadmOperationalModes.class) .child(Roadms.class) .child(Express.class) .child( org.opendaylight.yang.gen.v1.http .org.openroadm.operational.mode.catalog.rev211210 .operational.mode.roadm.express.parameters.express.OpenroadmOperationalMode.class, new org.opendaylight.yang.gen.v1.http .org.openroadm.operational.mode.catalog.rev211210 .operational.mode.roadm.express.parameters.express.OpenroadmOperationalModeKey( operationalModeId)) .build(); try { var omOptional = networkTransactionService .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid2) .get(); if (omOptional.isPresent()) { var orExpressOM = omOptional.get(); LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orExpressOM); cd = cd + orExpressOM.getMaxIntroducedCd().doubleValue(); pdl2 = pdl2 + Math.pow(orExpressOM.getMaxIntroducedPdl().getValue().doubleValue(), 2.0); dgd2 = dgd2 + Math.pow(orExpressOM.getMaxIntroducedDgd().doubleValue(), 2.0); onsrLin = onsrLin + Math.pow(10, -(orExpressOM.getOsnrPolynomialFit().getA().doubleValue() * Math.pow(pwrIn, 3) + orExpressOM.getOsnrPolynomialFit().getB().doubleValue() * Math.pow(pwrIn, 2) + orExpressOM.getOsnrPolynomialFit().getC().doubleValue() * pwrIn + orExpressOM.getOsnrPolynomialFit().getD().doubleValue() + 10 * Math.log10(spacing / 50.0)) / 10); } else { supportedMode = false; } } catch (InterruptedException | ExecutionException e) { onsrLin = 1; supportedMode = false; LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid2); throw new RuntimeException( "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid2 + " :" + e); } finally { networkTransactionService.close(); } break; case AMP: var omCatalogIid3 = InstanceIdentifier .builder(OperationalModeCatalog.class) .child(OpenroadmOperationalModes.class) .child(Amplifiers.class) .child(Amplifier.class) .child(OpenroadmOperationalMode.class, new OpenroadmOperationalModeKey(operationalModeId)) .build(); try { var omOptional = networkTransactionService .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid3) .get(); if (omOptional.isPresent()) { var orAmpOM = omOptional.get(); LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orAmpOM); networkTransactionService.close(); cd = cd + orAmpOM.getMaxIntroducedCd().doubleValue(); pdl2 = pdl2 + Math.pow(orAmpOM.getMaxIntroducedPdl().getValue().doubleValue(), 2.0); dgd2 = dgd2 + Math.pow(orAmpOM.getMaxIntroducedDgd().doubleValue(), 2.0); onsrLin = onsrLin + Math.pow(10, -(orAmpOM.getOsnrPolynomialFit().getA().doubleValue() * Math.pow(pwrIn, 3) + orAmpOM.getOsnrPolynomialFit().getB().doubleValue() * Math.pow(pwrIn, 2) + orAmpOM.getOsnrPolynomialFit().getC().doubleValue() * pwrIn + orAmpOM.getOsnrPolynomialFit().getD().doubleValue() + 10 * Math.log10(spacing / 50.0)) / 10); } else { supportedMode = false; } } catch (InterruptedException | ExecutionException e) { onsrLin = 1; supportedMode = false; LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid3); throw new RuntimeException( "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid3 + " :" + e); } finally { networkTransactionService.close(); } break; default: LOG.warn("Unsupported catalogNodeType {}", catalogNodeType); break; } if (supportedMode) { impairments.put("CD", cd); impairments.put("DGD2", dgd2); impairments.put("PDL2", pdl2); impairments.put("ONSRLIN", onsrLin); LOG.info("Accumulated CD is {} ps, DGD2 is {} ps and PDL2 is {} dB", cd, Math.sqrt(dgd2), Math.sqrt(pdl2)); LOG.info("Resulting OSNR is {} dB", 10 * Math.log10(1 / onsrLin)); } else { LOG.error("Operational Mode {} passed to getPceRoadmAmpParameters does not correspond to an OpenROADM mode" + "Parameters for amplifier and/or ROADMs can not be derived from specific-operational-modes.", operationalModeId); } return impairments; } /** * Non linear contribution computation. * Public method calculating non linear contribution among the path from * launched power and span length Formula comes from * OpenROADM_OSNR_Calculation_20220610 Tool The resulting contribution shall be * calculated for each fiber span and summed up * @param launchedPowerdB * The power launched in the span (shall account for Optical Distribution * Frame loss) * @param spanLength * Length of the span in km * @param spacing * OpenROADM power and osnr contribution calculations are based on * spacing between channels : the Media Channel (MC) width * * @return nonLinearOnsrContributionLin * The inverse of the NL OSNR contribution converted from dB to linear value */ public double calculateNLonsrContribution(double launchedPowerdB, double spanLength, double spacing) { double constanteC0 = 0 ; if (spacing > 162.5) { constanteC0 = CatalogConstant.NLCONSTANTC0GT1625; } else if (spacing > 112.5) { constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1625; } else if (spacing > 100.0) { constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1125; } else if (spacing > 87.5) { constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1000; } else { constanteC0 = CatalogConstant.NLCONSTANTC0UPTO875; } double nonLinearOnsrContributionLin = Math.pow(10.0, -(launchedPowerdB * CatalogConstant.NLCONSTANTC1 + constanteC0 + CatalogConstant.NLCONSTANTCE * Math.exp(CatalogConstant.NLCONSTANTEX * spanLength)) / 10); LOG.info(" OSNR Non Linear contribution is {} dB", launchedPowerdB * CatalogConstant.NLCONSTANTC1 + constanteC0 + CatalogConstant.NLCONSTANTCE * Math.exp(CatalogConstant.NLCONSTANTEX * spanLength)); return nonLinearOnsrContributionLin; } }