code wrap up and edfa model augmentation v2 (#30)

* JSON file based on Orange operator typical input
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>

* update of the standalone edfa model

creation of a new amlifier2.py = v2
creation of a json parser build_oa_json.py
the parser takes OA.json as input and newOA.json as output
creation of a pytest verification module amplifier_pytest.py

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>

* put the code together and transmission example script

-basic dijkstra propagation
-ase noise propagation based on amplifier model
-fake nli noise propagation
-integration of the amplifier model
-interpolation function in the edfa class
-code cleaning and units harmonization

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>

* mv transmission_main_example and rm _main__

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>

* 2nd edfa model and build_oa_json file

add a dual coil stages edfa model in case the nf polynomial fit is not known
add a build_oa_json file that convert the input files in
edfa_config.json file and pre-calculate the nf_model nf1, nf2 and
delta_p parameters
adding power violation check and input padding (below minimum gain) in the edfa model
class

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
This commit is contained in:
Jean-Luc Augé
2018-02-20 18:51:53 +01:00
committed by James
parent 1491c2361f
commit 0d3a86f1d8
12 changed files with 1010 additions and 201 deletions

View File

@@ -3,6 +3,7 @@
"dfg": [25.13596985, 25.11822814, 25.09542133, 25.06245771, 25.02602765, 24.99637953, 24.98167255, 24.97530668, 24.98320726, 24.99718565, 25.01757247, 25.03832781, 25.05495585, 25.0670719, 25.07091411, 25.07094365, 25.07114324, 25.07533627, 25.08731018, 25.10313936, 25.12276204, 25.14239479, 25.15945633, 25.17392704, 25.17673767, 25.17037141, 25.15216254, 25.1311431, 25.10802335, 25.08548777, 25.06916675, 25.05848176, 25.05447313, 25.05154441, 25.04946059, 25.04717849, 25.04551656, 25.04467649, 25.0407292, 25.03285408, 25.0234883, 25.01659234, 25.01332136, 25.01123434, 25.01030015, 25.00936548, 25.00873964, 25.00842535, 25.00696466, 25.0040431, 25.00070998, 24.9984232, 24.99306332, 24.98352421, 24.97125103, 24.96038108, 24.94888721, 24.93531489, 24.92131927, 24.90898697, 24.89896514, 24.88958463, 24.8808387, 24.87210092, 24.86462026, 24.85839773, 24.85445838, 24.85155443, 24.85176601, 24.85408014, 24.85909624, 24.86474458, 24.87203486, 24.8803652, 24.88910669, 24.89721313, 24.90282604, 24.9065669, 24.9086508, 24.91093944, 24.91343079, 24.91592344, 24.92155351, 24.93031861, 24.94052812, 24.94904669, 24.95757123, 24.96781845, 24.98180093, 24.99782686, 25.01393183, 25.02809846, 25.04032575, 25.05256981, 25.06479701, 25.07704697],
"dgt": [2.714526681131686, 2.705443819238505, 2.6947834587664494, 2.6841217449620203, 2.6681935771243177, 2.6521732021128046, 2.630396440815385, 2.602860350286428, 2.5696460593920065, 2.5364027376452056, 2.499446286796604, 2.4587748041127506, 2.414398437185221, 2.3699990328716107, 2.322373696229342, 2.271520771371253, 2.2174389328192197, 2.16337565384239, 2.1183028432496016, 2.082225099873648, 2.055100772005235, 2.0279625371819305, 2.0008103857988204, 1.9736443063300082, 1.9482128147680253, 1.9245345552113182, 1.9026104247588487, 1.8806927939516411, 1.862235672444246, 1.847275503201129, 1.835814081380705, 1.824381436842932, 1.8139629377087627, 1.8045606557581335, 1.7961751115773796, 1.7877868031023945, 1.7793941781790852, 1.7709972329654864, 1.7625959636196327, 1.7541903672600494, 1.7459181197626403, 1.737780757913635, 1.7297783508684146, 1.7217732861435076, 1.7137640932265894, 1.7057507692361864, 1.6918150918099673, 1.6719047669939942, 1.6460167077689267, 1.6201194134191075, 1.5986915141218316, 1.5817353179379183, 1.569199764184379, 1.5566577309558969, 1.545374152761467, 1.5353620432989845, 1.5266220576235803, 1.5178910621476225, 1.5097346239790443, 1.502153039909686, 1.495145456062699, 1.488134243479226, 1.48111939735681, 1.474100442252211, 1.4670307626366115, 1.4599103316162523, 1.45273959485914, 1.445565137158368, 1.4340878115214444, 1.418273806730323, 1.3981208704326855, 1.3779439775587023, 1.3598972673004606, 1.3439818461440451, 1.3301807335621048, 1.316383926863083, 1.3040618749785347, 1.2932153453410835, 1.2838336236692311, 1.2744470198196236, 1.2650555289898042, 1.2556591482982988, 1.2428104897182262, 1.2264996957264114, 1.2067249615595257, 1.1869318618366975, 1.1672278304018044, 1.1476135933863398, 1.1280891949729075, 1.108555289615659, 1.0895983485572227, 1.0712204022764056, 1.0534217504465226, 1.0356155337864215, 1.017807767853702, 1.0],
"nf_fit_coeff": [0.000168241, 0.0469961, 0.0359549, 5.82851],
"nf_ripple": [-0.315374332, -0.315374332, -0.3154009157100272, -0.3184914611751095, -0.32158358425400546, -0.3246772861549999, -0.32762368641496226, -0.3205413846123276, -0.31345546385118733, -0.3063659213569748, -0.29920267890990127, -0.27061972852631744, -0.24202215770774693, -0.21340995523361256, -0.18478227130158695, -0.14809761118389625, -0.11139416731807622, -0.07467192527357988, -0.038026748965679924, -0.019958469399422092, -0.0018809287980157928, 0.01620587996057356, 0.03430196400570967, 0.05240733047405406, 0.07052198650959736, 0.079578036683472, 0.08854664736190952, 0.0975198632319653, 0.10649768784154924, 0.0977413804499074, 0.08880343717266004, 0.07986089973284587, 0.0709137645874038, 0.06333589274056531, 0.055756212252058776, 0.04817263174786321, 0.04058514821716236, 0.03338159167571013, 0.026178308595650738, 0.018971315351761126, 0.011760609076833628, 0.01695029492275999, 0.02227499135770144, 0.02760243318910433, 0.03293262254079026, 0.038265561538776145, 0.04360125231127117, 0.03485699074348155, 0.025991055149117932, 0.017120541224980364, 0.008275758735920322, 0.0019423214065246042, -0.004394389017104359, -0.010734375072893196, -0.017077639301414434, -0.02467970289957285, -0.03229797040382168, -0.03992018009047725, -0.04753456632753024, -0.049234003141433724, -0.05093432003654719, -0.05263551769669225, -0.05433759680640246, -0.0560405580509193, -0.057718452237076875, -0.056840590379175944, -0.055962273198734966, -0.05508350034141658, -0.054204271452516814, -0.05839608872695511, -0.06262733016971533, -0.0668607690892037, -0.07090173625606945, -0.05209609730905224, -0.03328068412141294, -0.014455489070928059, 0.004315038757905716, 0.014839202394482527, 0.025368841662503576, 0.03590396083646565, 0.0464445641953214, 0.05699065602246746, 0.06754224060577406, 0.10002709623672751, 0.13258013095133617, 0.1651501336277331, 0.1977371175359939, 0.23194802687829724, 0.26618779883837107, 0.3004454365808535, 0.33472095409250663, 0.35929034770587287, 0.38384389188855605, 0.40841026111391787, 0.43298946543290784, 0.43298946543290784]
"nf_ripple": [-0.315374332, -0.315374332, -0.3154009157100272, -0.3184914611751095, -0.32158358425400546, -0.3246772861549999, -0.32762368641496226, -0.3205413846123276, -0.31345546385118733, -0.3063659213569748, -0.29920267890990127, -0.27061972852631744, -0.24202215770774693, -0.21340995523361256, -0.18478227130158695, -0.14809761118389625, -0.11139416731807622, -0.07467192527357988, -0.038026748965679924, -0.019958469399422092, -0.0018809287980157928, 0.01620587996057356, 0.03430196400570967, 0.05240733047405406, 0.07052198650959736, 0.079578036683472, 0.08854664736190952, 0.0975198632319653, 0.10649768784154924, 0.0977413804499074, 0.08880343717266004, 0.07986089973284587, 0.0709137645874038, 0.06333589274056531, 0.055756212252058776, 0.04817263174786321, 0.04058514821716236, 0.03338159167571013, 0.026178308595650738, 0.018971315351761126, 0.011760609076833628, 0.01695029492275999, 0.02227499135770144, 0.02760243318910433, 0.03293262254079026, 0.038265561538776145, 0.04360125231127117, 0.03485699074348155, 0.025991055149117932, 0.017120541224980364, 0.008275758735920322, 0.0019423214065246042, -0.004394389017104359, -0.010734375072893196, -0.017077639301414434, -0.02467970289957285, -0.03229797040382168, -0.03992018009047725, -0.04753456632753024, -0.049234003141433724, -0.05093432003654719, -0.05263551769669225, -0.05433759680640246, -0.0560405580509193, -0.057718452237076875, -0.056840590379175944, -0.055962273198734966, -0.05508350034141658, -0.054204271452516814, -0.05839608872695511, -0.06262733016971533, -0.0668607690892037, -0.07090173625606945, -0.05209609730905224, -0.03328068412141294, -0.014455489070928059, 0.004315038757905716, 0.014839202394482527, 0.025368841662503576, 0.03590396083646565, 0.0464445641953214, 0.05699065602246746, 0.06754224060577406, 0.10002709623672751, 0.13258013095133617, 0.1651501336277331, 0.1977371175359939, 0.23194802687829724, 0.26618779883837107, 0.3004454365808535, 0.33472095409250663, 0.35929034770587287, 0.38384389188855605, 0.40841026111391787, 0.43298946543290784, 0.43298946543290784],
"frequencies": []
}
}

