From: orenais Date: Fri, 29 Jul 2022 15:31:40 +0000 (+0200) Subject: Primitives to retrieve parameters from Catalog X-Git-Tag: 6.0.0~30^2~2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F18%2F101918%2F14;p=transportpce.git Primitives to retrieve parameters from Catalog - add a CatalogUtils and a Catalogconstant class in common module with primitives to retrieve parameters from Catalog - add a related Penalty comparator serializable class - add related Junit with a sample apidocCatalog json file that sticks to latest version of OpenROADM optical specification v5.1 JIRA: TRNSPRTPCE-518 Signed-off-by: orenais Change-Id: I8e898d86d44ea9a8f3a58eb581c3fe6515534221 --- diff --git a/common/pom.xml b/common/pom.xml index 3adffdf97..ad5162fb2 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -42,6 +42,11 @@ transportpce-ordmodels-device ${project.version} + + ${project.groupId}.ordmodels + transportpce-ordmodels-service + ${project.version} + ${project.groupId}.ordmodels transportpce-ordmodels-network diff --git a/common/src/main/java/org/opendaylight/transportpce/common/catalog/CatalogConstant.java b/common/src/main/java/org/opendaylight/transportpce/common/catalog/CatalogConstant.java new file mode 100644 index 000000000..423ab6924 --- /dev/null +++ b/common/src/main/java/org/opendaylight/transportpce/common/catalog/CatalogConstant.java @@ -0,0 +1,42 @@ +/* + * 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; + + + +/** + * Util class for Catalog constants and impairment calculation. + */ + + +public final class CatalogConstant { + + public static final String ORW100GSC = "OR-W-100G-SC"; + public static final String ORW100GOFEC316GBD = "OR-W-100G-oFEC-31.6Gbd"; + public static final String ORW200GOFEC316GBD = "OR-W-200G-oFEC-31.6Gbd"; + public static final String ORW200GOFEC631GBD = "OR-W-200G-oFEC-63.1Gbd"; + public static final String ORW300GOFEC631GBD = "OR-W-300G-oFEC-63.1Gbd"; + public static final String ORW400GOFEC631GBD = "OR-W-400G-oFEC-63.1Gbd"; + public static final String MWWRCORE = "MW-WR-core"; + public static final String MWMWCORE = "MW-MW-core"; + public static final String MWISTANDARD = "MWi-standard"; + public static final String MWILOWNOISE = "MWi-low-noise"; + public static final double NLCONSTANTC1 = -2.0; + public static final double NLCONSTANTC0UPTO875 = 43.4; + public static final double NLCONSTANTC0UPTO1000 = 45.4; + public static final double NLCONSTANTC0UPTO1125 = 46.1; + public static final double NLCONSTANTC0UPTO1625 = 48.6; + public static final double NLCONSTANTC0GT1625 = 60.0; + public static final double NLCONSTANTCE = 11.33; + public static final double NLCONSTANTEX = -0.09; + + public enum CatalogNodeType { ADD, DROP, EXPRESS, AMP, TSP } + + private CatalogConstant() { + } +} diff --git a/common/src/main/java/org/opendaylight/transportpce/common/catalog/CatalogUtils.java b/common/src/main/java/org/opendaylight/transportpce/common/catalog/CatalogUtils.java new file mode 100644 index 000000000..0fab6def1 --- /dev/null +++ b/common/src/main/java/org/opendaylight/transportpce/common/catalog/CatalogUtils.java @@ -0,0 +1,752 @@ +/* + * 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; + } + +} diff --git a/common/src/main/java/org/opendaylight/transportpce/common/catalog/PenaltiesComparator.java b/common/src/main/java/org/opendaylight/transportpce/common/catalog/PenaltiesComparator.java new file mode 100644 index 000000000..55f805d02 --- /dev/null +++ b/common/src/main/java/org/opendaylight/transportpce/common/catalog/PenaltiesComparator.java @@ -0,0 +1,23 @@ +/* + * 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.io.Serializable; +import java.util.Comparator; +import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.Penalties; + +@SuppressWarnings("serial") +@edu.umd.cs.findbugs.annotations.SuppressFBWarnings( + value = "SE_NO_SERIALVERSIONID", + justification = "https://github.com/rzwitserloot/lombok/wiki/WHY-NOT:-serialVersionUID") +public class PenaltiesComparator implements Comparator, Serializable { + @Override + public int compare(Penalties o1, Penalties o2) { + return Double.compare(o1.getUpToBoundary().doubleValue(), o2.getUpToBoundary().doubleValue()); + } +} diff --git a/common/src/test/java/org/opendaylight/transportpce/common/catalog/CatalogUtilsTest.java b/common/src/test/java/org/opendaylight/transportpce/common/catalog/CatalogUtilsTest.java new file mode 100644 index 000000000..e7e0ed782 --- /dev/null +++ b/common/src/test/java/org/opendaylight/transportpce/common/catalog/CatalogUtilsTest.java @@ -0,0 +1,248 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import org.eclipse.jdt.annotation.NonNull; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.mdsal.binding.api.WriteTransaction; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.transportpce.common.StringConstants; +import org.opendaylight.transportpce.common.network.NetworkTransactionImpl; +import org.opendaylight.transportpce.common.network.NetworkTransactionService; +import org.opendaylight.transportpce.common.network.RequestProcessor; +import org.opendaylight.transportpce.test.AbstractTest; +import org.opendaylight.transportpce.test.converter.DataObjectConverter; +import org.opendaylight.transportpce.test.converter.JSONDataObjectConverter; +import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.OperationalModeCatalog; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CatalogUtilsTest extends AbstractTest { + private static final Logger LOG = LoggerFactory.getLogger(CatalogUtilsTest.class); + private static final String CATALOG_FILE = "src/test/resources/apidocCatalog10_1OptSpecV5_1.json"; + private static OperationalModeCatalog omCatalog; + private static Map outputImpairments = new HashMap<>(); + + // + @BeforeClass + public static void setUp() throws InterruptedException, + ExecutionException { + DataObjectConverter dataObjectConverter = JSONDataObjectConverter + .createWithDataStoreUtil(getDataStoreContextUtil()); + try (Reader reader = new FileReader(CATALOG_FILE, StandardCharsets.UTF_8)) { + NormalizedNode normalizedNode = dataObjectConverter + .transformIntoNormalizedNode(reader).get(); + omCatalog = (OperationalModeCatalog) getDataStoreContextUtil() + .getBindingDOMCodecServices().fromNormalizedNode(YangInstanceIdentifier + .of(OperationalModeCatalog.QNAME), normalizedNode) + .getValue(); + @NonNull + WriteTransaction newWriteOnlyTransaction = getDataBroker().newWriteOnlyTransaction(); + newWriteOnlyTransaction + .put(LogicalDatastoreType.CONFIGURATION, + InstanceIdentifier.create(OperationalModeCatalog.class), + omCatalog); + newWriteOnlyTransaction.commit().get(); + } catch (IOException e) { + LOG.error("Cannot load OpenROADM part of Operational Mode Catalog ", e); + fail("Cannot load openROADM operational modes "); + } + } + + @Test + public void catalogPrimitivesTest() { + RequestProcessor reqProc = new RequestProcessor(getDataBroker()); + NetworkTransactionService netTransServ = new NetworkTransactionImpl(reqProc); + CatalogUtils catalogUtils = new CatalogUtils(netTransServ); + assertEquals("Checking retrieval of Operational Mode from Node Type ADD", + CatalogConstant.MWWRCORE, + catalogUtils.getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.ADD, + StringConstants.SERVICE_TYPE_100GE_T)); + assertEquals("Checking retrieval of Operational Mode from Node Type DROP", + CatalogConstant.MWWRCORE, + catalogUtils.getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.DROP, + StringConstants.SERVICE_TYPE_100GE_T)); + assertEquals("Checking retrieval of Operational Mode from Node Type EXPRESS", + CatalogConstant.MWMWCORE, + catalogUtils.getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.EXPRESS, + StringConstants.SERVICE_TYPE_100GE_T)); + assertEquals("Checking retrieval of Operational Mode from Node Type AMP", + CatalogConstant.MWISTANDARD, + catalogUtils.getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.AMP, + StringConstants.SERVICE_TYPE_100GE_T)); + assertEquals("Checking retrieval of Operational Mode from Node Type and service Type 100GE", + CatalogConstant.ORW100GSC, + catalogUtils.getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP, + StringConstants.SERVICE_TYPE_100GE_T)); + assertEquals("Checking retrieval of Operational Mode from Node Type and service Type OTU4", + CatalogConstant.ORW100GSC, + catalogUtils.getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP, + StringConstants.SERVICE_TYPE_OTU4)); + assertEquals("Checking retrieval of Operational Mode from Node Type and service Type OTUC2", + CatalogConstant.ORW200GOFEC316GBD, + catalogUtils.getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP, + StringConstants.SERVICE_TYPE_OTUC2)); + assertEquals("Checking retrieval of Operational Mode from Node Type and service Type OTUC3", + CatalogConstant.ORW300GOFEC631GBD, + catalogUtils.getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP, + StringConstants.SERVICE_TYPE_OTUC3)); + assertEquals("Checking retrieval of Operational Mode from Node Type and service Type 400GE", + CatalogConstant.ORW400GOFEC631GBD, + catalogUtils.getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP, + StringConstants.SERVICE_TYPE_400GE)); + assertEquals("Checking retrieval of Operational Mode from Node Type and service Type OTUC4", + CatalogConstant.ORW400GOFEC631GBD, + catalogUtils.getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP, + StringConstants.SERVICE_TYPE_OTUC4)); + assertEquals("Checking retrieval of channel spacing from Operational Mode 100G SC FEC", + 50.0, + catalogUtils.getPceTxTspChannelSpacing(CatalogConstant.ORW100GSC),0.005); + assertEquals("Checking retrieval of channel spacing from Operational Mode 100G OFEC 31.6", + 50.0, + catalogUtils.getPceTxTspChannelSpacing(CatalogConstant.ORW100GOFEC316GBD),0.005); + assertEquals("Checking retrieval of channel spacing from Operational Mode 200G OFEC 31.6", + 50.0, + catalogUtils.getPceTxTspChannelSpacing(CatalogConstant.ORW200GOFEC316GBD),0.005); + assertEquals("Checking retrieval of channel spacing from Operational Mode 200G OFEC 63.1", + 87.5, + catalogUtils.getPceTxTspChannelSpacing(CatalogConstant.ORW200GOFEC631GBD),0.005); + assertEquals("Checking retrieval of channel spacing from Operational Mode 300G OFEC 63.1 GBd", + 87.5, + catalogUtils.getPceTxTspChannelSpacing(CatalogConstant.ORW300GOFEC631GBD),0.005); + assertEquals("Checking retrieval of channel spacing from Operational Mode 400G OFEC 63.1 Gbd", + 87.5, + catalogUtils.getPceTxTspChannelSpacing(CatalogConstant.ORW400GOFEC631GBD),0.005); + assertEquals("Checking 100GSCFEC ONSR Lin", + 1345.6, + catalogUtils.getPceTxTspParameters(CatalogConstant.ORW100GSC, CatalogConstant.MWWRCORE) * 1000000.0, + 0.5); + assertEquals("Checking 100G OFEC 31.6 Gbauds ONSR Lin", + 450.7, + catalogUtils.getPceTxTspParameters(CatalogConstant.ORW100GOFEC316GBD, CatalogConstant.MWWRCORE) * 1000000.0, + 0.5); + assertEquals("Checking 200G OFEC 31.6 Gbauds ONSR Lin", + 450.7, + catalogUtils.getPceTxTspParameters(CatalogConstant.ORW200GOFEC316GBD, CatalogConstant.MWWRCORE) * 1000000.0, + 0.5); + assertEquals("Checking 200G OFEC 63.1 Gbauds ONSR Lin", + 450.7, + catalogUtils.getPceTxTspParameters(CatalogConstant.ORW200GOFEC631GBD, CatalogConstant.MWWRCORE) * 1000000.0, + 0.5); + assertEquals("Checking 300G OFEC 63.1 Gbauds ONSR Lin", + 450.7, + catalogUtils.getPceTxTspParameters(CatalogConstant.ORW300GOFEC631GBD, CatalogConstant.MWWRCORE) * 1000000.0, + 0.5); + assertEquals("Checking 400G OFEC 63.1 Gbauds ONSR Lin", + 450.7, + catalogUtils.getPceTxTspParameters(CatalogConstant.ORW400GOFEC631GBD, CatalogConstant.MWWRCORE) * 1000000.0, + 0.5); + assertEquals("Checking ONSR Lin = 0 for non valid OM", + 0.0, catalogUtils.getPceTxTspParameters("SPE-non-existing-mode", CatalogConstant.MWWRCORE) * 1000000.0, + 0.0); + assertEquals("Checking 100GSCFEC RX margin OOR due to CD", + -9996.9, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW100GSC, 18001.0, 0.0, 0.0, 20.0), 0.5); + assertEquals("Checking 100GSCFEC RX margin OOR due to PMD", + -9996.9, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW100GSC, 0.0, 30.1, 0.0, 20.0), 0.5); + assertEquals("Checking 100GSCFEC RX margin OOR due to PDL", + -9996.9, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW100GSC, 0.0, 0.0, 6.1, 20.0), 0.5); + assertEquals("Checking 100GSCFEC RX margin in Range at max tolerated penalty", + 3.0, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW100GSC, 17999.0, 29.9, 5.9, 20.0), 0.05); + assertEquals("Checking 400G OFEC 63.1 Gbauds RX margin OOR due to CD", + -9996.9, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW400GOFEC631GBD, 12001.0, 0.0, 0.0, 27.0), + 0.5); + assertEquals("Checking 400G OFEC 63.1 Gbauds RX margin OOR due to PMD", + -9996.9, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW400GOFEC631GBD, 0.0, 20.1, 0.0, 27.0), + 0.5); + assertEquals("Checking 400G OFEC 63.1 Gbauds RX margin OOR due to PDL", + -9996.9, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW400GOFEC631GBD, 0.0, 0.0, 4.1, 27.0), + 0.5); + assertEquals("Checking 400G OFEC 63.1 Gbauds RX margin in Range at max tolerated penalty", + 0.5, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW400GOFEC631GBD, 11999.0, 19.9, 3.9, 28.0), + 0.05); + assertEquals("Checking 400G OFEC 63.1 Gbauds RX margin in Range at intermediate tolerated penalty", + 0.5, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW400GOFEC631GBD, 3999.0, 9.9, 1.9, 25.5), + 0.05); + assertEquals("Checking 400G OFEC 63.1 Gbauds RX margin in Range at min tolerated penalty", + 0.5, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW400GOFEC631GBD, 3999.0, 9.9, 0.9, 25.0), + 0.05); + assertEquals("Checking 300G OFEC 63.1 Gbauds RX margin in Range at max tolerated penalty", + 0.5, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW300GOFEC631GBD, 17999.0, 24.9, 3.9, 25.0), + 0.05); + assertEquals("Checking 300G OFEC 63.1 Gbauds RX margin in Range at min tolerated penalty", + 0.5, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW300GOFEC631GBD, 3999.0, 9.9, 0.9, 22.0), + 0.05); + assertEquals("Checking 200G OFEC 63.1 Gbauds RX margin in Range at max tolerated penalty", + 0.5, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW200GOFEC631GBD, 23999.0, 24.9, 3.9, 21.0), + 0.05); + assertEquals("Checking 200G OFEC 63.1 Gbauds RX margin in Range at min tolerated penalty", + 0.5, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW200GOFEC631GBD, 3999.0, 9.9, 0.9, 18.0), + 0.05); + assertEquals("Checking 200G OFEC 31.6 Gbauds RX margin in Range at max tolerated penalty", + 0.5, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW200GOFEC316GBD, 23999.0, 29.9, 3.9, 24.5), + 0.05); + assertEquals("Checking 200G OFEC 31.6 Gbauds RX margin in Range at min tolerated penalty", + 0.5, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW200GOFEC316GBD, 3999.0, 9.9, 0.9, 21.5), + 0.05); + assertEquals("Checking 100G OFEC 31.6 Gbauds RX margin in Range at max tolerated penalty", + 0.5, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW100GOFEC316GBD, 47999.0, 29.9, 3.9, 16.0), + 0.05); + assertEquals("Checking 100G OFEC 31.6 Gbauds RX margin in Range at min tolerated penalty", + 0.5, catalogUtils.getPceRxTspParameters(CatalogConstant.ORW100GOFEC316GBD, 3999.0, 9.9, 0.9, 13.0), + 0.05); + assertEquals("Checking Margin negative for non valid OM", + -9999.9, catalogUtils.getPceRxTspParameters("SPE-non-existing-mode", 0.0, 0.0, 0.0, 30.0), 0.05); + outputImpairments.put("CD", 1025.0); + outputImpairments.put("DGD2", 18.0); + outputImpairments.put("PDL2", 6.25); + outputImpairments.put("ONSRLIN", 0.0016307685044580744); + // check how to add Delta on an object + assertEquals("Checking ROADM Express path contribution to impairments ", + outputImpairments, catalogUtils.getPceRoadmAmpParameters(CatalogConstant.CatalogNodeType.EXPRESS, + CatalogConstant.MWMWCORE,-15.0, 1000.0, 9.0, 4.0, 0.001000, 50.0)); + outputImpairments.put("ONSRLIN", 0.0013604391454046139); + assertEquals("Checking ROADM Express path contribution to impairments with 87.5 GHz spacing ", + outputImpairments, catalogUtils.getPceRoadmAmpParameters(CatalogConstant.CatalogNodeType.EXPRESS, + CatalogConstant.MWMWCORE,-15.0, 1000.0, 9.0, 4.0, 0.001000, 87.5)); + outputImpairments.put("ONSRLIN", 0.0015011872336272727); + assertEquals("Checking ROADM Add path contribution to impairments ", + outputImpairments, catalogUtils.getPceRoadmAmpParameters(CatalogConstant.CatalogNodeType.ADD, + CatalogConstant.MWWRCORE, -15.0, 1000.0, 9.0, 4.0, 0.001, 50.0)); + outputImpairments.put("ONSRLIN", 0.0016307685044580744); + assertEquals("Checking ROADM Drop path contribution to impairments ", + outputImpairments, catalogUtils.getPceRoadmAmpParameters(CatalogConstant.CatalogNodeType.DROP, + CatalogConstant.MWWRCORE, -15.0, 1000.0, 9.0, 4.0, 0.001, 50.0)); + outputImpairments.put("ONSRLIN", 0.0015010372326658573); + assertEquals("Checking Amp path contribution to impairments ", + outputImpairments, catalogUtils.getPceRoadmAmpParameters(CatalogConstant.CatalogNodeType.AMP, + CatalogConstant.MWISTANDARD, -15.0, 1025.0, 9.0, 5.76, 0.001, 50.0)); + assertEquals("Checking empty map returned in case wrong Operational mode provided ", + true, catalogUtils.getPceRoadmAmpParameters(CatalogConstant.CatalogNodeType.AMP, + "ThisIsNotAValidMode", -15.0,1000.0, 0.0, 0.0, 0.001, 50.0).isEmpty()); + outputImpairments.put("ONSRLIN", 1.0); + assertEquals("Checking empty map returned in case wrong Operational mode provided ", + true, catalogUtils.getPceRoadmAmpParameters(CatalogConstant.CatalogNodeType.AMP, + "OR-InvalidMode", -15.0, 1025.0, 18.0, 6.25, 0.001, 50.0).isEmpty()); + assertEquals("Checking Non Linear contribution calculation ", 0.000114266642501745, + catalogUtils.calculateNLonsrContribution(2, 70, 87.5), 0.000000005); + } +} \ No newline at end of file diff --git a/common/src/test/resources/apidocCatalog10_1OptSpecV5_1.json b/common/src/test/resources/apidocCatalog10_1OptSpecV5_1.json new file mode 100644 index 000000000..413c6f5cd --- /dev/null +++ b/common/src/test/resources/apidocCatalog10_1OptSpecV5_1.json @@ -0,0 +1,662 @@ +{ + "operational-mode-catalog": { + "openroadm-operational-modes": { + "grid-parameters": { + "min-central-frequency": "191.32500000", + "max-central-frequency": "196.12500000", + "central-frequency-granularity": "12.50000", + "min-spacing": "37.50000" + }, + "xponders-pluggables": { + "xponder-pluggable-openroadm-operational-mode": [ + { + "openroadm-operational-mode-id": "OR-W-100G-SC", + "baud-rate": "28.0", + "line-rate": "111.8", + "modulation-format": "dp-qpsk", + "min-TX-osnr": "33.000", + "TX-OOB-osnr": { + "WR-openroadm-operational-mode-id": "MW-WR-core", + "min-OOB-osnr-multi-channel-value": "31.000", + "min-OOB-osnr-single-channel-value": "43.000" + }, + "output-power-range": { + "WR-openroadm-operational-mode-id": "MW-WR-core", + "min-output-power": "-5.000", + "max-output-power": "0.000" + }, + "min-RX-osnr-tolerance": "17.000", + "min-input-power-at-RX-osnr": "-22.000", + "max-input-power": "1.000", + "channel-width": "40.00000", + "fec-type": "org-openroadm-common-types:scfec", + "penalties": [ + { + "parameter-and-unit": "CD-ps/nm", + "up-to-boundary": "18000", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "6", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "PMD-ps", + "up-to-boundary": "30", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-22", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "cross-talk-total-power-dB", + "up-to-boundary": "15", + "penalty-value": "0.200" + }, + { + "parameter-and-unit": "colorless-drop-adjacent-channel-crosstalk-GHz", + "up-to-boundary": "4", + "penalty-value": "0.200" + } + ] + }, + { + "openroadm-operational-mode-id": "OR-W-100G-oFEC-31.6Gbd", + "baud-rate": "31.6", + "line-rate": "126.3", + "modulation-format": "dp-qpsk", + "min-TX-osnr": "37.000", + "TX-OOB-osnr": { + "WR-openroadm-operational-mode-id": "MW-WR-core", + "min-OOB-osnr-multi-channel-value": "36.000" + }, + "output-power-range": { + "WR-openroadm-operational-mode-id": "MW-WR-core", + "min-output-power": "-5.000", + "max-output-power": "0.000" + }, + "min-RX-osnr-tolerance": "12.000", + "min-input-power-at-RX-osnr": "-18.000", + "max-input-power": "1.000", + "channel-width": "37.88400", + "fec-type": "org-openroadm-common-types:ofec", + "min-roll-off": "0.05", + "max-roll-off": "0.20", + "penalties": [ + { + "parameter-and-unit": "CD-ps/nm", + "up-to-boundary": "4000", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "CD-ps/nm", + "up-to-boundary": "48000", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "1", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "2", + "penalty-value": "1.000" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "4", + "penalty-value": "2.500" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "6", + "penalty-value": "4.000" + }, + { + "parameter-and-unit": "PMD-ps", + "up-to-boundary": "10", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "PMD-ps", + "up-to-boundary": "30", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-18", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-20", + "penalty-value": "1.000" + }, + { + "parameter-and-unit": "cross-talk-total-power-dB", + "up-to-boundary": "15", + "penalty-value": "0.200" + }, + { + "parameter-and-unit": "colorless-drop-adjacent-channel-crosstalk-GHz", + "up-to-boundary": "4", + "penalty-value": "0.200" + } + ] + }, + { + "openroadm-operational-mode-id": "OR-W-200G-oFEC-31.6Gbd", + "baud-rate": "31.6", + "line-rate": "252.6", + "modulation-format": "dp-qam16", + "min-TX-osnr": "37.000", + "TX-OOB-osnr": { + "WR-openroadm-operational-mode-id": "MW-WR-core", + "min-OOB-osnr-multi-channel-value": "36.000" + }, + "output-power-range": { + "WR-openroadm-operational-mode-id": "MW-WR-core", + "min-output-power": "-5.000", + "max-output-power": "0.000" + }, + "min-RX-osnr-tolerance": "20.500", + "min-input-power-at-RX-osnr": "-16.000", + "max-input-power": "1.000", + "channel-width": "37.88400", + "fec-type": "org-openroadm-common-types:ofec", + "min-roll-off": "0.05", + "max-roll-off": "0.20", + "penalties": [ + { + "parameter-and-unit": "CD-ps/nm", + "up-to-boundary": "4000", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "CD-ps/nm", + "up-to-boundary": "24000", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "1", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "2", + "penalty-value": "1.000" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "4", + "penalty-value": "2.500" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "6", + "penalty-value": "4.000" + }, + { + "parameter-and-unit": "PMD-ps", + "up-to-boundary": "10", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "PMD-ps", + "up-to-boundary": "30", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-16", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-18", + "penalty-value": "1.000" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-20", + "penalty-value": "2.000" + }, + { + "parameter-and-unit": "cross-talk-total-power-dB", + "up-to-boundary": "15", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "colorless-drop-adjacent-channel-crosstalk-GHz", + "up-to-boundary": "4", + "penalty-value": "0.500" + } + ] + }, + { + "openroadm-operational-mode-id": "OR-W-200G-oFEC-63.1Gbd", + "baud-rate": "63.1", + "line-rate": "252.6", + "modulation-format": "dp-qpsk", + "min-TX-osnr": "37.000", + "TX-OOB-osnr": { + "WR-openroadm-operational-mode-id": "MW-WR-core", + "min-OOB-osnr-multi-channel-value": "36.000" + }, + "output-power-range": { + "WR-openroadm-operational-mode-id": "MW-WR-core", + "min-output-power": "-5.000", + "max-output-power": "0.000" + }, + "min-RX-osnr-tolerance": "17.000", + "min-input-power-at-RX-osnr": "-18.000", + "max-input-power": "1.000", + "channel-width": "75.72000", + "fec-type": "org-openroadm-common-types:ofec", + "min-roll-off": "0.05", + "max-roll-off": "0.20", + "penalties": [ + { + "parameter-and-unit": "CD-ps/nm", + "up-to-boundary": "4000", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "CD-ps/nm", + "up-to-boundary": "24000", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "1", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "2", + "penalty-value": "1.000" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "4", + "penalty-value": "2.500" + }, + { + "parameter-and-unit": "PMD-ps", + "up-to-boundary": "10", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "PMD-ps", + "up-to-boundary": "25", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-18", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-20", + "penalty-value": "1.000" + }, + { + "parameter-and-unit": "cross-talk-total-power-dB", + "up-to-boundary": "15", + "penalty-value": "0.300" + }, + { + "parameter-and-unit": "colorless-drop-adjacent-channel-crosstalk-GHz", + "up-to-boundary": "4", + "penalty-value": "0.500" + } + ] + }, + { + "openroadm-operational-mode-id": "OR-W-300G-oFEC-63.1Gbd", + "baud-rate": "63.1", + "line-rate": "378.8", + "modulation-format": "dp-qam16", + "min-TX-osnr": "37.000", + "TX-OOB-osnr": { + "WR-openroadm-operational-mode-id": "MW-WR-core", + "min-OOB-osnr-multi-channel-value": "36.000" + }, + "output-power-range": { + "WR-openroadm-operational-mode-id": "MW-WR-core", + "min-output-power": "-5.000", + "max-output-power": "0.000" + }, + "min-RX-osnr-tolerance": "21.000", + "min-input-power-at-RX-osnr": "-16.000", + "max-input-power": "1.000", + "channel-width": "75.72000", + "fec-type": "org-openroadm-common-types:ofec", + "min-roll-off": "0.05", + "max-roll-off": "0.20", + "penalties": [ + { + "parameter-and-unit": "CD-ps/nm", + "up-to-boundary": "4000", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "CD-ps/nm", + "up-to-boundary": "18000", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "1.00", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "2.00", + "penalty-value": "1.000" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "4.00", + "penalty-value": "2.500" + }, + { + "parameter-and-unit": "PMD-ps", + "up-to-boundary": "10.00", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "PMD-ps", + "up-to-boundary": "25.00", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-16.00", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-18.00", + "penalty-value": "1.000" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-20.00", + "penalty-value": "2.000" + }, + { + "parameter-and-unit": "cross-talk-total-power-dB", + "up-to-boundary": "15.00", + "penalty-value": "0.300" + }, + { + "parameter-and-unit": "colorless-drop-adjacent-channel-crosstalk-GHz", + "up-to-boundary": "4.0", + "penalty-value": "0.500" + } + ] + }, + { + "openroadm-operational-mode-id": "OR-W-400G-oFEC-63.1Gbd", + "baud-rate": "63.1", + "line-rate": "505.1", + "modulation-format": "dp-qam8", + "min-TX-osnr": "37.000", + "TX-OOB-osnr": { + "WR-openroadm-operational-mode-id": "MW-WR-core", + "min-OOB-osnr-multi-channel-value": "36.000" + }, + "output-power-range": { + "WR-openroadm-operational-mode-id": "MW-WR-core", + "min-output-power": "-5.000", + "max-output-power": "0.000" + }, + "min-RX-osnr-tolerance": "24.000", + "min-input-power-at-RX-osnr": "-14.000", + "max-input-power": "1.000", + "channel-width": "75.72000", + "fec-type": "org-openroadm-common-types:ofec", + "min-roll-off": "0.05", + "max-roll-off": "0.20", + "penalties": [ + { + "parameter-and-unit": "CD-ps/nm", + "up-to-boundary": "4000", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "CD-ps/nm", + "up-to-boundary": "12000", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "1.00", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "2.00", + "penalty-value": "1.000" + }, + { + "parameter-and-unit": "PDL-dB", + "up-to-boundary": "4.00", + "penalty-value": "2.500" + }, + { + "parameter-and-unit": "PMD-ps", + "up-to-boundary": "10.00", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "PMD-ps", + "up-to-boundary": "20.00", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-14", + "penalty-value": "0.000" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-16.00", + "penalty-value": "1.000" + }, + { + "parameter-and-unit": "power-dBm", + "up-to-boundary": "-18.00", + "penalty-value": "2.000" + }, + { + "parameter-and-unit": "cross-talk-total-power-dB", + "up-to-boundary": "13.00", + "penalty-value": "0.300" + }, + { + "parameter-and-unit": "cross-talk-total-power-dB", + "up-to-boundary": "15.00", + "penalty-value": "0.500" + }, + { + "parameter-and-unit": "colorless-drop-adjacent-channel-crosstalk-GHz", + "up-to-boundary": "4.10", + "penalty-value": "0.500" + } + ] + } + ] + }, + "roadms": { + "Express": { + "openroadm-operational-mode": { + "openroadm-operational-mode-id": "MW-MW-core", + "per-channel-Pin-min": "-21.000", + "per-channel-Pin-max": "-9.000", + "max-introduced-pdl": "1.500", + "max-introduced-dgd": "3.00", + "max-introduced-cd": "25.00", + "osnr-polynomial-fit": { + "A": "-0.00059520", + "B": "-0.06250000", + "C": "-1.07100000", + "D": "27.99000000" + }, + "mask-power-vs-pin": [ + { + "lower-boundary": "0", + "upper-boundary": "6", + "C": "1.00000000", + "D": "-9.00000000", + "fiber-type": "smf" + }, + { + "lower-boundary": "6", + "upper-boundary": "8", + "C": "-0.00000000", + "D": "-3.00000000", + "fiber-type": "smf" + }, + { + "lower-boundary": "8", + "upper-boundary": "23", + "C": "0.33333334", + "D": "-5.66666667", + "fiber-type": "smf" + }, + { + "lower-boundary": "23", + "upper-boundary": "27", + "C": "0.00000000", + "D": "2.00000000", + "fiber-type": "smf" + } + ] + } + }, + "Add": { + "add-openroadm-operational-mode": { + "openroadm-operational-mode-id": "MW-WR-core", + "incremental-osnr": "33.000", + "per-channel-Pin-min": "-6.000", + "per-channel-Pin-max": "3.000", + "max-introduced-pdl": "1.500", + "max-introduced-dgd": "3.00", + "max-introduced-cd": "25.00", + "mask-power-vs-pin": [ + { + "lower-boundary": "0", + "upper-boundary": "6", + "C": "1.00000000", + "D": "-9.00000000", + "fiber-type": "smf" + }, + { + "lower-boundary": "6", + "upper-boundary": "8", + "C": "-0.00000000", + "D": "-3.00000000", + "fiber-type": "smf" + }, + { + "lower-boundary": "8", + "upper-boundary": "23", + "C": "0.33333334", + "D": "-5.66666667", + "fiber-type": "smf" + }, + { + "lower-boundary": "23", + "upper-boundary": "27", + "C": "0.00000000", + "D": "2.00000000", + "fiber-type": "smf" + } + ] + } + }, + "Drop": { + "openroadm-operational-mode": { + "openroadm-operational-mode-id": "MW-WR-core", + "per-channel-Pin-min": "-25.000", + "per-channel-Pin-max": "-9.000", + "max-introduced-pdl": "1.500", + "max-introduced-dgd": "3.00", + "max-introduced-cd": "25.00", + "osnr-polynomial-fit": { + "A": "-0.00059520", + "B": "-0.06250000", + "C": "-1.07100000", + "D": "27.99000000" + }, + "per-channel-Pout-min": "-22.000", + "per-channel-Pout-max": "1.000" + } + } + }, + "amplifiers": { + "Amplifier": { + "min-gain": "0.000", + "max-gain": "27.000", + "max-extended-gain": "31.000", + "mask-gain-ripple-vs-tilt": [ + { + "lower-boundary": "-4", + "upper-boundary": "-1", + "C": "-0.50", + "D": "1.00" + }, + { + "lower-boundary": "-1", + "upper-boundary": "0", + "C": "0.50", + "D": "2.00" + } + ], + "openroadm-operational-mode": [ + { + "openroadm-operational-mode-id": "MWi-standard", + "per-channel-Pin-min": "-31.000", + "per-channel-Pin-max": "-9.000", + "max-introduced-pdl": "0.70", + "max-introduced-dgd": "3.00", + "max-introduced-cd": "0.00", + "osnr-polynomial-fit": { + "A": "-0.00059520", + "B": "-0.06250000", + "C": "-1.07100000", + "D": "28.99000000" + }, + "per-channel-Pout-min": "-9.000", + "per-channel-Pout-max": "2.000" + }, + { + "openroadm-operational-mode-id": "MWi-low-noise", + "per-channel-Pin-min": "-31.000", + "per-channel-Pin-max": "-9.000", + "max-introduced-pdl": "0.700", + "max-introduced-dgd": "3.00", + "max-introduced-cd": "0.00", + "osnr-polynomial-fit": { + "A": "-0.00081040", + "B": "-0.06221000", + "C": "-0.58890000", + "D": "37.62000000" + }, + "per-channel-Pout-min": "-9.000", + "per-channel-Pout-max": "2.000" + } + ] + } + } + } + } +}