Merge pull request #199 from Orange-OpenSource/sys_margins

Sys margins
This commit is contained in:
James
2019-01-28 10:39:19 -05:00
committed by GitHub
11 changed files with 139 additions and 92 deletions

View File

@@ -98,7 +98,8 @@
], ],
"Roadms":[{ "Roadms":[{
"gain_mode_default_loss": 20, "gain_mode_default_loss": 20,
"power_mode_pout_target": -20 "power_mode_pout_target": -20,
"add_drop_osnr": 38
}], }],
"SI":[{ "SI":[{
"f_min": 191.3e12, "f_min": 191.3e12,
@@ -108,11 +109,8 @@
"power_dbm": 0, "power_dbm": 0,
"power_range_db": [0,0,0.5], "power_range_db": [0,0,0.5],
"roll_off": 0.15, "roll_off": 0.15,
"OSNR": 11, "tx_osnr": 40,
"bit_rate":100e9, "sys_margins": 0
"tx_osnr": 45,
"min_spacing": 37.5e9,
"cost":1
}], }],
"Transceiver":[ "Transceiver":[
{ {
@@ -129,7 +127,7 @@
"OSNR": 11, "OSNR": 11,
"bit_rate": 100e9, "bit_rate": 100e9,
"roll_off": 0.15, "roll_off": 0.15,
"tx_osnr": 45, "tx_osnr": 40,
"min_spacing": 37.5e9, "min_spacing": 37.5e9,
"cost":1 "cost":1
}, },
@@ -139,7 +137,7 @@
"OSNR": 15, "OSNR": 15,
"bit_rate": 200e9, "bit_rate": 200e9,
"roll_off": 0.15, "roll_off": 0.15,
"tx_osnr": 45, "tx_osnr": 40,
"min_spacing": 75e9, "min_spacing": 75e9,
"cost":1 "cost":1
} }
@@ -158,7 +156,7 @@
"OSNR": 12, "OSNR": 12,
"bit_rate": 100e9, "bit_rate": 100e9,
"roll_off": 0.15, "roll_off": 0.15,
"tx_osnr": 45, "tx_osnr": 40,
"min_spacing": 37.5e9, "min_spacing": 37.5e9,
"cost":1 "cost":1
}, },
@@ -168,7 +166,7 @@
"OSNR": 18, "OSNR": 18,
"bit_rate": 300e9, "bit_rate": 300e9,
"roll_off": 0.15, "roll_off": 0.15,
"tx_osnr": 45, "tx_osnr": 40,
"min_spacing": 62.5e9, "min_spacing": 62.5e9,
"cost":1 "cost":1
}, },
@@ -178,7 +176,7 @@
"OSNR": 21, "OSNR": 21,
"bit_rate": 400e9, "bit_rate": 400e9,
"roll_off": 0.15, "roll_off": 0.15,
"tx_osnr": 45, "tx_osnr": 40,
"min_spacing": 75e9, "min_spacing": 75e9,
"cost":1 "cost":1
}, },
@@ -188,7 +186,7 @@
"OSNR": 16, "OSNR": 16,
"bit_rate": 200e9, "bit_rate": 200e9,
"roll_off": 0.15, "roll_off": 0.15,
"tx_osnr": 45, "tx_osnr": 40,
"min_spacing": 75e9, "min_spacing": 75e9,
"cost":1 "cost":1
} }

View File