View File

@@ -13,6 +13,7 @@
"type": "Fiber",
"params": {
"length": 80,
"loss_coef": 0.2,
"length_units": "km"
},
"metadata": {
@@ -23,6 +24,10 @@
{
"uid": "Edfa1",
"type": "Edfa",
"operational": {
"gain_target": 16,
"tilt_target": 0
},
"config_from_json": "edfa_config.json",
"metadata": {
"latitude": 2,

313
examples/edfa_config.json Normal file
View File

@@ -0,0 +1,313 @@
{
"params": {
"gain_flatmax": 25,
"gain_min": 15,
"p_max": 21,
"nf_fit_coeff": [
0.000168241,
0.0469961,
0.0359549,
5.82851
],
"nf_ripple": [
-0.3110761646066259,
-0.3110761646066259,
-0.31110274831665313,
-0.31419329378173544,
-0.3172854168606314,
-0.32037911876162584,
-0.3233255190215882,
-0.31624321721895354,
-0.30915729645781326,
-0.30206775396360075,
-0.2949045115165272,
-0.26632156113294336,
-0.23772399031437283,
-0.20911178784023846,
-0.18048410390821285,
-0.14379944379052215,
-0.10709599992470213,
-0.07037375788020579,
-0.03372858157230583,
-0.015660302006048,
0.0024172385953583004,
0.020504047353947653,
0.03860013139908377,
0.05670549786742816,
0.07482015390297145,
0.0838762040768461,
0.09284481475528361,
0.1018180306253394,
0.11079585523492333,
0.1020395478432815,
0.09310160456603413,
0.08415906712621996,
0.07521193198077789,
0.0676340601339394,
0.06005437964543287,
0.052470799141237305,
0.044883315610536455,
0.037679759069084225,
0.03047647598902483,
0.02326948274513522,
0.01605877647020772,
0.021248462316134083,
0.02657315875107553,
0.03190060058247842,
0.03723078993416436,
0.04256372893215024,
0.047899419704645264,
0.03915515813685565,
0.030289222542492025,
0.021418708618354456,
0.012573926129294415,
0.006240488799898697,
-9.622162373026585e-05,
-0.006436207679519103,
-0.012779471908040341,
-0.02038153550619876,
-0.027999803010447587,
-0.035622012697103154,
-0.043236398934156144,
-0.04493583574805963,
-0.04663615264317309,
-0.048337350303318156,
-0.050039429413028365,
-0.051742390657545205,
-0.05342028484370278,
-0.05254242298580185,
-0.05166410580536087,
-0.05078533294804249,
-0.04990610405914272,
-0.05409792133358102,
-0.05832916277634124,
-0.06256260169582961,
-0.06660356886269536,
-0.04779792991567815,
-0.028982516728038848,
-0.010157321677553965,
0.00861320615127981,
0.01913736978785662,
0.029667009055877668,
0.04020212822983975,
0.050742731588695494,
0.061288823415841555,
0.07184040799914815,
0.1043252636301016,
0.13687829834471027,
0.1694483010211072,
0.202035284929368,
0.23624619427167134,
0.27048596623174515,
0.30474360397422756,
0.3390191214858807,
0.36358851509924695,
0.38814205928193013,
0.41270842850729195,
0.4372876328262819,
0.4372876328262819
],
"dgt": [
2.714526681131686,
2.705443819238505,
2.6947834587664494,
2.6841217449620203,
2.6681935771243177,
2.6521732021128046,
2.630396440815385,
2.602860350286428,
2.5696460593920065,
2.5364027376452056,
2.499446286796604,
2.4587748041127506,
2.414398437185221,
2.3699990328716107,
2.322373696229342,
2.271520771371253,
2.2174389328192197,
2.16337565384239,
2.1183028432496016,
2.082225099873648,
2.055100772005235,
2.0279625371819305,
2.0008103857988204,
1.9736443063300082,
1.9482128147680253,
1.9245345552113182,
1.9026104247588487,
1.8806927939516411,
1.862235672444246,
1.847275503201129,
1.835814081380705,
1.824381436842932,
1.8139629377087627,
1.8045606557581335,
1.7961751115773796,
1.7877868031023945,
1.7793941781790852,
1.7709972329654864,
1.7625959636196327,
1.7541903672600494,
1.7459181197626403,
1.737780757913635,
1.7297783508684146,
1.7217732861435076,
1.7137640932265894,
1.7057507692361864,
1.6918150918099673,
1.6719047669939942,
1.6460167077689267,
1.6201194134191075,
1.5986915141218316,
1.5817353179379183,
1.569199764184379,
1.5566577309558969,
1.545374152761467,
1.5353620432989845,
1.5266220576235803,
1.5178910621476225,
1.5097346239790443,
1.502153039909686,
1.495145456062699,
1.488134243479226,
1.48111939735681,
1.474100442252211,
1.4670307626366115,
1.4599103316162523,
1.45273959485914,
1.445565137158368,
1.4340878115214444,
1.418273806730323,
1.3981208704326855,
1.3779439775587023,
1.3598972673004606,
1.3439818461440451,
1.3301807335621048,
1.316383926863083,
1.3040618749785347,
1.2932153453410835,
1.2838336236692311,
1.2744470198196236,
1.2650555289898042,
1.2556591482982988,
1.2428104897182262,
1.2264996957264114,
1.2067249615595257,
1.1869318618366975,
1.1672278304018044,
1.1476135933863398,
1.1280891949729075,
1.108555289615659,
1.0895983485572227,
1.0712204022764056,
1.0534217504465226,
1.0356155337864215,
1.017807767853702,
1.0
],
"nf_model": {
"enabled": true,
"nf1": 5.727887800964238,
"nf2": 7.727887800964238,
"delta_p": 5.238350271545567
},
"gain_ripple": [
0.1359703369791596,
0.11822862697916037,
0.09542181697916163,
0.06245819697916133,
0.02602813697916062,
-0.0036199830208403228,
-0.018326963020840026,
-0.0246928330208398,
-0.016792253020838643,
-0.0028138630208403015,
0.017572956979162058,
0.038328296979159404,
0.054956336979159914,
0.0670723869791594,
0.07091459697916136,
0.07094413697916124,
0.07114372697916238,
0.07533675697916209,
0.08731066697916035,
0.10313984697916112,
0.12276252697916235,
0.14239527697916188,
0.15945681697916214,
0.1739275269791598,
0.1767381569791624,
0.17037189697916233,
0.15216302697916007,
0.13114358697916018,
0.10802383697916085,
0.08548825697916129,
0.06916723697916183,
0.05848224697916038,
0.05447361697916264,
0.05154489697916276,
0.04946107697915991,
0.04717897697916129,
0.04551704697916037,
0.04467697697916151,
0.04072968697916224,
0.03285456697916089,
0.023488786979161347,
0.01659282697915998,
0.013321846979160057,
0.011234826979162449,
0.01030063697916006,
0.00936596697916059,
0.00874012697916271,
0.00842583697916055,
0.006965146979162284,
0.0040435869791615175,
0.0007104669791608842,
-0.0015763130208377163,
-0.006936193020838033,
-0.016475303020840215,
-0.028748483020837767,
-0.039618433020837784,
-0.051112303020840244,
-0.06468462302083822,
-0.07868024302083754,
-0.09101254302083817,
-0.10103437302083762,
-0.11041488302083735,
-0.11916081302083725,
-0.12789859302083784,
-0.1353792530208402,
-0.14160178302083892,
-0.1455411330208385,
-0.1484450830208388,
-0.14823350302084037,
-0.14591937302083835,
-0.1409032730208395,
-0.13525493302083902,
-0.1279646530208396,
-0.11963431302083904,
-0.11089282302084058,
-0.1027863830208382,
-0.09717347302083823,
-0.09343261302083761,
-0.0913487130208388,
-0.08906007302083907,
-0.0865687230208394,
-0.08407607302083875,
-0.07844600302084004,
-0.06968090302083851,
-0.05947139302083926,
-0.05095282302083959,
-0.042428283020839785,
-0.03218106302083967,
-0.01819858302084043,
-0.0021726530208390216,
0.01393231697916164,
0.028098946979159933,
0.040326236979161934,
0.05257029697916238,
0.06479749697916048,
0.07704745697916238
]
}
}

View File

@@ -0,0 +1,15 @@
{
"gain_flatmax": 25,
"gain_min": 15,
"p_max": 21,
"nf_fit_coeff": "pNFfit3.txt",
"nf_ripple": "NFR_96.txt",
"dfg": "DFG_96.txt",
"dgt": "DGT_96.txt",
"nf_model":
{
"enabled": true,
"nf_min": 5.8,
"nf_max": 10
}
}

View File

@@ -0,0 +1,164 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Jan 30 12:32:00 2018
@author: jeanluc-auge
@comments about amplifier input files from Brian Taylor & Dave Boertjes
update an existing json file with all the 96ch txt files for a given amplifier type
amplifier type 'OA_type1' is hard coded but can be modified and other types added
returns an updated amplifier json file: output_json_file_name = 'edfa_config.json'
"""
import re
import sys
import json
import numpy as np
from gnpy.core.utils import lin2db, db2lin
"""amplifier file names
convert a set of amplifier files + input json definiton file into a valid edfa_json_file:
nf_fit_coeff: NF polynomial coefficients txt file (optional)
nf_ripple: NF ripple excursion txt file
dfg: gain txt file
dgt: dynamic gain txt file
input json file in argument (defult = 'OA.json')
the json input file should have the following fields:
{
"gain_flatmax": 25,
"gain_min": 15,
"p_max": 21,
"nf_fit_coeff": "pNFfit3.txt",
"nf_ripple": "NFR_96.txt",
"dfg": "DFG_96.txt",
"dgt": "DGT_96.txt",
"nf_model":
{
"enabled": true,
"nf_min": 5.8,
"nf_max": 10
}
}
gain_flat = max flat gain (dB)
gain_min = min gain (dB) : will consider an input VOA if below (TBD vs throwing an exception)
p_max = max power (dBm)
nf_fit = boolean (True, False) :
if False nf_fit_coeff are ignored and nf_model fields are used
"""
input_json_file_name = "OA.json" #default path
output_json_file_name = "edfa_config.json"
param_field ="params"
gain_min_field = "gain_min"
gain_max_field = "gain_flatmax"
gain_ripple_field = "dfg"
nf_ripple_field = "nf_ripple"
nf_fit_coeff = "nf_fit_coeff"
nf_model_field = "nf_model"
nf_model_enabled_field = "enabled"
nf_min_field ="nf_min"
nf_max_field = "nf_max"
def read_file(field, file_name):
"""read and format the 96 channels txt files describing the amplifier NF and ripple
convert dfg into gain ripple by removing the mean component
"""
#with open(path + file_name,'r') as this_file:
# data = this_file.read()
#data.strip()
#data = re.sub(r"([0-9])([ ]{1,3})([0-9-+])",r"\1,\3",data)
#data = list(data.split(","))
#data = [float(x) for x in data]
data = np.loadtxt(file_name)
if field == gain_ripple_field or field == nf_ripple_field:
#consider ripple excursion only to avoid redundant information
#because the max flat_gain is already given by the 'gain_flat' field in json
#remove the mean component
data = data - data.mean()
data = data.tolist()
return data
def nf_model(amp_dict):
if amp_dict[nf_model_field][nf_model_enabled_field] == True:
gain_min = amp_dict[gain_min_field]
gain_max = amp_dict[gain_max_field]
nf_min = amp_dict[nf_model_field][nf_min_field]
nf_max = amp_dict[nf_model_field][nf_max_field]
#use NF estimation model based on NFmin and NFmax in json OA file
delta_p = 5 #max power dB difference between 1st and 2nd stage coils
#dB g1a = (1st stage gain) - (internal voa attenuation)
g1a_min = gain_min - (gain_max-gain_min) - delta_p
g1a_max = gain_max - delta_p
#nf1 and nf2 are the nf of the 1st and 2nd stage coils
#calculate nf1 and nf2 values that solve nf_[min/max] = nf1 + nf2 / g1a[min/max]
nf2 = lin2db((db2lin(nf_min) - db2lin(nf_max)) / (1/db2lin(g1a_max)-1/db2lin(g1a_min)))
nf1 = lin2db(db2lin(nf_min)- db2lin(nf2)/db2lin(g1a_max)) #expression (1)
""" now checking and recalculating the results:
recalculate delta_p to check it is within [1-6] boundaries
This is to check that the nf_min and nf_max values from the json file
make sense. If not a warning is printed """
if nf1 < 4:
print('1st coil nf calculated value {} is too low: revise inputs'.format(nf1))
if nf2 < nf1 + 0.3 or nf2 > nf1 + 2:
"""nf2 should be with [nf1+0.5 - nf1 +2] boundaries
there shouldn't be very high nf differences between 2 coils
=> recalculate delta_p
"""
nf2 = max(nf2, nf1+0.3)
nf2 = min(nf2, nf1+2)
g1a_max = lin2db(db2lin(nf2) / (db2lin(nf_min) - db2lin(nf1))) #use expression (1)
delta_p = gain_max - g1a_max
g1a_min = gain_min - (gain_max-gain_min) - delta_p
if delta_p < 1 or delta_p > 6:
#delta_p should be > 1dB and < 6dB => consider user warning if not
print('1st coil vs 2nd coil calculated DeltaP {} is not valid: revise inputs'
.format(delta_p))
#check the calculated values for nf1 & nf2:
nf_min_calc = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_max))
nf_max_calc = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_min))
if (abs(nf_min_calc-nf_min) > 0.01) or (abs(nf_max_calc-nf_max) > 0.01):
print('nf model calculation failed with nf_min {} and nf_max {} calculated'
.format(nf_min_calc, nf_max_calc))
print('do not use the generated edfa_config.json file')
else :
(nf1, nf2, delta_p) = (0, 0, 0)
return (nf1, nf2, delta_p)
def input_json(path):
"""read the json input file and add all the 96 channels txt files
create the output json file with output_json_file_name"""
with open(path,'r') as edfa_json_file:
amp_text = edfa_json_file.read()
amp_dict = json.loads(amp_text)
for k, v in amp_dict.items():
if re.search(r'.txt$',str(v)) :
amp_dict[k] = read_file(k, v)
#calculate nf of 1st and 2nd coil for the nf_model if 'enabled'==true
(nf1, nf2, delta_p) = nf_model(amp_dict)
#rename nf_min and nf_max in nf1 and nf2 after the nf model calculation:
del amp_dict[nf_model_field][nf_min_field]
del amp_dict[nf_model_field][nf_max_field]
amp_dict[nf_model_field]['nf1'] = nf1
amp_dict[nf_model_field]['nf2'] = nf2
amp_dict[nf_model_field]['delta_p'] = delta_p
#rename dfg into gain_ripple after removing the average part:
amp_dict['gain_ripple'] = amp_dict.pop(gain_ripple_field)
new_amp_dict = {}
new_amp_dict[param_field] = amp_dict
amp_text = json.dumps(new_amp_dict, indent=4)
#print(amp_text)
with open(output_json_file_name,'w') as edfa_json_file:
edfa_json_file.write(amp_text)
if __name__ == '__main__':
if len(sys.argv) == 2:
path = sys.argv[1]
else:
path = input_json_file_name
input_json(path)

View File

@@ -0,0 +1,313 @@
{
"params": {
"gain_flatmax": 25,
"gain_min": 15,
"p_max": 21,
"nf_fit_coeff": [
0.000168241,
0.0469961,
0.0359549,
5.82851
],
"nf_ripple": [
-0.3110761646066259,
-0.3110761646066259,
-0.31110274831665313,
-0.31419329378173544,
-0.3172854168606314,
-0.32037911876162584,
-0.3233255190215882,
-0.31624321721895354,
-0.30915729645781326,
-0.30206775396360075,
-0.2949045115165272,
-0.26632156113294336,
-0.23772399031437283,
-0.20911178784023846,
-0.18048410390821285,
-0.14379944379052215,
-0.10709599992470213,
-0.07037375788020579,
-0.03372858157230583,
-0.015660302006048,
0.0024172385953583004,
0.020504047353947653,
0.03860013139908377,
0.05670549786742816,
0.07482015390297145,
0.0838762040768461,
0.09284481475528361,
0.1018180306253394,
0.11079585523492333,
0.1020395478432815,
0.09310160456603413,
0.08415906712621996,
0.07521193198077789,
0.0676340601339394,
0.06005437964543287,
0.052470799141237305,
0.044883315610536455,
0.037679759069084225,
0.03047647598902483,
0.02326948274513522,
0.01605877647020772,
0.021248462316134083,
0.02657315875107553,
0.03190060058247842,
0.03723078993416436,
0.04256372893215024,
0.047899419704645264,
0.03915515813685565,
0.030289222542492025,
0.021418708618354456,
0.012573926129294415,
0.006240488799898697,
-9.622162373026585e-05,
-0.006436207679519103,
-0.012779471908040341,
-0.02038153550619876,
-0.027999803010447587,
-0.035622012697103154,
-0.043236398934156144,
-0.04493583574805963,
-0.04663615264317309,
-0.048337350303318156,
-0.050039429413028365,
-0.051742390657545205,
-0.05342028484370278,
-0.05254242298580185,
-0.05166410580536087,
-0.05078533294804249,
-0.04990610405914272,
-0.05409792133358102,
-0.05832916277634124,
-0.06256260169582961,
-0.06660356886269536,
-0.04779792991567815,
-0.028982516728038848,
-0.010157321677553965,
0.00861320615127981,
0.01913736978785662,
0.029667009055877668,
0.04020212822983975,
0.050742731588695494,
0.061288823415841555,
0.07184040799914815,
0.1043252636301016,
0.13687829834471027,
0.1694483010211072,
0.202035284929368,
0.23624619427167134,
0.27048596623174515,
0.30474360397422756,
0.3390191214858807,
0.36358851509924695,
0.38814205928193013,
0.41270842850729195,
0.4372876328262819,
0.4372876328262819
],
"dgt": [
2.714526681131686,
2.705443819238505,
2.6947834587664494,
2.6841217449620203,
2.6681935771243177,
2.6521732021128046,
2.630396440815385,
2.602860350286428,
2.5696460593920065,
2.5364027376452056,
2.499446286796604,
2.4587748041127506,
2.414398437185221,
2.3699990328716107,
2.322373696229342,
2.271520771371253,
2.2174389328192197,
2.16337565384239,
2.1183028432496016,
2.082225099873648,
2.055100772005235,
2.0279625371819305,
2.0008103857988204,
1.9736443063300082,
1.9482128147680253,
1.9245345552113182,
1.9026104247588487,
1.8806927939516411,
1.862235672444246,
1.847275503201129,
1.835814081380705,
1.824381436842932,
1.8139629377087627,
1.8045606557581335,
1.7961751115773796,
1.7877868031023945,
1.7793941781790852,
1.7709972329654864,
1.7625959636196327,
1.7541903672600494,
1.7459181197626403,
1.737780757913635,
1.7297783508684146,
1.7217732861435076,
1.7137640932265894,
1.7057507692361864,
1.6918150918099673,
1.6719047669939942,
1.6460167077689267,
1.6201194134191075,
1.5986915141218316,
1.5817353179379183,
1.569199764184379,
1.5566577309558969,
1.545374152761467,
1.5353620432989845,
1.5266220576235803,
1.5178910621476225,
1.5097346239790443,
1.502153039909686,
1.495145456062699,
1.488134243479226,
1.48111939735681,
1.474100442252211,
1.4670307626366115,
1.4599103316162523,
1.45273959485914,
1.445565137158368,
1.4340878115214444,
1.418273806730323,
1.3981208704326855,
1.3779439775587023,
1.3598972673004606,
1.3439818461440451,
1.3301807335621048,
1.316383926863083,
1.3040618749785347,
1.2932153453410835,
1.2838336236692311,
1.2744470198196236,
1.2650555289898042,
1.2556591482982988,
1.2428104897182262,
1.2264996957264114,
1.2067249615595257,
1.1869318618366975,
1.1672278304018044,
1.1476135933863398,
1.1280891949729075,
1.108555289615659,
1.0895983485572227,
1.0712204022764056,
1.0534217504465226,
1.0356155337864215,
1.017807767853702,
1.0
],
"nf_model": {
"enabled": true,
"nf1": 5.727887800964238,
"nf2": 7.727887800964238,
"delta_p": 5.238350271545567
},
"gain_ripple": [
0.1359703369791596,
0.11822862697916037,
0.09542181697916163,
0.06245819697916133,
0.02602813697916062,
-0.0036199830208403228,
-0.018326963020840026,
-0.0246928330208398,
-0.016792253020838643,
-0.0028138630208403015,
0.017572956979162058,
0.038328296979159404,
0.054956336979159914,
0.0670723869791594,
0.07091459697916136,
0.07094413697916124,
0.07114372697916238,
0.07533675697916209,
0.08731066697916035,
0.10313984697916112,
0.12276252697916235,
0.14239527697916188,
0.15945681697916214,
0.1739275269791598,
0.1767381569791624,
0.17037189697916233,
0.15216302697916007,
0.13114358697916018,
0.10802383697916085,
0.08548825697916129,
0.06916723697916183,
0.05848224697916038,
0.05447361697916264,
0.05154489697916276,
0.04946107697915991,
0.04717897697916129,
0.04551704697916037,
0.04467697697916151,
0.04072968697916224,
0.03285456697916089,
0.023488786979161347,
0.01659282697915998,
0.013321846979160057,
0.011234826979162449,
0.01030063697916006,
0.00936596697916059,
0.00874012697916271,
0.00842583697916055,
0.006965146979162284,
0.0040435869791615175,
0.0007104669791608842,
-0.0015763130208377163,
-0.006936193020838033,
-0.016475303020840215,
-0.028748483020837767,
-0.039618433020837784,
-0.051112303020840244,
-0.06468462302083822,
-0.07868024302083754,
-0.09101254302083817,
-0.10103437302083762,
-0.11041488302083735,
-0.11916081302083725,
-0.12789859302083784,
-0.1353792530208402,
-0.14160178302083892,
-0.1455411330208385,
-0.1484450830208388,
-0.14823350302084037,
-0.14591937302083835,
-0.1409032730208395,
-0.13525493302083902,
-0.1279646530208396,
-0.11963431302083904,
-0.11089282302084058,
-0.1027863830208382,
-0.09717347302083823,
-0.09343261302083761,
-0.0913487130208388,
-0.08906007302083907,
-0.0865687230208394,
-0.08407607302083875,
-0.07844600302084004,
-0.06968090302083851,
-0.05947139302083926,
-0.05095282302083959,
-0.042428283020839785,
-0.03218106302083967,
-0.01819858302084043,
-0.0021726530208390216,
0.01393231697916164,
0.028098946979159933,
0.040326236979161934,
0.05257029697916238,
0.06479749697916048,
0.07704745697916238
]
}
}

View File

@@ -1,120 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Nov 10 17:50:46 2017
@author: briantaylor
"""
import numpy as np
from numpy import pi, cos, sqrt, log10
def c():
"""
Returns the speed of light in meters per second
"""
return 299792458.0
def itufs(spacing, startf=191.35, stopf=196.10):
"""Creates an array of frequencies whose default range is
191.35-196.10 THz
:param spacing: Frequency spacing in THz
:param starf: Start frequency in THz
:param stopf: Stop frequency in THz
:type spacing: float
:type startf: float
:type stopf: float
:return an array of frequnecies determined by the spacing parameter
:rtype: numpy.ndarray
"""
return np.arange(startf, stopf + spacing/2, spacing)
def h():
"""
Returns plank's constant in J*s
"""
return 6.62607004e-34
def lin2db(value):
return 10*log10(value)
def db2lin(value):
return 10**(value/10)
def wavelength2freq(value):
""" Converts wavelength units to frequeuncy units.
"""
return c()/value
def freq2wavelength(value):
""" Converts frequency units to wavelength units.
"""
return c()/value
def deltawl2deltaf(delta_wl, wavelength):
""" deltawl2deltaf(delta_wl, wavelength):
delta_wl is BW in wavelength units
wavelength is the center wl
units for delta_wl and wavelength must be same
:param delta_wl: delta wavelength BW in same units as wavelength
:param wavelength: wavelength BW is relevant for
:type delta_wl: float or numpy.ndarray
:type wavelength: float
:return: The BW in frequency units
:rtype: float or ndarray
"""
f = wavelength2freq(wavelength)
return delta_wl*f/wavelength
def deltaf2deltawl(delta_f, frequency):
""" deltawl2deltaf(delta_f, frequency):
converts delta frequency to delta wavelength
units for delta_wl and wavelength must be same
:param delta_f: delta frequency in same units as frequency
:param frequency: frequency BW is relevant for
:type delta_f: float or numpy.ndarray
:type frequency: float
:return: The BW in wavelength units
:rtype: float or ndarray
"""
wl = freq2wavelength(frequency)
return delta_f*wl/frequency
def rrc(ffs, baud_rate, alpha):
""" rrc(ffs, baud_rate, alpha): computes the root-raised cosine filter
function.
:param ffs: A numpy array of frequencies
:param baud_rate: The Baud Rate of the System
:param alpha: The roll-off factor of the filter
:type ffs: numpy.ndarray
:type baud_rate: float
:type alpha: float
:return: hf a numpy array of the filter shape
:rtype: numpy.ndarray
"""
Ts = 1/baud_rate
l_lim = (1 - alpha)/(2 * Ts)
r_lim = (1 + alpha)/(2 * Ts)
hf = np.zeros(np.shape(ffs))
slope_inds = np.where(
np.logical_and(np.abs(ffs) > l_lim, np.abs(ffs) < r_lim))
hf[slope_inds] = 0.5 * (1 + cos((pi * Ts / alpha) *
(np.abs(ffs[slope_inds]) - l_lim)))
p_inds = np.where(np.logical_and(np.abs(ffs) > 0, np.abs(ffs) < l_lim))
hf[p_inds] = 1
return sqrt(hf)

View File

@@ -1,3 +1,13 @@
#!/usr/bin/env
"""
@author: briantaylor
@author: giladgoldfarb
@author: jeanluc-auge
Transmission setup example:
reads from network json (default = examples/edfa/edfa_example_network.json)
propagates a 96 channels comb
"""
from argparse import ArgumentParser
from json import load
from sys import exit
@@ -6,12 +16,12 @@ from logging import getLogger, basicConfig, INFO, ERROR, DEBUG
from matplotlib.pyplot import show, axis
from networkx import (draw_networkx_nodes, draw_networkx_edges,
draw_networkx_labels)
draw_networkx_labels, dijkstra_path)
from gnpy.core import network_from_json
from gnpy.core.elements import Transceiver, Fiber
from gnpy.core.elements import Transceiver, Fiber, Edfa
from gnpy.core.info import SpectralInformation, Channel, Power
from gnpy.core.algorithms import closed_paths
#from gnpy.core.algorithms import closed_paths
logger = getLogger(__package__ or __file__)
@@ -29,6 +39,7 @@ def main(args):
json_data = load(f)
network = network_from_json(json_data)
"""jla put in comment
pos = {n: (n.lng, n.lat) for n in network.nodes()}
labels_pos = {n: (long-.5, lat-.5) for n, (long, lat) in pos.items()}
size = [20 if isinstance(n, Fiber) else 80 for n in network.nodes()]
@@ -36,17 +47,27 @@ def main(args):
for n in network.nodes()]
labels = {n: n.location.city if isinstance(n, Transceiver) else ''
for n in network.nodes()}
"""
si = SpectralInformation(
Channel(1, 193.95e12, '16-qam', 32e9, 0, # 193.95 THz, 32 Gbaud
Power(1e-3, 1e-6, 1e-6)), # 1 mW, 1uW, 1uW
Channel(1, 195.95e12, '16-qam', 32e9, 0, # 195.95 THz, 32 Gbaud
Power(1.2e-3, 1e-6, 1e-6)), # 1.2 mW, 1uW, 1uW
)
spacing = 0.05 #THz
si = SpectralInformation() # !! SI units W, Hz
si = si.update(carriers=tuple(Channel(f+1, (191.3+spacing*(f+1))*1e12,
32e9, 0.15, Power(1e-3, 0, 0)) for f in range(96)))
nodes = [n for n in network.nodes() if isinstance(n, Transceiver)]
source, sink = choice(nodes), choice(nodes)
source, sink = nodes[0], nodes[1]
results = dijkstra_path(network, source, sink)
print('dijkstra path:', results)
for ne in results:
si = ne(si)
print('total SNR comb in signal bandwidth', sink.snr(si))
#print(Rx_signal_power[0])
#p.array([c.power.signal+c.power.nli+c.power.ase for c in carriers])
"""jla put in comment
results = list(islice(closed_paths(network, source, sink, si), 3))
paths = [[n for _, n, _ in r] for r in results]
infos = {}
@@ -70,10 +91,11 @@ def main(args):
title(f'Propagating from {source.loc.city} to {sink.loc.city}')
axis('off')
show()
"""
parser = ArgumentParser()
parser.add_argument('filename', nargs='?', type=Path,
default= Path(__file__).parent / '../examples/coronet.conus.json')
default= Path(__file__).parent / 'edfa/edfa_example_network.json')
parser.add_argument('-v', '--verbose', action='count')
if __name__ == '__main__':

View File

@@ -1,31 +1,56 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Network elements class with SpectralInformation propagation using
__call__ and propagate methods
@author: giladgoldfarb
@author: briantaylor
@author: jeanluc-auge
@acknowledgement : Dave Boertjes
"""
import numpy as np
from scipy.constants import c, h
from gnpy.core.node import Node
from gnpy.core.units import UNITS
from gnpy.core.utils import lin2db, db2lin
from gnpy.core.utils import lin2db, db2lin, itufs
class Transceiver(Node):
def __init__(self, config):
super().__init__(config)
def snr(self, spectral_info):
osnr_ase = [lin2db(c.power.signal/c.power.ase)
for c in spectral_info.carriers
if c.power.ase>1e-13]
ratio_01nm = [lin2db(12.5e9/c.baud_rate) for c in spectral_info.carriers]
osnr_ase_01nm = [ase - ratio for ase, ratio in zip(osnr_ase, ratio_01nm)]
osnr_nli = [lin2db(c.power.signal/c.power.nli) for c in spectral_info.carriers]
snr = [lin2db(c.power.signal/(c.power.nli+c.power.ase)) for c in spectral_info.carriers]
print('OSNR in signal bandwidth={}dB and in 0.1nm={}dB'.format(osnr_ase[0], osnr_ase_01nm[0]))
return snr
def __call__(self, spectral_info):
return spectral_info
class Fiber(Node):
def __init__(self, config):
super().__init__(config)
self.length = self.params.length * \
UNITS[self.params.length_units]
UNITS[self.params.length_units] #length in km
self.loss_coef = self.params.loss_coef #lineic loss dB/km
self.lin_loss_coef = self.params.loss_coef / 4.3429448190325184
#TODO discuss factor 2 in the linear lineic attenuation
def __repr__(self):
return f'{type(self).__name__}(uid={self.uid}, length={self.length})'
def lin_attenuation(self):
attenuation = self.length * self.loss_coef
return db2lin(attenuation)
def effective_length(self, loss_coef):
alpha_dict = self.dbkm_2_lin(loss_coef)
alpha = alpha_dict['alpha_acoef']
@@ -63,11 +88,15 @@ class Fiber(Node):
return b2
def propagate(self, *carriers):
#TODO integrate and call the gn.ole module to calculate carrier nli noise in fiber
carrier_nli = db2lin(-28)*1e-3 #temporary Cte nli noise is added for debug
i=0
for carrier in carriers:
pwr = carrier.power
pwr = pwr._replace(signal=0.5 * pwr.signal * .5,
nonlinear_interference=2 * pwr.nli,
amplified_spontaneous_emission=2 * pwr.ase)
pwr = pwr._replace(signal=pwr.signal/self.lin_attenuation(),
nonlinear_interference=(pwr.nli+carrier_nli)/self.lin_attenuation(),
amplified_spontaneous_emission=pwr.ase/self.lin_attenuation())
i+=1
yield carrier._replace(power=pwr)
def __call__(self, spectral_info):
@@ -78,23 +107,60 @@ class Fiber(Node):
class Edfa(Node):
def __init__(self, config):
super().__init__(config)
self.gain_target = None
self.tilt_target = None
self.nf = None
self.interpol_dgt = None #inerpolated dynamic gain tilt: N numpy array
self.interpol_gain_ripple = None #gain ripple: N numpy array
self.interpol_nf_ripple = None #nf_ripple: N numpy array
self.channel_freq = None #SI channel frequencies: N numpy array
"""nf and gprofile attributs are set by interpol_params"""
self.nf = None #edfa nf @ operational.gain_target: N numpy array
self.gprofile = None
def noise_profile(self, gain, ffs, df):
""" noise_profile(nf, gain, ffs, df) computes amplifier ase
def interpol_params(self, frequencies, pin):
"""interpolate SI channel frequencies with the edfa dgt and gain_ripple frquencies from json
set the edfa class __init__ None parameters :
self.channel_freq, self.nf, self.interpol_dgt and self.interpol_gain_ripple
"""
#TODO read amplifier actual frequencies from additional params in json
amplifier_freq = itufs(0.05)*1e12 # Hz
self.channel_freq = frequencies
self.interpol_dgt = np.interp(self.channel_freq, amplifier_freq, self.params.dgt)
self.interpol_gain_ripple = np.interp(self.channel_freq, amplifier_freq, self.params.gain_ripple)
self.interpol_nf_ripple = np.interp(self.channel_freq, amplifier_freq, self.params.nf_ripple)
:param nf: Noise figure in dB
:param gain: Actual gain calculated for the EDFA in dB units
:param ffs: A numpy array of frequencies
:param df: the reference bw in THz
:type nf: numpy.ndarray
:type gain: numpy.ndarray
:type ffs: numpy.ndarray
:type df: float
:return: the asepower in dBm
:rtype: numpy.ndarray
"""check power saturation and correct target_gain accordingly:"""
tot_in_power_db = lin2db(np.sum(pin*1e3))
gain_target = min(self.operational.gain_target, self.params.p_max-tot_in_power_db)
self.operational.gain_target = gain_target
self._calc_nf()
self._gain_profile(pin)
def _calc_nf(self):
"""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 : tbd alarm rising or input VOA padding in case
#gain_min > gain_target TBD:
pad = max(self.params.gain_min - self.operational.gain_target, 0)
gain_target = self.operational.gain_target + pad
dg = gain_target - self.params.gain_flatmax # ! <0
if self.params.nf_model.enabled:
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))
else:
nf_avg = np.polyval(self.params.nf_fit_coeff, dg)
self.nf = self.interpol_nf_ripple + nf_avg + pad #input VOA = 1 for 1 NF degradation
def noise_profile(self, bw):
""" noise_profile(bw) computes amplifier ase (W) in signal bw (Hz)
noise is calculated at amplifier input
:bw: signal bandwidth = baud rate in Hz
:type bw: float
:return: the asepower in W in the signal bandwidth bw for 96 channels
:return type: numpy array of float
ASE POWER USING PER CHANNEL GAIN PROFILE
INPUTS:
@@ -122,22 +188,22 @@ class Edfa(Node):
quoting power spectral density in the same BW for both signal and ASE,
e.g. 12.5GHz."""
h_mWThz = 1e-3 * h * (1e14)**2
nf_lin = db2lin(self.nf)
g_lin = db2lin(gain)
ase = h_mWThz * df * ffs * (nf_lin * g_lin - 1)
asedb = lin2db(ase)
nchan = list(range(len(self.channel_freq)))
df = np.array([bw]*(nchan[-1] + 1)) #Hz
ase = h * df * self.channel_freq * db2lin(self.nf) #W
return ase #in W, @amplifier input
#checked 02/15/2018 @ 02:00pm -45dBm @ nf = 8.8dB in 32GHz
return asedb
def gain_profile(self, Pin):
def _gain_profile(self, pin):
"""
:param dfg: design flat gain
Pin : input power / channel in W
:param gain_ripple: design flat gain
:param dgt: design gain tilt
:param Pin: channing input power profile
:param Pin: total input power in W
:param gp: Average gain setpoint in dB units
:param gtp: gain tilt setting
:type dfg: numpy.ndarray
:type gain_ripple: numpy.ndarray
:type dgt: numpy.ndarray
:type Pin: numpy.ndarray
:type gp: float
@@ -147,7 +213,7 @@ class Edfa(Node):
AMPLIFICATION USING INPUT PROFILE
INPUTS:
DFG - vector of length number of channels or spectral slices
gain_ripple - vector of length number of channels or spectral slices
DGT - vector of length number of channels or spectral slices
Pin - input powers vector of length number of channels or
spectral slices
@@ -158,6 +224,7 @@ class Edfa(Node):
amp gain per channel or spectral slice
NOTE: there is no checking done for violations of the total output
power capability of the amp.
EDIT OF PREVIOUS NOTE: power violation now added in interpol_params
Ported from Matlab version written by David Boerges at Ciena.
Based on:
R. di Muro, "The Er3+ fiber gain coefficient derived from a dynamic
@@ -168,25 +235,23 @@ class Edfa(Node):
err_tolerance = 1.0e-11
simple_opt = True
# TODO make all values linear unit and convert to dB units as needed
# within this function.
nchan = np.arange(len(Pin))
# TODO check what param should be used (currently length(dgt))
nchan = np.arange(len(self.interpol_dgt))
# TODO find a way to use these or lose them. Primarily we should have
# a way to determine if exceeding the gain or output power of the amp
tot_in_power_db = lin2db(np.sum(db2lin(Pin)))
tot_in_power_db = lin2db(np.sum(pin*1e3)) # ! Pin expressed in W
# Linear fit to get the
p = np.polyfit(nchan, self.params.dgt, 1)
p = np.polyfit(nchan, self.interpol_dgt, 1)
dgt_slope = p[0]
# Calculate the target slope- Currently assumes equal spaced channels
# TODO make it so that supports arbitrary channel spacing.
targ_slope = self.tilt_target / (len(nchan) - 1)
targ_slope = self.operational.tilt_target / (len(nchan) - 1)
# 1st estimate of DGT scaling
dgts1 = targ_slope / dgt_slope
# when simple_opt is true code makes 2 attempts to compute gain and
# the internal voa value. This is currently here to provide direct
# comparison with original Matlab code. Will be removed.
@@ -195,52 +260,73 @@ class Edfa(Node):
if simple_opt:
# 1st estimate of Er gain & voa loss
g1st = np.array(self.params.dfg) + \
np.array(self.params.dgt) * dgts1
voa = lin2db(np.mean(db2lin(g1st))) - self.gain_target
g1st = np.array(self.interpol_gain_ripple) + self.params.gain_flatmax + \
np.array(self.interpol_dgt) * dgts1
voa = lin2db(np.mean(db2lin(g1st))) - self.operational.gain_target
# 2nd estimate of Amp ch gain using the channel input profile
g2nd = g1st - voa
pout_db = lin2db(np.sum(db2lin(Pin + g2nd)))
dgts2 = self.gain_target - (pout_db - tot_in_power_db)
pout_db = lin2db(np.sum(pin*1e3*db2lin(g2nd)))
dgts2 = self.operational.gain_target - (pout_db - tot_in_power_db)
# Center estimate of amp ch gain
xcent = dgts2
gcent = g1st - voa + np.array(self.params.dgt) * xcent
pout_db = lin2db(np.sum(db2lin(Pin + gcent)))
gcent = g1st - voa + np.array(self.interpol_dgt) * xcent
pout_db = lin2db(np.sum(pin*1e3*db2lin(gcent)))
gavg_cent = pout_db - tot_in_power_db
# Lower estimate of Amp ch gain
deltax = np.max(g1st) - np.min(g1st)
xlow = dgts2 - deltax
glow = g1st - voa + np.array(self.params.dgt) * xlow
pout_db = lin2db(np.sum(db2lin(Pin + glow)))
glow = g1st - voa + np.array(self.interpol_dgt) * xlow
pout_db = lin2db(np.sum(pin*1e3*db2lin(glow)))
gavg_low = pout_db - tot_in_power_db
# Upper gain estimate
xhigh = dgts2 + deltax
ghigh = g1st - voa + np.array(self.params.dgt) * xhigh
pout_db = lin2db(np.sum(db2lin(Pin + ghigh)))
ghigh = g1st - voa + np.array(self.interpol_dgt) * xhigh
pout_db = lin2db(np.sum(pin*1e3*db2lin(ghigh)))
gavg_high = pout_db - tot_in_power_db
# compute slope
slope1 = (gavg_low - gavg_cent) / (xlow - xcent)
slope2 = (gavg_cent - gavg_high) / (xcent - xhigh)
if np.abs(self.gain_target - gavg_cent) <= err_tolerance:
if np.abs(self.operational.gain_target - gavg_cent) <= err_tolerance:
dgts3 = xcent
elif self.gain_target < gavg_cent:
dgts3 = xcent - (gavg_cent - self.gain_target) / slope1
elif self.operational.gain_target < gavg_cent:
dgts3 = xcent - (gavg_cent - self.operational.gain_target) / slope1
else:
dgts3 = xcent + (-gavg_cent + self.gain_target) / slope2
dgts3 = xcent + (-gavg_cent + self.operational.gain_target) / slope2
gprofile = g1st - voa + np.array(self.params.dgt) * dgts3
gprofile = g1st - voa + np.array(self.interpol_dgt) * dgts3
#print(gprofile[0])
else:
gprofile = None
return gprofile
self.gprofile = gprofile
def calc_nf(self):
dg = self.gain_target - np.mean(self.params.dfg)
nf_avg = np.polyval(self.params.nf_fit_coeff, dg)
self.nf = self.params.nf_ripple + nf_avg
def propagate(self, *carriers):
"""add ase noise to the propagating carriers of SpectralInformation"""
i = 0
pin = np.array([c.power.signal+c.power.nli+c.power.ase for c in carriers]) #pin in W
freq = np.array([c.frequency for c in carriers])
#interpolate the amplifier vectors with the carriers freq, calculate nf & gain profile
self.interpol_params(freq, pin)
gain = db2lin(self.gprofile)
for carrier in carriers:
pwr = carrier.power
bw = carrier.baud_rate
carrier_ase = self.noise_profile(bw)[i]
pwr = pwr._replace(signal=pwr.signal*gain[i],
nonlinear_interference=pwr.nli*gain[i],
amplified_spontaneous_emission=(pwr.ase+carrier_ase)*gain[i])
i += 1
yield carrier._replace(power=pwr)
def __call__(self, spectral_info):
carriers = tuple(self.propagate(*spectral_info.carriers))
return spectral_info.update(carriers=carriers)

View File

@@ -17,10 +17,9 @@ class Power(namedtuple('Power', 'signal nonlinear_interference amplified_spontan
_ABBREVS = {'nli': 'nonlinear_interference',
'ase': 'amplified_spontaneous_emission',}
class Channel(namedtuple('Channel', 'channel_number frequency modulation baud_rate alpha power'), ConvenienceAccess):
class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off power'), ConvenienceAccess):
_ABBREVS = {'channel': 'channel_number',
'num_chan': 'channel_number',
'num_carriers': 'num_carriers',
'ffs': 'frequency',
'freq': 'frequency',}
@@ -30,13 +29,22 @@ class SpectralInformation(namedtuple('SpectralInformation', 'carriers'), Conveni
if __name__ == '__main__':
si = SpectralInformation(
Channel(1, 193.95e12, '16-qam', 32e9, 0, # 193.95 THz, 32 Gbaud
Channel(1, 193.95e12, 32e9, 0.15, # 193.95 THz, 32 Gbaud
Power(1e-3, 1e-6, 1e-6)), # 1 mW, 1uW, 1uW
Channel(1, 195.95e12, '16-qam', 32e9, 0, # 195.95 THz, 32 Gbaud
Channel(1, 195.95e12, 32e9, 0.15, # 195.95 THz, 32 Gbaud
Power(1.2e-3, 1e-6, 1e-6)), # 1.2 mW, 1uW, 1uW
)
si = SpectralInformation()
spacing = 0.05 #THz
si = si.update(carriers=tuple(Channel(f+1, 191.3+spacing*(f+1), 32e9, 0.15, Power(1e-3, f, 1)) for f in range(96)))
print(f'si = {si}')
print(f'si = {si.carriers[0].power.nli}')
print(f'si = {si.carriers[20].power.nli}')
"""
si2 = si.update(carriers=tuple(c.update(power = c.power.update(nli = c.power.nli * 1e5))
for c in si.carriers))
print(f'si2 = {si2}')
"""

View File

@@ -36,6 +36,8 @@ class Node:
self.params = self.config.params
if hasattr(self.config, 'metadata'):
self.metadata = self.config.metadata
if hasattr(self.config, 'operational'):
self.operational = self.config.operational
@property
def coords(self):

View File

@@ -1,2 +1,2 @@
UNITS = {'m': 1,
'km': 1e3}
UNITS = {'m': 1e-3,
'km': 1}