mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2025-11-01 02:28:05 +00:00
On a ROADM, the code would previously set the same per-carrier power
regardless of the channel spectrum width. With this patch, carriers are
equalized either by their:
- absolute power (same as before),
- power spectral density (PSD).
Also, it's possible to apply a per-channel power offset (in dB) which
will be applied to a specified channel on top of the selected
power-level or PSD strategy. The same offset can be also selected
through the `--spectrum` option via the `default_pdb` parameter.
The equalization policy can be set via the ROADM model (in the equipment
config) as well as on a per-instance basis.
The PSD is defined as the absolute power over a spectral bandwidth,
where the spectral bandwidth corresponds to the actual spectrum
occupation (without any applicable guard bands), as approximated by the
symbol rate. PSD is specified in mW/GHz. As an example, for a 32 GBaud
signal at 0.01 mW, the PSD is 0.01/32 = 3.125e-4 mW/GHz.
This has some implications on the power sweep and ROADM behavior. Same
as previously (with absolute power targets), the ROADM design determines
the power set points. Target power is usually the best (highest) power
that can be supported by the ROADMs, especially the Add/Drop and express
stages' losses, with the goal to maximize the power at the booster's
input. As such, the `--power` option (or the power sweep) doesn't
manipulate with ROADM's target output power, but only with the output
power of the amplifiers. With PSD equalization, the `--power` option is
interpreted as the power of the reference channel defined in equipment
config's `SI` container, and its PSD is used for propagation. Power
sweep is interpreted in the same way, e.g.:
"SI":[{
"f_min": 191.3e12,
"baud_rate": 32e9,
"f_max":195.1e12,
"spacing": 50e9,
"power_dbm": 0,
"power_range_db": [-1,1,1],
"roll_off": 0.15,
"tx_osnr": 40,
"sys_margins": 2
}],
...and with the PSD equalization in a ROADM:
{
"uid": "roadm A",
"type": "Roadm",
"params": {
"target_psd_out_mWperGHz": 3.125e-4,
}
},
{
"uid": "edfa in roadm A to toto",
"type": "Edfa",
"type_variety": "standard_medium_gain",
"operational": {
"gain_target": 22,
"delta_p": 2,
"tilt_target": 0.0,
"out_voa": 0
}
},
then we use the power steps of the power_range_db to compute resulting
powers of each carrier out of the booster amp:
power_db = psd2powerdbm(target_psd_out_mWperGHz, baud_rate)
sweep = power_db + delta_power for delta_power in power_range_db
Assuming one 32Gbaud and one 64Gbaud carriers:
32 Gbaud 64 Gbaud
roadmA out power
(sig+ase+nli) -20dBm -17dBm
EDFA out power
range[
-1 1dBm 4dBm
0 2dBm 5dBm
1 3dBm 6dBm
]
Design case:
Design is performed based on the reference channel set defined in SI
in equipment config (independantly of equalization process):
"SI":[{
"f_min": 191.3e12,
"baud_rate": 32e9,
"f_max":195.1e12,
"spacing": 50e9,
"power_dbm": -1,
"power_range_db": [0,0,1],
"roll_off": 0.15,
"tx_osnr": 40,
"sys_margins": 2
}],
`delta_p` values of amps refer to this reference channel, but are applicable
for any baudrate during propagation, e.g.:
{
"uid": "roadm A",
"type": "Roadm",
"params": {
"target_psd_out_mWperGHz": 2.717e-4,
}
},
{
"uid": "edfa in roadm A to toto",
"type": "Edfa",
"type_variety": "standard_medium_gain",
"operational": {
"gain_target": 22,
"delta_p": 2,
"tilt_target": 0.0,
"out_voa": 0
}
},
Then the output power for a 64 Gbaud carrier will be +4 =
= lin2db(db2lin(power_dbm + delta_p)/32e9 * 64e9)
= lin2db(db2lin(power_dbm + delta_p) * 2)
= powerdbm + delta + 3 = 4 dBm
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
Change-Id: I28bcfeb72b0e74380b087762bb92ba5d39219eb3
530 lines
24 KiB
Python
530 lines
24 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
# @Author: Esther Le Rouzic
|
|
# @Date: 2019-05-22
|
|
"""
|
|
@author: esther.lerouzic
|
|
checks that new equalization option give the same output as old one:
|
|
|
|
"""
|
|
|
|
from pathlib import Path
|
|
import pytest
|
|
from numpy.testing import assert_allclose, assert_array_equal, assert_raises
|
|
from numpy import array
|
|
|
|
from gnpy.core.utils import lin2db, automatic_nch, dbm2watt, power_dbm_to_psd_mw_ghz, watt2dbm, psd2powerdbm
|
|
from gnpy.core.network import build_network
|
|
from gnpy.core.elements import Roadm
|
|
from gnpy.core.info import create_input_spectral_information, Pref, create_arbitrary_spectral_information, \
|
|
ReferenceCarrier
|
|
from gnpy.core.equipment import trx_mode_params
|
|
from gnpy.core.exceptions import ConfigurationError
|
|
from gnpy.tools.json_io import network_from_json, load_equipment, load_network, _spectrum_from_json, load_json
|
|
from gnpy.topology.request import PathRequest, compute_constrained_path, propagate
|
|
|
|
|
|
TEST_DIR = Path(__file__).parent
|
|
EQPT_FILENAME = TEST_DIR / 'data/eqpt_config.json'
|
|
NETWORK_FILENAME = TEST_DIR / 'data/testTopology_expected.json'
|
|
|
|
|
|
@pytest.mark.parametrize('degree, equalization_type, target, expected_pch_out_dbm, expected_si',
|
|
[('east edfa in Lannion_CAS to Morlaix', 'target_pch_out_db', -20, -20, [-20, -20, -20, -20, -20]),
|
|
('east edfa in Lannion_CAS to Morlaix', 'target_psd_out_mWperGHz', 5e-4, -17.9588,
|
|
[-17.9588, -16.7778, -14.9485, -16.7778, -17.9588]),
|
|
('east edfa in Lannion_CAS to Corlay', 'target_pch_out_db', -20, -16, [-16, -16, -16, -16, -16]),
|
|
('east edfa in Lannion_CAS to Corlay', 'target_psd_out_mWperGHz', 5e-4, -16, [-16, -16, -16, -16, -16]),
|
|
('east edfa in Lannion_CAS to Stbrieuc', 'target_pch_out_db', -20, -17.16699,
|
|
[-17.16698771, -15.98599459, -14.15668776, -15.98599459, -17.16698771]),
|
|
('east edfa in Lannion_CAS to Stbrieuc', 'target_psd_out_mWperGHz', 5e-4, -17.16699,
|
|
[-17.16698771, -15.98599459, -14.15668776, -15.98599459, -17.16698771])])
|
|
@pytest.mark.parametrize('delta_pdb_per_channel', [[0, 0, 0, 0, 0], [1, 3, 0, -5, 0]])
|
|
def test_equalization_combination_degree(delta_pdb_per_channel, degree, equalization_type, target,
|
|
expected_pch_out_dbm, expected_si):
|
|
"""Check that ROADM correctly computes power of thr reference channel based on different
|
|
combination of equalization for ROADM and per degree
|
|
"""
|
|
roadm_config = {
|
|
"uid": "roadm Lannion_CAS",
|
|
"params": {
|
|
"per_degree_pch_out_db": {
|
|
"east edfa in Lannion_CAS to Corlay": -16
|
|
},
|
|
"per_degree_psd_out_mWperGHz": {
|
|
"east edfa in Lannion_CAS to Stbrieuc": 6e-4
|
|
},
|
|
equalization_type: target,
|
|
"add_drop_osnr": 38,
|
|
"pmd": 0,
|
|
"pdl": 0,
|
|
"restrictions": {
|
|
"preamp_variety_list": [],
|
|
"booster_variety_list": []
|
|
}
|
|
}
|
|
}
|
|
roadm = Roadm(**roadm_config)
|
|
frequency = 191e12 + array([0, 50e9, 150e9, 225e9, 275e9])
|
|
slot_width = array([37.5e9, 50e9, 75e9, 50e9, 37.5e9])
|
|
baud_rate = array([32e9, 42e9, 64e9, 42e9, 32e9])
|
|
signal = dbm2watt(array([-20.0, -18.0, -22.0, -25.0, -16.0]))
|
|
ref_carrier = ReferenceCarrier(baud_rate=32e9)
|
|
pref = Pref(p_span0=0, p_spani=0, ref_carrier=ref_carrier)
|
|
si = create_arbitrary_spectral_information(frequency=frequency, slot_width=slot_width,
|
|
signal=signal, baud_rate=baud_rate, roll_off=0.15,
|
|
delta_pdb_per_channel=delta_pdb_per_channel,
|
|
tx_osnr=None, ref_power=pref)
|
|
to_json_before_propagation = {
|
|
'uid': 'roadm Lannion_CAS',
|
|
'type': 'Roadm',
|
|
'params': {
|
|
equalization_type: target,
|
|
'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []},
|
|
'per_degree_pch_out_db': {
|
|
'east edfa in Lannion_CAS to Corlay': -16},
|
|
"per_degree_psd_out_mWperGHz": {
|
|
"east edfa in Lannion_CAS to Stbrieuc": 6e-4
|
|
}
|
|
},
|
|
'metadata': {'location': {'latitude': 0, 'longitude': 0, 'city': None, 'region': None}}
|
|
}
|
|
assert roadm.to_json == to_json_before_propagation
|
|
si = roadm(si, degree)
|
|
assert roadm.ref_pch_out_dbm == pytest.approx(expected_pch_out_dbm, rel=1e-4)
|
|
assert_allclose(expected_si, roadm.get_per_degree_power(degree, spectral_info=si), rtol=1e-3)
|
|
|
|
|
|
def test_wrong_element_config():
|
|
"""Check that 2 equalization correcty raise a config error
|
|
"""
|
|
roadm_config = {
|
|
"uid": "roadm Brest_KLA",
|
|
"params": {
|
|
"per_degree_pch_out_db": {},
|
|
"target_pch_out_db": -20,
|
|
"target_psd_out_mWperGHz": 3.125e-4,
|
|
"add_drop_osnr": 38,
|
|
"pmd": 0,
|
|
"pdl": 0,
|
|
"restrictions": {
|
|
"preamp_variety_list": [],
|
|
"booster_variety_list": []
|
|
}
|
|
},
|
|
"metadata": {
|
|
"location": {
|
|
"city": "Brest_KLA",
|
|
"region": "RLD",
|
|
"latitude": 4.0,
|
|
"longitude": 0.0
|
|
}
|
|
}
|
|
}
|
|
with pytest.raises(ConfigurationError):
|
|
_ = Roadm(**roadm_config)
|
|
|
|
|
|
def test_merge_equalization():
|
|
"""Check that if equalization is not defined default one is correctly take and
|
|
else that it is not overwritten
|
|
"""
|
|
json_data = {
|
|
"elements": [{
|
|
"uid": "roadm Brest_KLA",
|
|
"type": "Roadm"}],
|
|
"connections": []
|
|
}
|
|
equipment = load_equipment(EQPT_FILENAME)
|
|
network = network_from_json(json_data, equipment)
|
|
roadm = [n for n in network.nodes()][0]
|
|
assert roadm.target_pch_out_dbm == -20
|
|
delattr(equipment['Roadm']['default'], 'target_pch_out_db')
|
|
setattr(equipment['Roadm']['default'], 'target_psd_out_mWperGHz', power_dbm_to_psd_mw_ghz(-20, 32e9))
|
|
# json_data is changed (type is popped from json_data with network_from_json_function). Create a new one:
|
|
json_data = {
|
|
"elements": [{
|
|
"uid": "roadm Brest_KLA",
|
|
"type": "Roadm"}],
|
|
"connections": []
|
|
}
|
|
network = network_from_json(json_data, equipment)
|
|
roadm = [n for n in network.nodes()][0]
|
|
assert roadm.target_pch_out_dbm is None
|
|
assert roadm.target_psd_out_mWperGHz == 3.125e-4
|
|
json_data = {
|
|
"elements": [{
|
|
"uid": "roadm Brest_KLA",
|
|
"type": "Roadm",
|
|
"params": {"target_pch_out_db": -18}}],
|
|
"connections": []
|
|
}
|
|
network = network_from_json(json_data, equipment)
|
|
roadm = [n for n in network.nodes()][0]
|
|
assert roadm.target_pch_out_dbm == -18
|
|
assert roadm.target_psd_out_mWperGHz is None
|
|
json_data = {
|
|
"elements": [{
|
|
"uid": "roadm Brest_KLA",
|
|
"type": "Roadm",
|
|
"params": {"target_psd_out_mWperGHz": 5e-4}}],
|
|
"connections": []
|
|
}
|
|
network = network_from_json(json_data, equipment)
|
|
roadm = [n for n in network.nodes()][0]
|
|
assert roadm.target_pch_out_dbm is None
|
|
assert roadm.target_psd_out_mWperGHz == 5e-4
|
|
|
|
|
|
@pytest.mark.parametrize('target_out, delta_pdb_per_channel, correction',
|
|
[(-20, [0, 1, 3, 0.5, -2], [0, 0, 5, 5.5, 0]),
|
|
(-20, [0, 0, 0, 0, 0], [0, 0, 2, 5, 0]),
|
|
(-20, [-2, -2, -2, -2, -2], [0, 0, 0, 3, 0]),
|
|
(-20, [0, 2, -2, -5, 4], [0, 0, 0, 0, 0]),
|
|
(-25.5, [0, 1, 3, 0.5, -2], [0, 0, 0, 0, 0]), ])
|
|
def test_low_input_power(target_out, delta_pdb_per_channel, correction):
|
|
"""check that ROADM correctly equalizes on small examples, assumes p_span_0 = 0
|
|
case of power equalisation
|
|
"""
|
|
frequency = 191e12 + array([0, 50e9, 150e9, 225e9, 275e9])
|
|
slot_width = array([37.5e9, 50e9, 75e9, 50e9, 37.5e9])
|
|
baud_rate = array([32e9, 42e9, 64e9, 42e9, 32e9])
|
|
signal = dbm2watt(array([-20.0, -18.0, -22.0, -25.0, -16.0]))
|
|
target = target_out + array(delta_pdb_per_channel)
|
|
ref_carrier = ReferenceCarrier(baud_rate=32e9)
|
|
pref = Pref(p_span0=0, p_spani=-20, ref_carrier=ref_carrier)
|
|
si = create_arbitrary_spectral_information(frequency=frequency, slot_width=slot_width,
|
|
signal=signal, baud_rate=baud_rate, roll_off=0.15,
|
|
delta_pdb_per_channel=delta_pdb_per_channel,
|
|
tx_osnr=None, ref_power=pref)
|
|
roadm_config = {
|
|
"uid": "roadm Brest_KLA",
|
|
"params": {
|
|
"per_degree_pch_out_db": {},
|
|
"target_pch_out_db": target_out,
|
|
"add_drop_osnr": 38,
|
|
"pmd": 0,
|
|
"pdl": 0,
|
|
"restrictions": {
|
|
"preamp_variety_list": [],
|
|
"booster_variety_list": []
|
|
}
|
|
},
|
|
"metadata": {
|
|
"location": {
|
|
"city": "Brest_KLA",
|
|
"region": "RLD",
|
|
"latitude": 4.0,
|
|
"longitude": 0.0
|
|
}
|
|
}
|
|
}
|
|
roadm = Roadm(**roadm_config)
|
|
si = roadm(si, 'toto')
|
|
assert (watt2dbm(si.signal) == target - correction).all()
|
|
# in other words check that if target is below input power, target is applied else power is unchanged
|
|
assert (((watt2dbm(signal) >= target) * target + (watt2dbm(signal) < target) * watt2dbm(signal))
|
|
== watt2dbm(si.signal)).all()
|
|
|
|
|
|
@pytest.mark.parametrize('target_out, delta_pdb_per_channel, correction',
|
|
[(3.125e-4,
|
|
[0, 0, 0, 0, 0],
|
|
[0, 0, 2 + lin2db(64 / 32), 5 + lin2db(42 / 32), 0]),
|
|
(3.125e-4,
|
|
[1, 3, 0, -5, 0],
|
|
[1, 1 + lin2db(42 / 32), 2 + lin2db(64 / 32), 0 + lin2db(42 / 32), 0]), ])
|
|
def test_2low_input_power(target_out, delta_pdb_per_channel, correction):
|
|
"""check that ROADM correctly equalizes on small examples, assumes p_span_0 = 0
|
|
case of PSD equalisation
|
|
"""
|
|
frequency = 191e12 + array([0, 50e9, 150e9, 225e9, 275e9])
|
|
slot_width = array([37.5e9, 50e9, 75e9, 50e9, 37.5e9])
|
|
baud_rate = array([32e9, 42e9, 64e9, 42e9, 32e9])
|
|
signal = dbm2watt(array([-20.0, -18.0, -22.0, -25.0, -16.0]))
|
|
target = psd2powerdbm(target_out, baud_rate) + array(delta_pdb_per_channel)
|
|
ref_carrier = ReferenceCarrier(baud_rate=32e9)
|
|
pref = Pref(p_span0=0, p_spani=-20, ref_carrier=ref_carrier)
|
|
si = create_arbitrary_spectral_information(frequency=frequency, slot_width=slot_width,
|
|
signal=signal, baud_rate=baud_rate, roll_off=0.15,
|
|
delta_pdb_per_channel=delta_pdb_per_channel,
|
|
tx_osnr=None, ref_power=pref)
|
|
roadm_config = {
|
|
"uid": "roadm Brest_KLA",
|
|
"params": {
|
|
"per_degree_pch_out_db": {},
|
|
"target_psd_out_mWperGHz": target_out,
|
|
"add_drop_osnr": 38,
|
|
"pmd": 0,
|
|
"pdl": 0,
|
|
"restrictions": {
|
|
"preamp_variety_list": [],
|
|
"booster_variety_list": []
|
|
}
|
|
},
|
|
"metadata": {
|
|
"location": {
|
|
"city": "Brest_KLA",
|
|
"region": "RLD",
|
|
"latitude": 4.0,
|
|
"longitude": 0.0
|
|
}
|
|
}
|
|
}
|
|
roadm = Roadm(**roadm_config)
|
|
si = roadm(si, 'toto')
|
|
assert (watt2dbm(si.signal) == target - correction).all()
|
|
|
|
|
|
def net_setup(equipment):
|
|
""" common setup for tests: builds network, equipment and oms only once
|
|
"""
|
|
network = load_network(NETWORK_FILENAME, equipment)
|
|
spectrum = equipment['SI']['default']
|
|
p_db = spectrum.power_dbm
|
|
p_total_db = p_db + lin2db(automatic_nch(spectrum.f_min, spectrum.f_max, spectrum.spacing))
|
|
build_network(network, equipment, p_db, p_total_db)
|
|
return network
|
|
|
|
|
|
def create_voyager_req(equipment, source, dest, bidir, nodes_list, loose_list, mode, spacing, power_dbm):
|
|
""" create the usual request list according to parameters
|
|
"""
|
|
params = {'request_id': 'test_request',
|
|
'source': source,
|
|
'bidir': bidir,
|
|
'destination': dest,
|
|
'trx_type': 'Voyager',
|
|
'trx_mode': mode,
|
|
'format': mode,
|
|
'spacing': spacing,
|
|
'nodes_list': nodes_list,
|
|
'loose_list': loose_list,
|
|
'path_bandwidth': 100.0e9,
|
|
'effective_freq_slot': None}
|
|
trx_params = trx_mode_params(equipment, params['trx_type'], params['trx_mode'], True)
|
|
params.update(trx_params)
|
|
params['power'] = dbm2watt(power_dbm) if power_dbm else dbm2watt(equipment['SI']['default'].power_dbm)
|
|
f_min = params['f_min']
|
|
f_max_from_si = params['f_max']
|
|
params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing'])
|
|
return PathRequest(**params)
|
|
|
|
|
|
@pytest.mark.parametrize('power_dbm', [0, 1, -2, None])
|
|
@pytest.mark.parametrize('mode, slot_width', (['mode 1', 50e9], ['mode 2', 75e9]))
|
|
def test_initial_spectrum(mode, slot_width, power_dbm):
|
|
""" checks that propagation using the user defined spectrum identical to SI, gives same result as SI
|
|
"""
|
|
# first propagate without any req.initial_spectrum attribute
|
|
equipment = load_equipment(EQPT_FILENAME)
|
|
req = create_voyager_req(equipment, 'trx Brest_KLA', 'trx Vannes_KBE', False, ['trx Vannes_KBE'], ['STRICT'],
|
|
mode, slot_width, power_dbm)
|
|
network = net_setup(equipment)
|
|
path = compute_constrained_path(network, req)
|
|
infos_expected = propagate(path, req, equipment)
|
|
# then creates req.initial_spectrum attribute exactly corresponding to -spectrum option files
|
|
temp = [{
|
|
"f_min": 191.35e12 + slot_width,
|
|
"f_max": 196.15e12 - slot_width,
|
|
"baud_rate": req.baud_rate,
|
|
"slot_width": slot_width,
|
|
"roll_off": 0.15,
|
|
"tx_osnr": 40
|
|
}]
|
|
req.initial_spectrum = _spectrum_from_json(temp)
|
|
infos_actual = propagate(path, req, equipment)
|
|
print(infos_actual.frequency[0], infos_actual.frequency[-1])
|
|
print(infos_expected.frequency[0], infos_expected.frequency[-1])
|
|
|
|
assert_array_equal(infos_expected.frequency, infos_actual.frequency)
|
|
assert_array_equal(infos_expected.baud_rate, infos_actual.baud_rate)
|
|
assert_array_equal(infos_expected.slot_width, infos_actual.slot_width)
|
|
assert_array_equal(infos_expected.signal, infos_actual.signal)
|
|
assert_array_equal(infos_expected.nli, infos_actual.nli)
|
|
assert_array_equal(infos_expected.ase, infos_actual.ase)
|
|
assert_array_equal(infos_expected.roll_off, infos_actual.roll_off)
|
|
assert_array_equal(infos_expected.chromatic_dispersion, infos_actual.chromatic_dispersion)
|
|
assert_array_equal(infos_expected.pmd, infos_actual.pmd)
|
|
assert_array_equal(infos_expected.channel_number, infos_actual.channel_number)
|
|
assert_array_equal(infos_expected.number_of_channels, infos_actual.number_of_channels)
|
|
|
|
|
|
def test_initial_spectrum_not_identical():
|
|
""" checks that user defined spectrum overrides spectrum defined in SI
|
|
"""
|
|
# first propagate without any req.initial_spectrum attribute
|
|
equipment = load_equipment(EQPT_FILENAME)
|
|
req = create_voyager_req(equipment, 'trx Brest_KLA', 'trx Vannes_KBE', False, ['trx Vannes_KBE'], ['STRICT'],
|
|
'mode 1', 50e9, 0)
|
|
network = net_setup(equipment)
|
|
path = compute_constrained_path(network, req)
|
|
infos_expected = propagate(path, req, equipment)
|
|
# then creates req.initial_spectrum attribute exactly corresponding to -spectrum option files
|
|
temp = [{
|
|
"f_min": 191.4e12, # align f_min , f_max on Voyager f_min, f_mix and not SI !
|
|
"f_max": 196.1e12,
|
|
"baud_rate": 40e9,
|
|
"slot_width": 62.5e9,
|
|
"roll_off": 0.15,
|
|
"tx_osnr": 40
|
|
}]
|
|
req.initial_spectrum = _spectrum_from_json(temp)
|
|
infos_actual = propagate(path, req, equipment)
|
|
assert_raises(AssertionError, assert_array_equal, infos_expected.frequency, infos_actual.frequency)
|
|
assert_raises(AssertionError, assert_array_equal, infos_expected.baud_rate, infos_actual.baud_rate)
|
|
assert_raises(AssertionError, assert_array_equal, infos_expected.slot_width, infos_actual.slot_width)
|
|
assert_raises(AssertionError, assert_array_equal, infos_expected.signal, infos_actual.signal)
|
|
assert_raises(AssertionError, assert_array_equal, infos_expected.nli, infos_actual.nli)
|
|
assert_raises(AssertionError, assert_array_equal, infos_expected.ase, infos_actual.ase)
|
|
assert_raises(AssertionError, assert_array_equal, infos_expected.channel_number, infos_actual.channel_number)
|
|
assert_raises(AssertionError, assert_array_equal, infos_expected.number_of_channels, infos_actual.number_of_channels)
|
|
|
|
|
|
@pytest.mark.parametrize('with_initial_spectrum', [None, 0, +2, -0.5])
|
|
def test_target_psd_out_mwperghz(with_initial_spectrum):
|
|
""" checks that if target_psd_out_mWperGHz is defined, it is used as equalization, and it gives same result if
|
|
computed target is the same
|
|
"""
|
|
equipment = load_equipment(EQPT_FILENAME)
|
|
network = net_setup(equipment)
|
|
req = create_voyager_req(equipment, 'trx Brest_KLA', 'trx Vannes_KBE', False, ['trx Vannes_KBE'], ['STRICT'],
|
|
'mode 1', 50e9, with_initial_spectrum)
|
|
if with_initial_spectrum:
|
|
temp = [{
|
|
"f_min": 191.35e12, # align f_min , f_max on Voyager f_min, f_max and not SI !
|
|
"f_max": 196.05e12,
|
|
"baud_rate": req.baud_rate,
|
|
"slot_width": 50e9,
|
|
"roll_off": 0.15,
|
|
"tx_osnr": 40
|
|
}]
|
|
req.initial_spectrum = _spectrum_from_json(temp)
|
|
path = compute_constrained_path(network, req)
|
|
infos_expected = propagate(path, req, equipment)
|
|
# change default equalization to power spectral density
|
|
delattr(equipment['Roadm']['default'], 'target_pch_out_db')
|
|
setattr(equipment['Roadm']['default'], 'target_psd_out_mWperGHz', power_dbm_to_psd_mw_ghz(-20, 32e9))
|
|
# create a second instance with this roadm settings,
|
|
network2 = net_setup(equipment)
|
|
path2 = compute_constrained_path(network2, req)
|
|
infos_actual = propagate(path2, req, equipment)
|
|
# since baudrate is the same, resulting propagation should be the same as for power equalization
|
|
assert_array_equal(infos_expected.baud_rate, infos_actual.baud_rate)
|
|
assert_array_equal(infos_expected.slot_width, infos_actual.slot_width)
|
|
assert_array_equal(infos_expected.signal, infos_actual.signal)
|
|
assert_array_equal(infos_expected.nli, infos_actual.nli)
|
|
assert_array_equal(infos_expected.ase, infos_actual.ase)
|
|
assert_array_equal(infos_expected.roll_off, infos_actual.roll_off)
|
|
assert_array_equal(infos_expected.chromatic_dispersion, infos_actual.chromatic_dispersion)
|
|
assert_array_equal(infos_expected.pmd, infos_actual.pmd)
|
|
assert_array_equal(infos_expected.channel_number, infos_actual.channel_number)
|
|
assert_array_equal(infos_expected.number_of_channels, infos_actual.number_of_channels)
|
|
|
|
|
|
def ref_network():
|
|
""" Create a network instance with a instance of propagated path
|
|
"""
|
|
equipment = load_equipment(EQPT_FILENAME)
|
|
network = net_setup(equipment)
|
|
req0 = create_voyager_req(equipment, 'trx Brest_KLA', 'trx Vannes_KBE', False, ['trx Vannes_KBE'], ['STRICT'],
|
|
'mode 1', 50e9, 0)
|
|
path0 = compute_constrained_path(network, req0)
|
|
_ = propagate(path0, req0, equipment)
|
|
return network
|
|
|
|
|
|
@pytest.mark.parametrize('deltap', [0, +1.2, -0.5])
|
|
def test_target_psd_out_mwperghz_deltap(deltap):
|
|
""" checks that if target_psd_out_mWperGHz is defined, delta_p of amps is correctly updated
|
|
Power over 1.2dBm saturate amp with this test: TODO add a test on this saturation
|
|
"""
|
|
equipment = load_equipment(EQPT_FILENAME)
|
|
network = net_setup(equipment)
|
|
req = create_voyager_req(equipment, 'trx Brest_KLA', 'trx Vannes_KBE', False, ['trx Vannes_KBE'], ['STRICT'],
|
|
'mode 1', 50e9, deltap)
|
|
temp = [{
|
|
"f_min": 191.35e12, # align f_min , f_max on Voyager f_min, f_mix and not SI !
|
|
"f_max": 196.05e12,
|
|
"baud_rate": req.baud_rate,
|
|
"slot_width": 50e9,
|
|
"roll_off": 0.15,
|
|
"tx_osnr": 40
|
|
}]
|
|
req.initial_spectrum = _spectrum_from_json(temp)
|
|
path = compute_constrained_path(network, req)
|
|
_ = propagate(path, req, equipment)
|
|
# check that gain of booster is changed accordingly whereas gain of preamp and ila is not (no saturation case)
|
|
boosters = ['east edfa in Brest_KLA to Quimper', 'east edfa in Lorient_KMA to Vannes_KBE']
|
|
ila_preamps = ['east edfa in Quimper to Lorient_KMA', 'west edfa in Lorient_KMA to Quimper',
|
|
'west edfa in Vannes_KBE to Lorient_KMA']
|
|
for amp in boosters + ila_preamps:
|
|
expected_amp = next(n for n in ref_network() if n.uid == amp)
|
|
actual_amp = next(n for n in network.nodes() if n.uid == amp)
|
|
expected_gain = expected_amp.pout_db - expected_amp.pin_db
|
|
actual_gain = actual_amp.pout_db - actual_amp.pin_db
|
|
print(actual_amp)
|
|
if amp in boosters:
|
|
assert expected_gain + deltap == pytest.approx(actual_gain, rel=1e-3)
|
|
if amp in ila_preamps:
|
|
assert expected_gain == pytest.approx(actual_gain, rel=1e-3)
|
|
|
|
|
|
@pytest.mark.parametrize('case', ['SI', 'nodes'])
|
|
@pytest.mark.parametrize('deltap', [0, +2, -0.5])
|
|
@pytest.mark.parametrize('target', [-20, -21, -18])
|
|
@pytest.mark.parametrize('mode, slot_width', (['mode 1', 50e9], ['mode 2', 75e9]))
|
|
def test_equalization(case, deltap, target, mode, slot_width):
|
|
"""check that power target on roadm is correct for these cases; check on booster
|
|
- SI : target_pch_out_db / target_psd_out_mWperGHz
|
|
- node : target_pch_out_db / target_psd_out_mWperGHz
|
|
- per degree : target_pch_out_db / target_psd_out_mWperGHz
|
|
for these cases with and without power from user
|
|
"""
|
|
equipment = load_equipment(EQPT_FILENAME)
|
|
setattr(equipment['Roadm']['default'], 'target_pch_out_db', target)
|
|
req = create_voyager_req(equipment, 'trx Brest_KLA', 'trx Rennes_STA', False,
|
|
['east edfa in Brest_KLA to Quimper', 'roadm Lannion_CAS', 'trx Rennes_STA'],
|
|
['STRICT', 'STRICT', 'STRICT'],
|
|
mode, slot_width, deltap)
|
|
roadms = ['roadm Brest_KLA', 'roadm Lorient_KMA', 'roadm Lannion_CAS', 'roadm Rennes_STA']
|
|
# degree = {'roadm Brest_KLA': 'east edfa in Brest_KLA to Quimper',
|
|
# 'roadm Lorient_KMA': 'east edfa in Lorient_KMA to Loudeac'}
|
|
# boosters = ['east edfa in Brest_KLA to Quimper', 'east edfa in Lorient_KMA to Loudeac',
|
|
# 'east edfa in Lannion_CAS to Stbrieuc']
|
|
target_psd = power_dbm_to_psd_mw_ghz(target, 32e9)
|
|
ref = ReferenceCarrier(baud_rate=32e9)
|
|
if case == 'SI':
|
|
delattr(equipment['Roadm']['default'], 'target_pch_out_db')
|
|
setattr(equipment['Roadm']['default'], 'target_psd_out_mWperGHz',
|
|
target_psd)
|
|
network = net_setup(equipment)
|
|
elif case == 'nodes':
|
|
json_data = load_json(NETWORK_FILENAME)
|
|
for el in json_data['elements']:
|
|
if el['uid'] in roadms:
|
|
el['params'] = {'target_psd_out_mWperGHz': target_psd}
|
|
network = network_from_json(json_data, equipment)
|
|
spectrum = equipment['SI']['default']
|
|
p_db = spectrum.power_dbm
|
|
p_total_db = p_db + lin2db(automatic_nch(spectrum.f_min, spectrum.f_max, spectrum.spacing))
|
|
build_network(network, equipment, p_db, p_total_db)
|
|
# check that nodes not in roadms have target_pch_out_db not None
|
|
pw_roadms = [r for r in network.nodes() if r.uid not in roadms and isinstance(r, Roadm)]
|
|
for roadm in pw_roadms:
|
|
assert roadm.target_psd_out_mWperGHz is None
|
|
assert roadm.target_pch_out_dbm == target
|
|
path = compute_constrained_path(network, req)
|
|
si = create_input_spectral_information(
|
|
f_min=req.f_min, f_max=req.f_max, roll_off=req.roll_off, baud_rate=req.baud_rate, power=req.power,
|
|
spacing=req.spacing, tx_osnr=req.tx_osnr, ref_carrier=ref)
|
|
for i, el in enumerate(path):
|
|
if isinstance(el, Roadm):
|
|
si = el(si, degree=path[i + 1].uid)
|
|
if case in ['SI', 'nodes', 'degrees']:
|
|
assert_allclose(power_dbm_to_psd_mw_ghz(watt2dbm(si.signal + si.ase + si.nli), si.baud_rate),
|
|
target_psd, rtol=1e-3)
|
|
else:
|
|
si = el(si)
|
|
print(el.uid)
|