mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2025-11-02 02:57:52 +00:00
Merge pull request #215 from Orange-OpenSource/roadm_equalization
Roadm equalization
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
{ "Edfa":[{
|
||||
"type_variety": "high_detail_model_example",
|
||||
"type_def": "advanced_model",
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
},
|
||||
{
|
||||
"type_variety": "operator_model_example",
|
||||
"type_def": "variable_gain",
|
||||
@@ -37,6 +38,17 @@
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "std_high_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 35,
|
||||
"gain_min": 25,
|
||||
"p_max": 21,
|
||||
"nf_min": 5.5,
|
||||
"nf_max": 7,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "std_medium_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 26,
|
||||
@@ -59,6 +71,17 @@
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "high_power",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 16,
|
||||
"gain_min": 8,
|
||||
"p_max": 25,
|
||||
"nf_min": 9,
|
||||
"nf_max": 15,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "std_fixed_gain",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 21,
|
||||
@@ -66,7 +89,50 @@
|
||||
"p_max": 21,
|
||||
"nf0": 5.5,
|
||||
"allowed_for_design": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"type_variety": "4pumps_raman",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 12,
|
||||
"gain_min": 12,
|
||||
"p_max": 21,
|
||||
"nf0": -1,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "hybrid_4pumps_lowgain",
|
||||
"type_def": "dual_stage",
|
||||
"raman": true,
|
||||
"gain_min": 25,
|
||||
"preamp_variety": "4pumps_raman",
|
||||
"booster_variety": "std_low_gain",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "hybrid_4pumps_mediumgain",
|
||||
"type_def": "dual_stage",
|
||||
"raman": true,
|
||||
"gain_min": 25,
|
||||
"preamp_variety": "4pumps_raman",
|
||||
"booster_variety": "std_medium_gain",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "medium+low_gain",
|
||||
"type_def": "dual_stage",
|
||||
"gain_min": 25,
|
||||
"preamp_variety": "std_medium_gain",
|
||||
"booster_variety": "std_low_gain",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "medium+high_power",
|
||||
"type_def": "dual_stage",
|
||||
"gain_min": 25,
|
||||
"preamp_variety": "std_medium_gain",
|
||||
"booster_variety": "high_power",
|
||||
"allowed_for_design": false
|
||||
}
|
||||
],
|
||||
"Fiber":[{
|
||||
"type_variety": "SSMF",
|
||||
@@ -84,9 +150,11 @@
|
||||
"gamma": 0.000843
|
||||
}
|
||||
],
|
||||
"Spans":[{
|
||||
"Span":[{
|
||||
"power_mode":true,
|
||||
"delta_power_range_db": [0,0,0.5],
|
||||
"delta_power_range_db": [-2,3,0.5],
|
||||
"max_fiber_lineic_loss_for_raman": 0.25,
|
||||
"target_extended_gain": 2.5,
|
||||
"max_length": 150,
|
||||
"length_units": "km",
|
||||
"max_loss": 28,
|
||||
@@ -96,10 +164,13 @@
|
||||
"con_out": 0
|
||||
}
|
||||
],
|
||||
"Roadms":[{
|
||||
"gain_mode_default_loss": 20,
|
||||
"power_mode_pout_target": -20,
|
||||
"add_drop_osnr": 38
|
||||
"Roadm":[{
|
||||
"target_pch_out_db": -20,
|
||||
"add_drop_osnr": 38,
|
||||
"restrictions": {
|
||||
"preamp_variety_list":["low_gain_preamp", "high_gain_preamp"],
|
||||
"booster_variety_list":["std_booster"]
|
||||
}
|
||||
}],
|
||||
"SI":[{
|
||||
"f_min": 191.3e12,
|
||||
@@ -107,10 +178,10 @@
|
||||
"f_max":195.1e12,
|
||||
"spacing": 50e9,
|
||||
"power_dbm": 0,
|
||||
"power_range_db": [0,0,0.5],
|
||||
"power_range_db": [0,0,1],
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"sys_margins": 0
|
||||
"sys_margins": 2
|
||||
}],
|
||||
"Transceiver":[
|
||||
{
|
||||
|
||||
@@ -623,31 +623,473 @@
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Lannion_CAS to Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Corlay to Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Loudeac to Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Lorient_KMA to Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Lannion_CAS to Stbrieuc",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Stbrieuc to Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Stbrieuc",
|
||||
"region": "RLD",
|
||||
"latitude": 1.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Lannion_CAS to Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Lorient_KMA to Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Vannes_KBE to Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 4.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Rennes_STA to Stbrieuc",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Brest_KLA to Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 4.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Lannion_CAS to Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Corlay to Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Loudeac to Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Lorient_KMA to Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Lannion_CAS to Stbrieuc",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Stbrieuc to Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Stbrieuc",
|
||||
"region": "RLD",
|
||||
"latitude": 1.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Lannion_CAS to Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Lorient_KMA to Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Vannes_KBE to Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 4.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Rennes_STA to Stbrieuc",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Brest_KLA to Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 4.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "east edfa in Lannion_CAS to Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Lannion_CAS to Corlay",
|
||||
"to_node": "fiber (Lannion_CAS → Corlay)-F061"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay → Lannion_CAS)-F061",
|
||||
"to_node": "west edfa in Lannion_CAS to Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Lannion_CAS to Corlay",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "east edfa in Lannion_CAS to Stbrieuc"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Lannion_CAS to Stbrieuc",
|
||||
"to_node": "fiber (Lannion_CAS → Stbrieuc)-F056"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc → Lannion_CAS)-F056",
|
||||
"to_node": "west edfa in Lannion_CAS to Stbrieuc"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Lannion_CAS to Stbrieuc",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "east edfa in Lannion_CAS to Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Lannion_CAS to Morlaix",
|
||||
"to_node": "fiber (Lannion_CAS → Morlaix)-F059"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix → Lannion_CAS)-F059",
|
||||
"to_node": "west edfa in Lannion_CAS to Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Lannion_CAS to Morlaix",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
@@ -684,18 +1126,34 @@
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "east edfa in Lorient_KMA to Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Lorient_KMA to Loudeac",
|
||||
"to_node": "fiber (Lorient_KMA → Loudeac)-F054"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac → Lorient_KMA)-F054",
|
||||
"to_node": "west edfa in Lorient_KMA to Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Lorient_KMA to Loudeac",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "east edfa in Lorient_KMA to Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Lorient_KMA to Vannes_KBE",
|
||||
"to_node": "fiber (Lorient_KMA → Vannes_KBE)-F055"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Vannes_KBE → Lorient_KMA)-F055",
|
||||
"to_node": "west edfa in Lorient_KMA to Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Lorient_KMA to Vannes_KBE",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
@@ -708,10 +1166,18 @@
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "east edfa in Vannes_KBE to Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Vannes_KBE to Lorient_KMA",
|
||||
"to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA → Vannes_KBE)-F055",
|
||||
"to_node": "west edfa in Vannes_KBE to Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Vannes_KBE to Lorient_KMA",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
@@ -724,18 +1190,34 @@
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS → Stbrieuc)-F056",
|
||||
"to_node": "east edfa in Stbrieuc to Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Stbrieuc to Rennes_STA",
|
||||
"to_node": "fiber (Stbrieuc → Rennes_STA)-F057"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Rennes_STA → Stbrieuc)-F057",
|
||||
"to_node": "west edfa in Stbrieuc to Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Stbrieuc to Rennes_STA",
|
||||
"to_node": "fiber (Stbrieuc → Lannion_CAS)-F056"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "east edfa in Rennes_STA to Stbrieuc"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Rennes_STA to Stbrieuc",
|
||||
"to_node": "fiber (Rennes_STA → Stbrieuc)-F057"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc → Rennes_STA)-F057",
|
||||
"to_node": "west edfa in Rennes_STA to Stbrieuc"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Rennes_STA to Stbrieuc",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
@@ -764,10 +1246,18 @@
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "east edfa in Brest_KLA to Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Brest_KLA to Morlaix",
|
||||
"to_node": "fiber (Brest_KLA → Morlaix)-F060"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix → Brest_KLA)-F060",
|
||||
"to_node": "west edfa in Brest_KLA to Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Brest_KLA to Morlaix",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
|
||||
Binary file not shown.
@@ -23,7 +23,7 @@ from networkx import (draw_networkx_nodes, draw_networkx_edges,
|
||||
from numpy import mean
|
||||
from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element
|
||||
from gnpy.core.utils import load_json
|
||||
from gnpy.core.network import load_network, build_network, set_roadm_loss, save_network
|
||||
from gnpy.core.network import load_network, build_network, save_network
|
||||
from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch, automatic_spacing
|
||||
from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused, Fiber
|
||||
from gnpy.core.utils import db2lin, lin2db
|
||||
|
||||
@@ -106,8 +106,8 @@ def main(network, equipment, source, destination, req = None):
|
||||
}]
|
||||
result_dicts.update({'network': network_data})
|
||||
design_data = [{
|
||||
'power_mode' : equipment['Spans']['default'].power_mode,
|
||||
'span_power_range' : equipment['Spans']['default'].delta_power_range_db,
|
||||
'power_mode' : equipment['Span']['default'].power_mode,
|
||||
'span_power_range' : equipment['Span']['default'].delta_power_range_db,
|
||||
'design_pch' : equipment['SI']['default'].power_dbm,
|
||||
'baud_rate' : equipment['SI']['default'].baud_rate
|
||||
}]
|
||||
@@ -115,9 +115,9 @@ def main(network, equipment, source, destination, req = None):
|
||||
simulation_data = []
|
||||
result_dicts.update({'simulation results': simulation_data})
|
||||
|
||||
power_mode = equipment['Spans']['default'].power_mode
|
||||
power_mode = equipment['Span']['default'].power_mode
|
||||
print('\n'.join([f'Power mode is set to {power_mode}',
|
||||
f'=> it can be modified in eqpt_config.json - Spans']))
|
||||
f'=> it can be modified in eqpt_config.json - Span']))
|
||||
|
||||
pref_ch_db = lin2db(req.power*1e3) #reference channel power / span (SL=20dB)
|
||||
pref_total_db = pref_ch_db + lin2db(req.nb_channel) #reference total power / span (SL=20dB)
|
||||
@@ -136,25 +136,44 @@ def main(network, equipment, source, destination, req = None):
|
||||
print('invalid power range definition in eqpt_config, should be power_range_db: [lower, upper, step]')
|
||||
power_range = [0]
|
||||
|
||||
if not power_mode:
|
||||
#power cannot be changed in gain mode
|
||||
power_range = [0]
|
||||
for dp_db in power_range:
|
||||
req.power = db2lin(pref_ch_db + dp_db)*1e-3
|
||||
print(f'\nPropagating with input power = {lin2db(req.power*1e3):.2f}dBm :')
|
||||
if power_mode:
|
||||
print(f'\nPropagating with input power = {lin2db(req.power*1e3):.2f}dBm :')
|
||||
else:
|
||||
print(f'\nPropagating in gain mode: power cannot be set manually')
|
||||
infos = propagate2(path, req, equipment, show=len(power_range)==1)
|
||||
print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f}dBm :')
|
||||
if power_mode:
|
||||
print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f}dBm :')
|
||||
else:
|
||||
print(f'\nTransmission results:')
|
||||
#info message in gain mode
|
||||
print(destination)
|
||||
|
||||
#print(f'\n !!!!!!!!!!!!!!!!! TEST POINT !!!!!!!!!!!!!!!!!!!!!')
|
||||
#print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}')
|
||||
# => use "in" or "out" parameter
|
||||
# => use "nli" or "ase" or "signal" or "total" parameter
|
||||
|
||||
simulation_data.append({
|
||||
'Pch_dBm' : pref_ch_db + dp_db,
|
||||
'OSNR_ASE_0.1nm' : round(mean(destination.osnr_ase_01nm),2),
|
||||
'OSNR_ASE_signal_bw' : round(mean(destination.osnr_ase),2),
|
||||
'SNR_nli_signal_bw' : round(mean(destination.osnr_nli),2),
|
||||
'SNR_total_signal_bw' : round(mean(destination.snr),2)
|
||||
})
|
||||
# => use "nli" or "ase" or "signal" or "total" parameter
|
||||
if power_mode:
|
||||
simulation_data.append({
|
||||
'Pch_dBm' : pref_ch_db + dp_db,
|
||||
'OSNR_ASE_0.1nm' : round(mean(destination.osnr_ase_01nm),2),
|
||||
'OSNR_ASE_signal_bw' : round(mean(destination.osnr_ase),2),
|
||||
'SNR_nli_signal_bw' : round(mean(destination.osnr_nli),2),
|
||||
'SNR_total_signal_bw' : round(mean(destination.snr),2)
|
||||
})
|
||||
else:
|
||||
simulation_data.append({
|
||||
'gain_mode' : 'power canot be set',
|
||||
'OSNR_ASE_0.1nm' : round(mean(destination.osnr_ase_01nm),2),
|
||||
'OSNR_ASE_signal_bw' : round(mean(destination.osnr_ase),2),
|
||||
'SNR_nli_signal_bw' : round(mean(destination.osnr_nli),2),
|
||||
'SNR_total_signal_bw' : round(mean(destination.snr),2)
|
||||
})
|
||||
#info message in gain mode
|
||||
write_csv(result_dicts, 'simulation_result.csv')
|
||||
return path, infos
|
||||
|
||||
|
||||
@@ -111,11 +111,12 @@ class Eqpt(object):
|
||||
{
|
||||
'from_city': '',
|
||||
'to_city': '',
|
||||
'east_amp_type': '',
|
||||
'east_att_in': 0,
|
||||
'east_amp_gain': 0,
|
||||
'east_tilt': 0,
|
||||
'east_att_out': 0
|
||||
'east_amp_type': '',
|
||||
'east_att_in': 0,
|
||||
'east_amp_gain': None,
|
||||
'east_amp_dp': None,
|
||||
'east_tilt': 0,
|
||||
'east_att_out': None
|
||||
}
|
||||
|
||||
|
||||
@@ -163,8 +164,8 @@ def parse_headers(my_sheet, input_headers_dict, headers, start_line, slice_in):
|
||||
slice_out = read_slice(my_sheet, start_line+iteration, slice_in, h0)
|
||||
iteration += 1
|
||||
if slice_out == (-1, -1):
|
||||
if h0 == 'east':
|
||||
print(f'\x1b[1;31;40m'+f'CRITICAL: missing _east_ header above other headers (hierarchical) _ ABORT'+ '\x1b[0m')
|
||||
if h0 in ('east', 'Node A', 'Node Z', 'City') :
|
||||
print(f'\x1b[1;31;40m'+f'CRITICAL: missing _{h0}_ header: EXECUTION ENDS'+ '\x1b[0m')
|
||||
exit()
|
||||
else:
|
||||
print(f'missing header {h0}')
|
||||
@@ -344,6 +345,7 @@ def convert_file(input_filename, names_matching=False, filter_region=[]):
|
||||
'type': 'Edfa',
|
||||
'type_variety': e.east_amp_type,
|
||||
'operational': {'gain_target': e.east_amp_gain,
|
||||
'delta_p': e.east_amp_dp,
|
||||
'tilt_target': e.east_tilt,
|
||||
'out_voa' : e.east_att_out}
|
||||
}
|
||||
@@ -356,6 +358,7 @@ def convert_file(input_filename, names_matching=False, filter_region=[]):
|
||||
'type': 'Edfa',
|
||||
'type_variety': e.west_amp_type,
|
||||
'operational': {'gain_target': e.west_amp_gain,
|
||||
'delta_p': e.west_amp_dp,
|
||||
'tilt_target': e.west_tilt,
|
||||
'out_voa' : e.west_att_out}
|
||||
}
|
||||
@@ -420,6 +423,7 @@ def parse_excel(input_filename):
|
||||
'amp type': 'east_amp_type',
|
||||
'att_in': 'east_att_in',
|
||||
'amp gain': 'east_amp_gain',
|
||||
'delta p': 'east_amp_dp',
|
||||
'tilt': 'east_tilt',
|
||||
'att_out': 'east_att_out'
|
||||
},
|
||||
@@ -427,6 +431,7 @@ def parse_excel(input_filename):
|
||||
'amp type': 'west_amp_type',
|
||||
'att_in': 'west_att_in',
|
||||
'amp gain': 'west_amp_gain',
|
||||
'delta p': 'west_amp_dp',
|
||||
'tilt': 'west_tilt',
|
||||
'att_out': 'west_att_out'
|
||||
}
|
||||
@@ -566,12 +571,12 @@ def midpoint(city_a, city_b):
|
||||
#output_json_file_name = 'coronet_conus_example.json'
|
||||
#TODO get column size automatically from tupple size
|
||||
|
||||
NODES_COLUMN = 7
|
||||
NODES_COLUMN = 8
|
||||
NODES_LINE = 4
|
||||
LINKS_COLUMN = 16
|
||||
LINKS_LINE = 3
|
||||
EQPTS_LINE = 3
|
||||
EQPTS_COLUMN = 12
|
||||
EQPTS_COLUMN = 14
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('workbook', nargs='?', type=Path , default='meshTopologyExampleV2.xls')
|
||||
parser.add_argument('-f', '--filter-region', action='append', default=[])
|
||||
|
||||
@@ -41,7 +41,6 @@ class Transceiver(Node):
|
||||
with errstate(divide='ignore'):
|
||||
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]
|
||||
@@ -112,25 +111,21 @@ class Transceiver(Node):
|
||||
self._calc_snr(spectral_info)
|
||||
return spectral_info
|
||||
|
||||
RoadmParams = namedtuple('RoadmParams', 'loss')
|
||||
RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr')
|
||||
|
||||
class Roadm(Node):
|
||||
def __init__(self, *args, params=None, **kwargs):
|
||||
if params is None:
|
||||
# default loss value if not mentioned in loaded network json
|
||||
params = {'loss':None}
|
||||
def __init__(self, *args, params, **kwargs):
|
||||
super().__init__(*args, params=RoadmParams(**params), **kwargs)
|
||||
self.loss = self.params.loss
|
||||
self.target_pch_out_db = None #set in Networks.py by def set_roadm_loss
|
||||
self.effective_pch_out_db = None
|
||||
self.effective_loss = None #set in self.propagate
|
||||
self.loss = 0 #auto-design interest
|
||||
self.effective_loss = None
|
||||
self.effective_pch_out_db = self.params.target_pch_out_db
|
||||
self.passive = True
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
return {'uid' : self.uid,
|
||||
'type' : type(self).__name__,
|
||||
'params' : {'loss' : self.loss},
|
||||
'params' : {'target_pch_out_db' : self.effective_pch_out_db},
|
||||
'metadata' : {
|
||||
'location': self.metadata['location']._asdict()
|
||||
}
|
||||
@@ -141,26 +136,25 @@ class Roadm(Node):
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join([f'{type(self).__name__} {self.uid}',
|
||||
f' loss (dB): {self.effective_loss:.2f}',
|
||||
f' pch out (dBm): {self.effective_pch_out_db!r}'])
|
||||
f' effective loss (dB): {self.effective_loss:.2f}',
|
||||
f' pch out (dBm): {self.effective_pch_out_db!r}'])
|
||||
|
||||
def propagate(self, pref, *carriers):
|
||||
#pin_target and loss are read from eqpt_config.json['Roadm']
|
||||
#all ingress channels in xpress are set to this power level
|
||||
#but add channels are not, so we define an effective loss
|
||||
#in the case of add channels
|
||||
if self.target_pch_out_db:
|
||||
self.effective_loss = pref.pi - self.target_pch_out_db
|
||||
else:
|
||||
self.effective_loss = self.loss
|
||||
self.effective_pch_out_db = pref.pi - self.effective_loss
|
||||
attenuation = db2lin(self.effective_loss)
|
||||
|
||||
for carrier in carriers:
|
||||
self.effective_pch_out_db = min(pref.pi, self.params.target_pch_out_db)
|
||||
self.effective_loss = pref.pi - self.effective_pch_out_db
|
||||
carriers_power = array([c.power.signal +c.power.nli+c.power.ase for c in carriers])
|
||||
carriers_att = list(map(lambda x : lin2db(x*1e3)-self.params.target_pch_out_db, carriers_power))
|
||||
exceeding_att = -min(list(filter(lambda x: x < 0, carriers_att)), default = 0)
|
||||
carriers_att = list(map(lambda x: db2lin(x+exceeding_att), carriers_att))
|
||||
for carrier_att, carrier in zip(carriers_att, carriers) :
|
||||
pwr = carrier.power
|
||||
pwr = pwr._replace(signal=pwr.signal/attenuation,
|
||||
nonlinear_interference=pwr.nli/attenuation,
|
||||
amplified_spontaneous_emission=pwr.ase/attenuation)
|
||||
pwr = pwr._replace( signal = pwr.signal/carrier_att,
|
||||
nonlinear_interference = pwr.nli/carrier_att,
|
||||
amplified_spontaneous_emission = pwr.ase/carrier_att)
|
||||
yield carrier._replace(power=pwr)
|
||||
|
||||
def update_pref(self, pref):
|
||||
@@ -430,16 +424,16 @@ class EdfaParams:
|
||||
if params == {}:
|
||||
self.type_variety = ''
|
||||
self.type_def = ''
|
||||
self.gain_flatmax = 0
|
||||
self.gain_min = 0
|
||||
self.p_max = 0
|
||||
self.nf_model = None
|
||||
self.nf_fit_coeff = None
|
||||
self.nf_ripple = None
|
||||
self.dgt = None
|
||||
self.gain_ripple = None
|
||||
self.out_voa_auto = False
|
||||
self.allowed_for_design = None
|
||||
# self.gain_flatmax = 0
|
||||
# self.gain_min = 0
|
||||
# self.p_max = 0
|
||||
# self.nf_model = None
|
||||
# self.nf_fit_coeff = None
|
||||
# self.nf_ripple = None
|
||||
# self.dgt = None
|
||||
# self.gain_ripple = None
|
||||
# self.out_voa_auto = False
|
||||
# self.allowed_for_design = None
|
||||
|
||||
def update_params(self, kwargs):
|
||||
for k,v in kwargs.items() :
|
||||
@@ -447,10 +441,22 @@ class EdfaParams:
|
||||
if isinstance(v, dict) else v)
|
||||
|
||||
class EdfaOperational:
|
||||
def __init__(self, gain_target, tilt_target, out_voa=None):
|
||||
self.gain_target = gain_target
|
||||
self.tilt_target = tilt_target
|
||||
self.out_voa = out_voa
|
||||
default_values = \
|
||||
{
|
||||
'gain_target': None,
|
||||
'delta_p': None,
|
||||
'out_voa': None,
|
||||
'tilt_target': 0
|
||||
}
|
||||
|
||||
def __init__(self, **operational):
|
||||
self.update_attr(operational)
|
||||
|
||||
def update_attr(self, kwargs):
|
||||
clean_kwargs = {k:v for k,v in kwargs.items() if v !=''}
|
||||
for k,v in self.default_values.items():
|
||||
setattr(self, k, clean_kwargs.get(k,v))
|
||||
|
||||
def __repr__(self):
|
||||
return (f'{type(self).__name__}('
|
||||
f'gain_target={self.gain_target!r}, '
|
||||
@@ -479,14 +485,16 @@ class Edfa(Node):
|
||||
self.pin_db = None
|
||||
self.nch = None
|
||||
self.pout_db = None
|
||||
self.dp_db = None #delta P with Pref (power swwep) in power mode
|
||||
self.target_pch_out_db = None
|
||||
self.effective_pch_out_db = None
|
||||
self.passive = False
|
||||
self.effective_gain = self.operational.gain_target
|
||||
self.att_in = None
|
||||
self.carriers_in = None
|
||||
self.carriers_out = None
|
||||
self.effective_gain = self.operational.gain_target
|
||||
self.delta_p = self.operational.delta_p #delta P with Pref (power swwep) in power mode
|
||||
self.tilt_target = self.operational.tilt_target
|
||||
self.out_voa = self.operational.out_voa
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
@@ -494,9 +502,10 @@ class Edfa(Node):
|
||||
'type' : type(self).__name__,
|
||||
'type_variety' : self.params.type_variety,
|
||||
'operational' : {
|
||||
'gain_target' : self.operational.gain_target,
|
||||
'tilt_target' : self.operational.tilt_target,
|
||||
'out_voa' : self.operational.out_voa
|
||||
'gain_target' : self.effective_gain,
|
||||
'delta_p' : self.delta_p,
|
||||
'tilt_target' : self.tilt_target,
|
||||
'out_voa' : self.out_voa
|
||||
},
|
||||
'metadata' : {
|
||||
'location': self.metadata['location']._asdict()
|
||||
@@ -528,10 +537,10 @@ class Edfa(Node):
|
||||
f' pad att_in (dB): {self.att_in:.2f}',
|
||||
f' Power In (dBm): {self.pin_db:.2f}',
|
||||
f' Power Out (dBm): {self.pout_db:.2f}',
|
||||
f' Delta_P (dB): {self.dp_db!r}',
|
||||
f' Delta_P (dB): {self.delta_p!r}',
|
||||
f' target pch (dBm): {self.target_pch_out_db!r}',
|
||||
f' effective pch (dBm): {self.effective_pch_out_db!r}',
|
||||
f' output VOA (dB): {self.operational.out_voa:.2f}'])
|
||||
f' output VOA (dB): {self.out_voa:.2f}'])
|
||||
|
||||
def carriers(self, loc, attr):
|
||||
"""retrieve carriers information
|
||||
@@ -561,58 +570,101 @@ class Edfa(Node):
|
||||
amplifier_freq = itufs(0.05) * 1e12 # Hz
|
||||
self.channel_freq = frequencies
|
||||
self.interpol_dgt = interp(self.channel_freq, amplifier_freq, self.params.dgt)
|
||||
|
||||
self.interpol_gain_ripple = interp(self.channel_freq, amplifier_freq, self.params.gain_ripple)
|
||||
self.interpol_nf_ripple =interp(self.channel_freq, amplifier_freq, self.params.nf_ripple)
|
||||
|
||||
self.nch = frequencies.size
|
||||
self.pin_db = lin2db(sum(pin*1e3))
|
||||
|
||||
"""in power mode: dp_db is defined and can be used to calculate the power target
|
||||
"""in power mode: delta_p is defined and can be used to calculate the power target
|
||||
This power target is used calculate the amplifier gain"""
|
||||
if self.dp_db is not None:
|
||||
self.target_pch_out_db = round(self.dp_db + pref.p0, 2)
|
||||
if self.delta_p is not None:
|
||||
self.target_pch_out_db = round(self.delta_p + pref.p0, 2)
|
||||
self.effective_gain = self.target_pch_out_db - pref.pi
|
||||
else:
|
||||
self.effective_gain = self.operational.gain_target
|
||||
|
||||
"""check power saturation and correct target_gain accordingly:"""
|
||||
self.effective_gain = min(self.effective_gain, self.params.p_max - self.pin_db)
|
||||
|
||||
"""check power saturation and correct effective gain & power accordingly:"""
|
||||
self.effective_gain = min(
|
||||
self.effective_gain,
|
||||
self.params.p_max - (pref.pi + pref.neq_ch)
|
||||
)
|
||||
#print(self.uid, self.effective_gain, self.operational.gain_target)
|
||||
self.effective_pch_out_db = round(pref.pi + self.effective_gain, 2)
|
||||
|
||||
"""check power saturation and correct target_gain accordingly:"""
|
||||
#print(self.uid, self.effective_gain, self.pin_db, pref.pi)
|
||||
self.nf = self._calc_nf()
|
||||
self.gprofile = self._gain_profile(pin)
|
||||
|
||||
pout = (pin + self.noise_profile(baud_rates))*db2lin(self.gprofile)
|
||||
self.pout_db = lin2db(sum(pout*1e3))
|
||||
self.operational.gain_target = self.effective_gain
|
||||
# ase & nli are only calculated in signal bandwidth
|
||||
# pout_db is not the absolute full output power (negligible if sufficient channels)
|
||||
|
||||
def _nf(self, type_def, nf_model, nf_fit_coeff, gain_min, gain_flatmax, gain_target):
|
||||
#if hybrid raman, use edfa_gain_flatmax attribute, else use gain_flatmax
|
||||
#gain_flatmax = getattr(params, 'edfa_gain_flatmax', params.gain_flatmax)
|
||||
pad = max(gain_min - gain_target, 0)
|
||||
gain_target += pad
|
||||
dg = max(gain_flatmax - gain_target, 0)
|
||||
if type_def == 'variable_gain':
|
||||
g1a = gain_target - nf_model.delta_p - dg
|
||||
nf_avg = lin2db(db2lin(nf_model.nf1) + db2lin(nf_model.nf2)/db2lin(g1a))
|
||||
elif type_def == 'fixed_gain':
|
||||
nf_avg = nf_model.nf0
|
||||
elif type_def == 'openroadm':
|
||||
pin_ch = self.pin_db - lin2db(self.nch)
|
||||
# model OSNR = f(Pin)
|
||||
nf_avg = pin_ch - polyval(nf_model.nf_coef, pin_ch) + 58
|
||||
elif type_def == 'advanced_model':
|
||||
nf_avg = polyval(nf_fit_coeff, -dg)
|
||||
else :
|
||||
print(
|
||||
f'\x1b[1;31;40m'\
|
||||
+ f'CRITICAL: unrecognized type def _{self.params.type_def}_\n\
|
||||
=> please check eqpt_config.json'\
|
||||
+ '\x1b[0m'
|
||||
)
|
||||
exit()
|
||||
return nf_avg+pad, pad
|
||||
|
||||
def _calc_nf(self, avg = False):
|
||||
"""nf calculation based on 2 models: self.params.nf_model.enabled from json import:
|
||||
True => 2 stages amp modelling based on precalculated nf1, nf2 and delta_p in build_OA_json
|
||||
False => polynomial fit based on self.params.nf_fit_coeff"""
|
||||
# TODO|jla: TBD alarm rising or input VOA padding in case
|
||||
# gain_min > gain_target TBD:
|
||||
pad = max(self.params.gain_min - self.effective_gain, 0)
|
||||
self.att_in = pad
|
||||
gain_target = self.effective_gain + pad
|
||||
dg = max(self.params.gain_flatmax - gain_target, 0)
|
||||
if self.params.type_def == 'variable_gain':
|
||||
g1a = gain_target - self.params.nf_model.delta_p - dg
|
||||
nf_avg = lin2db(db2lin(self.params.nf_model.nf1) + db2lin(self.params.nf_model.nf2)/db2lin(g1a))
|
||||
elif self.params.type_def == 'fixed_gain':
|
||||
nf_avg = self.params.nf_model.nf0
|
||||
elif self.params.type_def == 'openroadm':
|
||||
pin_ch = self.pin_db - lin2db(self.nch)
|
||||
# model OSNR = f(Pin)
|
||||
nf_avg = pin_ch - polyval(self.params.nf_model.nf_coef, pin_ch) + 58
|
||||
else:
|
||||
nf_avg = polyval(self.params.nf_fit_coeff, -dg)
|
||||
if self.params.type_def == 'dual_stage':
|
||||
g1 = self.params.preamp_gain_flatmax
|
||||
g2 = self.effective_gain - g1
|
||||
nf1_avg, pad = self._nf( self.params.preamp_type_def,
|
||||
self.params.preamp_nf_model,
|
||||
self.params.preamp_nf_fit_coeff,
|
||||
self.params.preamp_gain_min,
|
||||
self.params.preamp_gain_flatmax,
|
||||
g1)
|
||||
#no padding expected for the 1stage because g1 = gain_max
|
||||
nf2_avg, pad = self._nf( self.params.booster_type_def,
|
||||
self.params.booster_nf_model,
|
||||
self.params.booster_nf_fit_coeff,
|
||||
self.params.booster_gain_min,
|
||||
self.params.booster_gain_flatmax,
|
||||
g2)
|
||||
nf_avg = lin2db(db2lin(nf1_avg) + db2lin(nf2_avg-g1))
|
||||
#no padding expected for the 1stage because g1 = gain_max
|
||||
pad = 0
|
||||
else:
|
||||
nf_avg, pad = self._nf( self.params.type_def,
|
||||
self.params.nf_model,
|
||||
self.params.nf_fit_coeff,
|
||||
self.params.gain_min,
|
||||
self.params.gain_flatmax,
|
||||
self.effective_gain)
|
||||
|
||||
self.att_in = pad # not used to attenuate carriers, only used in _repr_ and _str_
|
||||
if avg:
|
||||
return nf_avg + pad
|
||||
return nf_avg
|
||||
else:
|
||||
return self.interpol_nf_ripple + nf_avg + pad # input VOA = 1 for 1 NF degradation
|
||||
return self.interpol_nf_ripple + nf_avg # input VOA = 1 for 1 NF degradation
|
||||
|
||||
def noise_profile(self, df):
|
||||
""" noise_profile(bw) computes amplifier ase (W) in signal bw (Hz)
|
||||
@@ -705,7 +757,7 @@ class Edfa(Node):
|
||||
|
||||
# Calculate the target slope - currently assumes equal spaced channels
|
||||
# TODO|jla: support arbitrary channel spacing
|
||||
targ_slope = self.operational.tilt_target / (len(nb_channel) - 1)
|
||||
targ_slope = self.tilt_target / (len(nb_channel) - 1)
|
||||
|
||||
# first estimate of DGT scaling
|
||||
if abs(dgt_slope) > 0.001: # check for zero value due to flat dgt
|
||||
@@ -779,7 +831,7 @@ class Edfa(Node):
|
||||
|
||||
gains = db2lin(self.gprofile)
|
||||
carrier_ases = self.noise_profile(brate)
|
||||
att = db2lin(self.operational.out_voa)
|
||||
att = db2lin(self.out_voa)
|
||||
|
||||
for gain, carrier_ase, carrier in zip(gains, carrier_ases, carriers):
|
||||
pwr = carrier.power
|
||||
@@ -790,7 +842,7 @@ class Edfa(Node):
|
||||
|
||||
def update_pref(self, pref):
|
||||
return pref._replace(p_span0=pref.p0,
|
||||
p_spani=pref.pi + self.effective_gain - self.operational.out_voa)
|
||||
p_spani=pref.pi + self.effective_gain - self.out_voa)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
self.carriers_in = spectral_info.carriers
|
||||
|
||||
@@ -17,44 +17,124 @@ from json import load
|
||||
from gnpy.core.utils import lin2db, db2lin, load_json
|
||||
from collections import namedtuple
|
||||
from gnpy.core.elements import Edfa
|
||||
import time
|
||||
|
||||
Model_vg = namedtuple('Model_vg', 'nf1 nf2 delta_p')
|
||||
Model_fg = namedtuple('Model_fg', 'nf0')
|
||||
Model_openroadm = namedtuple('Model_openroadm', 'nf_coef')
|
||||
Fiber = namedtuple('Fiber', 'type_variety dispersion gamma')
|
||||
Spans = namedtuple('Spans', 'power_mode delta_power_range_db max_length length_units \
|
||||
max_loss padding EOL con_in con_out')
|
||||
Transceiver = namedtuple('Transceiver', 'type_variety frequency mode')
|
||||
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 \
|
||||
power_dbm power_range_db tx_osnr sys_margins')
|
||||
AmpBase = namedtuple(
|
||||
'AmpBase',
|
||||
'type_variety type_def gain_flatmax gain_min p_max'
|
||||
' nf_model nf_fit_coeff nf_ripple dgt gain_ripple out_voa_auto allowed_for_design')
|
||||
class Amp(AmpBase):
|
||||
def __new__(cls,
|
||||
type_variety, type_def, gain_flatmax, gain_min, p_max, nf_model=None,
|
||||
nf_fit_coeff=None, nf_ripple=None, dgt=None, gain_ripple=None,
|
||||
out_voa_auto=False, allowed_for_design=True):
|
||||
return super().__new__(cls,
|
||||
type_variety, type_def, gain_flatmax, gain_min, p_max,
|
||||
nf_model, nf_fit_coeff, nf_ripple, dgt, gain_ripple,
|
||||
out_voa_auto, allowed_for_design)
|
||||
Model_hybrid = namedtuple('Model_hybrid', 'nf_ram gain_ram edfa_variety')
|
||||
Model_dual_stage = namedtuple('Model_dual_stage', 'preamp_variety booster_variety')
|
||||
|
||||
class common:
|
||||
def update_attr(self, default_values, kwargs, name):
|
||||
clean_kwargs = {k:v for k,v in kwargs.items() if v !=''}
|
||||
for k,v in default_values.items():
|
||||
setattr(self, k, clean_kwargs.get(k,v))
|
||||
if k not in clean_kwargs and name != 'Amp' :
|
||||
print(f'\x1b[1;31;40m'+
|
||||
f'\n WARNING missing {k} attribute in eqpt_config.json[{name}]'
|
||||
f'\n default value is {k} = {v}'
|
||||
+ '\x1b[0m')
|
||||
time.sleep(1)
|
||||
|
||||
class SI(common):
|
||||
default_values =\
|
||||
{
|
||||
"f_min": 191.35e12,
|
||||
"f_max": 196.1e12,
|
||||
"baud_rate": 32e9,
|
||||
"spacing": 50e9,
|
||||
"power_dbm": 0,
|
||||
"power_range_db": [0,0,0.5],
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 45,
|
||||
"sys_margins": 0
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'SI')
|
||||
|
||||
class Span(common):
|
||||
default_values = \
|
||||
{
|
||||
'power_mode': True,
|
||||
'delta_power_range_db': None,
|
||||
'max_fiber_lineic_loss_for_raman': 0.25,
|
||||
'target_extended_gain': 2.5,
|
||||
'max_length': 150,
|
||||
'length_units': 'km',
|
||||
'max_loss': None,
|
||||
'padding': 10,
|
||||
'EOL': 0,
|
||||
'con_in': 0,
|
||||
'con_out': 0
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'Span')
|
||||
|
||||
class Roadm(common):
|
||||
default_values = \
|
||||
{
|
||||
'target_pch_out_db': -17,
|
||||
'add_drop_osnr': 100
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'Roadm')
|
||||
|
||||
class Transceiver(common):
|
||||
default_values = \
|
||||
{
|
||||
'type_variety': None,
|
||||
'frequency': None,
|
||||
'mode': {}
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'Transceiver')
|
||||
|
||||
class Fiber(common):
|
||||
default_values = \
|
||||
{
|
||||
'type_variety': '',
|
||||
'dispersion': None,
|
||||
'gamma': 0
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'Fiber')
|
||||
|
||||
class Amp(common):
|
||||
default_values = \
|
||||
{
|
||||
'type_variety': '',
|
||||
'type_def': '',
|
||||
'gain_flatmax': None,
|
||||
'gain_min': None,
|
||||
'p_max': None,
|
||||
'nf_model': None,
|
||||
'dual_stage_model': None,
|
||||
'nf_fit_coeff': None,
|
||||
'nf_ripple': None,
|
||||
'dgt': None,
|
||||
'gain_ripple': None,
|
||||
'out_voa_auto': False,
|
||||
'allowed_for_design': False,
|
||||
'raman': False
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'Amp')
|
||||
|
||||
@classmethod
|
||||
def from_advanced_json(cls, filename, **kwargs):
|
||||
with open(filename, encoding='utf-8') as f:
|
||||
json_data = load(f)
|
||||
return cls(**{**kwargs, **json_data, 'type_def':None, 'nf_model':None})
|
||||
def from_json(cls, filename, **kwargs):
|
||||
config = Path(filename).parent / 'default_edfa_config.json'
|
||||
|
||||
@classmethod
|
||||
def from_default_json(cls, filename, **kwargs):
|
||||
with open(filename, encoding='utf-8') as f:
|
||||
json_data = load(f)
|
||||
type_variety = kwargs['type_variety']
|
||||
type_def = kwargs.get('type_def', 'variable_gain') #default compatibility with older json eqpt files
|
||||
nf_def = None
|
||||
dual_stage_def = None
|
||||
|
||||
if type_def == 'fixed_gain':
|
||||
try:
|
||||
@@ -67,6 +147,8 @@ class Amp(AmpBase):
|
||||
del kwargs['nf_max']
|
||||
except KeyError: pass #nf_min and nf_max are not needed for fixed gain amp
|
||||
nf_def = Model_fg(nf0)
|
||||
elif type_def == 'advanced_model':
|
||||
config = Path(filename).parent / kwargs.pop('advanced_config_from_json')
|
||||
elif type_def == 'variable_gain':
|
||||
gain_min, gain_max = kwargs['gain_min'], kwargs['gain_flatmax']
|
||||
try: #nf_min and nf_max are expected for a variable gain amp
|
||||
@@ -87,7 +169,20 @@ class Amp(AmpBase):
|
||||
print(f'missing nf_coef input for amplifier: {type_variety} in eqpt_config.json')
|
||||
exit()
|
||||
nf_def = Model_openroadm(nf_coef)
|
||||
return cls(**{**kwargs, **json_data, 'nf_model': nf_def})
|
||||
elif type_def == 'dual_stage':
|
||||
try: #nf_ram and gain_ram are expected for a hybrid amp
|
||||
preamp_variety = kwargs.pop('preamp_variety')
|
||||
booster_variety = kwargs.pop('booster_variety')
|
||||
except KeyError:
|
||||
print(f'missing preamp/booster variety input for amplifier: {type_variety} in eqpt_config.json')
|
||||
exit()
|
||||
dual_stage_def = Model_dual_stage(preamp_variety, booster_variety)
|
||||
|
||||
with open(config, encoding='utf-8') as f:
|
||||
json_data = load(f)
|
||||
|
||||
return cls(**{**kwargs, **json_data,
|
||||
'nf_model': nf_def, 'dual_stage_model': dual_stage_def})
|
||||
|
||||
|
||||
def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
|
||||
@@ -123,7 +218,7 @@ def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
|
||||
g1a_max = lin2db(db2lin(nf2) / (db2lin(nf_min) - db2lin(nf1)))
|
||||
delta_p = gain_max - g1a_max
|
||||
g1a_min = gain_min - (gain_max-gain_min) - delta_p
|
||||
if not 1 < delta_p < 6:
|
||||
if not 1 < delta_p < 11:
|
||||
print(f'Computed \N{greek capital letter delta}P invalid \
|
||||
\n 1st coil vs 2nd coil calculated DeltaP {delta_p:.2f} for \
|
||||
\n amplifier {type_variety} is not valid: revise inputs \
|
||||
@@ -145,7 +240,7 @@ def edfa_nf(gain_target, variety_type, equipment):
|
||||
amp_params = equipment['Edfa'][variety_type]
|
||||
amp = Edfa(
|
||||
uid = f'calc_NF',
|
||||
params = amp_params._asdict(),
|
||||
params = amp_params.__dict__,
|
||||
operational = {
|
||||
'gain_target': gain_target,
|
||||
'tilt_target': 0
|
||||
@@ -243,6 +338,30 @@ def update_trx_osnr(equipment):
|
||||
m['OSNR'] = m['OSNR'] + equipment['SI']['default'].sys_margins
|
||||
return equipment
|
||||
|
||||
def update_dual_stage(equipment):
|
||||
edfa_dict = equipment['Edfa']
|
||||
for edfa in edfa_dict.values():
|
||||
if edfa.type_def == 'dual_stage':
|
||||
edfa_preamp = edfa_dict[edfa.dual_stage_model.preamp_variety]
|
||||
edfa_booster = edfa_dict[edfa.dual_stage_model.booster_variety]
|
||||
for k,v in edfa_preamp.__dict__.items():
|
||||
attr_k = 'preamp_'+k
|
||||
setattr(edfa, attr_k, v)
|
||||
for k,v in edfa_booster.__dict__.items():
|
||||
attr_k = 'booster_'+k
|
||||
setattr(edfa, attr_k, v)
|
||||
edfa.p_max = edfa_booster.p_max
|
||||
edfa.gain_flatmax = edfa_booster.gain_flatmax + edfa_preamp.gain_flatmax
|
||||
if edfa.gain_min < edfa_preamp.gain_min:
|
||||
print(
|
||||
f'\x1b[1;31;40m'\
|
||||
+ f'CRITICAL: dual stage {edfa.type_variety} min gain is lower than its preamp min gain\
|
||||
=> please increase its min gain in eqpt_config.json'\
|
||||
+ '\x1b[0m'
|
||||
)
|
||||
exit()
|
||||
return equipment
|
||||
|
||||
def equipment_from_json(json_data, filename):
|
||||
"""build global dictionnary eqpt_library that stores all eqpt characteristics:
|
||||
edfa type type_variety, fiber type_variety
|
||||
@@ -259,13 +378,9 @@ def equipment_from_json(json_data, filename):
|
||||
for entry in entries:
|
||||
subkey = entry.get('type_variety', 'default')
|
||||
if key == 'Edfa':
|
||||
if 'advanced_config_from_json' in entry:
|
||||
config = Path(filename).parent / entry.pop('advanced_config_from_json')
|
||||
equipment[key][subkey] = Amp.from_advanced_json(config, **entry)
|
||||
else:
|
||||
config = Path(filename).parent / 'default_edfa_config.json'
|
||||
equipment[key][subkey] = Amp.from_default_json(config, **entry)
|
||||
equipment[key][subkey] = Amp.from_json(filename, **entry)
|
||||
else:
|
||||
equipment[key][subkey] = typ(**entry)
|
||||
equipment = update_trx_osnr(equipment)
|
||||
equipment = update_dual_stage(equipment)
|
||||
return equipment
|
||||
|
||||
@@ -30,7 +30,7 @@ class ConvenienceAccess:
|
||||
|
||||
|
||||
class Power(namedtuple('Power', 'signal nonlinear_interference amplified_spontaneous_emission'), ConvenienceAccess):
|
||||
|
||||
"""carriers power in W"""
|
||||
_ABBREVS = {'nli': 'nonlinear_interference',
|
||||
'ase': 'amplified_spontaneous_emission',}
|
||||
|
||||
@@ -42,14 +42,18 @@ class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off
|
||||
'ffs': 'frequency',
|
||||
'freq': 'frequency',}
|
||||
|
||||
class Pref(namedtuple('Pref', 'p_span0, p_spani'), ConvenienceAccess):
|
||||
class Pref(namedtuple('Pref', 'p_span0, p_spani, neq_ch '), ConvenienceAccess):
|
||||
"""noiseless reference power in dBm:
|
||||
p0: inital target carrier power
|
||||
pi: carrier power after element i
|
||||
neqch: equivalent channel count in dB"""
|
||||
|
||||
_ABBREVS = {'p0' : 'p_span0',
|
||||
'pi' : 'p_spani'}
|
||||
|
||||
class SpectralInformation(namedtuple('SpectralInformation', 'pref carriers'), ConvenienceAccess):
|
||||
|
||||
def __new__(cls, pref=Pref(0, 0), *carriers):
|
||||
def __new__(cls, pref, carriers):
|
||||
return super().__new__(cls, pref, carriers)
|
||||
|
||||
def merge_input_spectral_information(*si):
|
||||
@@ -60,9 +64,10 @@ def merge_input_spectral_information(*si):
|
||||
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 = lin2db(power * 1e3)
|
||||
si = SpectralInformation(pref=Pref(pref, pref))
|
||||
nb_channel = automatic_nch(f_min, f_max, spacing)
|
||||
si = si.update(carriers=[
|
||||
si = SpectralInformation(
|
||||
pref=Pref(pref, pref, lin2db(nb_channel)),
|
||||
carriers=[
|
||||
Channel(f, (f_min+spacing*f),
|
||||
baud_rate, roll_off, Power(power, 0, 0)) for f in range(1,nb_channel+1)
|
||||
])
|
||||
|
||||
@@ -14,7 +14,7 @@ from numpy import arange
|
||||
from scipy.interpolate import interp1d
|
||||
from logging import getLogger
|
||||
from os import path
|
||||
from operator import itemgetter
|
||||
from operator import itemgetter, attrgetter
|
||||
from gnpy.core import elements
|
||||
from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm, Fused
|
||||
from gnpy.core.equipment import edfa_nf
|
||||
@@ -50,9 +50,9 @@ def network_from_json(json_data, equipment):
|
||||
for el_config in json_data['elements']:
|
||||
typ = el_config.pop('type')
|
||||
variety = el_config.pop('type_variety', 'default')
|
||||
if typ in equipment and variety in equipment[typ]:
|
||||
if typ in equipment and variety in equipment[typ]:
|
||||
extra_params = equipment[typ][variety]
|
||||
el_config.setdefault('params', {}).update(extra_params._asdict())
|
||||
el_config.setdefault('params', {}).update(extra_params.__dict__)
|
||||
elif typ in ['Edfa', 'Fiber']: #catch it now because the code will crash later!
|
||||
print( f'The {typ} of variety type {variety} was not recognized:'
|
||||
'\nplease check it is properly defined in the eqpt_config json file')
|
||||
@@ -87,17 +87,17 @@ def network_to_json(network):
|
||||
data.update(connections)
|
||||
return data
|
||||
|
||||
def select_edfa(gain_target, power_target, equipment):
|
||||
def select_edfa(raman_allowed, gain_target, power_target, equipment, uid):
|
||||
"""amplifer selection algorithm
|
||||
@Orange Jean-Luc Augé
|
||||
"""
|
||||
Edfa_list = namedtuple('Edfa_list', 'variety power gain nf')
|
||||
TARGET_EXTENDED_GAIN = 2.1
|
||||
#MAX_EXTENDED_GAIN = 5
|
||||
Edfa_list = namedtuple('Edfa_list', 'raman variety power gain_min nf')
|
||||
TARGET_EXTENDED_GAIN = equipment['Span']['default'].target_extended_gain
|
||||
edfa_dict = equipment['Edfa']
|
||||
pin = power_target - gain_target
|
||||
|
||||
edfa_list = [Edfa_list(
|
||||
raman=edfa.raman,
|
||||
variety=edfa_variety,
|
||||
power=min(
|
||||
pin
|
||||
@@ -106,76 +106,87 @@ def select_edfa(gain_target, power_target, equipment):
|
||||
edfa.p_max
|
||||
)
|
||||
-power_target,
|
||||
gain=edfa.gain_flatmax-gain_target,
|
||||
gain_min=
|
||||
gain_target+3
|
||||
-edfa.gain_min,
|
||||
nf=edfa_nf(gain_target, edfa_variety, equipment)) \
|
||||
for edfa_variety, edfa in edfa_dict.items()
|
||||
if edfa.allowed_for_design]
|
||||
|
||||
acceptable_gain_list = \
|
||||
list(filter(lambda x : x.gain>-TARGET_EXTENDED_GAIN, edfa_list))
|
||||
if len(acceptable_gain_list) < 1:
|
||||
#no amplifier satisfies the required gain, so pick the highest gain:
|
||||
gain_max = max(edfa_list, key=itemgetter(2)).gain
|
||||
#pick up all amplifiers that share this max gain:
|
||||
acceptable_gain_list = \
|
||||
list(filter(lambda x : x.gain-gain_max>-0.1, edfa_list))
|
||||
#filter on raman restriction
|
||||
raman_filter = lambda edfa: (edfa.raman and raman_allowed) or not edfa.raman
|
||||
edfa_list = list(filter(raman_filter, edfa_list))
|
||||
#print(f'\n{uid}, gain {gain_target}, {power_target}')
|
||||
#print('edfa',edfa_list)
|
||||
|
||||
#filter on min gain limitation:
|
||||
#consider gain_target+3 to allow some operation below min gain
|
||||
#(~counterpart to the extended gain range)
|
||||
acceptable_gain_min_list = \
|
||||
list(filter(lambda x : x.gain_min>0, edfa_list))
|
||||
if len(acceptable_gain_min_list) < 1:
|
||||
#do not take this empty list into account for the rest of the code
|
||||
#but issue a warning to the user
|
||||
print(
|
||||
f'\x1b[1;31;40m'\
|
||||
+ f'WARNING: target gain in node {uid} is below all available amplifiers min gain: \
|
||||
amplifier input padding will be assumed, consider increase fiber padding instead'\
|
||||
+ '\x1b[0m'
|
||||
)
|
||||
else:
|
||||
edfa_list = acceptable_gain_min_list
|
||||
#print('gain_min', acceptable_gain_min_list)
|
||||
|
||||
#filter on max power limitation:
|
||||
acceptable_power_list = \
|
||||
list(filter(lambda x : x.power>=0, acceptable_gain_list))
|
||||
list(filter(lambda x : x.power>=0, edfa_list))
|
||||
if len(acceptable_power_list) < 1:
|
||||
#no amplifier satisfies the required power, so pick the highest power:
|
||||
power_max = \
|
||||
max(acceptable_gain_list, key=itemgetter(1)).power
|
||||
power_max = max(edfa_list, key=attrgetter('power')).power
|
||||
#pick up all amplifiers that share this max gain:
|
||||
acceptable_power_list = \
|
||||
list(filter(lambda x : x.power-power_max>-0.1, acceptable_gain_list))
|
||||
list(filter(lambda x : x.power-power_max>-0.3, edfa_list))
|
||||
#print('power', acceptable_power_list)
|
||||
|
||||
# debug:
|
||||
# print(gain_target, power_target, '=>\n',acceptable_power_list)
|
||||
|
||||
# gain and power requirements are resolved,
|
||||
# =>chose the amp with the best NF among the acceptable ones:
|
||||
return min(acceptable_power_list, key=itemgetter(3)).variety #filter on NF
|
||||
selected_edfa = min(acceptable_power_list, key=attrgetter('nf')) #filter on NF
|
||||
power_reduction = round(min(selected_edfa.power, 0),2)
|
||||
if power_reduction < -0.5:
|
||||
print(
|
||||
f'\x1b[1;31;40m'\
|
||||
+ f'WARNING: target gain and power in node {uid}\n \
|
||||
is beyond all available amplifiers capabilities and/or extended_gain_range:\n\
|
||||
a power reduction of {power_reduction} is applied\n'\
|
||||
+ '\x1b[0m'
|
||||
)
|
||||
|
||||
return selected_edfa.variety, power_reduction
|
||||
|
||||
def set_roadm_loss(network, equipment, pref_ch_db):
|
||||
roadms = [roadm for roadm in network if isinstance(roadm, Roadm)]
|
||||
power_mode = equipment['Spans']['default'].power_mode
|
||||
default_roadm_loss = equipment['Roadms']['default'].gain_mode_default_loss
|
||||
pout_target = equipment['Roadms']['default'].power_mode_pout_target
|
||||
roadm_loss = pref_ch_db - pout_target
|
||||
|
||||
for roadm in roadms:
|
||||
if power_mode:
|
||||
roadm.loss = roadm_loss
|
||||
roadm.target_pch_out_db = pout_target
|
||||
elif roadm.loss == None:
|
||||
roadm.loss = default_roadm_loss
|
||||
|
||||
def target_power(dp_from_gain, network, node, equipment): #get_fiber_dp
|
||||
def target_power(network, node, equipment): #get_fiber_dp
|
||||
SPAN_LOSS_REF = 20
|
||||
POWER_SLOPE = 0.3
|
||||
power_mode = equipment['Spans']['default'].power_mode
|
||||
dp_range = list(equipment['Spans']['default'].delta_power_range_db)
|
||||
power_mode = equipment['Span']['default'].power_mode
|
||||
dp_range = list(equipment['Span']['default'].delta_power_range_db)
|
||||
node_loss = span_loss(network, node)
|
||||
|
||||
dp_gain_mode = 0
|
||||
try:
|
||||
dp_power_mode = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2])
|
||||
dp_power_mode = max(dp_range[0], dp_power_mode)
|
||||
dp_power_mode = min(dp_range[1], dp_power_mode)
|
||||
dp = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2])
|
||||
dp = max(dp_range[0], dp)
|
||||
dp = min(dp_range[1], dp)
|
||||
except KeyError:
|
||||
print(f'invalid delta_power_range_db definition in eqpt_config[Spans]'
|
||||
print(f'invalid delta_power_range_db definition in eqpt_config[Span]'
|
||||
f'delta_power_range_db: [lower_bound, upper_bound, step]')
|
||||
exit()
|
||||
|
||||
if dp_from_gain:
|
||||
dp_power_mode = dp_from_gain
|
||||
dp_gain_mode = dp_from_gain
|
||||
if isinstance(node, Roadm):
|
||||
dp_power_mode = 0
|
||||
|
||||
dp = dp_power_mode if power_mode else dp_gain_mode
|
||||
#print(f'{repr(node)} delta power in:\n{dp}dB')
|
||||
dp = 0
|
||||
|
||||
return dp
|
||||
|
||||
|
||||
def prev_node_generator(network, node):
|
||||
"""fused spans interest:
|
||||
iterate over all predecessors while they are Fused or Fiber type"""
|
||||
@@ -244,23 +255,22 @@ def find_last_node(network, node):
|
||||
pass
|
||||
return this_node
|
||||
|
||||
def set_amplifier_voa(amp, pref_total_db, power_mode):
|
||||
VOA_MARGIN = 0
|
||||
if amp.operational.out_voa is None:
|
||||
def set_amplifier_voa(amp, power_target, power_mode):
|
||||
VOA_MARGIN = 1 #do not maximize the VOA optimization
|
||||
if amp.out_voa is None:
|
||||
if power_mode:
|
||||
gain_target = amp.operational.gain_target
|
||||
pout = pref_total_db + amp.dp_db
|
||||
voa = min(amp.params.p_max-pout,
|
||||
amp.params.gain_flatmax-amp.operational.gain_target)
|
||||
voa = round2float(max(voa, 0), 0.5) - VOA_MARGIN if amp.params.out_voa_auto else 0
|
||||
amp.dp_db = amp.dp_db + voa
|
||||
amp.operational.gain_target = amp.operational.gain_target + voa
|
||||
gain_target = amp.effective_gain
|
||||
voa = min(amp.params.p_max-power_target,
|
||||
amp.params.gain_flatmax-amp.effective_gain)
|
||||
voa = max(round2float(max(voa, 0), 0.5) - VOA_MARGIN, 0) if amp.params.out_voa_auto else 0
|
||||
amp.delta_p = amp.delta_p + voa
|
||||
amp.effective_gain = amp.effective_gain + voa
|
||||
else:
|
||||
voa = 0 # no output voa optimization in gain mode
|
||||
amp.operational.out_voa = voa
|
||||
amp.out_voa = voa
|
||||
|
||||
def set_egress_amplifier(network, roadm, equipment, pref_total_db):
|
||||
power_mode = equipment['Spans']['default'].power_mode
|
||||
power_mode = equipment['Span']['default'].power_mode
|
||||
next_oms = (n for n in network.successors(roadm) if not isinstance(n, Transceiver))
|
||||
for oms in next_oms:
|
||||
#go through all the OMS departing from the Roadm
|
||||
@@ -271,30 +281,49 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db):
|
||||
# node = find_last_node(next_node)
|
||||
# next_node = next(n for n in network.successors(node))
|
||||
# next_node = find_last_node(next_node)
|
||||
prev_dp = 0
|
||||
dp = 0
|
||||
prev_dp = getattr(node.params, 'target_pch_out_db', 0)
|
||||
dp = prev_dp
|
||||
prev_voa = 0
|
||||
voa = 0
|
||||
while True:
|
||||
#go through all nodes in the OMS (loop until next Roadm instance)
|
||||
if isinstance(node, Edfa):
|
||||
node_loss = span_loss(network, prev_node)
|
||||
dp_from_gain = prev_dp + node.operational.gain_target - node_loss \
|
||||
if node.operational.gain_target > 0 else None
|
||||
dp = target_power(dp_from_gain, network, next_node, equipment)
|
||||
gain_target = node_loss + dp - prev_dp
|
||||
if node.out_voa:
|
||||
voa = node.out_voa
|
||||
if node.delta_p is None:
|
||||
dp = target_power(network, next_node, equipment)
|
||||
else:
|
||||
dp = node.delta_p
|
||||
gain_from_dp = node_loss + dp - prev_dp + prev_voa
|
||||
if node.effective_gain is None or power_mode:
|
||||
gain_target = gain_from_dp
|
||||
else: #gain mode with effective_gain
|
||||
gain_target = node.effective_gain
|
||||
dp = prev_dp - node_loss + gain_target
|
||||
#print(node.delta_p, dp, gain_target)
|
||||
power_target = pref_total_db + dp
|
||||
|
||||
if power_mode:
|
||||
node.dp_db = dp
|
||||
node.operational.gain_target = gain_target
|
||||
|
||||
if node.params.type_variety == '':
|
||||
power_target = pref_total_db + dp
|
||||
edfa_variety = select_edfa(gain_target, power_target, equipment)
|
||||
if node.params.type_variety == '' :
|
||||
raman_allowed = False
|
||||
if isinstance(prev_node, Fiber):
|
||||
max_fiber_lineic_loss_for_raman = \
|
||||
equipment['Span']['default'].max_fiber_lineic_loss_for_raman
|
||||
raman_allowed = prev_node.params.loss_coef < max_fiber_lineic_loss_for_raman
|
||||
edfa_variety, power_reduction = select_edfa(raman_allowed,
|
||||
gain_target, power_target, equipment, node.uid)
|
||||
extra_params = equipment['Edfa'][edfa_variety]
|
||||
node.params.update_params(extra_params._asdict())
|
||||
set_amplifier_voa(node, pref_total_db, power_mode)
|
||||
node.params.update_params(extra_params.__dict__)
|
||||
dp += power_reduction
|
||||
gain_target += power_reduction
|
||||
|
||||
node.delta_p = dp if power_mode else None
|
||||
node.effective_gain = gain_target
|
||||
set_amplifier_voa(node, power_target, power_mode)
|
||||
if isinstance(next_node, Roadm) or isinstance(next_node, Transceiver):
|
||||
break
|
||||
prev_dp = dp
|
||||
prev_voa = voa
|
||||
prev_node = node
|
||||
node = next_node
|
||||
# print(f'{node.uid}')
|
||||
@@ -319,7 +348,7 @@ def add_egress_amplifier(network, node):
|
||||
}
|
||||
},
|
||||
operational = {
|
||||
'gain_target': 0,
|
||||
'gain_target': None,
|
||||
'tilt_target': 0,
|
||||
})
|
||||
network.add_node(amp)
|
||||
@@ -421,7 +450,7 @@ def add_fiber_padding(network, fibers, padding):
|
||||
first_fiber.att_in = first_fiber.att_in + padding - this_span_loss
|
||||
|
||||
def build_network(network, equipment, pref_ch_db, pref_total_db):
|
||||
default_span_data = equipment['Spans']['default']
|
||||
default_span_data = equipment['Span']['default']
|
||||
max_length = int(default_span_data.max_length * UNITS[default_span_data.length_units])
|
||||
min_length = max(int(default_span_data.padding/0.2*1e3),50_000)
|
||||
bounds = range(min_length, max_length)
|
||||
@@ -431,7 +460,6 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
|
||||
padding = default_span_data.padding
|
||||
|
||||
#set raodm loss for gain_mode before to build network
|
||||
set_roadm_loss(network, equipment, pref_ch_db)
|
||||
fibers = [f for f in network.nodes() if isinstance(f, Fiber)]
|
||||
add_connector_loss(fibers, con_in, con_out, default_span_data.EOL)
|
||||
add_fiber_padding(network, fibers, padding)
|
||||
|
||||
@@ -23,7 +23,6 @@ from networkx.utils import pairwise
|
||||
from numpy import mean
|
||||
from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element
|
||||
from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused
|
||||
from gnpy.core.network import set_roadm_loss
|
||||
from gnpy.core.utils import db2lin, lin2db
|
||||
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power
|
||||
from copy import copy, deepcopy
|
||||
@@ -386,8 +385,6 @@ def compute_constrained_path(network, req):
|
||||
return total_path
|
||||
|
||||
def propagate(path, req, equipment, show=False):
|
||||
#update roadm loss in case of power sweep (power mode only)
|
||||
set_roadm_loss(path, equipment, lin2db(req.power*1e3))
|
||||
si = create_input_spectral_information(
|
||||
req.f_min, req.f_max, req.roll_off, req.baud_rate,
|
||||
req.power, req.spacing)
|
||||
@@ -395,12 +392,10 @@ def propagate(path, req, equipment, show=False):
|
||||
si = el(si)
|
||||
if show :
|
||||
print(el)
|
||||
path[-1].update_snr(req.tx_osnr, equipment['Roadms']['default'].add_drop_osnr)
|
||||
path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr)
|
||||
return path
|
||||
|
||||
def propagate2(path, req, equipment, show=False):
|
||||
#update roadm loss in case of power sweep (power mode only)
|
||||
set_roadm_loss(path, equipment, lin2db(req.power*1e3))
|
||||
si = create_input_spectral_information(
|
||||
req.f_min, req.f_max, req.roll_off, req.baud_rate,
|
||||
req.power, req.spacing)
|
||||
@@ -411,12 +406,10 @@ def propagate2(path, req, equipment, show=False):
|
||||
infos[el] = before_si, after_si
|
||||
if show :
|
||||
print(el)
|
||||
path[-1].update_snr(req.tx_osnr, equipment['Roadms']['default'].add_drop_osnr)
|
||||
path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr)
|
||||
return infos
|
||||
|
||||
def propagate_and_optimize_mode(path, req, equipment):
|
||||
#update roadm loss in case of power sweep (power mode only)
|
||||
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
|
||||
# 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
|
||||
@@ -442,7 +435,7 @@ def propagate_and_optimize_mode(path, req, equipment):
|
||||
si = el(si)
|
||||
for m in modes_to_explore :
|
||||
if path[-1].snr is not None:
|
||||
path[-1].update_snr(m['tx_osnr'], equipment['Roadms']['default'].add_drop_osnr)
|
||||
path[-1].update_snr(m['tx_osnr'], equipment['Roadm']['default'].add_drop_osnr)
|
||||
if round(min(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] :
|
||||
found_a_feasible_mode = True
|
||||
return path, m
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
{ "Edfa":[{
|
||||
"type_variety": "CienaDB_medium_gain",
|
||||
"type_def": "advanced_model",
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
@@ -52,7 +54,7 @@
|
||||
"gamma": 0.00127
|
||||
}
|
||||
],
|
||||
"Spans":[{
|
||||
"Span":[{
|
||||
"power_mode": true,
|
||||
"delta_power_range_db": [0,0,1],
|
||||
"max_length": 150,
|
||||
@@ -64,8 +66,7 @@
|
||||
"con_out": 0
|
||||
}
|
||||
],
|
||||
"Roadms":[{
|
||||
"gain_mode_default_loss": 20,
|
||||
"Roadm":[{
|
||||
"power_mode_pout_target": -20,
|
||||
"add_drop_osnr": 100
|
||||
}],
|
||||
|
||||
@@ -469,9 +469,10 @@
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"gain_target": null,
|
||||
"delta_p": null,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -487,9 +488,10 @@
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"gain_target": null,
|
||||
"delta_p": null,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -505,9 +507,10 @@
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"gain_target": null,
|
||||
"delta_p": null,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -523,9 +526,10 @@
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"gain_target": null,
|
||||
"delta_p": null,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
"out_voa": null
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -577,9 +577,10 @@
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"gain_target": null,
|
||||
"delta_p": null,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -595,9 +596,10 @@
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"gain_target": null,
|
||||
"delta_p": null,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
"out_voa": null
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -9,8 +9,8 @@ from json import load
|
||||
from gnpy.core.elements import Transceiver, Fiber, Edfa
|
||||
from gnpy.core.utils import lin2db, db2lin
|
||||
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref
|
||||
from gnpy.core.equipment import load_equipment, automatic_fmax
|
||||
from gnpy.core.network import build_network, load_network, set_roadm_loss
|
||||
from gnpy.core.equipment import load_equipment, automatic_fmax, automatic_nch
|
||||
from gnpy.core.network import build_network, load_network
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
@@ -79,7 +79,7 @@ def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si):
|
||||
pin = pin/db2lin(gain)
|
||||
baud_rates = array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
pref = Pref(0, -gain)
|
||||
pref = Pref(0, -gain, lin2db(len(frequencies)))
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
result = edfa.nf
|
||||
assert pytest.approx(nf_expected, abs=0.01) == result[0]
|
||||
@@ -93,7 +93,7 @@ def test_fixed_gain_nf(gain, nf_expected, setup_edfa_fixed_gain, si):
|
||||
pin = pin/db2lin(gain)
|
||||
baud_rates = array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
pref = Pref(0, -gain)
|
||||
pref = Pref(0, -gain, lin2db(len(frequencies)))
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
|
||||
assert pytest.approx(nf_expected, abs=0.01) == edfa.nf[0]
|
||||
@@ -118,7 +118,7 @@ def test_compare_nf_models(gain, setup_edfa_variable_gain, si):
|
||||
pin = pin/db2lin(gain)
|
||||
baud_rates = array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
pref = Pref(0, -gain)
|
||||
pref = Pref(0, -gain, lin2db(len(frequencies)))
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
nf_model = edfa.nf[0]
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
@@ -137,7 +137,7 @@ def test_ase_noise(gain, si, setup_edfa_variable_gain, setup_trx, bw):
|
||||
pin = array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers])
|
||||
baud_rates = array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
pref = Pref(0, 0)
|
||||
pref = Pref(0, 0, lin2db(len(frequencies)))
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
nf = edfa.nf
|
||||
pin = lin2db(pin[0]*1e3)
|
||||
|
||||
@@ -47,12 +47,8 @@ def propagation(input_power, con_in, con_out,dest):
|
||||
|
||||
p = input_power
|
||||
p = db2lin(p) * 1e-3
|
||||
spacing = 0.05 # THz
|
||||
si = SpectralInformation() # SI units: W, Hz
|
||||
si = si.update(carriers=[
|
||||
Channel(f, (191.3 + spacing * f) * 1e12, 32e9, 0.15, Power(p, 0, 0))
|
||||
for f in range(1,80)
|
||||
])
|
||||
spacing = 50e9 # THz
|
||||
si = create_input_spectral_information(191.3e12, 191.3e12+79*spacing, 0.15, 32e9, p, spacing)
|
||||
source = next(transceivers[uid] for uid in transceivers if uid == 'trx A')
|
||||
sink = next(transceivers[uid] for uid in transceivers if uid == dest)
|
||||
path = dijkstra_path(network, source, sink)
|
||||
|
||||
Reference in New Issue
Block a user