@@ -121,7 +121,7 @@
"type": "Roadm" "type": "Roadm"
}, },
{ {
"uid": "ingress fused spans in Corlay", "uid": "west fused spans in Corlay",
"metadata": { "metadata": {
"location": { "location": {
"city": "Corlay", "city": "Corlay",
@@ -133,7 +133,7 @@
"type": "Fused" "type": "Fused"
}, },
{ {
"uid": "ingress fused spans in Loudeac", "uid": "west fused spans in Loudeac",
"metadata": { "metadata": {
"location": { "location": {
"city": "Loudeac", "city": "Loudeac",
@@ -145,7 +145,7 @@
"type": "Fused" "type": "Fused"
}, },
{ {
"uid": "ingress fused spans in Morlaix", "uid": "west fused spans in Morlaix",
"metadata": { "metadata": {
"location": { "location": {
"city": "Morlaix", "city": "Morlaix",
@@ -157,7 +157,7 @@
"type": "Fused" "type": "Fused"
}, },
{ {
"uid": "egress fused spans in Corlay", "uid": "east fused spans in Corlay",
"metadata": { "metadata": {
"location": { "location": {
"city": "Corlay", "city": "Corlay",
@@ -169,7 +169,7 @@
"type": "Fused" "type": "Fused"
}, },
{ {
"uid": "egress fused spans in Loudeac", "uid": "east fused spans in Loudeac",
"metadata": { "metadata": {
"location": { "location": {
"city": "Loudeac", "city": "Loudeac",
@@ -181,7 +181,7 @@
"type": "Fused" "type": "Fused"
}, },
{ {
"uid": "egress fused spans in Morlaix", "uid": "east fused spans in Morlaix",
"metadata": { "metadata": {
"location": { "location": {
"city": "Morlaix", "city": "Morlaix",
@@ -652,34 +652,34 @@
}, },
{ {
"from_node": "fiber (Lannion_CAS → Corlay)-F061", "from_node": "fiber (Lannion_CAS → Corlay)-F061",
"to_node": "ingress fused spans in Corlay" "to_node": "west fused spans in Corlay"
}, },
{ {
"from_node": "ingress fused spans in Corlay", "from_node": "west fused spans in Corlay",
"to_node": "fiber (Corlay → Loudeac)-F010" "to_node": "fiber (Corlay → Loudeac)-F010"
}, },
{ {
"from_node": "fiber (Loudeac → Corlay)-F010", "from_node": "fiber (Loudeac → Corlay)-F010",
"to_node": "egress fused spans in Corlay" "to_node": "east fused spans in Corlay"
}, },
{ {
"from_node": "egress fused spans in Corlay", "from_node": "east fused spans in Corlay",
"to_node": "fiber (Corlay → Lannion_CAS)-F061" "to_node": "fiber (Corlay → Lannion_CAS)-F061"
}, },
{ {
"from_node": "fiber (Corlay → Loudeac)-F010", "from_node": "fiber (Corlay → Loudeac)-F010",
"to_node": "ingress fused spans in Loudeac" "to_node": "west fused spans in Loudeac"
}, },
{ {
"from_node": "ingress fused spans in Loudeac", "from_node": "west fused spans in Loudeac",
"to_node": "fiber (Loudeac → Lorient_KMA)-F054" "to_node": "fiber (Loudeac → Lorient_KMA)-F054"
}, },
{ {
"from_node": "fiber (Lorient_KMA → Loudeac)-F054", "from_node": "fiber (Lorient_KMA → Loudeac)-F054",
"to_node": "egress fused spans in Loudeac" "to_node": "east fused spans in Loudeac"
}, },
{ {
"from_node": "egress fused spans in Loudeac", "from_node": "east fused spans in Loudeac",
"to_node": "fiber (Loudeac → Corlay)-F010" "to_node": "fiber (Loudeac → Corlay)-F010"
}, },
{ {
@@ -748,18 +748,18 @@
}, },
{ {
"from_node": "fiber (Lannion_CAS → Morlaix)-F059", "from_node": "fiber (Lannion_CAS → Morlaix)-F059",
"to_node": "ingress fused spans in Morlaix" "to_node": "west fused spans in Morlaix"
}, },
{ {
"from_node": "ingress fused spans in Morlaix", "from_node": "west fused spans in Morlaix",
"to_node": "fiber (Morlaix → Brest_KLA)-F060" "to_node": "fiber (Morlaix → Brest_KLA)-F060"
}, },
{ {
"from_node": "fiber (Brest_KLA → Morlaix)-F060", "from_node": "fiber (Brest_KLA → Morlaix)-F060",
"to_node": "egress fused spans in Morlaix" "to_node": "east fused spans in Morlaix"
}, },
{ {
"from_node": "egress fused spans in Morlaix", "from_node": "east fused spans in Morlaix",
"to_node": "fiber (Morlaix → Lannion_CAS)-F059" "to_node": "fiber (Morlaix → Lannion_CAS)-F059"
}, },
{ {

View File

@@ -74,14 +74,20 @@ def requests_from_json(json_data,equipment):
# params['power'] is updated # params['power'] is updated
if req['path-constraints']['te-bandwidth']['output-power']: if req['path-constraints']['te-bandwidth']['output-power']:
params['power'] = req['path-constraints']['te-bandwidth']['output-power'] params['power'] = req['path-constraints']['te-bandwidth']['output-power']
# same process for nb-channel # same process for nb-channel
fmin = params['frequency']['min'] f_min = params['f_min']
fmax = params['frequency']['max'] f_max_from_si = params['f_max']
if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None : if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None :
params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel']
params['nb_channel'] = nch
spacing = params['spacing']
params['f_max'] = f_min + nch*spacing
else : else :
params['nb_channel'] = automatic_nch(fmin,fmax,params['spacing']) params['nb_channel'] = automatic_nch(f_min,f_max_from_si,params['spacing'])
consitency_check(params)
consistency_check(params, f_max_from_si)
try : try :
params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth'] params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth']
except KeyError: except KeyError:
@@ -89,10 +95,10 @@ def requests_from_json(json_data,equipment):
requests_list.append(Path_request(**params)) requests_list.append(Path_request(**params))
return requests_list return requests_list
def consitency_check(params): def consistency_check(params, f_max_from_si):
fmin = params['frequency']['min'] f_min = params['f_min']
fmax = params['frequency']['max'] f_max = params['f_max']
max_recommanded_nb_channels = automatic_nch(fmin,fmax, max_recommanded_nb_channels = automatic_nch(f_min,f_max,
params['spacing']) params['spacing'])
if params['baud_rate'] is not None: if params['baud_rate'] is not None:
#implicitely means that a mode is defined with min_spacing #implicitely means that a mode is defined with min_spacing
@@ -103,10 +109,10 @@ def consitency_check(params):
print(msg) print(msg)
logger.critical(msg) logger.critical(msg)
exit() exit()
if params['nb_channel']>max_recommanded_nb_channels: if f_max>f_max_from_si:
msg = dedent(f''' msg = dedent(f'''
Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz and requested spacing {params["spacing"]*1e-9}GHz Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz and requested spacing {params["spacing"]*1e-9}GHz
is not consistent with frequency range {fmin*1e-12} THz, {fmax*1e-12} THz, min recommanded spacing {params["min_spacing"]*1e-9}GHz. is not consistent with frequency range {f_min*1e-12} THz, {f_max*1e-12} THz, min recommanded spacing {params["min_spacing"]*1e-9}GHz.
max recommanded nb of channels is {max_recommanded_nb_channels} max recommanded nb of channels is {max_recommanded_nb_channels}
Computation stopped.''') Computation stopped.''')
logger.critical(msg) logger.critical(msg)
@@ -206,7 +212,7 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist):
logger.warning(msg) logger.warning(msg)
total_path = [] total_path = []
else: else:
total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment, show=False) total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment)
# if no baudrate satisfies spacing, no mode is returned and an empty path is returned # if no baudrate satisfies spacing, no mode is returned and an empty path is returned
# a warning is shown in the propagate_and_optimize_mode # a warning is shown in the propagate_and_optimize_mode
if mode is not None : if mode is not None :

View File

@@ -25,7 +25,7 @@ from collections import namedtuple
from gnpy.core.node import Node from gnpy.core.node import Node
from gnpy.core.units import UNITS from gnpy.core.units import UNITS
from gnpy.core.utils import lin2db, db2lin, itufs from gnpy.core.utils import lin2db, db2lin, itufs, snr_sum
class Transceiver(Node): class Transceiver(Node):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -35,20 +35,47 @@ class Transceiver(Node):
self.osnr_nli = None self.osnr_nli = None
self.snr = None self.snr = None
self.passive = False self.passive = False
self.baud_rate = None
def _calc_snr(self, spectral_info): def _calc_snr(self, spectral_info):
with errstate(divide='ignore'): with errstate(divide='ignore'):
self.osnr_ase = [lin2db(divide(c.power.signal, c.power.ase)) self.baud_rate = [c.baud_rate for c in spectral_info.carriers]
ratio_01nm = [lin2db(12.5e9/b_rate) for b_rate in self.baud_rate]
#set raw values to record original calculation, before update_snr()
self.raw_osnr_ase = [lin2db(divide(c.power.signal, c.power.ase))
for c in spectral_info.carriers] for c in spectral_info.carriers]
ratio_01nm = [lin2db(12.5e9/c.baud_rate) self.raw_osnr_ase_01nm = [ase - ratio for ase, ratio
in zip(self.raw_osnr_ase, ratio_01nm)]
self.raw_osnr_nli = [lin2db(divide(c.power.signal, c.power.nli))
for c in spectral_info.carriers] for c in spectral_info.carriers]
self.osnr_ase_01nm = [ase - ratio for ase, ratio self.raw_snr = [lin2db(divide(c.power.signal, c.power.nli+c.power.ase))
in zip(self.osnr_ase, ratio_01nm)]
self.osnr_nli = [lin2db(divide(c.power.signal, c.power.nli))
for c in spectral_info.carriers]
self.snr = [lin2db(divide(c.power.signal, c.power.nli+c.power.ase))
for c in spectral_info.carriers] for c in spectral_info.carriers]
self.osnr_ase = self.raw_osnr_ase
self.osnr_ase_01nm = self.raw_osnr_ase_01nm
self.osnr_nli = self.raw_osnr_nli
self.snr = self.raw_snr
def update_snr(self, *args):
"""
snr_added in 0.1nm
compute SNR penalties such as transponder Tx_osnr or Roadm add_drop_osnr
only applied in request.py / propagate on the last Trasceiver node of the path
all penalties are added in a single call because to avoid uncontrolled cumul
"""
#use raw_values so that the added snr penalties are not cumulated
snr_added = 0
for s in args:
snr_added += db2lin(-s)
snr_added = -lin2db(snr_added)
self.osnr_ase = list(map(lambda x,y:snr_sum(x,y,snr_added),
self.raw_osnr_ase, self.baud_rate))
self.snr = list(map(lambda x,y:snr_sum(x,y,snr_added),
self.raw_snr, self.baud_rate))
self.osnr_ase_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added),
self.raw_osnr_ase_01nm))
@property @property
def to_json(self): def to_json(self):
return {'uid' : self.uid, return {'uid' : self.uid,

View File

@@ -25,9 +25,9 @@ Fiber = namedtuple('Fiber', 'type_variety dispersion gamma')
Spans = namedtuple('Spans', 'power_mode delta_power_range_db max_length length_units \ Spans = namedtuple('Spans', 'power_mode delta_power_range_db max_length length_units \
max_loss padding EOL con_in con_out') max_loss padding EOL con_in con_out')
Transceiver = namedtuple('Transceiver', 'type_variety frequency mode') Transceiver = namedtuple('Transceiver', 'type_variety frequency mode')
Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pout_target') Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pout_target add_drop_osnr')
SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \ SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \
power_dbm power_range_db OSNR bit_rate tx_osnr min_spacing cost') power_dbm power_range_db tx_osnr sys_margins')
AmpBase = namedtuple( AmpBase = namedtuple(
'AmpBase', 'AmpBase',
'type_variety type_def gain_flatmax gain_min p_max' 'type_variety type_def gain_flatmax gain_min p_max'
@@ -162,6 +162,8 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
try: try:
trxs = equipment['Transceiver'] trxs = equipment['Transceiver']
#if called from path_requests_run.py, trx_mode is filled with None when not specified by user
#if called from transmission_main.py, trx_mode is ''
if trx_mode is not None: if trx_mode is not None:
mode_params = next(mode for trx in trxs \ mode_params = next(mode for trx in trxs \
if trx == trx_type_variety \ if trx == trx_type_variety \
@@ -184,7 +186,8 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
"min_spacing":None, "min_spacing":None,
"cost":None} "cost":None}
trx_params = {**mode_params} trx_params = {**mode_params}
trx_params['frequency'] = equipment['Transceiver'][trx_type_variety].frequency trx_params['f_min'] = equipment['Transceiver'][trx_type_variety].frequency['min']
trx_params['f_max'] = equipment['Transceiver'][trx_type_variety].frequency['max']
# TODO: novel automatic feature maybe unwanted if spacing is specified # TODO: novel automatic feature maybe unwanted if spacing is specified
# trx_params['spacing'] = automatic_spacing(trx_params['baud_rate']) # trx_params['spacing'] = automatic_spacing(trx_params['baud_rate'])
@@ -198,21 +201,18 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
else: else:
# default transponder charcteristics # default transponder charcteristics
# mainly used with transmission_main_example.py # mainly used with transmission_main_example.py
trx_params['frequency'] = {'min': default_si_data.f_min, 'max': default_si_data.f_max} trx_params['f_min'] = default_si_data.f_min
trx_params['f_max'] = default_si_data.f_max
trx_params['baud_rate'] = default_si_data.baud_rate trx_params['baud_rate'] = default_si_data.baud_rate
trx_params['spacing'] = default_si_data.spacing trx_params['spacing'] = default_si_data.spacing
trx_params['OSNR'] = default_si_data.OSNR trx_params['OSNR'] = None
trx_params['bit_rate'] = default_si_data.bit_rate trx_params['bit_rate'] = None
trx_params['cost'] = default_si_data.cost trx_params['cost'] = None
trx_params['roll_off'] = default_si_data.roll_off trx_params['roll_off'] = default_si_data.roll_off
trx_params['nb_channel'] = automatic_nch(trx_params['frequency']['min'],
trx_params['frequency']['max'],
trx_params['spacing'])
trx_params['tx_osnr'] = default_si_data.tx_osnr trx_params['tx_osnr'] = default_si_data.tx_osnr
trx_params['min_spacing'] = default_si_data.min_spacing trx_params['min_spacing'] = None
nch = automatic_nch(trx_params['frequency']['min'], nch = automatic_nch(trx_params['f_min'], trx_params['f_max'], trx_params['spacing'])
trx_params['frequency']['max'], trx_params['nb_channel'] = nch
trx_params['spacing'])
print(f'There are {nch} channels propagating') print(f'There are {nch} channels propagating')
trx_params['power'] = db2lin(default_si_data.power_dbm)*1e-3 trx_params['power'] = db2lin(default_si_data.power_dbm)*1e-3
@@ -229,10 +229,20 @@ def automatic_spacing(baud_rate):
def automatic_nch(f_min, f_max, spacing): def automatic_nch(f_min, f_max, spacing):
return int((f_max - f_min)//spacing) return int((f_max - f_min)//spacing)
def automatic_fmax(f_min, spacing, nch):
return f_min + spacing * nch
def load_equipment(filename): def load_equipment(filename):
json_data = load_json(filename) json_data = load_json(filename)
return equipment_from_json(json_data, filename) return equipment_from_json(json_data, filename)
def update_trx_osnr(equipment):
"""add sys_margins to all Transceivers OSNR values"""
for trx in equipment['Transceiver'].values():
for m in trx.mode:
m['OSNR'] = m['OSNR'] + equipment['SI']['default'].sys_margins
return equipment
def equipment_from_json(json_data, filename): def equipment_from_json(json_data, filename):
"""build global dictionnary eqpt_library that stores all eqpt characteristics: """build global dictionnary eqpt_library that stores all eqpt characteristics:
edfa type type_variety, fiber type_variety edfa type type_variety, fiber type_variety
@@ -257,4 +267,5 @@ def equipment_from_json(json_data, filename):
equipment[key][subkey] = Amp.from_default_json(config, **entry) equipment[key][subkey] = Amp.from_default_json(config, **entry)
else: else:
equipment[key][subkey] = typ(**entry) equipment[key][subkey] = typ(**entry)
equipment = update_trx_osnr(equipment)
return equipment return equipment

View File

@@ -14,6 +14,7 @@ from numpy import array
from gnpy.core.utils import lin2db, db2lin from gnpy.core.utils import lin2db, db2lin
from json import loads from json import loads
from gnpy.core.utils import load_json from gnpy.core.utils import load_json
from gnpy.core.equipment import automatic_nch, automatic_spacing
class ConvenienceAccess: class ConvenienceAccess:
@@ -56,18 +57,17 @@ def merge_input_spectral_information(*si):
#TODO #TODO
pass pass
def create_input_spectral_information(f_min, roll_off, baud_rate, power, spacing, nb_channel, tx_osnr): def create_input_spectral_information(f_min, f_max, roll_off, baud_rate, power, spacing):
# pref in dB : convert power lin into power in dB # pref in dB : convert power lin into power in dB
pref = lin2db(power * 1e3) pref = lin2db(power * 1e3)
ase_power = (power / db2lin(tx_osnr)) * (baud_rate / 12.5e9)
si = SpectralInformation(pref=Pref(pref, pref)) si = SpectralInformation(pref=Pref(pref, pref))
nb_channel = automatic_nch(f_min, f_max, spacing)
si = si.update(carriers=[ si = si.update(carriers=[
Channel(f, (f_min+spacing*f), Channel(f, (f_min+spacing*f),
baud_rate, roll_off, Power(power, 0, ase_power)) for f in range(1,nb_channel+1) baud_rate, roll_off, Power(power, 0, 0)) for f in range(1,nb_channel+1)
]) ])
return si return si
if __name__ == '__main__': if __name__ == '__main__':
pref = lin2db(power * 1e3) pref = lin2db(power * 1e3)
si = SpectralInformation( si = SpectralInformation(

View File

@@ -34,7 +34,7 @@ logger = getLogger(__name__)
RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+ RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+
' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off tx_osnr min_spacing cost path_bandwidth') ' trx_mode nodes_list loose_list spacing power nb_channel f_min f_max format baud_rate OSNR bit_rate roll_off tx_osnr min_spacing cost path_bandwidth')
DisjunctionParams = namedtuple('DisjunctionParams','disjunction_id relaxable link_diverse node_diverse disjunctions_req') DisjunctionParams = namedtuple('DisjunctionParams','disjunction_id relaxable link_diverse node_diverse disjunctions_req')
class Path_request: class Path_request:
@@ -51,7 +51,8 @@ class Path_request:
self.spacing = params.spacing self.spacing = params.spacing
self.power = params.power self.power = params.power
self.nb_channel = params.nb_channel self.nb_channel = params.nb_channel
self.frequency = params.frequency self.f_min = params.f_min
self.f_max = params.f_max
self.format = params.format self.format = params.format
self.OSNR = params.OSNR self.OSNR = params.OSNR
self.bit_rate = params.bit_rate self.bit_rate = params.bit_rate
@@ -388,23 +389,23 @@ def propagate(path, req, equipment, show=False):
#update roadm loss in case of power sweep (power mode only) #update roadm loss in case of power sweep (power mode only)
set_roadm_loss(path, equipment, lin2db(req.power*1e3)) set_roadm_loss(path, equipment, lin2db(req.power*1e3))
si = create_input_spectral_information( si = create_input_spectral_information(
req.frequency['min'], req.roll_off, req.baud_rate, req.f_min, req.f_max, req.roll_off, req.baud_rate,
req.power, req.spacing, req.nb_channel, req.tx_osnr) req.power, req.spacing)
for el in path: for el in path:
si = el(si) si = el(si)
if show : if show :
print(el) print(el)
path[-1].update_snr(req.tx_osnr, equipment['Roadms']['default'].add_drop_osnr)
return path return path
def propagate_and_optimize_mode(path, req, equipment, show=False): def propagate_and_optimize_mode(path, req, equipment):
#update roadm loss in case of power sweep (power mode only) #update roadm loss in case of power sweep (power mode only)
set_roadm_loss(path, equipment, lin2db(req.power*1e3)) set_roadm_loss(path, equipment, lin2db(req.power*1e3))
# if mode is unknown : loops on the modes starting from the highest baudrate fiting in the # if mode is unknown : loops on the modes starting from the highest baudrate fiting in the
# spacing. TODO add a min_spacing attribute in transceivers. for now just using baudrate*1.1
# step 1: create an ordered list of modes based on baudrate # step 1: create an ordered list of modes based on baudrate
baudrate_to_explore = list(set([m['baud_rate'] for m in equipment['Transceiver'][req.tsp].mode baudrate_to_explore = list(set([m['baud_rate'] for m in equipment['Transceiver'][req.tsp].mode
if float(m['min_spacing'])<= req.spacing])) if float(m['min_spacing'])<= req.spacing]))
# TODO be carefull on limits cases if min_spacing very close to req spacing eg 50.001 50.000 # TODO be carefull on limits cases if spacing very close to req spacing eg 50.001 50.000
baudrate_to_explore = sorted(baudrate_to_explore, reverse=True) baudrate_to_explore = sorted(baudrate_to_explore, reverse=True)
if baudrate_to_explore : if baudrate_to_explore :
# at least 1 baudrate can be tested wrt spacing # at least 1 baudrate can be tested wrt spacing
@@ -418,15 +419,14 @@ def propagate_and_optimize_mode(path, req, equipment, show=False):
found_a_feasible_mode = False found_a_feasible_mode = False
# TODO : the case of roll of is not included: for now use SI one # TODO : the case of roll of is not included: for now use SI one
# TODO : if the loop in mode optimization does not have a feasible path, then bugs # TODO : if the loop in mode optimization does not have a feasible path, then bugs
for m in modes_to_explore :
si = create_input_spectral_information( si = create_input_spectral_information(
req.frequency['min'], equipment['SI']['default'].roll_off, req.f_min, req.f_max, equipment['SI']['default'].roll_off,
b, req.power, req.spacing, req.nb_channel, m['tx_osnr']) b, req.power, req.spacing)
for el in path: for el in path:
si = el(si) si = el(si)
if show : for m in modes_to_explore :
print(el)
if path[-1].snr is not None: if path[-1].snr is not None:
path[-1].update_snr(m['tx_osnr'], equipment['Roadms']['default'].add_drop_osnr)
if round(min(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : if round(min(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] :
found_a_feasible_mode = True found_a_feasible_mode = True
return path, m return path, m
@@ -869,7 +869,8 @@ def compare_reqs(req1,req2,disjlist) :
req1.spacing == req2.spacing and \ req1.spacing == req2.spacing and \
req1.power == req2.power and \ req1.power == req2.power and \
req1.nb_channel == req2.nb_channel and \ req1.nb_channel == req2.nb_channel and \
req1.frequency == req2.frequency and \ req1.f_min == req2.f_min and \
req1.f_max == req2.f_max and \
req1.format == req2.format and \ req1.format == req2.format and \
req1.OSNR == req2.OSNR and \ req1.OSNR == req2.OSNR and \
req1.roll_off == req2.roll_off and \ req1.roll_off == req2.roll_off and \

View File

@@ -120,6 +120,10 @@ def freq2wavelength(value):
""" """
return c() / value return c() / value
def snr_sum(snr, bw, snr_added, bw_added=12.5e9):
snr_added = snr_added - lin2db(bw/bw_added)
snr = -lin2db(db2lin(-snr)+db2lin(-snr_added))
return snr
def deltawl2deltaf(delta_wl, wavelength): def deltawl2deltaf(delta_wl, wavelength):
""" deltawl2deltaf(delta_wl, wavelength): """ deltawl2deltaf(delta_wl, wavelength):

View File

@@ -66,7 +66,8 @@
], ],
"Roadms":[{ "Roadms":[{
"gain_mode_default_loss": 20, "gain_mode_default_loss": 20,
"power_mode_pout_target": -20 "power_mode_pout_target": -20,
"add_drop_osnr": 100
}], }],
"SI":[{ "SI":[{
"f_min": 191.3e12, "f_min": 191.3e12,
@@ -76,11 +77,8 @@
"power_dbm": 0, "power_dbm": 0,
"power_range_db": [0,0.5,0.5], "power_range_db": [0,0.5,0.5],
"roll_off": 0.15, "roll_off": 0.15,
"OSNR": 15,
"bit_rate":100e9,
"tx_osnr": 100, "tx_osnr": 100,
"min_spacing": 50e9, "sys_margins": 0
"cost":1
}], }],
"Transceiver":[ "Transceiver":[
{ {

View File

@@ -9,7 +9,7 @@ from json import load
from gnpy.core.elements import Transceiver, Fiber, Edfa from gnpy.core.elements import Transceiver, Fiber, Edfa
from gnpy.core.utils import lin2db, db2lin from gnpy.core.utils import lin2db, db2lin
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref
from gnpy.core.equipment import load_equipment from gnpy.core.equipment import load_equipment, automatic_fmax
from gnpy.core.network import build_network, load_network, set_roadm_loss from gnpy.core.network import build_network, load_network, set_roadm_loss
from pathlib import Path from pathlib import Path
import pytest import pytest
@@ -66,7 +66,9 @@ def setup_trx():
def si(nch_and_spacing, bw): def si(nch_and_spacing, bw):
"""parametrize a channel comb with nb_channel, spacing and signal bw""" """parametrize a channel comb with nb_channel, spacing and signal bw"""
nb_channel, spacing = nch_and_spacing nb_channel, spacing = nch_and_spacing
return create_input_spectral_information(191.3e12, 0.15, bw, 1e-3, spacing, nb_channel, 100) f_min = 191.3e12
f_max = automatic_fmax(f_min, spacing, nb_channel)
return create_input_spectral_information(f_min, f_max, 0.15, bw, 1e-3, spacing)
@pytest.mark.parametrize("gain, nf_expected", [(10, 15), (15, 10), (25, 5.8)]) @pytest.mark.parametrize("gain, nf_expected", [(10, 15), (15, 10), (25, 5.8)])
def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si): def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si):

View File

@@ -74,7 +74,7 @@ def test_automaticmodefeature(net,eqpt,serv,expected_mode):
path_res_list.append(pathreq.format) path_res_list.append(pathreq.format)
total_path = propagate(total_path,pathreq,equipment, show=False) total_path = propagate(total_path,pathreq,equipment, show=False)
else: else:
total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment, show=False) total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment)
# if no baudrate satisfies spacing, no mode is returned and an empty path is returned # if no baudrate satisfies spacing, no mode is returned and an empty path is returned
# a warning is shown in the propagate_and_optimize_mode # a warning is shown in the propagate_and_optimize_mode
if mode is not None : if mode is not None :