mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2025-11-01 18:47:48 +00:00
improving path_request_run robustness to wrong excel input
- adding a test to check that transponder types and modes are part of the eqpt library - adding a formating on numeical value input for path request ids - automatically printing the results into a csv file in addition to the json file and with the saime name+.csv - printing results in a more pretty way for demo Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
This commit is contained in:
@@ -53,7 +53,16 @@ class Element:
|
|||||||
|
|
||||||
class Request_element(Element):
|
class Request_element(Element):
|
||||||
def __init__(self,Request,eqpt_filename):
|
def __init__(self,Request,eqpt_filename):
|
||||||
self.request_id = int(Request.request_id)
|
# request_id is str
|
||||||
|
# excel has automatic number formatting that adds .0 on integer values
|
||||||
|
# the next lines recover the pure int value, assuming this .0 is unwanted
|
||||||
|
if not isinstance(Request.request_id,str):
|
||||||
|
value = str(int(Request.request_id))
|
||||||
|
if value.endswith('.0'):
|
||||||
|
value = value[:-2]
|
||||||
|
self.request_id = value
|
||||||
|
else:
|
||||||
|
self.request_id = Request.request_id
|
||||||
self.source = Request.source
|
self.source = Request.source
|
||||||
self.destination = Request.destination
|
self.destination = Request.destination
|
||||||
self.srctpid = f'trx {Request.source}'
|
self.srctpid = f'trx {Request.source}'
|
||||||
@@ -61,21 +70,27 @@ class Request_element(Element):
|
|||||||
# test that trx_type belongs to eqpt_config.json
|
# test that trx_type belongs to eqpt_config.json
|
||||||
# if not replace it with a default
|
# if not replace it with a default
|
||||||
equipment = load_equipment(eqpt_filename)
|
equipment = load_equipment(eqpt_filename)
|
||||||
if equipment['Transceiver'][Request.trx_type]:
|
try :
|
||||||
self.trx_type = Request.trx_type
|
if equipment['Transceiver'][Request.trx_type]:
|
||||||
self.mode = Request.mode
|
self.trx_type = Request.trx_type
|
||||||
else:
|
if [mode for mode in equipment['Transceiver'][Request.trx_type].mode]:
|
||||||
#TODO : this case must raise an error instead of using Voyager
|
self.mode = Request.mode
|
||||||
self.trx_type = 'Voyager_16QAM'
|
except KeyError:
|
||||||
print(f'Transceiver type {Request.trx_type} is not defined in {eqpt_filename}')
|
msg = f'could not find tsp : {Request.trx_type} with mode: {Request.mode} in eqpt library \nComputation stopped.'
|
||||||
print('replaced by Voyager_16QAM')
|
#print(msg)
|
||||||
|
logger.critical(msg)
|
||||||
|
exit()
|
||||||
|
# excel input are in GHz and dBm
|
||||||
self.spacing = Request.spacing * 1e9
|
self.spacing = Request.spacing * 1e9
|
||||||
self.power = db2lin(Request.power) * 1e-3
|
self.power = db2lin(Request.power) * 1e-3
|
||||||
self.nb_channel = int(Request.nb_channel)
|
self.nb_channel = int(Request.nb_channel)
|
||||||
if isinstance(Request.disjoint_from,str):
|
if not isinstance(Request.disjoint_from,str):
|
||||||
self.disjoint_from = [int(n) for n in Request.disjoint_from.split()]
|
value = str(int(Request.disjoint_from))
|
||||||
|
if value.endswith('.0'):
|
||||||
|
value = value[:-2]
|
||||||
else:
|
else:
|
||||||
self.disjoint_from = [int(Request.disjoint_from)]
|
value = Request.disjoint_from
|
||||||
|
self.disjoint_from = [n for n in value.split()]
|
||||||
self.nodes_list = []
|
self.nodes_list = []
|
||||||
if Request.nodes_list :
|
if Request.nodes_list :
|
||||||
self.nodes_list = Request.nodes_list.split(' | ')
|
self.nodes_list = Request.nodes_list.split(' | ')
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from gnpy.core.network import load_network, build_network, set_roadm_loss
|
|||||||
from gnpy.core.equipment import load_equipment, trx_mode_params
|
from gnpy.core.equipment import load_equipment, trx_mode_params
|
||||||
from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused
|
from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused
|
||||||
from gnpy.core.utils import db2lin, lin2db
|
from gnpy.core.utils import db2lin, lin2db
|
||||||
from gnpy.core.request import Path_request, Result_element, compute_constrained_path, propagate
|
from gnpy.core.request import Path_request, Result_element, compute_constrained_path, propagate, jsontocsv
|
||||||
from copy import copy, deepcopy
|
from copy import copy, deepcopy
|
||||||
|
|
||||||
#EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json'
|
#EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json'
|
||||||
@@ -138,17 +138,31 @@ if __name__ == '__main__':
|
|||||||
test = compute_path(network, equipment, pths)
|
test = compute_path(network, equipment, pths)
|
||||||
|
|
||||||
#TODO write results
|
#TODO write results
|
||||||
print("demand\tsnr@bandwidth\tsnr@0.1nm")
|
|
||||||
|
header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR']
|
||||||
|
data = []
|
||||||
|
data.append(header)
|
||||||
for i, p in enumerate(test):
|
for i, p in enumerate(test):
|
||||||
if p:
|
if p:
|
||||||
print(f'{pths[i].source} to {pths[i].destination} : \t{round(mean(p[-1].snr),2)} ,\
|
line = [f'{pths[i].source} to {pths[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\
|
||||||
\t{round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}')
|
f'{round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}',\
|
||||||
|
f'{pths[i].OSNR}']
|
||||||
else:
|
else:
|
||||||
print(f'no path from {pths[i].source} to {pths[i].destination} ')
|
line = [f'no path from {pths[i].source} to {pths[i].destination} ']
|
||||||
|
data.append(line)
|
||||||
|
|
||||||
|
col_width = max(len(word) for row in data for word in row) # padding
|
||||||
|
for row in data:
|
||||||
|
print(''.join(word.ljust(col_width) for word in row))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if args.output :
|
if args.output :
|
||||||
result = []
|
result = []
|
||||||
for p in test:
|
for p in test:
|
||||||
result.append(Result_element(pths[test.index(p)],p))
|
result.append(Result_element(pths[test.index(p)],p))
|
||||||
with open(args.output, 'w') as f:
|
with open(args.output, 'w') as f:
|
||||||
f.write(dumps(path_result_json(result), indent=2))
|
f.write(dumps(path_result_json(result), indent=2))
|
||||||
|
fnamecsv = next(s for s in args.output.split('.')) + '.csv'
|
||||||
|
with open(fnamecsv,"w") as fcsv :
|
||||||
|
jsontocsv(path_result_json(result),equipment,fcsv)
|
||||||
@@ -14,15 +14,11 @@ and write results in an CSV file
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from sys import exit
|
|
||||||
from csv import writer
|
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from json import dumps, loads
|
from json import loads
|
||||||
from gnpy.core.equipment import load_equipment
|
from gnpy.core.equipment import load_equipment
|
||||||
from gnpy.core.utils import lin2db
|
from gnpy.core.request import jsontocsv
|
||||||
|
|
||||||
START_LINE = 5
|
|
||||||
|
|
||||||
|
|
||||||
parser = ArgumentParser(description = 'A function that writes json path results in an excel sheet.')
|
parser = ArgumentParser(description = 'A function that writes json path results in an excel sheet.')
|
||||||
@@ -30,65 +26,14 @@ parser.add_argument('filename', nargs='?', type = Path)
|
|||||||
parser.add_argument('output_filename', nargs='?', type = Path)
|
parser.add_argument('output_filename', nargs='?', type = Path)
|
||||||
parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json')
|
parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
print(f'coucou {args.output_filename}')
|
|
||||||
with open(args.output_filename,"w") as file :
|
with open(args.output_filename,"w") as file :
|
||||||
mywriter = writer(file)
|
|
||||||
mywriter.writerow(('path-id','source','destination','transponder-type',\
|
|
||||||
'transponder-mode','baud rate (Gbaud)', 'input power (dBm)','path','OSNR@bandwidth','OSNR@0.1nm','SNR@bandwidth','SNR@0.1nm','Pass?'))
|
|
||||||
|
|
||||||
with open(args.filename) as f:
|
with open(args.filename) as f:
|
||||||
|
print(f'Reading {args.filename}')
|
||||||
json_data = loads(f.read())
|
json_data = loads(f.read())
|
||||||
equipment = load_equipment(args.eqpt_filename)
|
equipment = load_equipment(args.eqpt_filename)
|
||||||
tspjsondata = equipment['Transceiver']
|
print(f'Writing in {args.output_filename}')
|
||||||
#print(tspjsondata)
|
jsontocsv(json_data,equipment,file)
|
||||||
for p in json_data['path']:
|
|
||||||
path_id = int(p['path-id'])
|
|
||||||
source = p['path-properties']['path-route-objects'][0]\
|
|
||||||
['path-route-object']['unnumbered-hop']['node-id']
|
|
||||||
destination = p['path-properties']['path-route-objects'][-1]\
|
|
||||||
['path-route-object']['unnumbered-hop']['node-id']
|
|
||||||
pth = ' | '.join([ e['path-route-object']['unnumbered-hop']['node-id']
|
|
||||||
for e in p['path-properties']['path-route-objects']])
|
|
||||||
|
|
||||||
[tsp,mode] = p['path-properties']['path-route-objects'][0]\
|
|
||||||
['path-route-object']['unnumbered-hop']['hop-type'].split(' - ')
|
|
||||||
|
|
||||||
# find the min acceptable OSNR, baud rate from the eqpt library based on tsp (tupe) and mode (format)
|
|
||||||
try:
|
|
||||||
[minosnr, baud_rate] = next([m['OSNR'] , m['baud_rate']]
|
|
||||||
for m in equipment['Transceiver'][tsp].mode if m['format']==mode)
|
|
||||||
|
|
||||||
# for debug
|
|
||||||
# print(f'coucou {baud_rate}')
|
|
||||||
except IndexError:
|
|
||||||
msg = f'could not find tsp : {self.tsp} with mode: {self.tsp_mode} in eqpt library'
|
|
||||||
|
|
||||||
raise ValueError(msg)
|
|
||||||
output_snr = next(e['accumulative-value']
|
|
||||||
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm')
|
|
||||||
output_snrbandwidth = next(e['accumulative-value']
|
|
||||||
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@bandwidth')
|
|
||||||
output_osnr = next(e['accumulative-value']
|
|
||||||
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@0.1nm')
|
|
||||||
output_osnrbandwidth = next(e['accumulative-value']
|
|
||||||
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@bandwidth')
|
|
||||||
power = next(e['accumulative-value']
|
|
||||||
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'reference_power')
|
|
||||||
mywriter.writerow((path_id,
|
|
||||||
source,
|
|
||||||
destination,
|
|
||||||
tsp,
|
|
||||||
mode,
|
|
||||||
baud_rate*1e-9,
|
|
||||||
round(lin2db(power)+30,2),
|
|
||||||
pth,
|
|
||||||
output_osnrbandwidth,
|
|
||||||
output_osnr,
|
|
||||||
output_snrbandwidth,
|
|
||||||
output_snr,
|
|
||||||
output_snr >= minosnr
|
|
||||||
))
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from gnpy.core.network import set_roadm_loss
|
|||||||
from gnpy.core.utils import db2lin, lin2db
|
from gnpy.core.utils import db2lin, lin2db
|
||||||
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power
|
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power
|
||||||
from copy import copy, deepcopy
|
from copy import copy, deepcopy
|
||||||
|
from csv import writer
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ class Path_request:
|
|||||||
|
|
||||||
class Result_element(Element):
|
class Result_element(Element):
|
||||||
def __init__(self,path_request,computed_path):
|
def __init__(self,path_request,computed_path):
|
||||||
self.path_id = int(path_request.request_id)
|
self.path_id = path_request.request_id
|
||||||
self.path_request = path_request
|
self.path_request = path_request
|
||||||
self.computed_path = computed_path
|
self.computed_path = computed_path
|
||||||
hop_type = []
|
hop_type = []
|
||||||
@@ -83,56 +84,125 @@ class Result_element(Element):
|
|||||||
uid = property(lambda self: repr(self))
|
uid = property(lambda self: repr(self))
|
||||||
@property
|
@property
|
||||||
def pathresult(self):
|
def pathresult(self):
|
||||||
return {
|
if not self.computed_path:
|
||||||
'path-id': self.path_id,
|
return {
|
||||||
'path-properties':{
|
'path-id': self.path_id,
|
||||||
'path-metric': [
|
'path-properties':{
|
||||||
{
|
'path-metric': [
|
||||||
'metric-type': 'SNR@bandwidth',
|
{
|
||||||
'accumulative-value': round(mean(self.computed_path[-1].snr),2)
|
'metric-type': 'SNR@bandwidth',
|
||||||
},
|
'accumulative-value': 'None'
|
||||||
{
|
},
|
||||||
'metric-type': 'SNR@0.1nm',
|
{
|
||||||
'accumulative-value': round(mean(self.computed_path[-1].snr+lin2db(self.path_request.baud_rate/12.5e9)),2)
|
'metric-type': 'SNR@0.1nm',
|
||||||
},
|
'accumulative-value': 'None'
|
||||||
{
|
},
|
||||||
'metric-type': 'OSNR@bandwidth',
|
{
|
||||||
'accumulative-value': round(mean(self.computed_path[-1].osnr_ase),2)
|
'metric-type': 'OSNR@bandwidth',
|
||||||
},
|
'accumulative-value': 'None'
|
||||||
{
|
},
|
||||||
'metric-type': 'OSNR@0.1nm',
|
{
|
||||||
'accumulative-value': round(mean(self.computed_path[-1].osnr_ase_01nm),2)
|
'metric-type': 'OSNR@0.1nm',
|
||||||
},
|
'accumulative-value': 'None'
|
||||||
{
|
},
|
||||||
'metric-type': 'reference_power',
|
{
|
||||||
'accumulative-value': self.path_request.power
|
'metric-type': 'reference_power',
|
||||||
}
|
'accumulative-value': self.path_request.power
|
||||||
],
|
}
|
||||||
'path-srlgs': {
|
],
|
||||||
'usage': 'not used yet',
|
'path-srlgs': {
|
||||||
'values': 'not used yet'
|
'usage': 'not used yet',
|
||||||
},
|
'values': 'not used yet'
|
||||||
'path-route-objects': [
|
},
|
||||||
{
|
'path-route-objects': [
|
||||||
'path-route-object': {
|
{
|
||||||
'index': self.computed_path.index(n),
|
'path-route-object': {
|
||||||
'unnumbered-hop': {
|
'index': 0,
|
||||||
'node-id': n.uid,
|
'unnumbered-hop': {
|
||||||
'link-tp-id': n.uid,
|
'node-id': self.path_request.source,
|
||||||
'hop-type': self.hop_type[self.computed_path.index(n)],
|
'link-tp-id': self.path_request.source,
|
||||||
'direction': 'not used'
|
'hop-type': ' - '.join([self.path_request.tsp, self.path_request.tsp_mode]),
|
||||||
|
'direction': 'not used'
|
||||||
|
},
|
||||||
|
'label-hop': {
|
||||||
|
'te-label': {
|
||||||
|
'generic': 'not used yet',
|
||||||
|
'direction': 'not used yet'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'label-hop': {
|
{
|
||||||
'te-label': {
|
'path-route-object': {
|
||||||
'generic': 'not used yet',
|
'index': 1,
|
||||||
'direction': 'not used yet'
|
'unnumbered-hop': {
|
||||||
|
'node-id': self.path_request.destination,
|
||||||
|
'link-tp-id': self.path_request.destination,
|
||||||
|
'hop-type': ' - '.join([self.path_request.tsp, self.path_request.tsp_mode]),
|
||||||
|
'direction': 'not used'
|
||||||
|
},
|
||||||
|
'label-hop': {
|
||||||
|
'te-label': {
|
||||||
|
'generic': 'not used yet',
|
||||||
|
'direction': 'not used yet'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} for n in self.computed_path
|
]
|
||||||
]
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
'path-id': self.path_id,
|
||||||
|
'path-properties':{
|
||||||
|
'path-metric': [
|
||||||
|
{
|
||||||
|
'metric-type': 'SNR@bandwidth',
|
||||||
|
'accumulative-value': round(mean(self.computed_path[-1].snr),2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'metric-type': 'SNR@0.1nm',
|
||||||
|
'accumulative-value': round(mean(self.computed_path[-1].snr+lin2db(self.path_request.baud_rate/12.5e9)),2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'metric-type': 'OSNR@bandwidth',
|
||||||
|
'accumulative-value': round(mean(self.computed_path[-1].osnr_ase),2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'metric-type': 'OSNR@0.1nm',
|
||||||
|
'accumulative-value': round(mean(self.computed_path[-1].osnr_ase_01nm),2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'metric-type': 'reference_power',
|
||||||
|
'accumulative-value': self.path_request.power
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'path-srlgs': {
|
||||||
|
'usage': 'not used yet',
|
||||||
|
'values': 'not used yet'
|
||||||
|
},
|
||||||
|
'path-route-objects': [
|
||||||
|
{
|
||||||
|
'path-route-object': {
|
||||||
|
'index': self.computed_path.index(n),
|
||||||
|
'unnumbered-hop': {
|
||||||
|
'node-id': n.uid,
|
||||||
|
'link-tp-id': n.uid,
|
||||||
|
'hop-type': self.hop_type[self.computed_path.index(n)],
|
||||||
|
'direction': 'not used'
|
||||||
|
},
|
||||||
|
'label-hop': {
|
||||||
|
'te-label': {
|
||||||
|
'generic': 'not used yet',
|
||||||
|
'direction': 'not used yet'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} for n in self.computed_path
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def json(self):
|
def json(self):
|
||||||
@@ -192,4 +262,69 @@ def propagate(path, req, equipment, show=False):
|
|||||||
si = el(si)
|
si = el(si)
|
||||||
if show :
|
if show :
|
||||||
print(el)
|
print(el)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def jsontocsv(json_data,equipment,fileout):
|
||||||
|
# read json path result file in accordance with:
|
||||||
|
# Yang model for requesting Path Computation
|
||||||
|
# draft-ietf-teas-yang-path-computation-01.txt.
|
||||||
|
# and write results in an CSV file
|
||||||
|
|
||||||
|
mywriter = writer(fileout)
|
||||||
|
mywriter.writerow(('path-id','source','destination','transponder-type',\
|
||||||
|
'transponder-mode','baud rate (Gbaud)', 'input power (dBm)','path',\
|
||||||
|
'OSNR@bandwidth','OSNR@0.1nm','SNR@bandwidth','SNR@0.1nm','Pass?'))
|
||||||
|
tspjsondata = equipment['Transceiver']
|
||||||
|
#print(tspjsondata)
|
||||||
|
for p in json_data['path']:
|
||||||
|
path_id = p['path-id']
|
||||||
|
source = p['path-properties']['path-route-objects'][0]\
|
||||||
|
['path-route-object']['unnumbered-hop']['node-id']
|
||||||
|
destination = p['path-properties']['path-route-objects'][-1]\
|
||||||
|
['path-route-object']['unnumbered-hop']['node-id']
|
||||||
|
pth = ' | '.join([ e['path-route-object']['unnumbered-hop']['node-id']
|
||||||
|
for e in p['path-properties']['path-route-objects']])
|
||||||
|
|
||||||
|
[tsp,mode] = p['path-properties']['path-route-objects'][0]\
|
||||||
|
['path-route-object']['unnumbered-hop']['hop-type'].split(' - ')
|
||||||
|
|
||||||
|
# find the min acceptable OSNR, baud rate from the eqpt library based on tsp (tupe) and mode (format)
|
||||||
|
try:
|
||||||
|
[minosnr, baud_rate] = next([m['OSNR'] , m['baud_rate']]
|
||||||
|
for m in equipment['Transceiver'][tsp].mode if m['format']==mode)
|
||||||
|
|
||||||
|
# for debug
|
||||||
|
# print(f'coucou {baud_rate}')
|
||||||
|
except IndexError:
|
||||||
|
msg = f'could not find tsp : {self.tsp} with mode: {self.tsp_mode} in eqpt library'
|
||||||
|
|
||||||
|
raise ValueError(msg)
|
||||||
|
output_snr = next(e['accumulative-value']
|
||||||
|
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm')
|
||||||
|
output_snrbandwidth = next(e['accumulative-value']
|
||||||
|
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@bandwidth')
|
||||||
|
output_osnr = next(e['accumulative-value']
|
||||||
|
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@0.1nm')
|
||||||
|
output_osnrbandwidth = next(e['accumulative-value']
|
||||||
|
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@bandwidth')
|
||||||
|
power = next(e['accumulative-value']
|
||||||
|
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'reference_power')
|
||||||
|
if isinstance(output_snr, str):
|
||||||
|
isok = ''
|
||||||
|
else:
|
||||||
|
isok = output_snr >= minosnr
|
||||||
|
mywriter.writerow((path_id,
|
||||||
|
source,
|
||||||
|
destination,
|
||||||
|
tsp,
|
||||||
|
mode,
|
||||||
|
baud_rate*1e-9,
|
||||||
|
round(lin2db(power)+30,2),
|
||||||
|
pth,
|
||||||
|
output_osnrbandwidth,
|
||||||
|
output_osnr,
|
||||||
|
output_snrbandwidth,
|
||||||
|
output_snr,
|
||||||
|
isok
|
||||||
|
))
|
||||||
Reference in New Issue
Block a user