mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2025-10-30 17:47:50 +00:00
Compare commits
183 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9be0607b2e | ||
|
|
6dc3f2ffa6 | ||
|
|
df7cbf0b76 | ||
|
|
19b6378b1a | ||
|
|
29cb2b50a8 | ||
|
|
5932c014a0 | ||
|
|
a3f75e9af0 | ||
|
|
9ed31e2c4e | ||
|
|
8599f63fbf | ||
|
|
07de78cb05 | ||
|
|
f764bbe080 | ||
|
|
7aa343b767 | ||
|
|
69198779a7 | ||
|
|
3088032ad8 | ||
|
|
dfaa12598d | ||
|
|
1c724cdc6c | ||
|
|
b59423fb01 | ||
|
|
96bceed102 | ||
|
|
f8e146b9b9 | ||
|
|
c54ddb644a | ||
|
|
2be616411f | ||
|
|
7415744807 | ||
|
|
23905a90f4 | ||
|
|
83444b329e | ||
|
|
9898dc85a9 | ||
|
|
661287f600 | ||
|
|
353c6a77e9 | ||
|
|
8c006fec3f | ||
|
|
5896b7ce6a | ||
|
|
f831f6cb2c | ||
|
|
b505a1ae01 | ||
|
|
a0fac17c0e | ||
|
|
851f606fc0 | ||
|
|
8940430ee0 | ||
|
|
06d3927275 | ||
|
|
7cea13e90d | ||
|
|
de2a504078 | ||
|
|
df14b441a3 | ||
|
|
ab1391440f | ||
|
|
310d32dcea | ||
|
|
f8cd822c92 | ||
|
|
3ade885e41 | ||
|
|
008a88192c | ||
|
|
639e7f012c | ||
|
|
c8ecc16648 | ||
|
|
225fb1ec0c | ||
|
|
c943e0e9b4 | ||
|
|
3d8ac83fcc | ||
|
|
06399ca8af | ||
|
|
8f8fc13ded | ||
|
|
2b018cb9a5 | ||
|
|
9577f4c9a3 | ||
|
|
771d98cc10 | ||
|
|
91e875d54c | ||
|
|
da39f1489f | ||
|
|
2940576681 | ||
|
|
a49c137b78 | ||
|
|
87e748cd83 | ||
|
|
94c2e332bb | ||
|
|
1437b6010e | ||
|
|
5fc203482d | ||
|
|
ff6d81b749 | ||
|
|
167e644bd0 | ||
|
|
d1c7489768 | ||
|
|
a783e165dd | ||
|
|
45bdd82864 | ||
|
|
79c5cb6b78 | ||
|
|
63ade5fdef | ||
|
|
853b8c7aa3 | ||
|
|
0655fb60de | ||
|
|
69b28e3508 | ||
|
|
5c16d9539f | ||
|
|
e3acf02bde | ||
|
|
10268e82d4 | ||
|
|
bc44bf726a | ||
|
|
9839681bc0 | ||
|
|
beb292cb07 | ||
|
|
29c4134f60 | ||
|
|
a6157f328d | ||
|
|
c6432e2c28 | ||
|
|
4a756bf2a9 | ||
|
|
b77cc5cd40 | ||
|
|
9f94af6ff7 | ||
|
|
7aba40cd5b | ||
|
|
fb6c17c5ff | ||
|
|
4504d80c80 | ||
|
|
8a8c8989cb | ||
|
|
20731dbe4b | ||
|
|
55393ca9eb | ||
|
|
e9aa4d5601 | ||
|
|
6d49769df9 | ||
|
|
0333e9d094 | ||
|
|
53bedca50a | ||
|
|
b9518ca987 | ||
|
|
794e713d6d | ||
|
|
5e1fd7501e | ||
|
|
c86ea206d9 | ||
|
|
b810cf84c2 | ||
|
|
15bc5db2ed | ||
|
|
4845d9005e | ||
|
|
bed4e9f1e1 | ||
|
|
7b20db10cc | ||
|
|
d59b3e8c46 | ||
|
|
8fccbb0ac2 | ||
|
|
f36a610e52 | ||
|
|
479a2f358e | ||
|
|
8795c357ae | ||
|
|
d8cb7526bb | ||
|
|
6ead8e391b | ||
|
|
362f45083d | ||
|
|
36218037ec | ||
|
|
584b56bc83 | ||
|
|
d33602e131 | ||
|
|
4304b49bf0 | ||
|
|
4f5325acac | ||
|
|
f8a40bfaf0 | ||
|
|
52dfb20a2b | ||
|
|
34b20cdfe0 | ||
|
|
f462201499 | ||
|
|
5106bdf634 | ||
|
|
b93c6dbcbd | ||
|
|
2ca141baba | ||
|
|
01fe5d2147 | ||
|
|
74830cede4 | ||
|
|
2f21cc29f7 | ||
|
|
05f8d97d68 | ||
|
|
04c4795192 | ||
|
|
ff6e379b6f | ||
|
|
25cfc375bc | ||
|
|
627184ef2d | ||
|
|
180e1178ef | ||
|
|
1c33454ce3 | ||
|
|
c7abe11dd8 | ||
|
|
7a6feccc8b | ||
|
|
f05e578f51 | ||
|
|
277ebea2b5 | ||
|
|
4982c5e147 | ||
|
|
726e217205 | ||
|
|
695c538ee5 | ||
|
|
e9b287ceac | ||
|
|
ecdae68b62 | ||
|
|
e737564b32 | ||
|
|
75c4a8d96d | ||
|
|
28dc1b050f | ||
|
|
4a9a7e31ba | ||
|
|
92da31f905 | ||
|
|
afd264c816 | ||
|
|
d676e3e217 | ||
|
|
4af117aacf | ||
|
|
b27e567a5e | ||
|
|
8ec9a5bf99 | ||
|
|
f129f92cfd | ||
|
|
ef87373d4b | ||
|
|
ff938d4ced | ||
|
|
ae555e0ce1 | ||
|
|
44db6093f5 | ||
|
|
d4bf6ce201 | ||
|
|
0b1c7bfa30 | ||
|
|
80f8a345b5 | ||
|
|
c929ce0257 | ||
|
|
7d3ab357d0 | ||
|
|
84d87602b8 | ||
|
|
14938ea7dd | ||
|
|
be0c052e5e | ||
|
|
af90a6658e | ||
|
|
9720c979b3 | ||
|
|
7c86186416 | ||
|
|
404dbfe725 | ||
|
|
02eac208dd | ||
|
|
bc9a0432cd | ||
|
|
dba7846d6c | ||
|
|
238d7723bb | ||
|
|
d2724bb1a5 | ||
|
|
8e046a9b50 | ||
|
|
1b808afd5d | ||
|
|
8d6f69eb05 | ||
|
|
c4562df955 | ||
|
|
8e1e8a8be3 | ||
|
|
363e92e072 | ||
|
|
c5a52fdb6d | ||
|
|
2dd096cfab | ||
|
|
80e1ce12ce | ||
|
|
2777d957e4 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.ipynb_checkpoints
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
4
.readthedocs.yml
Normal file
4
.readthedocs.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
build:
|
||||
image: latest
|
||||
python:
|
||||
version: 3.6
|
||||
@@ -3,7 +3,8 @@ python:
|
||||
- "3.6"
|
||||
# command to install dependencies
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
- python setup.py install
|
||||
# command to run tests
|
||||
before_script:
|
||||
script:
|
||||
- pytest
|
||||
|
||||
@@ -13,7 +13,9 @@ To learn how to contribute, please see CONTRIBUTING.md
|
||||
- Gert Grammel (Juniper Networks) <ggrammel@juniper.net>
|
||||
- Gilad Goldfarb (Facebook) <giladg@fb.com>
|
||||
- James Powell (Telecom Infra Project) <james.powell@telecominfraproject.com>
|
||||
- Jeanluc Auge (Orange) <jeanluc.auge@orange.com>
|
||||
- Jeanluc Augé (Orange) <jeanluc.auge@orange.com>
|
||||
- Jonas Mårtensson (RISE) <jonas.martensson@ri.se>
|
||||
- Mattia Cantono (Politecnico di Torino) <mattia.cantono@polito.it>
|
||||
- Roberts Miculens (Lattelecom) <roberts.miculens@lattelecom.lv>
|
||||
- Vittorio Curri (Politecnico di Torino) <vittorio.curri@polito.it>
|
||||
- Xufeng Liu (Jabil) <xufeng_liu@jabil.com>
|
||||
|
||||
259
Excel_userguide.rst
Normal file
259
Excel_userguide.rst
Normal file
@@ -0,0 +1,259 @@
|
||||
|
||||
How to prepare the Excel input file
|
||||
-----------------------------------
|
||||
|
||||
`examples/transmission_main_example.py <examples/transmission_main_example.py>`_ gives the possibility to use an excel input file instead of a json file. The program then will generate the corresponding json file for you.
|
||||
|
||||
The file named 'meshTopologyExampleV2.xls' is an example.
|
||||
|
||||
In order to work the excel file MUST contain at least 2 sheets:
|
||||
|
||||
- Nodes
|
||||
- Links
|
||||
|
||||
(In progress) The File MAY contain an additional sheet:
|
||||
|
||||
- Eqt
|
||||
- Service
|
||||
|
||||
Nodes sheet
|
||||
-----------
|
||||
|
||||
Nodes sheet contains seven columns.
|
||||
Each line represents a 'node' (ROADM site or an in line amplifier site ILA)::
|
||||
|
||||
City (Mandatory) ; State ; Country ; Region ; Latitude ; Longitude ; Type
|
||||
|
||||
- **City** is used for the name of a node of the graph. It accepts letters, numbers,underscore,dash, blank... (not exhaustive). The user may want to avoid commas for future CSV exports.
|
||||
|
||||
**City name MUST be unique**
|
||||
|
||||
- **Type** is not mandatory.
|
||||
|
||||
- If not filled, it will be interpreted as an 'ILA' site if node degree is 2 and as a ROADM otherwise.
|
||||
- If filled, it can take "ROADM", "FUSED" or "ILA" values. If another string is used, it will be considered as not filled. FUSED means that ingress and egress spans will be fused together.
|
||||
|
||||
- *State*, *Country*, *Region* are not mandatory.
|
||||
"Region" is a holdover from the CORONET topology reference file `CORONET_Global_Topology.xls <examples/CORONET_Global_Topology.xls>`_. CORONET separates its network into geographical regions (Europe, Asia, Continental US.) This information is not used by gnpy.
|
||||
|
||||
- *Longitude*, *Latitude* are not mandatory. If filled they should contain numbers.
|
||||
|
||||
**There MUST NOT be empty line(s) between two nodes lines**
|
||||
|
||||
|
||||
Links sheet
|
||||
-----------
|
||||
|
||||
Links sheet must contain sixteen columns::
|
||||
|
||||
<-- east cable from a to z --> <-- west from z to -->
|
||||
NodeA ; NodeZ ; Distance km ; Fiber type ; Lineic att ; Con_in ; Con_out ; PMD ; Cable Id ; Distance km ; Fiber type ; Lineic att ; Con_in ; Con_out ; PMD ; Cable Id
|
||||
|
||||
|
||||
Links sheets MUST contain all links between nodes defined in Nodes sheet.
|
||||
Each line represents a 'bidir link' between two nodes. The two directions are represented on a single line with "east cable from a to z" fields and "west from z to a" fields. Values for 'a to z' may be different from values from 'z to a'.
|
||||
Since both direction of a bidir 'a-z' link are described on the same line (east and west), 'z to a' direction MUST NOT be repeated in a different line. If repeated, it will generate another parrallel bidir link between the same end nodes.
|
||||
|
||||
|
||||
Parameters for "east cable from a to z" and "west from z to a" are detailed in 2x7 columns. If not filled, "west from z to a" is copied from "east cable from a to z".
|
||||
|
||||
For example, a line filled with::
|
||||
|
||||
node6 ; node3 ; 80 ; SSMF ; 0.2 ; 0.5 ; 0.5 ; 0.1 ; cableB ; ; ; 0.21 ; 0.2 ; ; ;
|
||||
|
||||
will generate a unidir fiber span from node6 to node3 with::
|
||||
|
||||
[node6 node3 80 SSMF 0.2 0.5 0.5 0.1 cableB]
|
||||
|
||||
and a fiber span from node3 to node6::
|
||||
|
||||
[node6 node3 80 SSMF 0.21 0.2 0.5 0.1 cableB] attributes.
|
||||
|
||||
- **NodeA** and **NodeZ** are Mandatory.
|
||||
They are the two endpoints of the link. They MUST contain a node name from the **City** names listed in Nodes sheet.
|
||||
|
||||
- **Distance km** is not mandatory.
|
||||
It is the link length.
|
||||
|
||||
- If filled it MUST contain numbers. If empty it is replaced by a default "80" km value.
|
||||
- If value is below 150 km, it is considered as a single (bidirectional) fiber span.
|
||||
- If value is over 150 km the `transmission_main_example.py <examples/transmission_main_example.py>`_ program will automatically suppose that intermediate span description are required and will generate fiber spans elements with "_1","_2", ... trailing strings which are not visible in the json output. The reason for the splitting is that current edfa usually do not support large span loss. The current assumption is that links larger than 150km will require intermediate amplification. This value will be revisited when Raman amplification is added”
|
||||
|
||||
- **Fiber type** is not mandatory.
|
||||
|
||||
If filled it must contain types listed in `eqpt_config.json <examples/eqpt_config.json>`_ in "Fiber" list "type_variety".
|
||||
If not filled it takes "SSMF" as default value.
|
||||
|
||||
- **Lineic att** is not mandatory.
|
||||
|
||||
It is the lineic attenuation expressed in dB/km.
|
||||
If filled it must contain positive numbers.
|
||||
If not filled it takes "0.2" dB/km value
|
||||
|
||||
- *Con_in*, *Con_out* are not mandatory.
|
||||
|
||||
They are the connector loss in dB at ingress and egress of the fiber spans.
|
||||
If filled they must contain positive numbers.
|
||||
If not filled they take "0.5" dB default value.
|
||||
|
||||
- *PMD* is not mandatory and and is not used yet.
|
||||
|
||||
It is the PMD value of the link in ps.
|
||||
If filled they must contain positive numbers.
|
||||
If not filled, it takes "0.1" ps value.
|
||||
|
||||
- *Cable Id* is not mandatory.
|
||||
If filled they must contain strings with the same constraint as "City" names. Its value is used to differenate links having the same end points. In this case different Id should be used. Cable Ids are not meant to be unique in general.
|
||||
|
||||
|
||||
|
||||
|
||||
(in progress)
|
||||
|
||||
Eqpt sheet
|
||||
----------
|
||||
|
||||
Eqt sheet is optional. It lists the amplifiers types and characteristics on each degree of the *Node A* line.
|
||||
Eqpt sheet must contain twelve columns::
|
||||
|
||||
<-- east cable from a to z --> <-- west from z to a -->
|
||||
Node A ; Node Z ; amp type ; att_in ; amp gain ; tilt ; att_out ; amp type ; att_in ; amp gain ; tilt ; att_out
|
||||
|
||||
If the sheet is present, it MUST have as many lines as egress directions of ROADMs defined in Links Sheet.
|
||||
|
||||
For example, consider the following list of links (A,B and C being a ROADM and amp# ILAs)
|
||||
|
||||
::
|
||||
|
||||
A - amp1
|
||||
amp1 - amp2
|
||||
Amp2 - B
|
||||
A - amp3
|
||||
amp3 - C
|
||||
|
||||
then Eqpt sheet should contain:
|
||||
- one line for each ILAs: amp1, amp2, amp3
|
||||
- one line for each degree 1 ROADMs B and C
|
||||
- two lines for ROADM A which is a degree 2 ROADM
|
||||
|
||||
::
|
||||
|
||||
A - amp1
|
||||
amp1 - amp2
|
||||
Amp2 - B
|
||||
A - amp3
|
||||
amp3 - C
|
||||
B - amp2
|
||||
C - amp3
|
||||
|
||||
|
||||
In case you already have filled Nodes and Links sheets `create_eqpt_sheet.py <examples/create_eqpt_sheet.py>`_ can be used to automatically create a template for the mandatory entries of the list.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ cd examples
|
||||
$ python create_eqpt_sheet.py meshTopologyExampleV2.xls
|
||||
|
||||
This generates a text file meshTopologyExampleV2_eqt_sheet.txt whose content can be directly copied into the Eqt sheet of the excel file. The user then can fill the values in the rest of the columns.
|
||||
|
||||
|
||||
- **Node A** is mandatory. It is the name of the node (as listed in Nodes sheet).
|
||||
If Node A is a 'ROADM' (Type attribute in sheet Node), its number of occurence must be equal to its degree.
|
||||
If Node A is an 'ILA' it should appear only once.
|
||||
|
||||
- **Node Z** is mandatory. It is the egress direction from the *Node A* site. Multiple Links between the same Node A and NodeZ is not supported.
|
||||
|
||||
- **amp type** is not mandatory.
|
||||
If filled it must contain types listed in `eqpt_config.json <examples/eqpt_config.json>`_ in "Edfa" list "type_variety".
|
||||
If not filled it takes "std_medium_gain" as default value.
|
||||
|
||||
- **amp_gain** is not mandatory. It is the value to be set on the amplifier (in dB).
|
||||
If not filled, it will be determined with design rules in the convert.py file.
|
||||
If filled, it must contain positive numbers.
|
||||
|
||||
- *att_in* and *att_out* are not mandatory and are not used yet. They are the value of the attenautor at input and output of amplifier (in dB).
|
||||
If filled they must contain positive numbers.
|
||||
|
||||
- *tilt* --TODO--
|
||||
|
||||
# to be completed #
|
||||
|
||||
(in progress)
|
||||
|
||||
Service sheet
|
||||
-------------
|
||||
|
||||
Service sheet is optional. It lists the services for which path and feasibility must be computed with path_requests_run.py.
|
||||
|
||||
Service sheet must contain 11 columns::
|
||||
|
||||
route id ; Source ; Destination ; TRX type ; Mode ; System: spacing ; System: input power (dBm) ; System: nb of channels ; routing: disjoint from ; routing: path ; routing: is loose?
|
||||
|
||||
- **route id** is mandatory. It must be unique. It is the identifier of the request. It can be an integer or a string (do not use blank or dash)
|
||||
|
||||
- **Source** is mandatory. It is the name of the source node (as listed in Nodes sheet). Source MUST be a ROADM node. (TODO: relax this and accept trx entries)
|
||||
|
||||
- **Destination** is mandatory. It is the name of the destination node (as listed in Nodes sheet). Source MUST be a ROADM node. (TODO: relax this and accept trx entries)
|
||||
|
||||
- **TRX type and mode** are mandatory. They are the variety type and selected mode of the transceiver to be used for the propagation simulation. These modes MUST be defined in the equipment library. The format of the mode is used as the name of the mode. (TODO: maybe add another mode id on Transceiver library ?). In particular the mode selection defines the channel baudrate to be used for the propagation simulation.
|
||||
|
||||
- **System: spacing ; System: input power (dBm) ; System: nb of channels** are mandatory input defining the system parameters for the propagation simulation.
|
||||
|
||||
- spacing is the channel spacing defined in GHz
|
||||
- input power is the channel optical input power in dBm
|
||||
- nb of channels is the number of channels to be used for the simulation.
|
||||
|
||||
- **routing: disjoint from ; routing: path ; routing: is loose?** are optional.
|
||||
|
||||
- disjoint from: (work not started) identifies the requests from which this request must be disjoint. It is not used yet
|
||||
- path: is the set of ROADM nodes that must be used by the path. It must contain the list of ROADM names that the path must cross. TODO : only ROADM nodes are accepted in this release. Relax this with any type of nodes.
|
||||
- is loose? (in progress) 'no' value means that the list of nodes should be strictly followed, while any other value means that the constraint may be relaxed if the node is not reachable.
|
||||
|
||||
# to be completed #
|
||||
|
||||
convert_service_sheet.py
|
||||
------------------------
|
||||
|
||||
|
||||
`convert_service_sheet.py <examples/convert_service_sheet.py>`_ converts the service sheet to a json file following the Yang model for requesting Path Computation defined in `draft-ietf-teas-yang-path-computation-01.txt <https://www.ietf.org/id/draft-ietf-teas-yang-path-computation-01.pdf>`_. TODO: verify that this implementation is correct + give feedback to ietf on what is missing for our specific application.
|
||||
For PSE use, additional fields with trx type and mode have been added to the te-bandwidth field.
|
||||
|
||||
**Usage**: convert_service_sheet.py [-h] [-v] [-o OUTPUT] [workbook_name.xls]
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ cd examples
|
||||
$ python convert_service_sheet.py meshTopologyExampleV2.xls -o service_file.json
|
||||
|
||||
-o output_file.json is an optional parameter:
|
||||
|
||||
- if not used, the program output the json data on standard output and on a json file with name 'workbook_name_services.json'.
|
||||
|
||||
A template for the json file can be found here: `service_template.json <service_template.json>`_
|
||||
|
||||
path_requests_run.py
|
||||
------------------------
|
||||
|
||||
**Usage**: path_requests_run.py [-h] [-v] [-o OUTPUT]
|
||||
[network_filename xls or json] [service_filename xls or json] [eqpt_filename json]
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ cd examples
|
||||
$ python path_requests_run.py meshTopologyExampleV2.xls service_file.json eqpt_file -o output_file.json
|
||||
|
||||
A function that computes performances for a list of services provided in the service file (accepts json or excel format.
|
||||
|
||||
If no output file is given, the computation is shown on standard output for demo.
|
||||
If a file is specified with the optional -o argument, the result of the computation is converted into a json format following the Yang model for requesting Path Computation defined in `draft-ietf-teas-yang-path-computation-01.txt <https://www.ietf.org/id/draft-ietf-teas-yang-path-computation-01.pdf>`_. TODO: verify that this implementation is correct + give feedback to ietf on what is missing for our specific application.
|
||||
|
||||
A template for the result of computation json file can be found here: `path_result_template.json <path_result_template.json>`_
|
||||
|
||||
Important note: path_requests_run.py is not a network dimensionning tool : each service does not reserve spectrum, or occupy ressources such as transponders. It only computes path feasibility assuming the spectrum (between defined frequencies) is loaded with "nb of channels" spaced by "spacing" values as specified in the system parameters input in the service file, each cannel having the same characteristics in terms of baudrate, format, ... as the service transponder. The transceiver element acts as a "logical starting/stopping point" for the spectral information propagation. At that point it is not meant to represent the capacity of add drop ports
|
||||
As a result transponder type is not part of the network info. it is related to the list of services requests.
|
||||
|
||||
In a next step we plan to provide required features to enable dimensionning : alocation of ressources, counting channels, limitation of the number of channels, ...
|
||||
|
||||
(in progress)
|
||||
|
||||
|
||||
476
README.rst
476
README.rst
@@ -1,15 +1,15 @@
|
||||
====
|
||||
====================================================================
|
||||
`gnpy`: mesh optical network route planning and optimization library
|
||||
====
|
||||
====================================================================
|
||||
|
||||
|docs| |build|
|
||||
|
||||
**gnpy is an open-source, community-developed library for building route planning
|
||||
and optimization tools in real-world mesh optical networks.**
|
||||
**`gnpy` is an open-source, community-developed library for building route
|
||||
planning and optimization tools in real-world mesh optical networks.**
|
||||
|
||||
`gnpy <http://github.com/telecominfraproject/gnpy>`_ is:
|
||||
`gnpy <http://github.com/telecominfraproject/oopt-gnpy>`__ is:
|
||||
|
||||
- a sponsored project of the `OOPT/PSE <http://telecominfraproject.com/project-groups-2/backhaul-projects/open-optical-packet-transport/>`_ working group of the `Telecom Infra Project <http://telecominfraproject.com>`_.
|
||||
- a sponsored project of the `OOPT/PSE <https://telecominfraproject.com/open-optical-packet-transport/>`_ working group of the `Telecom Infra Project <http://telecominfraproject.com>`_.
|
||||
- fully community-driven, fully open source library
|
||||
- driven by a consortium of operators, vendors, and academic researchers
|
||||
- intended for rapid development of production-grade route planning tools
|
||||
@@ -18,63 +18,142 @@ and optimization tools in real-world mesh optical networks.**
|
||||
|
||||
Documentation: https://gnpy.readthedocs.io
|
||||
|
||||
Installation
|
||||
------------
|
||||
Branches and Tagged Releases
|
||||
----------------------------
|
||||
|
||||
``gnpy`` is hosted in the `Python Package Index <http://pypi.org/>`_ (`gnpy <https://pypi.org/project/gnpy/>`_). It can be installed via:
|
||||
- the `master <https://github.com/Telecominfraproject/oopt-gnpy/tree/master>`_ branch contains stable, validated code. It is updated from develop on a release schedule determined by the OOPT-PSE Working Group. For more information about the validation process, see: https://github.com/Telecominfraproject/oopt-gnpy/wiki/Testing-for-Quality
|
||||
- the `develop <https://github.com/Telecominfraproject/oopt-gnpy/tree/develop>`_ branch contains the latest code under active development, which may not be fully validated and tested.
|
||||
- the `phase-1 <https://github.com/Telecominfraproject/oopt-gnpy/tree/phase-1>`_ branch contains code for Phase I of the OOPT-PSE efforts and is kept only for reference. This branch is unmaintained.
|
||||
|
||||
A brief outline of major (tagged) `gnpy` releases:
|
||||
|
||||
+---------------+-------------+-----------------------------------------------+
|
||||
| release date | version tag | notes |
|
||||
+===============+=============+===============================================+
|
||||
| Oct 16, 2018 | v1.0 | - first "production"-ready release |
|
||||
| | | - open network element model (EDFA, GN-model) |
|
||||
| | | - auto-design functionality |
|
||||
| | | - path request functionality |
|
||||
+---------------+-------------+-----------------------------------------------+
|
||||
|
||||
How to Install
|
||||
--------------
|
||||
|
||||
**Note**: `gnpy` supports Python 3 only. Python 2 is not supported.
|
||||
`gnpy` requires Python ≥3.6
|
||||
|
||||
**Note**: the `gnpy` maintainers strongly recommend the use of Anaconda for
|
||||
managing dependencies.
|
||||
|
||||
It is recommended that you use a "virtual environment" when installing `gnpy`.
|
||||
Do not install `gnpy` on your system Python.
|
||||
|
||||
We recommend the use of the Anaconda Python distribution
|
||||
(https://www.anaconda.com/download) which comes with many scientific computing
|
||||
dependencies pre-installed. Anaconda creates a base "virtual environment" for
|
||||
you automatically. You can also create and manage your conda "virtual
|
||||
environments" yourself (see:
|
||||
https://conda.io/docs/user-guide/tasks/manage-environments.html)
|
||||
|
||||
To activate your Anaconda virtual environment, you may need to do the
|
||||
following:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ pip install gnpy
|
||||
$ source /path/to/anaconda/bin/activate # activate Anaconda base environment
|
||||
(base) $ # note the change to the prompt
|
||||
|
||||
It can also be installed directly from the repo.
|
||||
You can check which Anaconda environment you are using with:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ git clone https://github.com/telecominfraproject/gnpy
|
||||
$ cd gnpy
|
||||
$ python setup.py install
|
||||
(base) $ conda env list # list all environments
|
||||
# conda environments:
|
||||
#
|
||||
base * /src/install/anaconda3
|
||||
|
||||
Both approaches above will handle installing any additional software dependencies.
|
||||
(base) $ echo $CONDA_DEFAULT_ENV # show default environment
|
||||
base
|
||||
|
||||
**Note**: *We recommend the use of the Anaconda Python distribution
|
||||
(https://www.anaconda.com/download) which comes with many scientific
|
||||
computing dependencies pre-installed.*
|
||||
You can check your version of Python with the following. If you are using
|
||||
Anaconda's Python 3, you should see similar output as below. Your results may
|
||||
be slightly different depending on your Anaconda installation path and the
|
||||
exact version of Python you are using.
|
||||
|
||||
Instructions for Use
|
||||
--------------------
|
||||
.. code-block:: shell
|
||||
|
||||
$ which python # check which Python executable is used
|
||||
/path/to/anaconda/bin/python
|
||||
$ python -V # check your Python version
|
||||
Python 3.6.5 :: Anaconda, Inc.
|
||||
|
||||
From within your Anaconda Python 3 environment, you can clone the master branch
|
||||
of the `gnpy` repo and install it with:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ git clone https://github.com/Telecominfraproject/oopt-gnpy # clone the repo
|
||||
$ cd oopt-gnpy
|
||||
$ python setup.py install # install
|
||||
|
||||
To test that `gnpy` was successfully installed, you can run this command. If it
|
||||
executes without a `ModuleNotFoundError`, you have successfully installed
|
||||
`gnpy`.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ python -c 'import gnpy' # attempt to import gnpy
|
||||
|
||||
$ cd oopt-gnpy
|
||||
$ pytest # run tests
|
||||
|
||||
Instructions for First Use
|
||||
--------------------------
|
||||
|
||||
``gnpy`` is a library for building route planning and optimization tools.
|
||||
|
||||
It ships with a number of example programs. Release versions will ship with
|
||||
fully-functional programs.
|
||||
|
||||
|
||||
**Note**: *If you are a network operator or involved in route planning and
|
||||
optimization for your organization, please contact project maintainer James
|
||||
Powell <james.powell@telecominfraproject>. gnpy is looking for users with
|
||||
specific, delineated use cases to drive requirements for future
|
||||
development.*
|
||||
|
||||
**To get started, run the main transmission example:**
|
||||
|
||||
**To get started, run the transmission example:**
|
||||
**Note**: *Examples should be run from the examples/ folder.*
|
||||
|
||||
.. code-block:: shell
|
||||
$ pwd
|
||||
/path/to/oopt-gnpy
|
||||
$ cd examples
|
||||
$ python transmission_main_example.py
|
||||
|
||||
$ python examples/transmission_main_example.py
|
||||
|
||||
By default, this script operates on a single span network defined in `examples/edfa/edfa_example_network.json <examples/edfa/edfa_example_network.json>`_
|
||||
By default, this script operates on a single span network defined in
|
||||
`examples/edfa_example_network.json <examples/edfa_example_network.json>`_
|
||||
|
||||
You can specify a different network at the command line as follows. For
|
||||
example, to use the CORONET Continental US (CONUS) network defined in `examples/coronet_conus_example.json <examples/coronet_conus_example.json>`_:
|
||||
example, to use the CORONET Continental US (CONUS) network defined in
|
||||
`examples/coronet_conus_example.json <examples/coronet_conus_example.json>`_:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ python examples/transmission_main_example.py examples/coronet_conus_example.json
|
||||
$ cd examples
|
||||
$ python transmission_main_example.py CORONET_Global_Topology.json
|
||||
|
||||
This script will calculate the average signal osnr and snr across 93 network
|
||||
elements (transceiver, ROADMs, fibers, and amplifiers) between Abilene, Texas
|
||||
and Albany, New York.
|
||||
It is also possible to use an Excel file input (for example
|
||||
`examples/CORONET_Global_Topology.xls <examples/CORONET_Global_Topology.xls>`_).
|
||||
The Excel file will be processed into a JSON file with the same prefix. For
|
||||
further instructions on how to prepare the Excel input file, see
|
||||
`Excel_userguide.rst <Excel_userguide.rst>`_.
|
||||
|
||||
The main transmission example will calculate the average signal OSNR and SNR
|
||||
across 93 network elements (transceiver, ROADMs, fibers, and amplifiers)
|
||||
between two transceivers selected by the user. (By default, for the CORONET US
|
||||
network, it will show the transmission of spectral information between Abilene,
|
||||
Texas and Albany, New York.)
|
||||
|
||||
This script calculates the average signal OSNR = |OSNR| and SNR = |SNR|.
|
||||
|
||||
@@ -87,16 +166,338 @@ interference noise.
|
||||
.. |Pase| replace:: P\ :sub:`ase`
|
||||
.. |Pnli| replace:: P\ :sub:`nli`
|
||||
|
||||
Further Instructions for Use (`transmission_main_example.py`, `path_requests_run.py`)
|
||||
-------------------------------------------------------------------------------------
|
||||
|
||||
Design and transmission parameters are defined in a dedicated json file. By
|
||||
default, this information is read from `examples/eqpt_config.json
|
||||
<examples/eqpt_config.json>`_. This file defines the equipement librairies that
|
||||
can be customized (EDFAs, fibers, and transcievers).
|
||||
|
||||
It also defines the simulation parameters (spans, ROADMs, and the spectral
|
||||
information to transmit.)
|
||||
|
||||
The EDFA equipment library is a list of supported amplifiers. New amplifiers
|
||||
can be added and existing ones removed. Three different noise models are available:
|
||||
|
||||
1. `'type_def': 'variable_gain'` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: `nf_min`, `nf_max`, and `gain_flatmax`. It is not a simple interpolation but a 2-stage NF calculation.
|
||||
2. `'type_def': 'fixed_gain'` is a fixed gain model. `NF == Cte == nf0` if `gain_min < gain < gain_flatmax`
|
||||
3. `'type_def': None` is an advanced model. A detailed json configuration file is required (by default `examples/advanced_config_from.json <examples/advanced_config_from.json>`_.) It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled.
|
||||
|
||||
For all amplifier models:
|
||||
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| field | type | description |
|
||||
+======================+===========+=========================================+
|
||||
| `type_variety` | (string) | a unique name to ID the amplifier in the|
|
||||
| | | JSON/Excel template topology input file |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `out_voa_auto` | (boolean) | auto_design feature to optimize the |
|
||||
| | | amplifier output VOA. If true, output |
|
||||
| | | VOA is present and will be used to push |
|
||||
| | | amplifier gain to its maximum, within |
|
||||
| | | EOL power margins. |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `allowed_for_design` | (boolean) | If false, the amplifier will not be |
|
||||
| | | picked by auto-design but it can still |
|
||||
| | | be used as a manual input (from JSON or |
|
||||
| | | Excel template topology files.) |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
|
||||
The fiber library currently describes SSMF but additional fiber types can be entered by the user following the same model:
|
||||
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| field | type | description |
|
||||
+======================+===========+=========================================+
|
||||
| `type_variety` | (string) | a unique name to ID the amplifier in the|
|
||||
| | | JSON or Excel template topology input |
|
||||
| | | file |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `dispersion` | (number) | (s.m-1.m-1) |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `gamma` | (number) | 2pi.n2/(lambda*Aeff) (w-2.m-1) |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
|
||||
The transceiver equipment library is a list of supported transceivers. New
|
||||
transceivers can be added and existing ones removed at will by the user. It is
|
||||
used to determine the service list path feasibility when running the
|
||||
path_request_run.py routine.
|
||||
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| field | type | description |
|
||||
+======================+===========+=========================================+
|
||||
| `type_variety` | (string) | a unique name to ID the amplifier in |
|
||||
| | | the JSON or Excel template topology |
|
||||
| | | input file |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `frequency` | (number) | Min/max as below. |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `mode` | (number) | a list of modes supported by the |
|
||||
| | | transponder. New modes can be added at |
|
||||
| | | will by the user. The modes are specific|
|
||||
| | | to each transponder type_variety. |
|
||||
| | | Each mode is described as below. |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
|
||||
The modes are defined as follows:
|
||||
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| field | type | description |
|
||||
+======================+===========+=========================================+
|
||||
| `format` | (string) | a unique name to ID the mode. |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `baud_rate` | (number) | in Hz |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `OSNR` | (number) | min required OSNR in 0.1nm (dB) |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `bit_rate` | (number) | in bit/s |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `roll_off` | (number) | |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
|
||||
Simulation parameters are defined as follows.
|
||||
|
||||
Auto-design automatically creates EDFA amplifier network elements when they are
|
||||
missing, after a fiber, or between a ROADM and a fiber. This auto-design
|
||||
functionality can be manually and locally deactivated by introducing a `Fused`
|
||||
network element after a `Fiber` or a `Roadm` that doesn't need amplification.
|
||||
The amplifier is chosen in the EDFA list of the equipment library based on
|
||||
gain, power, and NF criteria. Only the EDFA that are marked
|
||||
`'allowed_for_design': true` are considered.
|
||||
|
||||
For amplifiers defined in the topology JSON input but whose gain = 0
|
||||
(placeholder), auto-design will set its gain automatically: see `power_mode` in
|
||||
the `Spans` library to find out how the gain is calculated.
|
||||
|
||||
Span configuration is performed as followws. It is not a list (which may change
|
||||
in later releases,) and the user can only modify the value of existing
|
||||
parameters:
|
||||
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| field | type | description |
|
||||
+========================+===========+=============================================+
|
||||
| `power_mode` | (boolean) | If false, gain mode. Auto-design sets |
|
||||
| | | amplifier gain = preceeding span loss, |
|
||||
| | | unless the amplifier exists and its |
|
||||
| | | gain > 0 in the topology input json. |
|
||||
| | | If true, power mode (recommended for |
|
||||
| | | auto-design and power sweep.) |
|
||||
| | | Auto-design sets amplifier power |
|
||||
| | | according to delta_power_range. If the |
|
||||
| | | amplifier exists with gain > 0 in the |
|
||||
| | | topology json input, then its gain is |
|
||||
| | | translated into a power target/channel. |
|
||||
| | | Moreover, when performing a power sweep |
|
||||
| | | (see power_range_db in the SI |
|
||||
| | | configuration library) the power sweep |
|
||||
| | | is performed w/r/t this power target, |
|
||||
| | | regardless of preceeding amplifiers |
|
||||
| | | power saturation/limitations. |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `delta_power_range_db` | (number) | Auto-design only, power-mode |
|
||||
| | | only. Specifies the [min, max, step] |
|
||||
| | | power excursion/span. It is a relative |
|
||||
| | | power excursion w/r/t the |
|
||||
| | | power_dbm + power_range_db |
|
||||
| | | (power sweep if applicable) defined in |
|
||||
| | | the SI configuration library. This |
|
||||
| | | relative power excursion is = 1/3 of |
|
||||
| | | the span loss difference with the |
|
||||
| | | reference 20 dB span. The 1/3 slope is |
|
||||
| | | derived from the GN model equations. |
|
||||
| | | For example, a 23 dB span loss will be |
|
||||
| | | set to 1 dB more power than a 20 dB |
|
||||
| | | span loss. The 20 dB reference spans |
|
||||
| | | will *always* be set to |
|
||||
| | | power = power_dbm + power_range_db. |
|
||||
| | | To configure the same power in all |
|
||||
| | | spans, use `[0, 0, 0]`. All spans will |
|
||||
| | | be set to |
|
||||
| | | power = power_dbm + power_range_db. |
|
||||
| | | To configure the same power in all spans |
|
||||
| | | and 3 dB more power just for the longest |
|
||||
| | | spans: `[0, 3, 3]`. The longest spans are |
|
||||
| | | set to |
|
||||
| | | power = power_dbm + power_range_db + 3. |
|
||||
| | | To configure a 4 dB power range across |
|
||||
| | | all spans in 0.5 dB steps: `[-2, 2, 0.5]`. |
|
||||
| | | A 17 dB span is set to |
|
||||
| | | power = power_dbm + power_range_db - 1, |
|
||||
| | | a 20 dB span to |
|
||||
| | | power = power_dbm + power_range_db and |
|
||||
| | | a 23 dB span to |
|
||||
| | | power = power_dbm + power_range_db + 1 |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `max_length` | (number) | Split fiber lengths > max_length. |
|
||||
| | | Interest to support high level |
|
||||
| | | topologies that do not specify in line |
|
||||
| | | amplification sites. For example the |
|
||||
| | | CORONET_Global_Topology.xls defines |
|
||||
| | | links > 1000km between 2 sites: it |
|
||||
| | | couldn't be simulated if these links |
|
||||
| | | were not splitted in shorter span |
|
||||
| | | lengths. |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `length_unit` | "m"/"km" | Unit for max_length. |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `max_loss` | (number) | Not used in the current code |
|
||||
| | | implementation. |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `padding` | (number) | In dB. Min span loss before putting an |
|
||||
| | | attenuator before fiber. Attenuator |
|
||||
| | | value |
|
||||
| | | Fiber.att_in = max(0, padding - span_loss). |
|
||||
| | | Padding can be set manually to reach a |
|
||||
| | | higher padding value for a given fiber |
|
||||
| | | by filling in the Fiber/params/att_in |
|
||||
| | | field in the topology json input [1] |
|
||||
| | | but if span_loss = length * loss_coef |
|
||||
| | | + att_in + con_in + con_out < padding, |
|
||||
| | | the specified att_in value will be |
|
||||
| | | completed to have span_loss = padding. |
|
||||
| | | Therefore it is not possible to set |
|
||||
| | | span_loss < padding. |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `EOL` | (number) | All fiber span loss ageing. The value |
|
||||
| | | is added to the con_out (fiber output |
|
||||
| | | connector). So the design and the path |
|
||||
| | | feasibility are performed with |
|
||||
| | | span_loss + EOL. EOL cannot be set |
|
||||
| | | manually for a given fiber span |
|
||||
| | | (workaround is to specify higher con_out |
|
||||
| | | loss for this fiber). |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `con_in`, `con_out` | (number) | Default values if Fiber/params/con_in/out |
|
||||
| | | is None in the topology input |
|
||||
| | | description. This default value is |
|
||||
| | | ignored if a Fiber/params/con_in/out |
|
||||
| | | value is input in the topology for a |
|
||||
| | | given Fiber. |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
|
||||
**[1]**
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"uid": "fiber (A1->A2)",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params":
|
||||
{
|
||||
"type_variety": "SSMF",
|
||||
"length": 120.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
}
|
||||
}
|
||||
|
||||
ROADMs can be configured as follows. The user can only modify the value of
|
||||
existing parmeters:
|
||||
|
||||
+-------------------------+-----------+---------------------------------------------+
|
||||
| field | type | description |
|
||||
+=========================+===========+=============================================+
|
||||
|`gain_mode_default_loss` | (number) | Default value if Roadm/params/loss is |
|
||||
| | | None in the topology input description. |
|
||||
| | | This default value is ignored if a |
|
||||
| | | params/loss value is input in the |
|
||||
| | | topology for a given ROADM. |
|
||||
+-------------------------+-----------+---------------------------------------------+
|
||||
|`power_mode_pref` | (number) | Power mode only. Auto-design sets the |
|
||||
| | | power of ROADM ingress amplifiers to |
|
||||
| | | power_dbm + power_range_db, |
|
||||
| | | regardless of existing gain settings |
|
||||
| | | from the topology JSON input. |
|
||||
| | | Auto-design sets the Roadm loss so that |
|
||||
| | | its egress channel power = power_mode_pref, |
|
||||
| | | regardless of existing loss settings |
|
||||
| | | from the topology JSON input. It means |
|
||||
| | | that the ouput power from a ROADM (and |
|
||||
| | | therefore its OSNR contribution) is Cte |
|
||||
| | | and not depending from power_dbm and |
|
||||
| | | power_range_db sweep settings. This |
|
||||
| | | choice is meant to reflect some typical |
|
||||
| | | control loop algorithms. |
|
||||
+-------------------------+-----------+---------------------------------------------+
|
||||
|
||||
The `SpectralInformation` object can be configured as follows. The user can
|
||||
only modify the value of existing parameters. It defines a spectrum of N
|
||||
identical carriers. While the code libraries allow for different carriers and
|
||||
power levels, the current user parametrization only allows one carrier type and
|
||||
one power/channel definition.
|
||||
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| field | type | description |
|
||||
+======================+===========+===========================================+
|
||||
| `f_min/max` | (number) | In Hz. Carrier min max excursion |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `baud_rate` | (number) | In Hz. Simulated baud rate. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `spacing` | (number) | In Hz. Carrier spacing. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `roll_off` | (number) | Not used. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `OSNR` | (number) | Not used. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `bit_rate` | (number) | Not used. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `power_dbm` | (number) | Reference channel power. In gain mode |
|
||||
| | | (see spans/power_mode = false), all gain |
|
||||
| | | settings are offset w/r/t this reference |
|
||||
| | | power. In power mode, it is the |
|
||||
| | | reference power for |
|
||||
| | | Spans/delta_power_range_db. For example, |
|
||||
| | | if delta_power_range_db = `[0,0,0]`, the |
|
||||
| | | same power=power_dbm is launched in every |
|
||||
| | | spans. The network design is performed |
|
||||
| | | with the power_dbm value: even if a |
|
||||
| | | power sweep is defined (see after) the |
|
||||
| | | design is not repeated. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `power_range_db` | (number) | Power sweep excursion around power_dbm. |
|
||||
| | | It is not the min and max channel power |
|
||||
| | | values! The reference power becomes: |
|
||||
| | | power_range_db + power_dbm. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
|
||||
The `transmission_main_example.py <examples/transmission_main_example.py>`_
|
||||
script propagates a specrum of 96 channels at 32 Gbaud, 50 GHz spacing and 0
|
||||
script propagates a specrum of channels at 32 Gbaud, 50 GHz spacing and 0
|
||||
dBm/channel. These are not yet parametrized but can be modified directly in the
|
||||
script (via the SpectralInformation tuple) to accomodate any baud rate,
|
||||
script (via the SpectralInformation structure) to accomodate any baud rate,
|
||||
spacing, power or channel count demand.
|
||||
|
||||
The amplifier's gain is set to exactly compsenate for the loss in each network
|
||||
The amplifier's gain is set to exactly compensate for the loss in each network
|
||||
element. The amplifier is currently defined with gain range of 15 dB to 25 dB
|
||||
and 21 dBm max output power. Ripple and NF models are defined in
|
||||
`examples/edfa_config.json <examples/edfa_config.json>`_
|
||||
`examples/std_medium_gain_advanced_config.json <examples/std_medium_gain_advanced_config.json>`_
|
||||
|
||||
Use `examples/path_requests_run.py <examples/path_requests_run.py>`_ to run multiple optimizations as follows:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ python path_requests_run.py -h
|
||||
Usage: path_requests_run.py [-h] [-v] [-o OUTPUT] [network_filename] [service_filename] [eqpt_filename]
|
||||
|
||||
The `network_filename` and `service_filename` can be an XLS or JSON file. The `eqpt_filename` must be a JSON file.
|
||||
|
||||
To see an example of it, run:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ cd examples
|
||||
$ python path_requests_run.py meshTopologyExampleV2.xls meshTopologyExampleV2_services.json eqpt_config.json -o output_file.json
|
||||
|
||||
This program requires a list of connections to be estimated and the equipment
|
||||
library. The program computes performances for the list of services (accepts
|
||||
json or excel format) using the same spectrum propagation modules as
|
||||
transmission_main_example.py. Explanation on the Excel template is provided in
|
||||
the `Excel_userguide.rst <Excel_userguide.rst#service-sheet>`_. Template for
|
||||
the json format can be found here: `service_template.json
|
||||
<service_template.json>`_.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
@@ -158,8 +559,8 @@ implementations.
|
||||
:alt: Documentation Status
|
||||
:scale: 100%
|
||||
|
||||
.. |build| image:: https://travis-ci.org/mcantono/gnpy.svg?branch=develop
|
||||
:target: https://travis-ci.org/mcantono/gnpy
|
||||
.. |build| image:: https://travis-ci.com/Telecominfraproject/oopt-gnpy.svg?branch=develop
|
||||
:target: https://travis-ci.com/Telecominfraproject/oopt-gnpy
|
||||
:alt: Build Status
|
||||
:scale: 100%
|
||||
|
||||
@@ -187,5 +588,4 @@ License
|
||||
|
||||
``gnpy`` is distributed under a standard BSD 3-Clause License.
|
||||
|
||||
See `LICENSE <LICENSE>`_ for more details.
|
||||
|
||||
See `LICENSE <LICENSE>`__ for more details.
|
||||
|
||||
8318
examples/CORONET_Global_Topology.json
Normal file
8318
examples/CORONET_Global_Topology.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
examples/CORONET_Global_Topology.xls
Normal file
BIN
examples/CORONET_Global_Topology.xls
Normal file
Binary file not shown.
0
examples/__init__.py
Normal file
0
examples/__init__.py
Normal file
31
examples/convert_service_sheet.py
Normal file
31
examples/convert_service_sheet.py
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
convert_service_sheet.py
|
||||
========================
|
||||
|
||||
XLS parser that can be called to create a JSON request file in accordance with
|
||||
Yang model for requesting path computation.
|
||||
|
||||
See: draft-ietf-teas-yang-path-computation-01.txt
|
||||
"""
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO
|
||||
from json import dumps
|
||||
|
||||
from gnpy.core.service_sheet import Request, Element, Request_element
|
||||
from gnpy.core.service_sheet import parse_row, parse_excel, convert_service_sheet
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, CRITICAL))
|
||||
logger.info(f'Converting Service sheet {args.workbook!r} into gnpy JSON format')
|
||||
if args.output is None:
|
||||
data = convert_service_sheet(args.workbook,'eqpt_config.json')
|
||||
print(dumps(data, indent=2))
|
||||
else:
|
||||
data = convert_service_sheet(args.workbook,'eqpt_config.json',args.output)
|
||||
File diff suppressed because it is too large
Load Diff
103
examples/create_eqpt_sheet.py
Normal file
103
examples/create_eqpt_sheet.py
Normal file
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
create_eqpt_sheet.py
|
||||
====================
|
||||
|
||||
XLS parser that can be called to create a "City" column in the "Eqpt" sheet.
|
||||
|
||||
If not present in the "Nodes" sheet, the "Type" column will be implicitly
|
||||
determined based on the topology.
|
||||
"""
|
||||
|
||||
from sys import exit
|
||||
try:
|
||||
from xlrd import open_workbook
|
||||
except ModuleNotFoundError:
|
||||
exit('Required: `pip install xlrd`')
|
||||
from argparse import ArgumentParser
|
||||
from collections import namedtuple, defaultdict
|
||||
|
||||
|
||||
Shortlink = namedtuple('Link', 'src dest')
|
||||
|
||||
Shortnode = namedtuple('Node', 'nodename eqt')
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('workbook', nargs='?', default='meshTopologyExampleV2.xls',
|
||||
help = 'create the mandatory columns in Eqpt sheet ')
|
||||
all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows))
|
||||
|
||||
def read_excel(input_filename):
|
||||
with open_workbook(input_filename) as wb:
|
||||
# reading Links sheet
|
||||
links_sheet = wb.sheet_by_name('Links')
|
||||
links = []
|
||||
nodeoccuranceinlinks = []
|
||||
links_by_src = defaultdict(list)
|
||||
links_by_dest = defaultdict(list)
|
||||
for row in all_rows(links_sheet, start=5):
|
||||
links.append(Shortlink(row[0].value,row[1].value))
|
||||
links_by_src[row[0].value].append(Shortnode(row[1].value,''))
|
||||
links_by_dest[row[1].value].append(Shortnode(row[0].value,''))
|
||||
#print(f'source {links[len(links)-1].src} dest {links[len(links)-1].dest}')
|
||||
nodeoccuranceinlinks.append(row[0].value)
|
||||
nodeoccuranceinlinks.append(row[1].value)
|
||||
|
||||
# reading Nodes sheet
|
||||
nodes_sheet = wb.sheet_by_name('Nodes')
|
||||
nodes = []
|
||||
node_degree = []
|
||||
for row in all_rows(nodes_sheet, start=5) :
|
||||
|
||||
temp_eqt = row[6].value
|
||||
# verify node degree to confirm eqt type
|
||||
node_degree.append(nodeoccuranceinlinks.count(row[0].value))
|
||||
if temp_eqt.lower() == 'ila' and nodeoccuranceinlinks.count(row[0].value) !=2 :
|
||||
print(f'Inconsistancy: node {nodes[len(nodes)-1]} has degree \
|
||||
{node_degree[len(nodes)-1]} and can not be an ILA ... replaced by ROADM')
|
||||
temp_eqt = 'ROADM'
|
||||
if temp_eqt == '' and nodeoccuranceinlinks.count(row[0].value) == 2 :
|
||||
temp_eqt = 'ILA'
|
||||
if temp_eqt == '' and nodeoccuranceinlinks.count(row[0].value) != 2 :
|
||||
temp_eqt = 'ROADM'
|
||||
# print(f'node {nodes[len(nodes)-1]} eqt {temp_eqt}')
|
||||
nodes.append(Shortnode(row[0].value,temp_eqt))
|
||||
# print(len(nodes)-1)
|
||||
print(f'reading: node {nodes[len(nodes)-1].nodename} eqpt {temp_eqt}')
|
||||
return links,nodes, links_by_src , links_by_dest
|
||||
|
||||
def create_eqt_template(links,nodes, links_by_src , links_by_dest, input_filename):
|
||||
output_filename = f'{input_filename[:-4]}_eqpt_sheet.txt'
|
||||
with open(output_filename,'w') as my_file :
|
||||
# print header similar to excel
|
||||
my_file.write('OPTIONAL\n\n\n\
|
||||
\t\tNode a egress amp (from a to z)\t\t\t\t\tNode a ingress amp (from z to a) \
|
||||
\nNode A \tNode Z \tamp type \tatt_in \tamp gain \ttilt \tatt_out\
|
||||
amp type \tatt_in \tamp gain \ttilt \tatt_out\n')
|
||||
|
||||
tab = []
|
||||
temp = []
|
||||
i = 0
|
||||
for lk in links:
|
||||
if [e for n,e in nodes if n==lk.src][0] != 'FUSED' :
|
||||
temp = [lk.src , lk.dest]
|
||||
tab.append(temp)
|
||||
my_file.write(f'{temp[0]}\t{temp[1]}\n')
|
||||
for n in nodes :
|
||||
if n.eqt.lower() == 'roadm' :
|
||||
for src in links_by_dest[n.nodename] :
|
||||
temp = [n.nodename , src.nodename]
|
||||
tab.append(temp)
|
||||
# print(temp)
|
||||
my_file.write(f'{temp[0]}\t{temp[1]}\n')
|
||||
i = i + 1
|
||||
print(f'File {output_filename} successfully created with Node A - Node Z ' +
|
||||
' entries for Eqpt sheet in excel file.')
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
input_filename = args.workbook
|
||||
links,nodes,links_by_src, links_by_dest = read_excel(input_filename)
|
||||
create_eqt_template(links,nodes, links_by_src , links_by_dest , input_filename)
|
||||
296
examples/default_edfa_config.json
Normal file
296
examples/default_edfa_config.json
Normal file
@@ -0,0 +1,296 @@
|
||||
{
|
||||
"nf_ripple": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"gain_ripple": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"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
|
||||
]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
-1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01
|
||||
-2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02
|
||||
-1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01
|
||||
-2.0500000000000000e+01 -2.0489473680000000e+01 -2.0478947370000000e+01 -2.0468421050000000e+01 -2.0457894740000000e+01 -2.0447368420000000e+01 -2.0436842110000001e+01 -2.0426315790000000e+01 -2.0415789470000000e+01 -2.0405263160000001e+01 -2.0394736840000000e+01 -2.0384210530000001e+01 -2.0373684210000000e+01 -2.0363157890000000e+01 -2.0352631580000001e+01 -2.0342105260000000e+01 -2.0331578950000001e+01 -2.0321052630000001e+01 -2.0310526320000001e+01 -2.0300000000000001e+01 -2.0289473680000000e+01 -2.0278947370000001e+01 -2.0268421050000001e+01 -2.0257894740000001e+01 -2.0247368420000001e+01 -2.0236842110000001e+01 -2.0226315790000001e+01 -2.0215789470000001e+01 -2.0205263160000001e+01 -2.0194736840000001e+01 -2.0184210530000001e+01 -2.0173684210000001e+01 -2.0163157890000001e+01 -2.0152631580000001e+01 -2.0142105260000001e+01 -2.0131578950000002e+01 -2.0121052630000001e+01 -2.0110526320000002e+01 -2.0100000000000001e+01 -2.0089473680000001e+01 -2.0078947370000002e+01 -2.0068421050000001e+01 -2.0057894739999998e+01 -2.0047368420000002e+01 -2.0036842109999998e+01 -2.0026315790000002e+01 -2.0015789470000001e+01 -2.0005263159999998e+01 -1.9994736840000002e+01 -1.9984210529999999e+01 -1.9973684209999998e+01 -1.9963157890000002e+01 -1.9952631579999998e+01 -1.9942105260000002e+01 -1.9931578949999999e+01 -1.9921052629999998e+01 -1.9910526319999999e+01 -1.9899999999999999e+01 -1.9889473679999998e+01 -1.9878947369999999e+01 -1.9868421049999998e+01 -1.9857894739999999e+01 -1.9847368419999999e+01 -1.9836842109999999e+01 -1.9826315789999999e+01 -1.9815789469999999e+01 -1.9805263159999999e+01 -1.9794736839999999e+01 -1.9784210529999999e+01 -1.9773684209999999e+01 -1.9763157889999999e+01 -1.9752631579999999e+01 -1.9742105259999999e+01 -1.9731578949999999e+01 -1.9721052629999999e+01 -1.9710526320000000e+01 -1.9699999999999999e+01 -1.9689473679999999e+01 -1.9678947369999999e+01 -1.9668421049999999e+01 -1.9657894740000000e+01 -1.9647368419999999e+01 -1.9636842110000000e+01 -1.9626315790000000e+01 -1.9615789469999999e+01 -1.9605263160000000e+01 -1.9594736839999999e+01 -1.9584210530000000e+01 -1.9573684210000000e+01 -1.9563157889999999e+01 -1.9552631580000000e+01 -1.9542105260000000e+01 -1.9531578950000000e+01 -1.9521052630000000e+01 -1.9510526320000000e+01 -1.9500000000000000e+01
|
||||
-2.0500000000000000e+01 -2.0489473680000000e+01 -2.0478947370000000e+01 -2.0468421050000000e+01 -2.0457894740000000e+01 -2.0447368420000000e+01 -2.0436842110000001e+01 -2.0426315790000000e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.9573684210000000e+01 -1.9563157889999999e+01 -1.9552631580000000e+01 -1.9542105260000000e+01 -1.9531578950000000e+01 -1.9521052630000000e+01 -1.9510526320000000e+01 -1.9500000000000000e+01
|
||||
-1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.4460000000000001e+01
|
||||
-1.4460000000000001e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01
|
||||
-1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.4460000000000001e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02
|
||||
@@ -1,8 +0,0 @@
|
||||
7.0000000000000000e+01 1.1700000000000000e+02 1.0800000000000000e+02 1.0800000000000000e+02 3.2000000000000000e+01 7.0000000000000000e+01 1.0800000000000000e+02 9.7000000000000000e+01 1.1600000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
|
||||
7.9000000000000000e+01 1.1000000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01 7.1000000000000000e+01 1.1400000000000000e+02 1.1100000000000000e+02 1.1700000000000000e+02 1.1200000000000000e+02 3.2000000000000000e+01 6.6000000000000000e+01 1.0800000000000000e+02 1.1700000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01
|
||||
7.9000000000000000e+01 1.1000000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01 7.1000000000000000e+01 1.1400000000000000e+02 1.1100000000000000e+02 1.1700000000000000e+02 1.1200000000000000e+02 3.2000000000000000e+01 8.2000000000000000e+01 1.0100000000000000e+02 1.0000000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01
|
||||
7.0000000000000000e+01 1.1700000000000000e+02 1.0800000000000000e+02 1.0800000000000000e+02 3.2000000000000000e+01 1.1900000000000000e+02 3.2000000000000000e+01 8.3000000000000000e+01 8.2000000000000000e+01 8.3000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
|
||||
6.6000000000000000e+01 1.1100000000000000e+02 1.1600000000000000e+02 1.0400000000000000e+02 3.2000000000000000e+01 6.9000000000000000e+01 1.1000000000000000e+02 1.0000000000000000e+02 1.1500000000000000e+02 3.2000000000000000e+01 1.1900000000000000e+02 3.2000000000000000e+01 8.3000000000000000e+01 8.2000000000000000e+01 8.3000000000000000e+01
|
||||
1.0400000000000000e+02 1.0100000000000000e+02 9.7000000000000000e+01 1.1800000000000000e+02 1.2100000000000000e+02 3.2000000000000000e+01 9.8000000000000000e+01 1.0800000000000000e+02 1.1700000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
|
||||
1.0400000000000000e+02 1.0100000000000000e+02 9.7000000000000000e+01 1.1800000000000000e+02 1.2100000000000000e+02 3.2000000000000000e+01 1.1400000000000000e+02 1.0100000000000000e+02 1.0000000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
|
||||
1.1900000000000000e+02 1.1100000000000000e+02 1.1400000000000000e+02 1.1500000000000000e+02 1.1600000000000000e+02 3.2000000000000000e+01 9.9000000000000000e+01 9.7000000000000000e+01 1.1500000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"params": {
|
||||
"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],
|
||||
"frequencies": []
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from gnpy.core.utils import (load_json,
|
||||
itufs,
|
||||
freq2wavelength,
|
||||
lin2db,
|
||||
db2lin)
|
||||
from gnpy.core import network
|
||||
|
||||
topology = load_json('edfa_example_network.json')
|
||||
nw = network.network_from_json(topology)
|
||||
pch2d_legend_data = np.loadtxt('Pchan2DLegend.txt')
|
||||
pch2d = np.loadtxt('Pchan2D.txt')
|
||||
|
||||
ch_spacing = 0.05
|
||||
fc = itufs(ch_spacing)
|
||||
lc = freq2wavelength(fc) / 1000
|
||||
nchan = np.arange(len(lc))
|
||||
df = np.ones(len(lc)) * ch_spacing
|
||||
|
||||
edfa1 = [n for n in nw.nodes() if n.uid == 'Edfa1'][0]
|
||||
edfa1.gain_target = 20.0
|
||||
edfa1.tilt_target = -0.7
|
||||
edfa1.calc_nf()
|
||||
|
||||
results = []
|
||||
for Pin in pch2d:
|
||||
chgain = edfa1.gain_profile(Pin)
|
||||
pase = edfa1.noise_profile(chgain, fc, df)
|
||||
pout = lin2db(db2lin(Pin + chgain) + db2lin(pase))
|
||||
results.append(pout)
|
||||
|
||||
# Generate legend text
|
||||
|
||||
pch2d_legend = []
|
||||
for ea in pch2d_legend_data:
|
||||
s = ''.join([chr(xx) for xx in ea.astype(dtype=int)]).strip()
|
||||
pch2d_legend.append(s)
|
||||
|
||||
# Plot
|
||||
axis_font = {'fontname': 'Arial', 'size': '16', 'fontweight': 'bold'}
|
||||
title_font = {'fontname': 'Arial', 'size': '17', 'fontweight': 'bold'}
|
||||
tic_font = {'fontname': 'Arial', 'size': '12'}
|
||||
plt.rcParams["font.family"] = "Arial"
|
||||
plt.figure()
|
||||
plt.plot(nchan, pch2d.T, '.-', lw=2)
|
||||
plt.xlabel('Channel Number', **axis_font)
|
||||
plt.ylabel('Channel Power [dBm]', **axis_font)
|
||||
plt.title('Input Power Profiles for Different Channel Loading', **title_font)
|
||||
plt.legend(pch2d_legend, loc=5)
|
||||
plt.grid()
|
||||
plt.ylim((-100, -10))
|
||||
plt.xlim((0, 110))
|
||||
plt.xticks(np.arange(0, 100, 10), **tic_font)
|
||||
plt.yticks(np.arange(-110, -10, 10), **tic_font)
|
||||
plt.figure()
|
||||
for result in results:
|
||||
plt.plot(nchan, result, '.-', lw=2)
|
||||
plt.title('Output Power w/ ASE for Different Channel Loading', **title_font)
|
||||
plt.xlabel('Channel Number', **axis_font)
|
||||
plt.ylabel('Channel Power [dBm]', **axis_font)
|
||||
plt.grid()
|
||||
plt.ylim((-50, 10))
|
||||
plt.xlim((0, 100))
|
||||
plt.xticks(np.arange(0, 100, 10), **tic_font)
|
||||
plt.yticks(np.arange(-50, 10, 10), **tic_font)
|
||||
plt.legend(pch2d_legend, loc=5)
|
||||
plt.show()
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"network_name": "EDFA Example Network - P2P",
|
||||
"elements": [{
|
||||
"uid": "Site A",
|
||||
"uid": "Site_A",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -15,13 +15,15 @@
|
||||
{
|
||||
"uid": "Span1",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"dispersion": 16.7E-6,
|
||||
"gamma": 1.27E-3
|
||||
},
|
||||
"att_in": 0,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"region": "",
|
||||
@@ -33,11 +35,12 @@
|
||||
{
|
||||
"uid": "Edfa1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 16,
|
||||
"tilt_target": 0
|
||||
"gain_target": 17,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"config_from_json": "edfa_config.json",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"region": "",
|
||||
@@ -47,13 +50,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Site B",
|
||||
"uid": "Site_B",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Site B",
|
||||
"region": "",
|
||||
"latitude": 3,
|
||||
"latitude": 2,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
@@ -61,7 +64,7 @@
|
||||
|
||||
],
|
||||
"connections": [{
|
||||
"from_node": "Site A",
|
||||
"from_node": "Site_A",
|
||||
"to_node": "Span1"
|
||||
},
|
||||
{
|
||||
@@ -70,7 +73,7 @@
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa1",
|
||||
"to_node": "Site B"
|
||||
"to_node": "Site_B"
|
||||
}
|
||||
|
||||
]
|
||||
0
examples/edfa_model/DFG_96.txt
Executable file → Normal file
0
examples/edfa_model/DFG_96.txt
Executable file → Normal file
0
examples/edfa_model/DGT_96.txt
Executable file → Normal file
0
examples/edfa_model/DGT_96.txt
Executable file → Normal file
0
examples/edfa_model/NFR_96.txt
Executable file → Normal file
0
examples/edfa_model/NFR_96.txt
Executable file → Normal file
@@ -1,15 +1,5 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
"nf_ripple": "NFR0_96.txt",
|
||||
"gain_ripple": "DFG0_96.txt",
|
||||
"dgt": "DGT_96.txt"
|
||||
}
|
||||
0
examples/edfa_model/Pchan2D.txt
Executable file → Normal file
0
examples/edfa_model/Pchan2D.txt
Executable file → Normal file
0
examples/edfa_model/Pchan2DLegend.txt
Executable file → Normal file
0
examples/edfa_model/Pchan2DLegend.txt
Executable file → Normal file
@@ -47,7 +47,7 @@ nf_fit = boolean (True, False) :
|
||||
"""
|
||||
|
||||
input_json_file_name = "OA.json" #default path
|
||||
output_json_file_name = "edfa_config.json"
|
||||
output_json_file_name = "default_edfa_config.json"
|
||||
param_field ="params"
|
||||
gain_min_field = "gain_min"
|
||||
gain_max_field = "gain_flatmax"
|
||||
@@ -71,6 +71,7 @@ def read_file(field, file_name):
|
||||
#data = list(data.split(","))
|
||||
#data = [float(x) for x in data]
|
||||
data = np.loadtxt(file_name)
|
||||
print(len(data), 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
|
||||
@@ -79,54 +80,6 @@ def read_file(field, file_name):
|
||||
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"""
|
||||
@@ -138,20 +91,7 @@ def input_json(path):
|
||||
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)
|
||||
amp_text = json.dumps(amp_dict, indent=4)
|
||||
#print(amp_text)
|
||||
with open(output_json_file_name,'w') as edfa_json_file:
|
||||
edfa_json_file.write(amp_text)
|
||||
|
||||
0
examples/edfa_model/pNFfit3.txt
Executable file → Normal file
0
examples/edfa_model/pNFfit3.txt
Executable file → Normal file
185
examples/eqpt_config.json
Normal file
185
examples/eqpt_config.json
Normal file
@@ -0,0 +1,185 @@
|
||||
{ "Edfa":[{
|
||||
"type_variety": "high_detail_model_example",
|
||||
"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",
|
||||
"gain_flatmax": 26,
|
||||
"gain_min": 15,
|
||||
"p_max": 23,
|
||||
"nf_min": 6,
|
||||
"nf_max": 10,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "std_medium_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 26,
|
||||
"gain_min": 15,
|
||||
"p_max": 23,
|
||||
"nf_min": 6,
|
||||
"nf_max": 10,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "std_low_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 16,
|
||||
"gain_min": 8,
|
||||
"p_max": 23,
|
||||
"nf_min": 6.5,
|
||||
"nf_max": 11,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "std_fixed_gain",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 21,
|
||||
"gain_min": 20,
|
||||
"p_max": 21,
|
||||
"nf0": 5.5,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "test",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"nf_min": 7,
|
||||
"nf_max": 11,
|
||||
"allowed_for_design": false
|
||||
}
|
||||
],
|
||||
"Fiber":[{
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127
|
||||
}
|
||||
],
|
||||
"Spans":[{
|
||||
"power_mode":true,
|
||||
"delta_power_range_db": [0,0,0.5],
|
||||
"max_length": 150,
|
||||
"length_units": "km",
|
||||
"max_loss": 28,
|
||||
"padding": 10,
|
||||
"EOL": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
}
|
||||
],
|
||||
"Roadms":[{
|
||||
"gain_mode_default_loss": 20,
|
||||
"power_mode_pref": -20
|
||||
}],
|
||||
"SI":[{
|
||||
"f_min": 191.3e12,
|
||||
"baud_rate": 32e9,
|
||||
"f_max":195.1e12,
|
||||
"spacing": 50e9,
|
||||
"power_dbm": 0,
|
||||
"power_range_db": [0,0,0.5],
|
||||
"roll_off": 0.15,
|
||||
"OSNR": 11,
|
||||
"bit_rate":100e9
|
||||
}],
|
||||
"Transceiver":[
|
||||
{
|
||||
"type_variety": "vendorA_trx-type1",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
|
||||
"format": "mode 1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 11,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15
|
||||
},
|
||||
{
|
||||
"format": "mode 2",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 15,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15
|
||||
},
|
||||
{
|
||||
"format": "PS_SP64_1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 11,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15
|
||||
},
|
||||
{
|
||||
"format": "mode_2",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 15,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type_variety": "Voyager",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
"format": "16QAM",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 19,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15
|
||||
},
|
||||
{
|
||||
"format": "QPSK",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 12,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"type_variety": "Voyager_16QAM",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
"format": "16QAM",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 19,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15
|
||||
},
|
||||
{
|
||||
"format": "QPSK",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 12,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
196
examples/fused_roadm_example_network.json
Normal file
196
examples/fused_roadm_example_network.json
Normal file
@@ -0,0 +1,196 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx Site_A",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Site_A",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Site_C",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Site_C",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Site_A",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Site_A",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"loss": 17
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm Site_C",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Site_C",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Site_B",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Site_B",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Site_B",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Site_B",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Site_A \u2192 Site_B)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Site_B \u2192 Site_C)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Site_B \u2192 Site_A)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Site_C \u2192 Site_B)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "roadm Site_A",
|
||||
"to_node": "fiber (Site_A \u2192 Site_B)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Site_B \u2192 Site_A)-",
|
||||
"to_node": "roadm Site_A"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Site_A \u2192 Site_B)-",
|
||||
"to_node": "ingress fused spans in Site_B"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Site_B",
|
||||
"to_node": "fiber (Site_B \u2192 Site_C)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Site_C \u2192 Site_B)-",
|
||||
"to_node": "egress fused spans in Site_B"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Site_B",
|
||||
"to_node": "fiber (Site_B \u2192 Site_A)-"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Site_C",
|
||||
"to_node": "fiber (Site_C \u2192 Site_B)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Site_B \u2192 Site_C)-",
|
||||
"to_node": "roadm Site_C"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Site_A",
|
||||
"to_node": "roadm Site_A"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Site_A",
|
||||
"to_node": "trx Site_A"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Site_C",
|
||||
"to_node": "roadm Site_C"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Site_C",
|
||||
"to_node": "trx Site_C"
|
||||
}
|
||||
]
|
||||
}
|
||||
646
examples/meshTopologyExampleV2.json
Normal file
646
examples/meshTopologyExampleV2.json
Normal file
@@ -0,0 +1,646 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 4.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 4.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 4.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 4.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Morlaix",
|
||||
"region": "RLD",
|
||||
"latitude": 3.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Morlaix",
|
||||
"region": "RLD",
|
||||
"latitude": 3.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Corlay)-F061",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Loudeac)-F010",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Brest_KLA)-F060",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 3.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Lannion_CAS)-F061",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Corlay)-F010",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Brest_KLA \u2192 Morlaix)-F060",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 3.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Corlay)-F061"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Lannion_CAS)-F061",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Corlay)-F061",
|
||||
"to_node": "ingress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Loudeac)-F010"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Corlay)-F010",
|
||||
"to_node": "egress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Lannion_CAS)-F061"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Loudeac)-F010",
|
||||
"to_node": "ingress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
|
||||
"to_node": "egress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Corlay)-F010"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
|
||||
"to_node": "ingress fused spans in Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Morlaix",
|
||||
"to_node": "fiber (Morlaix \u2192 Brest_KLA)-F060"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Brest_KLA \u2192 Morlaix)-F060",
|
||||
"to_node": "egress fused spans in Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Morlaix",
|
||||
"to_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "fiber (Brest_KLA \u2192 Morlaix)-F060"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Brest_KLA)-F060",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lannion_CAS",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "trx Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lorient_KMA",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "trx Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Vannes_KBE",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "trx Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Rennes_STA",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "trx Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Brest_KLA",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "trx Brest_KLA"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
examples/meshTopologyExampleV2.xls
Normal file
BIN
examples/meshTopologyExampleV2.xls
Normal file
Binary file not shown.
165
examples/meshTopologyExampleV2_services.json
Normal file
165
examples/meshTopologyExampleV2_services.json
Normal file
@@ -0,0 +1,165 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": 0,
|
||||
"source": "Lorient_KMA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": 1,
|
||||
"source": "Brest_KLA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx Brest_KLA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": [
|
||||
{
|
||||
"index": 0,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "Lannion_CAS",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "Lorient_KMA",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": 3,
|
||||
"source": "Lannion_CAS",
|
||||
"destination": "Rennes_STA",
|
||||
"src-tp-id": "trx Lannion_CAS",
|
||||
"dst-tp-id": "trx Rennes_STA",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": 4,
|
||||
"source": "Rennes_STA",
|
||||
"destination": "Lannion_CAS",
|
||||
"src-tp-id": "trx Rennes_STA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": 64,
|
||||
"output-power": 0.0019952623149688794
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronisation": [
|
||||
{
|
||||
"synchonization-id": 0,
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchonization-id": 3,
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
3,
|
||||
4
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
examples/meshTopologyToy.xls
Normal file
BIN
examples/meshTopologyToy.xls
Normal file
Binary file not shown.
165
examples/path_requests_run.py
Executable file
165
examples/path_requests_run.py
Executable file
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
path_requests_run.py
|
||||
====================
|
||||
|
||||
Reads a JSON request file in accordance with the Yang model
|
||||
for requesting path computation and returns path results in terms
|
||||
of path and feasibilty.
|
||||
|
||||
See: draft-ietf-teas-yang-path-computation-01.txt
|
||||
"""
|
||||
|
||||
from sys import exit
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
from collections import namedtuple
|
||||
from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO
|
||||
from json import dumps, loads
|
||||
from networkx import (draw_networkx_nodes, draw_networkx_edges,
|
||||
draw_networkx_labels, dijkstra_path, NetworkXNoPath)
|
||||
from numpy import mean
|
||||
from examples.convert_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
|
||||
from gnpy.core.equipment import load_equipment, trx_mode_params
|
||||
from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused
|
||||
from gnpy.core.utils import db2lin, lin2db
|
||||
from gnpy.core.request import Path_request, Result_element, compute_constrained_path, propagate, jsontocsv
|
||||
from copy import copy, deepcopy
|
||||
|
||||
#EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json'
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
parser = ArgumentParser(description = 'A function that computes performances for a list of services provided in a json file or an excel sheet.')
|
||||
parser.add_argument('network_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls')
|
||||
parser.add_argument('service_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls')
|
||||
parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json')
|
||||
parser.add_argument('-v', '--verbose', action='count')
|
||||
parser.add_argument('-o', '--output', default=None)
|
||||
|
||||
|
||||
def requests_from_json(json_data,equipment):
|
||||
requests_list = []
|
||||
|
||||
for req in json_data['path-request']:
|
||||
#print(f'{req}')
|
||||
params = {}
|
||||
params['request_id'] = req['request-id']
|
||||
params['source'] = req['src-tp-id']
|
||||
params['destination'] = req['dst-tp-id']
|
||||
params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type']
|
||||
params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode']
|
||||
params['format'] = params['trx_mode']
|
||||
nd_list = req['optimizations']['explicit-route-include-objects']
|
||||
params['nodes_list'] = [n['unnumbered-hop']['node-id'] for n in nd_list]
|
||||
params['loose_list'] = [n['unnumbered-hop']['hop-type'] for n in nd_list]
|
||||
params['spacing'] = req['path-constraints']['te-bandwidth']['spacing']
|
||||
|
||||
trx_params = trx_mode_params(equipment,params['trx_type'],params['trx_mode'],True)
|
||||
params.update(trx_params)
|
||||
params['power'] = req['path-constraints']['te-bandwidth']['output-power']
|
||||
params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel']
|
||||
|
||||
requests_list.append(Path_request(**params))
|
||||
|
||||
return requests_list
|
||||
|
||||
|
||||
def load_requests(filename,eqpt_filename):
|
||||
if filename.suffix.lower() == '.xls':
|
||||
logger.info('Automatically converting requests from XLS to JSON')
|
||||
json_data = convert_service_sheet(filename,eqpt_filename)
|
||||
else:
|
||||
with open(filename) as f:
|
||||
json_data = loads(f.read())
|
||||
return json_data
|
||||
|
||||
def compute_path(network, equipment, pathreqlist):
|
||||
|
||||
path_res_list = []
|
||||
|
||||
for pathreq in pathreqlist:
|
||||
#need to rebuid the network for each path because the total power
|
||||
#can be different and the choice of amplifiers in autodesign is power dependant
|
||||
#but the design is the same if the total power is the same
|
||||
#TODO parametrize the total spectrum power so the same design can be shared
|
||||
p_db = lin2db(pathreq.power*1e3)
|
||||
p_total_db = p_db + lin2db(pathreq.nb_channel)
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
pathreq.nodes_list.append(pathreq.destination)
|
||||
#we assume that the destination is a strict constraint
|
||||
pathreq.loose_list.append('strict')
|
||||
print(f'Computing path from {pathreq.source} to {pathreq.destination}')
|
||||
print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output
|
||||
total_path = compute_constrained_path(network, pathreq)
|
||||
print(f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}\n')
|
||||
# for debug
|
||||
# print(f'{pathreq.baud_rate} {pathreq.power} {pathreq.spacing} {pathreq.nb_channel}')
|
||||
if total_path :
|
||||
total_path = propagate(total_path,pathreq,equipment, show=False)
|
||||
else:
|
||||
total_path = []
|
||||
# we record the last tranceiver object in order to have th whole
|
||||
# information about spectrum. Important Note: since transceivers
|
||||
# attached to roadms are actually logical elements to simulate
|
||||
# performance, several demands having the same destination may use
|
||||
# the same transponder for the performance simaulation. This is why
|
||||
# we use deepcopy: to ensure each propagation is recorded and not
|
||||
# overwritten
|
||||
|
||||
path_res_list.append(deepcopy(total_path))
|
||||
return path_res_list
|
||||
|
||||
def path_result_json(pathresult):
|
||||
data = {
|
||||
'path': [n.json for n in pathresult]
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, CRITICAL))
|
||||
logger.info(f'Computing path requests {args.service_filename} into JSON format')
|
||||
# for debug
|
||||
# print( args.eqpt_filename)
|
||||
data = load_requests(args.service_filename,args.eqpt_filename)
|
||||
equipment = load_equipment(args.eqpt_filename)
|
||||
network = load_network(args.network_filename,equipment)
|
||||
pths = requests_from_json(data, equipment)
|
||||
print(pths)
|
||||
test = compute_path(network, equipment, pths)
|
||||
|
||||
#TODO write results
|
||||
|
||||
header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR']
|
||||
data = []
|
||||
data.append(header)
|
||||
for i, p in enumerate(test):
|
||||
if p:
|
||||
line = [f'{pths[i].source} to {pths[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\
|
||||
f'{round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}',\
|
||||
f'{pths[i].OSNR}']
|
||||
else:
|
||||
line = [f'no path from {pths[i].source} to {pths[i].destination} ']
|
||||
data.append(line)
|
||||
|
||||
col_width = max(len(word) for row in data for word in row) # padding
|
||||
for row in data:
|
||||
print(''.join(word.ljust(col_width) for word in row))
|
||||
|
||||
|
||||
|
||||
if args.output :
|
||||
result = []
|
||||
for p in test:
|
||||
result.append(Result_element(pths[test.index(p)],p))
|
||||
with open(args.output, 'w') as f:
|
||||
f.write(dumps(path_result_json(result), indent=2))
|
||||
fnamecsv = next(s for s in args.output.split('.')) + '.csv'
|
||||
with open(fnamecsv,"w") as fcsv :
|
||||
jsontocsv(path_result_json(result),equipment,fcsv)
|
||||
301
examples/std_medium_gain_advanced_config.json
Normal file
301
examples/std_medium_gain_advanced_config.json
Normal file
@@ -0,0 +1,301 @@
|
||||
{ "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
|
||||
],
|
||||
"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
|
||||
]
|
||||
}
|
||||
253
examples/transmission_main_example.py
Normal file → Executable file
253
examples/transmission_main_example.py
Normal file → Executable file
@@ -1,90 +1,205 @@
|
||||
#!/usr/bin/env
|
||||
"""
|
||||
@author: briantaylor
|
||||
@author: giladgoldfarb
|
||||
@author: jeanluc-auge
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
Transmission setup example:
|
||||
reads from network json (default = examples/edfa/edfa_example_network.json)
|
||||
propagates a 96 channels comb
|
||||
"""
|
||||
'''
|
||||
transmission_main_example.py
|
||||
============================
|
||||
|
||||
Main example for transmission simulation.
|
||||
|
||||
Reads from network JSON (by default, `edfa_example_network.json`)
|
||||
'''
|
||||
|
||||
from gnpy.core.equipment import load_equipment, trx_mode_params
|
||||
from gnpy.core.utils import db2lin, lin2db, write_csv
|
||||
from argparse import ArgumentParser
|
||||
from json import load
|
||||
from sys import exit
|
||||
from pathlib import Path
|
||||
from json import loads
|
||||
from collections import Counter
|
||||
from logging import getLogger, basicConfig, INFO, ERROR, DEBUG
|
||||
|
||||
from numpy import arange, mean
|
||||
from matplotlib.pyplot import show, axis, figure, title
|
||||
from networkx import (draw_networkx_nodes, draw_networkx_edges,
|
||||
draw_networkx_labels, dijkstra_path)
|
||||
from gnpy.core.network import load_network, build_network, save_network
|
||||
from gnpy.core.elements import Transceiver, Fiber, Edfa, Roadm
|
||||
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref
|
||||
from gnpy.core.request import Path_request, RequestParams, compute_constrained_path, propagate
|
||||
|
||||
from gnpy.core import network_from_json, build_network
|
||||
from gnpy.core.elements import Transceiver, Fiber, Edfa
|
||||
from gnpy.core.info import SpectralInformation, Channel, Power
|
||||
#from gnpy.core.algorithms import closed_paths
|
||||
logger = getLogger(__name__)
|
||||
|
||||
logger = getLogger(__package__ or __file__)
|
||||
|
||||
def format_si(spectral_infos):
|
||||
return '\n'.join([
|
||||
f'#{idx} Carrier(frequency={c.frequency},\n power=Power(signal={c.power.signal}, nli={c.power.nli}, ase={c.power.ase}))'
|
||||
for idx, si in sorted(set(spectral_infos))
|
||||
for c in set(si.carriers)
|
||||
])
|
||||
|
||||
logger = getLogger('gnpy.core')
|
||||
|
||||
def main(args):
|
||||
with open(args.filename) as f:
|
||||
json_data = load(f)
|
||||
|
||||
network = network_from_json(json_data)
|
||||
build_network(network)
|
||||
|
||||
spacing = 0.05 #THz
|
||||
si = SpectralInformation() # !! SI units W, Hz
|
||||
si = si.update(carriers=tuple(Channel(f, (191.3+spacing*f)*1e12,
|
||||
32e9, 0.15, Power(1e-3, 0, 0)) for f in range(1,97)))
|
||||
|
||||
trx = [n for n in network.nodes() if isinstance(n, Transceiver)]
|
||||
source, sink = trx[0], trx[1]
|
||||
|
||||
path = dijkstra_path(network, source, sink)
|
||||
print(f'There are {len(path)} network elements between {source} and {sink}')
|
||||
|
||||
for el in path:
|
||||
si = el(si)
|
||||
print(el)
|
||||
|
||||
nodelist = [n for n in network.nodes() if isinstance(n, (Transceiver, Fiber))]
|
||||
pathnodes = [n for n in path if isinstance(n, (Transceiver, Fiber))]
|
||||
edgelist = [(u, v) for u, v in zip(pathnodes, pathnodes[1:])]
|
||||
node_color = ['#ff0000' if n is source or n is sink else
|
||||
'#900000' if n in path else '#ffdfdf'
|
||||
for n in nodelist]
|
||||
edge_color = ['#ff9090' if u in path and v in path else '#ababab'
|
||||
for u, v in edgelist]
|
||||
labels = {n: n.location.city if isinstance(n, Transceiver) else ''
|
||||
for n in pathnodes}
|
||||
def plot_results(network, path, source, destination):
|
||||
path_edges = set(zip(path[:-1], path[1:]))
|
||||
edges = set(network.edges()) - path_edges
|
||||
pos = {n: (n.lng, n.lat) for n in network.nodes()}
|
||||
labels = {n: n.location.city for n in network.nodes() if isinstance(n, Transceiver)}
|
||||
city_labels = set(labels.values())
|
||||
for n in network.nodes():
|
||||
if n.location.city and n.location.city not in city_labels:
|
||||
labels[n] = n.location.city
|
||||
city_labels.add(n.location.city)
|
||||
label_pos = pos
|
||||
|
||||
fig = figure()
|
||||
pos = {n: (n.lng, n.lat) for n in nodelist}
|
||||
kwargs = {'figure': fig, 'pos': pos}
|
||||
plot = draw_networkx_nodes(network, nodelist=nodelist, node_color=node_color, **kwargs)
|
||||
draw_networkx_edges(network, edgelist=edgelist, edge_color=edge_color, **kwargs)
|
||||
draw_networkx_labels(network, labels=labels, font_size=14, **kwargs)
|
||||
title(f'Propagating from {source.loc.city} to {sink.loc.city}')
|
||||
plot = draw_networkx_nodes(network, nodelist=network.nodes(), node_color='#ababab', **kwargs)
|
||||
draw_networkx_nodes(network, nodelist=path, node_color='#ff0000', **kwargs)
|
||||
draw_networkx_edges(network, edgelist=edges, edge_color='#ababab', **kwargs)
|
||||
draw_networkx_edges(network, edgelist=path_edges, edge_color='#ff0000', **kwargs)
|
||||
draw_networkx_labels(network, labels=labels, font_size=14, **{**kwargs, 'pos': label_pos})
|
||||
title(f'Propagating from {source.loc.city} to {destination.loc.city}')
|
||||
axis('off')
|
||||
show()
|
||||
|
||||
|
||||
def main(network, equipment, source, destination, req = None):
|
||||
result_dicts = {}
|
||||
network_data = [{
|
||||
'network_name' : str(args.filename),
|
||||
'source' : source.uid,
|
||||
'destination' : destination.uid
|
||||
}]
|
||||
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,
|
||||
'design_pch' : equipment['SI']['default'].power_dbm,
|
||||
'baud_rate' : equipment['SI']['default'].baud_rate
|
||||
}]
|
||||
result_dicts.update({'design': design_data})
|
||||
simulation_data = []
|
||||
result_dicts.update({'simulation results': simulation_data})
|
||||
|
||||
power_mode = equipment['Spans']['default'].power_mode
|
||||
print('\n'.join([f'Power mode is set to {power_mode}',
|
||||
f'=> it can be modified in eqpt_config.json - Spans']))
|
||||
|
||||
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)
|
||||
build_network(network, equipment, pref_ch_db, pref_total_db)
|
||||
path = compute_constrained_path(network, req)
|
||||
|
||||
spans = [s.length for s in path if isinstance(s, Fiber)]
|
||||
print(f'\nThere are {len(spans)} fiber spans over {sum(spans):.0f}m between {source.uid} and {destination.uid}')
|
||||
print(f'\nNow propagating between {source.uid} and {destination.uid}:')
|
||||
|
||||
try:
|
||||
power_range = list(arange(*equipment['SI']['default'].power_range_db))
|
||||
last = equipment['SI']['default'].power_range_db[-2]
|
||||
if len(power_range) == 0 : #bad input that will lead to no simulation
|
||||
power_range = [0] #better than an error message
|
||||
else:
|
||||
power_range.append(last)
|
||||
except TypeError:
|
||||
print('invalid power range definition in eqpt_config, should be power_range_db: [lower, upper, step]')
|
||||
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 :')
|
||||
propagate(path, req, equipment, show=len(power_range)==1)
|
||||
print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f}dBm :')
|
||||
print(destination)
|
||||
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)
|
||||
})
|
||||
write_csv(result_dicts, 'simulation_result.csv')
|
||||
return path
|
||||
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('filename', nargs='?', type=Path,
|
||||
default= Path(__file__).parent / 'edfa/edfa_example_network.json')
|
||||
parser.add_argument('-e', '--equipment', type=Path,
|
||||
default=Path(__file__).parent / 'eqpt_config.json')
|
||||
parser.add_argument('-pl', '--plot', action='store_true', default=False)
|
||||
parser.add_argument('-v', '--verbose', action='count')
|
||||
parser.add_argument('-l', '--list-nodes', action='store_true', default=False, help='list all transceiver nodes')
|
||||
parser.add_argument('-po', '--power', default=0, help='channel ref power in dBm')
|
||||
#parser.add_argument('-plb', '--power-lower-bound', default=0, help='power sweep lower bound')
|
||||
#parser.add_argument('-pub', '--power-upper-bound', default=1, help='power sweep upper bound')
|
||||
parser.add_argument('filename', nargs='?', type=Path,
|
||||
default=Path(__file__).parent / 'edfa_example_network.json')
|
||||
parser.add_argument('source', nargs='?', help='source node')
|
||||
parser.add_argument('destination', nargs='?', help='destination node')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
level = {1: INFO, 2: DEBUG}.get(args.verbose, ERROR)
|
||||
logger.setLevel(level)
|
||||
basicConfig()
|
||||
exit(main(args))
|
||||
basicConfig(level={0: ERROR, 1: INFO, 2: DEBUG}.get(args.verbose, ERROR))
|
||||
|
||||
equipment = load_equipment(args.equipment)
|
||||
# logger.info(equipment)
|
||||
# print(args.filename)
|
||||
network = load_network(args.filename, equipment)
|
||||
# print(network)
|
||||
|
||||
transceivers = {n.uid: n for n in network.nodes() if isinstance(n, Transceiver)}
|
||||
|
||||
if not transceivers:
|
||||
exit('Network has no transceivers!')
|
||||
if len(transceivers) < 2:
|
||||
exit('Network has only one transceiver!')
|
||||
|
||||
if args.list_nodes:
|
||||
for uid in transceivers:
|
||||
print(uid)
|
||||
exit()
|
||||
|
||||
if args.source:
|
||||
try:
|
||||
source = next(transceivers[uid] for uid in transceivers if uid == args.source)
|
||||
except StopIteration as e:
|
||||
#TODO code a more advanced regex to find nodes match
|
||||
nodes_suggestion = [uid for uid in transceivers \
|
||||
if args.source.lower() in uid.lower()]
|
||||
source = transceivers[nodes_suggestion[0]] \
|
||||
if len(nodes_suggestion)>0 else list(transceivers.values())[0]
|
||||
print(f'invalid souce node specified, did you mean:\
|
||||
\n{nodes_suggestion}?\
|
||||
\n{args.source!r}, replaced with {source.uid}')
|
||||
del transceivers[source.uid]
|
||||
else:
|
||||
logger.info('No source node specified: picking random transceiver')
|
||||
source = list(transceivers.values())[0]
|
||||
|
||||
if args.destination:
|
||||
try:
|
||||
destination = next(transceivers[uid] for uid in transceivers if uid == args.destination)
|
||||
except StopIteration as e:
|
||||
nodes_suggestion = [uid for uid in transceivers \
|
||||
if args.destination.lower() in uid.lower()]
|
||||
destination = transceivers[nodes_suggestion[0]] \
|
||||
if len(nodes_suggestion)>0 else list(transceivers.values())[0]
|
||||
print(f'invalid destination node specified, did you mean:\
|
||||
\n{nodes_suggestion}?\
|
||||
\n{args.destination!r}, replaced with {destination.uid}')
|
||||
else:
|
||||
logger.info('No source node specified: picking random transceiver')
|
||||
destination = list(transceivers.values())[1]
|
||||
|
||||
logger.info(f'source = {args.source!r}')
|
||||
logger.info(f'destination = {args.destination!r}')
|
||||
|
||||
params = {}
|
||||
params['request_id'] = 0
|
||||
params['trx_type'] = ''
|
||||
params['trx_mode'] = ''
|
||||
params['source'] = source.uid
|
||||
params['destination'] = destination.uid
|
||||
params['nodes_list'] = [destination.uid]
|
||||
params['loose_list'] = ['strict']
|
||||
params['format'] = ''
|
||||
trx_params = trx_mode_params(equipment)
|
||||
if args.power:
|
||||
trx_params['power'] = db2lin(float(args.power))*1e-3
|
||||
params.update(trx_params)
|
||||
req = Path_request(**params)
|
||||
path = main(network, equipment, source, destination, req)
|
||||
save_network(args.filename, network)
|
||||
|
||||
if args.plot:
|
||||
plot_results(network, path, source, destination)
|
||||
|
||||
36
examples/write_path_jsontocsv.py
Normal file
36
examples/write_path_jsontocsv.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
write_path_jsontocsv.py
|
||||
========================
|
||||
|
||||
Reads JSON path result file in accordance with the Yang model for requesting
|
||||
path computation and writes results to a CSV file.
|
||||
|
||||
See: draft-ietf-teas-yang-path-computation-01.txt
|
||||
"""
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
from json import loads
|
||||
from gnpy.core.equipment import load_equipment
|
||||
from gnpy.core.request import jsontocsv
|
||||
|
||||
|
||||
parser = ArgumentParser(description = 'A function that writes json path results in an excel sheet.')
|
||||
parser.add_argument('filename', nargs='?', type = Path)
|
||||
parser.add_argument('output_filename', nargs='?', type = Path)
|
||||
parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json')
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.output_filename,"w") as file :
|
||||
with open(args.filename) as f:
|
||||
print(f'Reading {args.filename}')
|
||||
json_data = loads(f.read())
|
||||
equipment = load_equipment(args.eqpt_filename)
|
||||
print(f'Writing in {args.output_filename}')
|
||||
jsontocsv(json_data,equipment,file)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
########################################################################
|
||||
# _____ ___ ____ ____ ____ _____ #
|
||||
# |_ _|_ _| _ \ | _ \/ ___|| ____| #
|
||||
@@ -23,7 +24,6 @@ operators of large-scale mesh optical networks.
|
||||
:license: BSD 3-Clause, see LICENSE for more details.
|
||||
'''
|
||||
|
||||
|
||||
from . import elements
|
||||
from .execute import *
|
||||
from .network import *
|
||||
|
||||
387
gnpy/core/convert.py
Executable file
387
gnpy/core/convert.py
Executable file
@@ -0,0 +1,387 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
gnpy.core.convert
|
||||
=================
|
||||
|
||||
This module contains utilities for converting between XLS and JSON.
|
||||
|
||||
The input XLS file must contain sheets named "Nodes" and "Links".
|
||||
It may optionally contain a sheet named "Eqpt".
|
||||
|
||||
In the "Nodes" sheet, only the "City" column is mandatory. The column "Type"
|
||||
can be determined automatically given the topology (e.g., if degree 2, ILA;
|
||||
otherwise, ROADM.) Incorrectly specified types (e.g., ILA for node of
|
||||
degree ≠ 2) will be automatically corrected.
|
||||
|
||||
In the "Links" sheet, only the first three columns ("Node A", "Node Z" and
|
||||
"east Distance (km)") are mandatory. Missing "west" information is copied from
|
||||
the "east" information so that it is possible to input undirected data.
|
||||
"""
|
||||
|
||||
from sys import exit
|
||||
try:
|
||||
from xlrd import open_workbook
|
||||
except ModuleNotFoundError:
|
||||
exit('Required: `pip install xlrd`')
|
||||
from argparse import ArgumentParser
|
||||
from collections import namedtuple, Counter, defaultdict
|
||||
from itertools import chain
|
||||
from json import dumps
|
||||
from pathlib import Path
|
||||
|
||||
all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows))
|
||||
|
||||
class Node(namedtuple('Node', 'city state country region latitude longitude node_type')):
|
||||
def __new__(cls, city, state='', country='', region='', latitude=0, longitude=0, node_type='ILA'):
|
||||
values = [latitude, longitude, node_type]
|
||||
default_values = [0, 0, 'ILA']
|
||||
values = [x[0] if x[0] != '' else x[1] for x in zip(values,default_values)]
|
||||
return super().__new__(cls, city, state, country, region, *values)
|
||||
|
||||
class Link(namedtuple('Link', 'from_city to_city \
|
||||
east_distance east_fiber east_lineic east_con_in east_con_out east_pmd east_cable \
|
||||
west_distance west_fiber west_lineic west_con_in west_con_out west_pmd west_cable \
|
||||
distance_units')):
|
||||
def __new__(cls, from_city, to_city,
|
||||
east_distance, east_fiber='SSMF', east_lineic=0.2,
|
||||
east_con_in=None, east_con_out=None, east_pmd=0.1, east_cable='',
|
||||
west_distance='', west_fiber='', west_lineic='',
|
||||
west_con_in='', west_con_out='', west_pmd='', west_cable='',
|
||||
distance_units='km'):
|
||||
east_values = [east_distance, east_fiber, east_lineic, east_con_in, east_con_out,
|
||||
east_pmd, east_cable]
|
||||
west_values = [west_distance, west_fiber, west_lineic, west_con_in, west_con_out,
|
||||
west_pmd, west_cable]
|
||||
default_values = [80,'SSMF',0.2,None,None,0.1,'']
|
||||
east_values = [x[0] if x[0] != '' else x[1] for x in zip(east_values,default_values)]
|
||||
west_values = [x[0] if x[0] != '' else x[1] for x in zip(west_values,east_values)]
|
||||
return super().__new__(cls, from_city, to_city, *east_values, *west_values, distance_units)
|
||||
|
||||
class Eqpt(namedtuple('Eqpt', 'from_city to_city \
|
||||
egress_amp_type egress_att_in egress_amp_gain egress_amp_tilt egress_amp_att_out\
|
||||
ingress_amp_type ingress_att_in ingress_amp_gain ingress_amp_tilt ingress_amp_att_out')):
|
||||
def __new__(cls, from_city='', to_city='',
|
||||
egress_amp_type='', egress_att_in=0, egress_amp_gain=0, egress_amp_tilt=0, egress_amp_att_out=0,
|
||||
ingress_amp_type='', ingress_att_in=0, ingress_amp_gain=0, ingress_amp_tilt=0, ingress_amp_att_out=0):
|
||||
values = [from_city, to_city,
|
||||
egress_amp_type, egress_att_in, egress_amp_gain, egress_amp_tilt, egress_amp_att_out,
|
||||
ingress_amp_type, ingress_att_in, ingress_amp_gain, ingress_amp_tilt, ingress_amp_att_out]
|
||||
default_values = ['','','',0,0,0,0,'',0,0,0,0]
|
||||
values = [x[0] if x[0] != '' else x[1] for x in zip(values,default_values)]
|
||||
return super().__new__(cls, *values)
|
||||
|
||||
def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city):
|
||||
try :
|
||||
test_nodes = [n for n in nodes_by_city if not n in links_by_city]
|
||||
test_links = [n for n in links_by_city if not n in nodes_by_city]
|
||||
test_eqpts = [n for n in eqpts_by_city if not n in nodes_by_city]
|
||||
assert (test_nodes == [] or test_nodes == [''])\
|
||||
and (test_links == [] or test_links ==[''])\
|
||||
and (test_eqpts == [] or test_eqpts ==[''])
|
||||
except AssertionError:
|
||||
print(f'!names in Nodes and Links sheets do no match, check:\
|
||||
\n{test_nodes} in Nodes sheet\
|
||||
\n{test_links} in Links sheet\
|
||||
\n{test_eqpts} in Eqpt sheet')
|
||||
exit(1)
|
||||
|
||||
for city,link in links_by_city.items():
|
||||
if nodes_by_city[city].node_type.lower()=='ila' and len(link) != 2:
|
||||
#wrong input: ILA sites can only be Degree 2
|
||||
# => correct to make it a ROADM and remove entry in links_by_city
|
||||
#TODO : put in log rather than print
|
||||
print(f'invalid node type ({nodes_by_city[city].node_type})\
|
||||
specified in {city}, replaced by ROADM')
|
||||
nodes_by_city[city] = nodes_by_city[city]._replace(node_type='ROADM')
|
||||
nodes = [n._replace(node_type='ROADM') if n.city==city else n for n in nodes]
|
||||
return nodes
|
||||
|
||||
def convert_file(input_filename, filter_region=[]):
|
||||
nodes, links, eqpts = parse_excel(input_filename)
|
||||
|
||||
if filter_region:
|
||||
nodes = [n for n in nodes if n.region.lower() in filter_region]
|
||||
cities = {n.city for n in nodes}
|
||||
links = [lnk for lnk in links if lnk.from_city in cities and
|
||||
lnk.to_city in cities]
|
||||
cities = {lnk.from_city for lnk in links} | {lnk.to_city for lnk in links}
|
||||
nodes = [n for n in nodes if n.city in cities]
|
||||
|
||||
global nodes_by_city
|
||||
nodes_by_city = {n.city: n for n in nodes}
|
||||
|
||||
global links_by_city
|
||||
links_by_city = defaultdict(list)
|
||||
for link in links:
|
||||
links_by_city[link.from_city].append(link)
|
||||
links_by_city[link.to_city].append(link)
|
||||
|
||||
global eqpts_by_city
|
||||
eqpts_by_city = defaultdict(list)
|
||||
for eqpt in eqpts:
|
||||
eqpts_by_city[eqpt.from_city].append(eqpt)
|
||||
|
||||
nodes = sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city)
|
||||
|
||||
data = {
|
||||
'elements':
|
||||
[{'uid': f'trx {x.city}',
|
||||
'metadata': {'location': {'city': x.city,
|
||||
'region': x.region,
|
||||
'latitude': x.latitude,
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Transceiver'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm'] +
|
||||
[{'uid': f'roadm {x.city}',
|
||||
'metadata': {'location': {'city': x.city,
|
||||
'region': x.region,
|
||||
'latitude': x.latitude,
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Roadm'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm'] +
|
||||
[{'uid': f'ingress fused spans in {x.city}',
|
||||
'metadata': {'location': {'city': x.city,
|
||||
'region': x.region,
|
||||
'latitude': x.latitude,
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Fused'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'fused'] +
|
||||
[{'uid': f'egress fused spans in {x.city}',
|
||||
'metadata': {'location': {'city': x.city,
|
||||
'region': x.region,
|
||||
'latitude': x.latitude,
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Fused'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'fused'] +
|
||||
[{'uid': f'fiber ({x.from_city} → {x.to_city})-{x.east_cable}',
|
||||
'metadata': {'location': midpoint(nodes_by_city[x.from_city],
|
||||
nodes_by_city[x.to_city])},
|
||||
'type': 'Fiber',
|
||||
'type_variety': x.east_fiber,
|
||||
'params': {'length': round(x.east_distance, 3),
|
||||
'length_units': x.distance_units,
|
||||
'loss_coef': x.east_lineic,
|
||||
'con_in':x.east_con_in,
|
||||
'con_out':x.east_con_out}
|
||||
}
|
||||
for x in links] +
|
||||
[{'uid': f'fiber ({x.to_city} → {x.from_city})-{x.west_cable}',
|
||||
'metadata': {'location': midpoint(nodes_by_city[x.from_city],
|
||||
nodes_by_city[x.to_city])},
|
||||
'type': 'Fiber',
|
||||
'type_variety': x.west_fiber,
|
||||
'params': {'length': round(x.west_distance, 3),
|
||||
'length_units': x.distance_units,
|
||||
'loss_coef': x.west_lineic,
|
||||
'con_in':x.west_con_in,
|
||||
'con_out':x.west_con_out}
|
||||
} # missing ILA construction
|
||||
for x in links] +
|
||||
[{'uid': f'egress edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
'latitude': nodes_by_city[e.from_city].latitude,
|
||||
'longitude': nodes_by_city[e.from_city].longitude}},
|
||||
'type': 'Edfa',
|
||||
'type_variety': e.egress_amp_type,
|
||||
'operational': {'gain_target': e.egress_amp_gain,
|
||||
'tilt_target': e.egress_amp_tilt}
|
||||
}
|
||||
for e in eqpts if e.egress_amp_type.lower() != ''] +
|
||||
[{'uid': f'ingress edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
'latitude': nodes_by_city[e.from_city].latitude,
|
||||
'longitude': nodes_by_city[e.from_city].longitude}},
|
||||
'type': 'Edfa',
|
||||
'type_variety': e.ingress_amp_type,
|
||||
'operational': {'gain_target': e.ingress_amp_gain,
|
||||
'tilt_target': e.ingress_amp_tilt}
|
||||
}
|
||||
for e in eqpts if e.ingress_amp_type.lower() != ''],
|
||||
'connections':
|
||||
list(chain.from_iterable([eqpt_connection_by_city(n.city)
|
||||
for n in nodes]))
|
||||
+
|
||||
list(chain.from_iterable(zip(
|
||||
[{'from_node': f'trx {x.city}',
|
||||
'to_node': f'roadm {x.city}'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower()=='roadm'],
|
||||
[{'from_node': f'roadm {x.city}',
|
||||
'to_node': f'trx {x.city}'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower()=='roadm'])))
|
||||
}
|
||||
|
||||
#print(dumps(data, indent=2))
|
||||
# output_json_file_name = input_filename.split(".")[0]+".json"
|
||||
suffix_filename = str(input_filename.suffixes[0])
|
||||
full_input_filename = str(input_filename)
|
||||
split_filename = [full_input_filename[0:len(full_input_filename)-len(suffix_filename)] , suffix_filename[1:]]
|
||||
output_json_file_name = split_filename[0]+'.json'
|
||||
with open(output_json_file_name,'w') as edfa_json_file:
|
||||
edfa_json_file.write(dumps(data, indent=2))
|
||||
return output_json_file_name
|
||||
|
||||
def parse_excel(input_filename):
|
||||
with open_workbook(input_filename) as wb:
|
||||
nodes_sheet = wb.sheet_by_name('Nodes')
|
||||
links_sheet = wb.sheet_by_name('Links')
|
||||
try:
|
||||
eqpt_sheet = wb.sheet_by_name('Eqpt')
|
||||
except:
|
||||
#eqpt_sheet is optional
|
||||
eqpt_sheet = None
|
||||
|
||||
|
||||
# sanity check
|
||||
"""
|
||||
header = [x.value.strip() for x in nodes_sheet.row(4)]
|
||||
expected = ['City', 'State', 'Country', 'Region', 'Latitude', 'Longitude']
|
||||
if header != expected:
|
||||
raise ValueError(f'Malformed header on Nodes sheet: {header} != {expected}')
|
||||
"""
|
||||
|
||||
nodes = []
|
||||
for row in all_rows(nodes_sheet, start=5):
|
||||
nodes.append(Node(*(x.value for x in row[0:NODES_COLUMN])))
|
||||
#check input
|
||||
expected_node_types = ('ROADM', 'ILA', 'FUSED')
|
||||
nodes = [n._replace(node_type='ILA')
|
||||
if not (n.node_type in expected_node_types) else n for n in nodes]
|
||||
|
||||
# sanity check
|
||||
"""
|
||||
header = [x.value.strip() for x in links_sheet.row(4)]
|
||||
expected = ['Node A', 'Node Z',
|
||||
'Distance (km)', 'Fiber type', 'lineic att', 'Con_in', 'Con_out', 'PMD', 'Cable id',
|
||||
'Distance (km)', 'Fiber type', 'lineic att', 'Con_in', 'Con_out', 'PMD', 'Cable id']
|
||||
if header != expected:
|
||||
raise ValueError(f'Malformed header on Nodes sheet: {header} != {expected}')
|
||||
"""
|
||||
links = []
|
||||
for row in all_rows(links_sheet, start=5):
|
||||
links.append(Link(*(x.value for x in row[0:LINKS_COLUMN])))
|
||||
|
||||
eqpts = []
|
||||
if eqpt_sheet != None:
|
||||
for row in all_rows(eqpt_sheet, start=5):
|
||||
eqpts.append(Eqpt(*(x.value for x in row[0:EQPTS_COLUMN])))
|
||||
|
||||
# sanity check
|
||||
all_cities = Counter(n.city for n in nodes)
|
||||
if len(all_cities) != len(nodes):
|
||||
ValueError(f'Duplicate city: {all_cities}')
|
||||
if any(ln.from_city not in all_cities or
|
||||
ln.to_city not in all_cities for ln in links):
|
||||
ValueError(f'Bad link.')
|
||||
|
||||
return nodes, links, eqpts
|
||||
|
||||
|
||||
def eqpt_connection_by_city(city_name):
|
||||
other_cities = fiber_dest_from_source(city_name)
|
||||
subdata = []
|
||||
if nodes_by_city[city_name].node_type.lower() in ('ila', 'fused'):
|
||||
# Then len(other_cities) == 2
|
||||
direction = ['ingress', 'egress']
|
||||
for i in range(2):
|
||||
from_ = fiber_link(other_cities[i], city_name)
|
||||
in_ = eqpt_in_city_to_city(city_name, other_cities[0],direction[i])
|
||||
to_ = fiber_link(city_name, other_cities[1-i])
|
||||
subdata += connect_eqpt(from_, in_, to_)
|
||||
elif nodes_by_city[city_name].node_type.lower() == 'roadm':
|
||||
for other_city in other_cities:
|
||||
from_ = f'roadm {city_name}'
|
||||
in_ = eqpt_in_city_to_city(city_name, other_city)
|
||||
to_ = fiber_link(city_name, other_city)
|
||||
subdata += connect_eqpt(from_, in_, to_)
|
||||
|
||||
from_ = fiber_link(other_city, city_name)
|
||||
in_ = eqpt_in_city_to_city(city_name, other_city, "ingress")
|
||||
to_ = f'roadm {city_name}'
|
||||
subdata += connect_eqpt(from_, in_, to_)
|
||||
return subdata
|
||||
|
||||
|
||||
def connect_eqpt(from_, in_, to_):
|
||||
connections = []
|
||||
if in_ !='':
|
||||
connections = [{'from_node': from_, 'to_node': in_},
|
||||
{'from_node': in_, 'to_node': to_}]
|
||||
else:
|
||||
connections = [{'from_node': from_, 'to_node': to_}]
|
||||
return connections
|
||||
|
||||
|
||||
def eqpt_in_city_to_city(in_city, to_city, direction='egress'):
|
||||
rev_direction = 'ingress' if direction == 'egress' else 'egress'
|
||||
amp_direction = f'{direction}_amp_type'
|
||||
amp_rev_direction = f'{rev_direction}_amp_type'
|
||||
return_eqpt = ''
|
||||
if in_city in eqpts_by_city:
|
||||
for e in eqpts_by_city[in_city]:
|
||||
if nodes_by_city[in_city].node_type.lower() == 'roadm':
|
||||
if e.to_city == to_city and getattr(e, amp_direction) != '':
|
||||
return_eqpt = f'{direction} edfa in {e.from_city} to {e.to_city}'
|
||||
elif nodes_by_city[in_city].node_type.lower() == 'ila':
|
||||
if e.to_city != to_city:
|
||||
direction = rev_direction
|
||||
amp_direction = amp_rev_direction
|
||||
if getattr(e, amp_direction) != '':
|
||||
return_eqpt = f'{direction} edfa in {e.from_city} to {e.to_city}'
|
||||
if nodes_by_city[in_city].node_type.lower() == 'fused':
|
||||
return_eqpt = f'{direction} fused spans in {in_city}'
|
||||
return return_eqpt
|
||||
|
||||
|
||||
def fiber_dest_from_source(city_name):
|
||||
destinations = []
|
||||
links_from_city = links_by_city[city_name]
|
||||
for l in links_from_city:
|
||||
if l.from_city == city_name:
|
||||
destinations.append(l.to_city)
|
||||
else:
|
||||
destinations.append(l.from_city)
|
||||
return destinations
|
||||
|
||||
|
||||
def fiber_link(from_city, to_city):
|
||||
source_dest = (from_city, to_city)
|
||||
link = links_by_city[from_city]
|
||||
l = next(l for l in link if l.from_city in source_dest and l.to_city in source_dest)
|
||||
if l.from_city == from_city:
|
||||
fiber = f'fiber ({l.from_city} → {l.to_city})-{l.east_cable}'
|
||||
else:
|
||||
fiber = f'fiber ({l.to_city} → {l.from_city})-{l.west_cable}'
|
||||
return fiber
|
||||
|
||||
|
||||
def midpoint(city_a, city_b):
|
||||
lats = city_a.latitude, city_b.latitude
|
||||
longs = city_a.longitude, city_b.longitude
|
||||
try:
|
||||
result = {
|
||||
'latitude': sum(lats) / 2,
|
||||
'longitude': sum(longs) / 2
|
||||
}
|
||||
except :
|
||||
result = {
|
||||
'latitude': 0,
|
||||
'longitude': 0
|
||||
}
|
||||
return result
|
||||
|
||||
#output_json_file_name = 'coronet_conus_example.json'
|
||||
#TODO get column size automatically from tupple size
|
||||
NODES_COLUMN = 7
|
||||
LINKS_COLUMN = 16
|
||||
EQPTS_COLUMN = 12
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('workbook', nargs='?', type=Path , default='meshTopologyExampleV2.xls')
|
||||
parser.add_argument('-f', '--filter-region', action='append', default=[])
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
convert_file(args.workbook, args.filter_region)
|
||||
@@ -18,67 +18,145 @@ Network elements MUST implement two attributes .uid and .name representing a
|
||||
unique identifier and a printable name.
|
||||
'''
|
||||
|
||||
|
||||
import numpy as np
|
||||
from numpy import abs, arange, arcsinh, array, exp
|
||||
from numpy import interp, log10, mean, pi, polyfit, polyval, sum
|
||||
from scipy.constants import c, h
|
||||
from collections import namedtuple
|
||||
|
||||
from gnpy.core.node import Node
|
||||
from gnpy.core.units import UNITS
|
||||
from gnpy.core.utils import lin2db, db2lin, itufs
|
||||
|
||||
|
||||
class Transceiver(Node):
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.osnr_ase_01nm = None
|
||||
self.osnr_ase = None
|
||||
self.osnr_nli = None
|
||||
self.snr = None
|
||||
self.passive = False
|
||||
|
||||
def _calc_snr(self, spectral_info):
|
||||
ase = [c.power.ase for c in spectral_info.carriers]
|
||||
nli = [c.power.nli for c in spectral_info.carriers]
|
||||
if min(ase)>1e-20:
|
||||
if min(ase) > 1e-20:
|
||||
self.osnr_ase = [lin2db(c.power.signal/c.power.ase)
|
||||
for c in spectral_info.carriers]
|
||||
ratio_01nm = [lin2db(12.5e9/c.baud_rate) for c in spectral_info.carriers]
|
||||
for c in spectral_info.carriers]
|
||||
ratio_01nm = [lin2db(12.5e9/c.baud_rate)
|
||||
for c in spectral_info.carriers]
|
||||
self.osnr_ase_01nm = [ase - ratio for ase, ratio
|
||||
in zip(self.osnr_ase, ratio_01nm)]
|
||||
if min(nli)>1e-20:
|
||||
in zip(self.osnr_ase, ratio_01nm)]
|
||||
if min(nli) > 1e-20:
|
||||
self.osnr_nli = [lin2db(c.power.signal/c.power.nli)
|
||||
for c in spectral_info.carriers]
|
||||
for c in spectral_info.carriers]
|
||||
self.snr = [lin2db(c.power.signal/(c.power.nli+c.power.ase))
|
||||
for c in spectral_info.carriers]
|
||||
for c in spectral_info.carriers]
|
||||
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
return {'uid' : self.uid,
|
||||
'type' : type(self).__name__,
|
||||
'metadata' : {
|
||||
'location': self.metadata['location']._asdict()
|
||||
}
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return (f'{type(self).__name__}('
|
||||
'uid={self.uid!r}, '
|
||||
'config={self.config!r}, '
|
||||
'osnr_ase_01nm={osnr_ase_01nm!r}, '
|
||||
'osnr_ase={osnr_ase!r}, '
|
||||
'osnr_ase_nli={osnr_ase_nli!r}, '
|
||||
'snr={snr!r})')
|
||||
f'uid={self.uid!r}, '
|
||||
f'osnr_ase_01nm={self.osnr_ase_01nm!r}, '
|
||||
f'osnr_ase={self.osnr_ase!r}, '
|
||||
f'osnr_nli={self.osnr_nli!r}, '
|
||||
f'snr={self.snr!r})')
|
||||
|
||||
def __str__(self):
|
||||
if self.snr is None or self.osnr_ase is None:
|
||||
return f'{type(self).__name__} {self.uid}'
|
||||
snr = round(np.mean(self.snr),2)
|
||||
osnr_ase = round(np.mean(self.osnr_ase),2)
|
||||
osnr_ase_01nm = round(np.mean(self.osnr_ase_01nm), 2)
|
||||
|
||||
snr = round(mean(self.snr),2)
|
||||
osnr_ase = round(mean(self.osnr_ase),2)
|
||||
osnr_ase_01nm = round(mean(self.osnr_ase_01nm), 2)
|
||||
|
||||
return '\n'.join([f'{type(self).__name__} {self.uid}',
|
||||
f' OSNR ASE (1nm): {np.mean(self.osnr_ase_01nm):.2f}',
|
||||
f' OSNR ASE (signal bw): {np.mean(self.osnr_ase):.2f}',
|
||||
f' SNR total (signal bw): {np.mean(snr):.2f}'])
|
||||
|
||||
f' OSNR ASE (1nm): {osnr_ase_01nm:.2f}',
|
||||
f' OSNR ASE (signal bw): {osnr_ase:.2f}',
|
||||
f' SNR total (signal bw): {snr:.2f}'])
|
||||
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
self._calc_snr(spectral_info)
|
||||
return spectral_info
|
||||
|
||||
RoadmParams = namedtuple('RoadmParams', 'loss')
|
||||
|
||||
class Roadm(Node):
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
self.loss = 20 #dB
|
||||
def __init__(self, *args, params=None, **kwargs):
|
||||
if params is None:
|
||||
# default loss value if not mentioned in loaded network json
|
||||
params = {'loss':None}
|
||||
super().__init__(*args, params=RoadmParams(**params), **kwargs)
|
||||
self.loss = self.params.loss
|
||||
self.pch_out = None
|
||||
self.passive = True
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
return {'uid' : self.uid,
|
||||
'type' : type(self).__name__,
|
||||
'params' : {'loss' : self.loss},
|
||||
'metadata' : {
|
||||
'location': self.metadata['location']._asdict()
|
||||
}
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return f'{type(self).__name__}(uid={self.uid!r}, loss={self.loss!r})'
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join([f'{type(self).__name__} {self.uid}',
|
||||
f' loss (dB): {self.loss:.2f}',
|
||||
f' pch out (dBm): {self.pch_out!r}'])
|
||||
|
||||
def propagate(self, *carriers):
|
||||
attenuation = db2lin(self.loss)
|
||||
|
||||
for carrier in carriers:
|
||||
pwr = carrier.power
|
||||
pwr = pwr._replace(signal=pwr.signal/attenuation,
|
||||
nonlinear_interference=pwr.nli/attenuation,
|
||||
amplified_spontaneous_emission=pwr.ase/attenuation)
|
||||
yield carrier._replace(power=pwr)
|
||||
|
||||
def update_pref(self, pref):
|
||||
self.pch_out = round(pref.pi - self.loss, 2)
|
||||
return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
carriers = tuple(self.propagate(*spectral_info.carriers))
|
||||
pref = self.update_pref(spectral_info.pref)
|
||||
return spectral_info.update(carriers=carriers, pref=pref)
|
||||
|
||||
FusedParams = namedtuple('FusedParams', 'loss')
|
||||
|
||||
class Fused(Node):
|
||||
def __init__(self, *args, params=None, **kwargs):
|
||||
if params is None:
|
||||
# default loss value if not mentioned in loaded network json
|
||||
params = {'loss':1}
|
||||
super().__init__(*args, params=FusedParams(**params), **kwargs)
|
||||
self.loss = self.params.loss
|
||||
self.passive = True
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
return {'uid' : self.uid,
|
||||
'type' : type(self).__name__,
|
||||
'metadata' : {
|
||||
'location': self.metadata['location']._asdict()
|
||||
}
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return f'{type(self).__name__}(uid={self.uid!r}, loss={self.loss!r})'
|
||||
@@ -97,45 +175,103 @@ class Roadm(Node):
|
||||
amplified_spontaneous_emission=pwr.ase/attenuation)
|
||||
yield carrier._replace(power=pwr)
|
||||
|
||||
def update_pref(self, pref):
|
||||
return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
carriers = tuple(self.propagate(*spectral_info.carriers))
|
||||
return spectral_info.update(carriers=carriers)
|
||||
pref = self.update_pref(spectral_info.pref)
|
||||
return spectral_info.update(carriers=carriers, pref=pref)
|
||||
|
||||
FiberParams = namedtuple('FiberParams', 'type_variety length loss_coef length_units \
|
||||
att_in con_in con_out dispersion gamma')
|
||||
|
||||
class Fiber(Node):
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
self.length = self.params.length * \
|
||||
UNITS[self.params.length_units] #length in m
|
||||
self.loss_coef = self.params.loss_coef*1e-3 #lineic loss dB/m
|
||||
self.lin_loss_coef = self.params.loss_coef / (20*np.log10(np.exp(1)))
|
||||
self.dispersion = self.params.dispersion #s/m/m
|
||||
self.gamma = self.params.gamma #1/W/m
|
||||
self.loss = self.loss_coef * self.length #dB loss: useful for polymorphism (roadm, fiber, att)
|
||||
#TODO discuss factor 2 in the linear lineic attenuation
|
||||
def __init__(self, *args, params=None, **kwargs):
|
||||
if params is None:
|
||||
params = {}
|
||||
if 'con_in' not in params:
|
||||
# if not defined in the network json connector loss in/out
|
||||
# the None value will be updated in network.py[build_network]
|
||||
# with default values from eqpt_config.json[Spans]
|
||||
params['con_in'] = None
|
||||
params['con_out'] = None
|
||||
if 'att_in' not in params:
|
||||
#fixed attenuator for padding
|
||||
params['att_in'] = 0
|
||||
|
||||
super().__init__(*args, params=FiberParams(**params), **kwargs)
|
||||
self.type_variety = self.params.type_variety
|
||||
self.length = self.params.length * UNITS[self.params.length_units] # in m
|
||||
self.loss_coef = self.params.loss_coef * 1e-3 # lineic loss dB/m
|
||||
self.lin_loss_coef = self.params.loss_coef / (20 * log10(exp(1)))
|
||||
self.att_in = self.params.att_in
|
||||
self.con_in = self.params.con_in
|
||||
self.con_out = self.params.con_out
|
||||
self.dispersion = self.params.dispersion # s/m/m
|
||||
self.gamma = self.params.gamma # 1/W/m
|
||||
self.pch_out = None
|
||||
# TODO|jla: discuss factor 2 in the linear lineic attenuation
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
return {'uid' : self.uid,
|
||||
'type' : type(self).__name__,
|
||||
'type_variety' : self.type_variety,
|
||||
'params' : {
|
||||
#have to specify each because namedtupple cannot be updated :(
|
||||
'type_variety' : self.type_variety,
|
||||
'length' : self.length/UNITS[self.params.length_units],
|
||||
'loss_coef' : self.loss_coef*1e3,
|
||||
'length_units' : self.params.length_units,
|
||||
'att_in' : self.att_in,
|
||||
'con_in' : self.con_in,
|
||||
'con_out' : self.con_out
|
||||
},
|
||||
'metadata' : {
|
||||
'location': self.metadata['location']._asdict()
|
||||
}
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return f'{type(self).__name__}(uid={self.uid!r}, length={self.length!r}, loss={self.loss!r})'
|
||||
return f'{type(self).__name__}(uid={self.uid!r}, length={round(self.length*1e-3,1)!r}km, loss={round(self.loss,1)!r}dB)'
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join([f'{type(self).__name__} {self.uid}',
|
||||
f' length (m): {self.length:.2f}',
|
||||
f' loss (dB): {self.loss:.2f}'])
|
||||
return '\n'.join([f'{type(self).__name__} {self.uid}',
|
||||
f' type_variety: {self.type_variety}',
|
||||
f' length (km): {round(self.length*1e-3):.2f}',
|
||||
f' pad att_in (dB): {self.att_in:.2f}',
|
||||
f' total loss (dB): {self.loss:.2f}',
|
||||
f' (includes conn loss (dB) in: {self.con_in:.2f} out: {self.con_out:.2f})',
|
||||
f' (conn loss out includes EOL margin defined in eqpt_config.json)'])
|
||||
|
||||
@property
|
||||
def fiber_loss(self):
|
||||
# dB fiber loss, not including padding attenuator
|
||||
return self.loss_coef * self.length + self.con_in + self.con_out
|
||||
|
||||
@property
|
||||
def loss(self):
|
||||
#total loss incluiding padding att_in: useful for polymorphism with roadm loss
|
||||
return self.loss_coef * self.length + self.con_in + self.con_out + self.att_in
|
||||
|
||||
@property
|
||||
def passive(self):
|
||||
return True
|
||||
|
||||
@property
|
||||
def lin_attenuation(self):
|
||||
attenuation = self.length * self.loss_coef
|
||||
return db2lin(attenuation)
|
||||
return db2lin(self.length * self.loss_coef)
|
||||
|
||||
@property
|
||||
def effective_length(self):
|
||||
alpha_dict = self.dbkm_2_lin()
|
||||
alpha = alpha_dict['alpha_acoef']
|
||||
leff = (1 - np.exp(-2 * alpha * self.length)) / (2*alpha)
|
||||
_, alpha = self.dbkm_2_lin()
|
||||
leff = (1 - exp(-2 * alpha * self.length)) / (2 * alpha)
|
||||
return leff
|
||||
|
||||
@property
|
||||
def asymptotic_length(self):
|
||||
alpha_dict = self.dbkm_2_lin()
|
||||
alpha = alpha_dict['alpha_acoef']
|
||||
_, alpha = self.dbkm_2_lin()
|
||||
aleff = 1 / (2 * alpha)
|
||||
return aleff
|
||||
|
||||
@@ -146,37 +282,33 @@ class Fiber(Node):
|
||||
value ref_wavelength is not entered 1550e-9m will be assumed.
|
||||
ref_wavelength can be a numpy array.
|
||||
"""
|
||||
#TODO: discuss beta2 as method or attribute
|
||||
# TODO|jla: discuss beta2 as method or attribute
|
||||
wl = 1550e-9 if ref_wavelength is None else ref_wavelength
|
||||
D = np.abs(self.dispersion)
|
||||
b2 = (wl**2) * D / (2 * np.pi * c) # 10^21 scales [ps^2/km]
|
||||
D = abs(self.dispersion)
|
||||
b2 = (wl ** 2) * D / (2 * pi * c) # 10^21 scales [ps^2/km]
|
||||
return b2 # s/Hz/m
|
||||
|
||||
def dbkm_2_lin(self):
|
||||
""" calculates the linear loss coefficient
|
||||
"""
|
||||
# alpha_pcoef is linear loss coefficient in dB/km^-1
|
||||
# alpha_acoef is linear loss field amplitude coefficient in m^-1
|
||||
alpha_pcoef = self.loss_coef
|
||||
alpha_acoef = alpha_pcoef / (2 * 10*np.log10(np.exp(1)))
|
||||
s = 'alpha_pcoef is linear loss coefficient in [dB/km^-1] units'
|
||||
s = ''.join([s, "alpha_acoef is linear loss field amplitude \
|
||||
coefficient in [m^-1] units"])
|
||||
d = {'alpha_pcoef': alpha_pcoef,
|
||||
'alpha_acoef': alpha_acoef,
|
||||
'description:': s}
|
||||
return d
|
||||
alpha_acoef = alpha_pcoef / (2 * 10 * log10(exp(1)))
|
||||
return alpha_pcoef, alpha_acoef
|
||||
|
||||
def _psi(self, carrier, interfering_carrier):
|
||||
""" Calculates eq. 123 from arXiv:1209.0394.
|
||||
"""
|
||||
if carrier.num_chan == interfering_carrier.num_chan: # SCI
|
||||
psi = np.arcsinh(0.5 * np.pi**2 * self.asymptotic_length
|
||||
* abs(self.beta2()) * carrier.baud_rate**2)
|
||||
else: # XCI
|
||||
if carrier.num_chan == interfering_carrier.num_chan: # SCI
|
||||
psi = arcsinh(0.5 * pi**2 * self.asymptotic_length
|
||||
* abs(self.beta2()) * carrier.baud_rate**2)
|
||||
else: # XCI
|
||||
delta_f = carrier.freq - interfering_carrier.freq
|
||||
psi = np.arcsinh(np.pi**2 * self.asymptotic_length * abs(self.beta2()) *
|
||||
carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate))
|
||||
psi -= np.arcsinh(np.pi**2 * self.asymptotic_length * abs(self.beta2()) *
|
||||
carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate))
|
||||
psi = arcsinh(pi**2 * self.asymptotic_length * abs(self.beta2())
|
||||
* carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate))
|
||||
psi -= arcsinh(pi**2 * self.asymptotic_length * abs(self.beta2())
|
||||
* carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate))
|
||||
|
||||
return psi
|
||||
|
||||
@@ -191,47 +323,129 @@ class Fiber(Node):
|
||||
g_nli = 0
|
||||
for interfering_carrier in carriers:
|
||||
psi = self._psi(carrier, interfering_carrier)
|
||||
g_nli += (interfering_carrier.power.signal/interfering_carrier.baud_rate)**2 *\
|
||||
(carrier.power.signal/carrier.baud_rate) * psi
|
||||
g_nli += (interfering_carrier.power.signal/interfering_carrier.baud_rate)**2 \
|
||||
* (carrier.power.signal/carrier.baud_rate) * psi
|
||||
|
||||
g_nli *= (16 / 27) * (self.gamma * self.effective_length)**2 /\
|
||||
(2 * np.pi * abs(self.beta2()) * self.asymptotic_length)
|
||||
g_nli *= (16 / 27) * (self.gamma * self.effective_length)**2 \
|
||||
/ (2 * pi * abs(self.beta2()) * self.asymptotic_length)
|
||||
|
||||
carrier_nli = carrier.baud_rate*g_nli
|
||||
carrier_nli = carrier.baud_rate * g_nli
|
||||
return carrier_nli
|
||||
|
||||
def propagate(self, *carriers):
|
||||
|
||||
i=0
|
||||
# apply connector_att_in on all carriers before computing gn analytics premiere partie pas bonne
|
||||
attenuation = db2lin(self.con_in + self.att_in)
|
||||
|
||||
chan = []
|
||||
for carrier in carriers:
|
||||
pwr = carrier.power
|
||||
pwr = pwr._replace(signal=pwr.signal/attenuation,
|
||||
nonlinear_interference=pwr.nli/attenuation,
|
||||
amplified_spontaneous_emission=pwr.ase/attenuation)
|
||||
carrier = carrier._replace(power=pwr)
|
||||
chan.append(carrier)
|
||||
|
||||
carriers = tuple(f for f in chan)
|
||||
|
||||
# propagate in the fiber and apply attenuation out
|
||||
attenuation = db2lin(self.con_out)
|
||||
for carrier in carriers:
|
||||
pwr = carrier.power
|
||||
carrier_nli = self._gn_analytic(carrier, *carriers)
|
||||
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
|
||||
pwr = pwr._replace(signal=pwr.signal/self.lin_attenuation/attenuation,
|
||||
nonlinear_interference=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation,
|
||||
amplified_spontaneous_emission=pwr.ase/self.lin_attenuation/attenuation)
|
||||
yield carrier._replace(power=pwr)
|
||||
|
||||
def update_pref(self, pref):
|
||||
self.pch_out = round(pref.pi - self.loss, 2)
|
||||
return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
carriers = tuple(self.propagate(*spectral_info.carriers))
|
||||
return spectral_info.update(carriers=carriers)
|
||||
pref = self.update_pref(spectral_info.pref)
|
||||
return spectral_info.update(carriers=carriers, pref=pref)
|
||||
|
||||
class EdfaParams:
|
||||
def __init__(self, **params):
|
||||
self.update_params(params)
|
||||
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
|
||||
|
||||
def update_params(self, kwargs):
|
||||
for k,v in kwargs.items() :
|
||||
setattr(self, k, update_params(**v)
|
||||
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
|
||||
def __repr__(self):
|
||||
return (f'{type(self).__name__}('
|
||||
f'gain_target={self.gain_target!r}, '
|
||||
f'tilt_target={self.tilt_target!r})')
|
||||
|
||||
class Edfa(Node):
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
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, gprofile, pin and pout attributs are set by interpol_params"""
|
||||
self.nf = None #dB edfa nf at operational.gain_target: N numpy array
|
||||
def __init__(self, *args, params={}, operational={}, **kwargs):
|
||||
#TBC is this useful? put in comment for now:
|
||||
#if params is None:
|
||||
# params = {}
|
||||
#if operational is None:
|
||||
# operational = {}
|
||||
super().__init__(
|
||||
*args,
|
||||
params=EdfaParams(**params),
|
||||
operational=EdfaOperational(**operational),
|
||||
**kwargs
|
||||
)
|
||||
self.interpol_dgt = None # interpolated dynamic gain tilt
|
||||
self.interpol_gain_ripple = None # gain ripple
|
||||
self.interpol_nf_ripple = None # nf_ripple
|
||||
self.channel_freq = None # SI channel frequencies
|
||||
# nf, gprofile, pin and pout attributes are set by interpol_params
|
||||
self.nf = None # dB edfa nf at operational.gain_target
|
||||
self.gprofile = None
|
||||
self.pin_db = None
|
||||
self.pout_db = None
|
||||
self.dp_db = None #delta P with Pref (power swwep) in power mode
|
||||
self.target_pch_db = None
|
||||
self.effective_pch_db = None
|
||||
self.passive = False
|
||||
self.effective_gain = self.operational.gain_target
|
||||
self.att_in = None
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
return {'uid' : self.uid,
|
||||
'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
|
||||
},
|
||||
'metadata' : {
|
||||
'location': self.metadata['location']._asdict()
|
||||
}
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return (f'{type(self).__name__}(uid={self.uid!r}, '
|
||||
f'type_variety={self.params.type_variety!r}'
|
||||
f'interpol_dgt={self.interpol_dgt!r}, '
|
||||
f'interpol_gain_ripple={self.interpol_gain_ripple!r}, '
|
||||
f'interpol_nf_ripple={self.interpol_nf_ripple!r}, '
|
||||
@@ -244,55 +458,73 @@ class Edfa(Node):
|
||||
def __str__(self):
|
||||
if self.pin_db is None or self.pout_db is None:
|
||||
return f'{type(self).__name__} {self.uid}'
|
||||
|
||||
nf = mean(self.nf)
|
||||
return '\n'.join([f'{type(self).__name__} {self.uid}',
|
||||
f' gain (dB): {self.operational.gain_target:.2f}',
|
||||
f' noise figure (dB): {np.mean(self.nf):.2f}',
|
||||
f' Power In (dBm): {self.pin_db:.2f}',
|
||||
f' Power Out (dBm): {self.pout_db:.2f}'])
|
||||
f' type_variety: {self.params.type_variety}',
|
||||
f' effective gain(dB): {self.effective_gain:.2f}',
|
||||
f' (before att_in and before output VOA)',
|
||||
f' noise figure (dB): {nf:.2f}',
|
||||
f' (including att_in)',
|
||||
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' target pch (dBm): {self.target_pch_db!r}',
|
||||
f' effective pch (dBm): {self.effective_pch_db!r}',
|
||||
f' output VOA (dB): {self.operational.out_voa:.2f}'])
|
||||
|
||||
def interpol_params(self, frequencies, pin, baud_rates):
|
||||
def interpol_params(self, frequencies, pin, baud_rates, pref):
|
||||
"""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
|
||||
# TODO|jla: 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)
|
||||
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.pin_db = lin2db(np.sum(pin*1e3))
|
||||
self.pin_db = lin2db(sum(pin*1e3))
|
||||
"""check power saturation and correct target_gain accordingly:"""
|
||||
gain_target = min(self.operational.gain_target, self.params.p_max-self.pin_db)
|
||||
self.operational.gain_target = gain_target
|
||||
|
||||
if self.dp_db is not None:
|
||||
self.target_pch_db = round(self.dp_db + pref.p0, 2)
|
||||
self.effective_gain = self.target_pch_db - pref.pi
|
||||
|
||||
self.effective_gain = min(self.effective_gain, self.params.p_max - self.pin_db)
|
||||
self.effective_pch_db = round(pref.pi + self.effective_gain, 2)
|
||||
|
||||
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(np.sum(pout*1e3))
|
||||
# ! ase & nli are only calculated in signal bandwidth
|
||||
# => pout_db is not the absolute full ouput power (negligible if sufficient channels)
|
||||
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 _calc_nf(self):
|
||||
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 : 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
|
||||
# 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
|
||||
else:
|
||||
nf_avg = np.polyval(self.params.nf_fit_coeff, dg)
|
||||
|
||||
nf_array = self.interpol_nf_ripple + nf_avg + pad #input VOA = 1 for 1 NF degradation
|
||||
return nf_array
|
||||
nf_avg = polyval(self.params.nf_fit_coeff, -dg)
|
||||
if avg:
|
||||
return nf_avg + pad
|
||||
else:
|
||||
return self.interpol_nf_ripple + nf_avg + pad # input VOA = 1 for 1 NF degradation
|
||||
|
||||
def noise_profile(self, df):
|
||||
""" noise_profile(bw) computes amplifier ase (W) in signal bw (Hz)
|
||||
@@ -330,10 +562,10 @@ class Edfa(Node):
|
||||
quoting power spectral density in the same BW for both signal and ASE,
|
||||
e.g. 12.5GHz."""
|
||||
|
||||
ase = h * df * self.channel_freq * db2lin(self.nf) #W
|
||||
return ase #in W, @amplifier input
|
||||
ase = h * df * self.channel_freq * db2lin(self.nf) # W
|
||||
return ase # in W at amplifier input
|
||||
|
||||
def _gain_profile(self, pin):
|
||||
def _gain_profile(self, pin, err_tolerance=1.0e-11, simple_opt=True):
|
||||
"""
|
||||
Pin : input power / channel in W
|
||||
|
||||
@@ -371,109 +603,108 @@ class Edfa(Node):
|
||||
tilt technique", Journal of Lightwave Technology, Vol. 18, Iss. 3,
|
||||
Pp. 343-347, 2000.
|
||||
"""
|
||||
err_tolerance = 1.0e-11
|
||||
simple_opt = True
|
||||
|
||||
# TODO check what param should be used (currently length(dgt))
|
||||
nchan = np.arange(len(self.interpol_dgt))
|
||||
# TODO|jla: check what param should be used (currently length(dgt))
|
||||
nb_channel = arange(len(self.interpol_dgt))
|
||||
|
||||
# TODO find a way to use these or lose them. Primarily we should have
|
||||
# TODO|jla: 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(pin*1e3)) # ! Pin expressed in W
|
||||
tot_in_power_db = self.pin_db # Pin in W
|
||||
|
||||
# Linear fit to get the
|
||||
p = np.polyfit(nchan, self.interpol_dgt, 1)
|
||||
# linear fit to get the
|
||||
p = polyfit(nb_channel, 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.operational.tilt_target / (len(nchan) - 1)
|
||||
# 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)
|
||||
|
||||
# 1st estimate of DGT scaling
|
||||
if abs(dgt_slope) > 0.001: # add check for div 0 due to flat dgt
|
||||
# first estimate of DGT scaling
|
||||
if abs(dgt_slope) > 0.001: # check for zero value due to flat dgt
|
||||
dgts1 = targ_slope / dgt_slope
|
||||
else:
|
||||
dgts1 = 0
|
||||
# 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.
|
||||
# TODO replace with loop
|
||||
|
||||
if simple_opt:
|
||||
# when simple_opt is true, make 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.
|
||||
# TODO|jla: replace with loop
|
||||
|
||||
# 1st estimate of Er gain & voa loss
|
||||
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
|
||||
if not simple_opt:
|
||||
return
|
||||
|
||||
# 2nd estimate of Amp ch gain using the channel input profile
|
||||
g2nd = g1st - voa
|
||||
# first estimate of Er gain & VOA loss
|
||||
g1st = array(self.interpol_gain_ripple) + self.params.gain_flatmax \
|
||||
+ array(self.interpol_dgt) * dgts1
|
||||
voa = lin2db(mean(db2lin(g1st))) - self.effective_gain
|
||||
|
||||
pout_db = lin2db(np.sum(pin*1e3*db2lin(g2nd)))
|
||||
dgts2 = self.operational.gain_target - (pout_db - tot_in_power_db)
|
||||
# second estimate of amp ch gain using the channel input profile
|
||||
g2nd = g1st - voa
|
||||
|
||||
# Center estimate of amp ch gain
|
||||
xcent = dgts2
|
||||
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
|
||||
pout_db = lin2db(sum(pin*1e3*db2lin(g2nd)))
|
||||
dgts2 = self.effective_gain - (pout_db - tot_in_power_db)
|
||||
|
||||
# Lower estimate of Amp ch gain
|
||||
deltax = np.max(g1st) - np.min(g1st)
|
||||
# ! if no ripple deltax = 0 => xlow = xcent: div 0
|
||||
# add check for flat gain response :
|
||||
if abs(deltax) > 0.05: #enough ripple to consider calculation and avoid div 0
|
||||
xlow = dgts2 - deltax
|
||||
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
|
||||
# center estimate of amp ch gain
|
||||
xcent = dgts2
|
||||
gcent = g1st - voa + array(self.interpol_dgt) * xcent
|
||||
pout_db = lin2db(sum(pin*1e3*db2lin(gcent)))
|
||||
gavg_cent = pout_db - tot_in_power_db
|
||||
|
||||
# Upper gain estimate
|
||||
xhigh = dgts2 + deltax
|
||||
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
|
||||
# Lower estimate of amp ch gain
|
||||
deltax = max(g1st) - min(g1st)
|
||||
# if no ripple deltax = 0 and xlow = xcent: div 0
|
||||
# TODO|jla: add check for flat gain response
|
||||
if abs(deltax) <= 0.05: # not enough ripple to consider calculation
|
||||
return g1st - voa
|
||||
|
||||
# compute slope
|
||||
slope1 = (gavg_low - gavg_cent) / (xlow - xcent)
|
||||
slope2 = (gavg_cent - gavg_high) / (xcent - xhigh)
|
||||
xlow = dgts2 - deltax
|
||||
glow = g1st - voa + array(self.interpol_dgt) * xlow
|
||||
pout_db = lin2db(sum(pin * 1e3 * db2lin(glow)))
|
||||
gavg_low = pout_db - tot_in_power_db
|
||||
|
||||
if np.abs(self.operational.gain_target - gavg_cent) <= err_tolerance:
|
||||
dgts3 = xcent
|
||||
elif self.operational.gain_target < gavg_cent:
|
||||
dgts3 = xcent - (gavg_cent - self.operational.gain_target) / slope1
|
||||
else:
|
||||
dgts3 = xcent + (-gavg_cent + self.operational.gain_target) / slope2
|
||||
# upper gain estimate
|
||||
xhigh = dgts2 + deltax
|
||||
ghigh = g1st - voa + array(self.interpol_dgt) * xhigh
|
||||
pout_db = lin2db(sum(pin * 1e3 * db2lin(ghigh)))
|
||||
gavg_high = pout_db - tot_in_power_db
|
||||
|
||||
gprofile = g1st - voa + np.array(self.interpol_dgt) * dgts3
|
||||
else: #not enough ripple
|
||||
gprofile = g1st - voa
|
||||
else: #simple_opt
|
||||
gprofile = None
|
||||
# compute slope
|
||||
slope1 = (gavg_low - gavg_cent) / (xlow - xcent)
|
||||
slope2 = (gavg_cent - gavg_high) / (xcent - xhigh)
|
||||
|
||||
return gprofile
|
||||
if abs(self.effective_gain - gavg_cent) <= err_tolerance:
|
||||
dgts3 = xcent
|
||||
elif self.effective_gain < gavg_cent:
|
||||
dgts3 = xcent - (gavg_cent - self.effective_gain) / slope1
|
||||
else:
|
||||
dgts3 = xcent + (-gavg_cent + self.effective_gain) / slope2
|
||||
|
||||
def propagate(self, *carriers):
|
||||
return g1st - voa + array(self.interpol_dgt) * dgts3
|
||||
|
||||
def propagate(self, pref, *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])
|
||||
brate = np.array([c.baud_rate for c in carriers])
|
||||
#interpolate the amplifier vectors with the carriers freq, calculate nf & gain profile
|
||||
self.interpol_params(freq, pin, brate)
|
||||
gain = db2lin(self.gprofile)
|
||||
carrier_ase = self.noise_profile(brate)
|
||||
pin = array([c.power.signal+c.power.nli+c.power.ase for c in carriers]) # pin in W
|
||||
freq = array([c.frequency for c in carriers])
|
||||
brate = array([c.baud_rate for c in carriers])
|
||||
# interpolate the amplifier vectors with the carriers freq, calculate nf & gain profile
|
||||
self.interpol_params(freq, pin, brate, pref)
|
||||
|
||||
for carrier in carriers:
|
||||
gains = db2lin(self.gprofile)
|
||||
carrier_ases = self.noise_profile(brate)
|
||||
att = db2lin(self.operational.out_voa)
|
||||
|
||||
for gain, carrier_ase, carrier in zip(gains, carrier_ases, carriers):
|
||||
pwr = carrier.power
|
||||
bw = carrier.baud_rate
|
||||
pwr = pwr._replace(signal=pwr.signal*gain[i],
|
||||
nonlinear_interference=pwr.nli*gain[i],
|
||||
amplified_spontaneous_emission=(pwr.ase+carrier_ase[i])*gain[i])
|
||||
i += 1
|
||||
pwr = pwr._replace(signal=pwr.signal*gain/att,
|
||||
nonlinear_interference=pwr.nli*gain/att,
|
||||
amplified_spontaneous_emission=(pwr.ase+carrier_ase)*gain/att)
|
||||
yield carrier._replace(power=pwr)
|
||||
|
||||
def update_pref(self, pref):
|
||||
return pref._replace(p_span0=pref.p0,
|
||||
p_spani=pref.pi + self.effective_gain - self.operational.out_voa)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
carriers = tuple(self.propagate(*spectral_info.carriers))
|
||||
return spectral_info.update(carriers=carriers)
|
||||
carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers))
|
||||
pref = self.update_pref(spectral_info.pref)
|
||||
return spectral_info.update(carriers=carriers, pref=pref)
|
||||
|
||||
224
gnpy/core/equipment.py
Normal file
224
gnpy/core/equipment.py
Normal file
@@ -0,0 +1,224 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
gnpy.core.equipment
|
||||
===================
|
||||
|
||||
This module contains functionality for specifying equipment.
|
||||
'''
|
||||
|
||||
from numpy import clip, polyval
|
||||
from sys import exit
|
||||
from operator import itemgetter
|
||||
from math import isclose
|
||||
from pathlib import Path
|
||||
from json import loads
|
||||
from gnpy.core.utils import lin2db, db2lin, load_json
|
||||
from collections import namedtuple
|
||||
from gnpy.core.elements import Edfa
|
||||
|
||||
Model_vg = namedtuple('Model_vg', 'nf1 nf2 delta_p')
|
||||
Model_fg = namedtuple('Model_fg', 'nf0')
|
||||
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_pref')
|
||||
SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \
|
||||
power_dbm power_range_db OSNR bit_rate')
|
||||
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)
|
||||
|
||||
@classmethod
|
||||
def from_advanced_json(cls, filename, **kwargs):
|
||||
with open(filename) as f:
|
||||
json_data = loads(f.read())
|
||||
return cls(**{**kwargs, **json_data, 'type_def':None, 'nf_model':None})
|
||||
|
||||
@classmethod
|
||||
def from_default_json(cls, filename, **kwargs):
|
||||
with open(filename) as f:
|
||||
json_data = loads(f.read())
|
||||
type_variety = kwargs['type_variety']
|
||||
type_def = kwargs.get('type_def', 'variable_gain') #default compatibility with older json eqpt files
|
||||
nf_def = None
|
||||
|
||||
if type_def == 'fixed_gain':
|
||||
try:
|
||||
nf0 = kwargs.pop('nf0')
|
||||
except KeyError: #nf0 is expected for a fixed gain amp
|
||||
print(f'missing nf0 value input for amplifier: {type_variety} in eqpt_config.json')
|
||||
exit()
|
||||
try: #remove all remaining nf inputs
|
||||
del kwargs['nf_min']
|
||||
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 == '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
|
||||
nf_min = kwargs.pop('nf_min')
|
||||
nf_max = kwargs.pop('nf_max')
|
||||
except KeyError:
|
||||
print(f'missing nf_min/max value input for amplifier: {type_variety} in eqpt_config.json')
|
||||
exit()
|
||||
try: #remove all remaining nf inputs
|
||||
del kwargs['nf0']
|
||||
except KeyError: pass #nf0 is not needed for variable gain amp
|
||||
nf1, nf2, delta_p = nf_model(type_variety, gain_min, gain_max, nf_min, nf_max)
|
||||
nf_def = Model_vg(nf1, nf2, delta_p)
|
||||
return cls(**{**kwargs, **json_data, 'nf_model': nf_def})
|
||||
|
||||
|
||||
def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
|
||||
if nf_min < -10:
|
||||
print(f'Invalid nf_min value {nf_min!r} for amplifier {type_variety}')
|
||||
exit()
|
||||
if nf_max < -10:
|
||||
print(f'Invalid nf_max value {nf_max!r} for amplifier {type_variety}')
|
||||
exit()
|
||||
|
||||
# NF estimation model based on nf_min and nf_max
|
||||
# delta_p: max power dB difference between first and second stage coils
|
||||
# dB g1a: first stage gain - internal VOA attenuation
|
||||
# nf1, nf2: first and second stage coils
|
||||
# calculated by solving nf_{min,max} = nf1 + nf2 / g1a{min,max}
|
||||
delta_p = 5
|
||||
g1a_min = gain_min - (gain_max - gain_min) - delta_p
|
||||
g1a_max = gain_max - delta_p
|
||||
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))
|
||||
|
||||
if nf1 < 4:
|
||||
print(f'First coil value too low {nf1} for amplifier {type_variety}')
|
||||
exit()
|
||||
|
||||
# Check 1 dB < delta_p < 6 dB to ensure nf_min and nf_max values make sense.
|
||||
# There shouldn't be high nf differences between the two coils:
|
||||
# nf2 should be nf1 + 0.3 < nf2 < nf1 + 2
|
||||
# If not, recompute and check delta_p
|
||||
if not nf1 + 0.3 < nf2 < nf1 + 2:
|
||||
nf2 = clip(nf2, nf1 + 0.3, nf1 + 2)
|
||||
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:
|
||||
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 \
|
||||
\n calculated 1st coil NF = {nf1:.2f}, 2nd coil NF = {nf2:.2f}')
|
||||
exit()
|
||||
# Check calculated values for nf1 and nf2
|
||||
calc_nf_min = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_max))
|
||||
if not isclose(nf_min, calc_nf_min, abs_tol=0.01):
|
||||
print(f'nf_min does not match calc_nf_min, {nf_min} vs {calc_nf_min} for amp {type_variety}')
|
||||
exit()
|
||||
calc_nf_max = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_min))
|
||||
if not isclose(nf_max, calc_nf_max, abs_tol=0.01):
|
||||
print(f'nf_max does not match calc_nf_max, {nf_max} vs {calc_nf_max} for amp {type_variety}')
|
||||
exit()
|
||||
|
||||
return nf1, nf2, delta_p
|
||||
|
||||
def edfa_nf(gain_target, variety_type, equipment):
|
||||
amp_params = equipment['Edfa'][variety_type]
|
||||
amp = Edfa(
|
||||
uid = f'calc_NF',
|
||||
params = amp_params._asdict(),
|
||||
operational = {
|
||||
'gain_target': gain_target,
|
||||
'tilt_target': 0,
|
||||
})
|
||||
return amp._calc_nf(True)
|
||||
|
||||
def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=False):
|
||||
"""return the trx and SI parameters from eqpt_config for a given type_variety and mode (ie format)"""
|
||||
trx_params = {}
|
||||
default_si_data = equipment['SI']['default']
|
||||
try:
|
||||
trxs = equipment['Transceiver']
|
||||
mode_params = next(mode for trx in trxs \
|
||||
if trx == trx_type_variety \
|
||||
for mode in trxs[trx].mode \
|
||||
if mode['format'] == trx_mode)
|
||||
trx_params = {**mode_params}
|
||||
trx_params['frequency'] = equipment['Transceiver'][trx_type_variety].frequency
|
||||
# TODO: novel automatic feature maybe unwanted if spacing is specified
|
||||
trx_params['spacing'] = automatic_spacing(trx_params['baud_rate'])
|
||||
except StopIteration :
|
||||
if error_message:
|
||||
print(f'could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library')
|
||||
print('Computation stopped.')
|
||||
exit()
|
||||
else:
|
||||
# default transponder charcteristics
|
||||
trx_params['frequency'] = {'min': default_si_data.f_min, 'max': default_si_data.f_max}
|
||||
trx_params['baud_rate'] = default_si_data.baud_rate
|
||||
trx_params['spacing'] = default_si_data.spacing
|
||||
trx_params['OSNR'] = default_si_data.OSNR
|
||||
trx_params['bit_rate'] = default_si_data.bit_rate
|
||||
trx_params['roll_off'] = default_si_data.roll_off
|
||||
trx_params['power'] = db2lin(default_si_data.power_dbm)*1e-3
|
||||
trx_params['nb_channel'] = automatic_nch(trx_params['frequency']['min'],
|
||||
trx_params['frequency']['max'],
|
||||
trx_params['spacing'])
|
||||
print('N channels = ', trx_params['nb_channel'])
|
||||
return trx_params
|
||||
|
||||
def automatic_spacing(baud_rate):
|
||||
"""return the min possible channel spacing for a given baud rate"""
|
||||
spacing_list = [(38e9,50e9), (67e9,75e9), (92e9,100e9)] #list of possible tuples
|
||||
#[(max_baud_rate, spacing_for_this_baud_rate)]
|
||||
acceptable_spacing_list = list(filter(lambda x : x[0]>baud_rate, spacing_list))
|
||||
if len(acceptable_spacing_list) < 1:
|
||||
#can't find an adequate spacing from the list, so default to:
|
||||
return baud_rate*1.2
|
||||
else:
|
||||
#chose the lowest possible spacing
|
||||
return min(acceptable_spacing_list, key=itemgetter(0))[1]
|
||||
|
||||
def automatic_nch(f_min, f_max, spacing):
|
||||
return int((f_max - f_min)//spacing)
|
||||
|
||||
def load_equipment(filename):
|
||||
json_data = load_json(filename)
|
||||
return equipment_from_json(json_data, filename)
|
||||
|
||||
def equipment_from_json(json_data, filename):
|
||||
"""build global dictionnary eqpt_library that stores all eqpt characteristics:
|
||||
edfa type type_variety, fiber type_variety
|
||||
from the eqpt_config.json (filename parameter)
|
||||
also read advanced_config_from_json file parameters for edfa if they are available:
|
||||
typically nf_ripple, dfg gain ripple, dgt and nf polynomial nf_fit_coeff
|
||||
if advanced_config_from_json file parameter is not present: use nf_model:
|
||||
requires nf_min and nf_max values boundaries of the edfa gain range
|
||||
"""
|
||||
equipment = {}
|
||||
for key, entries in json_data.items():
|
||||
for entry in entries:
|
||||
if key not in equipment:
|
||||
equipment[key] = {}
|
||||
subkey = entry.get('type_variety', 'default')
|
||||
typ = globals()[key]
|
||||
if key == 'Edfa':
|
||||
if 'advanced_config_from_json' in entry:
|
||||
config = Path(filename).parent / entry.pop('advanced_config_from_json')
|
||||
typ = lambda **kws: Amp.from_advanced_json(config, **kws)
|
||||
else:
|
||||
config = Path(filename).parent / 'default_edfa_config.json'
|
||||
typ = lambda **kws: Amp.from_default_json(config, **kws)
|
||||
equipment[key][subkey] = typ(**entry)
|
||||
return equipment
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
gnpy.core.execute
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
gnpy.core.info
|
||||
@@ -9,7 +10,10 @@ This module contains classes for modelling SpectralInformation.
|
||||
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
from numpy import array
|
||||
from gnpy.core.utils import lin2db
|
||||
from json import loads
|
||||
from gnpy.core.utils import load_json
|
||||
|
||||
class ConvenienceAccess:
|
||||
|
||||
@@ -37,15 +41,36 @@ 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 SpectralInformation(namedtuple('SpectralInformation', 'carriers'), ConvenienceAccess):
|
||||
_ABBREVS = {'p0' : 'p_span0',
|
||||
'pi' : 'p_spani'}
|
||||
|
||||
def __new__(cls, *carriers):
|
||||
return super().__new__(cls, carriers)
|
||||
class SpectralInformation(namedtuple('SpectralInformation', 'pref carriers'), ConvenienceAccess):
|
||||
|
||||
def __new__(cls, pref=Pref(0, 0), *carriers):
|
||||
return super().__new__(cls, pref, carriers)
|
||||
|
||||
def merge_input_spectral_information(*si):
|
||||
"""mix channel combs of different baud rates and power"""
|
||||
#TODO
|
||||
pass
|
||||
|
||||
def create_input_spectral_information(f_min, roll_off, baud_rate, power, spacing, nb_channel):
|
||||
# pref in dB : convert power lin into power in dB
|
||||
pref = lin2db(power * 1e3)
|
||||
si = SpectralInformation(pref=Pref(pref, pref))
|
||||
si = si.update(carriers=[
|
||||
Channel(f, (f_min+spacing*f),
|
||||
baud_rate, roll_off, Power(power, 0, 0)) for f in range(1,nb_channel+1)
|
||||
])
|
||||
return si
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pref = lin2db(power * 1e3)
|
||||
si = SpectralInformation(
|
||||
Pref(pref, pref),
|
||||
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, 32e9, 0.15, # 195.95 THz, 32 Gbaud
|
||||
@@ -53,7 +78,7 @@ if __name__ == '__main__':
|
||||
)
|
||||
|
||||
si = SpectralInformation()
|
||||
spacing = 0.05 #THz
|
||||
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)))
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
gnpy.core.network
|
||||
@@ -7,26 +8,57 @@ gnpy.core.network
|
||||
This module contains functions for constructing networks of network elements.
|
||||
'''
|
||||
|
||||
|
||||
from gnpy.core.convert import convert_file
|
||||
from networkx import DiGraph
|
||||
|
||||
from numpy import arange
|
||||
from logging import getLogger
|
||||
from os import path
|
||||
from operator import itemgetter
|
||||
from gnpy.core import elements
|
||||
from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm
|
||||
from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm, Fused
|
||||
from gnpy.core.equipment import edfa_nf
|
||||
from gnpy.core.units import UNITS
|
||||
from gnpy.core.utils import load_json, save_json, round2float, db2lin, lin2db
|
||||
from sys import exit
|
||||
from collections import namedtuple
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
MAX_SPAN_LENGTH = 125000
|
||||
TARGET_SPAN_LENGTH = 100000
|
||||
MIN_SPAN_LENGTH = 75000
|
||||
def load_network(filename, equipment):
|
||||
json_filename = ''
|
||||
if filename.suffix.lower() == '.xls':
|
||||
logger.info('Automatically generating topology JSON file')
|
||||
json_filename = convert_file(filename)
|
||||
elif filename.suffix.lower() == '.json':
|
||||
json_filename = filename
|
||||
else:
|
||||
raise ValueError(f'unsuported topology filename extension {filename.suffix.lower()}')
|
||||
json_data = load_json(json_filename)
|
||||
return network_from_json(json_data, equipment)
|
||||
|
||||
def save_network(filename, network):
|
||||
filename_output = path.splitext(filename)[0] + '_auto_design.json'
|
||||
json_data = network_to_json(network)
|
||||
save_json(json_data, filename_output)
|
||||
|
||||
def network_from_json(json_data):
|
||||
def network_from_json(json_data, equipment):
|
||||
# NOTE|dutc: we could use the following, but it would tie our data format
|
||||
# too closely to the graph library
|
||||
# from networkx import node_link_graph
|
||||
g = DiGraph()
|
||||
for el_config in json_data['elements']:
|
||||
g.add_node(getattr(elements, el_config['type'])(el_config))
|
||||
typ = el_config.pop('type')
|
||||
variety = el_config.pop('type_variety', 'default')
|
||||
if typ in equipment and variety in equipment[typ]:
|
||||
extra_params = equipment[typ][variety]
|
||||
el_config.setdefault('params', {}).update(extra_params._asdict())
|
||||
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')
|
||||
exit()
|
||||
cls = getattr(elements, typ)
|
||||
el = cls(**el_config)
|
||||
g.add_node(el)
|
||||
|
||||
nodes = {k.uid: k for k in g.nodes()}
|
||||
|
||||
@@ -36,89 +68,353 @@ def network_from_json(json_data):
|
||||
|
||||
return g
|
||||
|
||||
def calculate_new_length(fiber_length):
|
||||
result = (fiber_length, 1)
|
||||
if fiber_length > MAX_SPAN_LENGTH:
|
||||
n_spans = int(fiber_length // TARGET_SPAN_LENGTH)
|
||||
def network_to_json(network):
|
||||
data = {
|
||||
'elements': [n.to_json for n in network]
|
||||
}
|
||||
connections = {
|
||||
'connections': [{"from_node": n.uid,
|
||||
"to_node": next_n.uid}
|
||||
for n in network
|
||||
for next_n in network.successors(n) if next_n is not None]
|
||||
}
|
||||
data.update(connections)
|
||||
return data
|
||||
|
||||
length1 = fiber_length / (n_spans+1)
|
||||
result1 = (length1, n_spans+1)
|
||||
delta1 = TARGET_SPAN_LENGTH-length1
|
||||
def select_edfa(gain_target, power_target, equipment):
|
||||
"""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_dict = equipment['Edfa']
|
||||
pin = power_target - gain_target
|
||||
|
||||
length2 = fiber_length / n_spans
|
||||
delta2 = length2-TARGET_SPAN_LENGTH
|
||||
result2 = (length2, n_spans)
|
||||
edfa_list = [Edfa_list(
|
||||
variety=edfa_variety,
|
||||
power=min(
|
||||
pin
|
||||
+edfa.gain_flatmax
|
||||
+TARGET_EXTENDED_GAIN,
|
||||
edfa.p_max
|
||||
)
|
||||
-power_target,
|
||||
gain=edfa.gain_flatmax-gain_target,
|
||||
nf=edfa_nf(gain_target, edfa_variety, equipment)) \
|
||||
for edfa_variety, edfa in edfa_dict.items()
|
||||
if edfa.allowed_for_design]
|
||||
|
||||
if length1<MIN_SPAN_LENGTH and length2<MAX_SPAN_LENGTH:
|
||||
result = result2
|
||||
elif length2>MAX_SPAN_LENGTH and length1>MIN_SPAN_LENGTH:
|
||||
result = result1
|
||||
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))
|
||||
acceptable_power_list = \
|
||||
list(filter(lambda x : x.power>=0, acceptable_gain_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
|
||||
#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))
|
||||
# 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
|
||||
|
||||
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
|
||||
pref_roadm_db = equipment['Roadms']['default'].power_mode_pref
|
||||
roadm_loss = pref_ch_db - pref_roadm_db
|
||||
|
||||
for roadm in roadms:
|
||||
if power_mode:
|
||||
roadm.loss = roadm_loss
|
||||
elif roadm.loss == None:
|
||||
roadm.loss = default_roadm_loss
|
||||
|
||||
def target_power(dp_from_gain, 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)
|
||||
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)
|
||||
except KeyError:
|
||||
print(f'invalid delta_power_range_db definition in eqpt_config[Spans]'
|
||||
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')
|
||||
|
||||
return dp
|
||||
|
||||
|
||||
def prev_node_generator(network, node):
|
||||
"""fused spans interest:
|
||||
iterate over all predecessors while they are Fused or Fiber type"""
|
||||
prev_node = next(n for n in network.predecessors(node))
|
||||
# yield and re-iterate
|
||||
if isinstance(prev_node, Fused) or isinstance(node, Fused):
|
||||
yield prev_node
|
||||
yield from prev_node_generator(network, prev_node)
|
||||
else:
|
||||
StopIteration
|
||||
|
||||
def next_node_generator(network, node):
|
||||
"""fused spans interest:
|
||||
iterate over all successors while they are Fused or Fiber type"""
|
||||
next_node = next(n for n in network.successors(node))
|
||||
# yield and re-iterate
|
||||
if isinstance(next_node, Fused) or isinstance(node, Fused):
|
||||
yield next_node
|
||||
yield from next_node_generator(network, next_node)
|
||||
else:
|
||||
StopIteration
|
||||
|
||||
def span_loss(network, node):
|
||||
"""Fused span interest:
|
||||
return the total span loss of all the fibers spliced by a Fused node"""
|
||||
loss = node.loss if node.passive else 0
|
||||
try:
|
||||
prev_node = next(n for n in network.predecessors(node))
|
||||
if isinstance(prev_node, Fused):
|
||||
loss += sum(n.loss for n in prev_node_generator(network, node))
|
||||
except StopIteration:
|
||||
pass
|
||||
try:
|
||||
next_node = next(n for n in network.successors(node))
|
||||
if isinstance(next_node, Fused):
|
||||
loss += sum(n.loss for n in next_node_generator(network, node))
|
||||
except StopIteration:
|
||||
pass
|
||||
return loss
|
||||
|
||||
def find_first_node(network, node):
|
||||
"""Fused node interest:
|
||||
returns the 1st node at the origin of a succession of fused nodes
|
||||
(aka no amp in between)"""
|
||||
this_node = node
|
||||
for this_node in prev_node_generator(network, node):
|
||||
pass
|
||||
return this_node
|
||||
|
||||
def find_last_node(network, node):
|
||||
"""Fused node interest:
|
||||
returns the last node in a succession of fused nodes
|
||||
(aka no amp in between)"""
|
||||
this_node = node
|
||||
for this_node in next_node_generator(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:
|
||||
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
|
||||
else:
|
||||
if delta1 < delta2:
|
||||
result = result1
|
||||
else:
|
||||
result = result2
|
||||
voa = 0 # no output voa optimization in gain mode
|
||||
amp.operational.out_voa = voa
|
||||
|
||||
return result
|
||||
def set_egress_amplifier(network, roadm, equipment, pref_total_db):
|
||||
power_mode = equipment['Spans']['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
|
||||
node = roadm
|
||||
prev_node = roadm
|
||||
next_node = oms
|
||||
# if isinstance(next_node, Fused): #support ROADM wo egress amp for metro applications
|
||||
# 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
|
||||
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
|
||||
|
||||
def split_fiber(network, fiber):
|
||||
new_length, n_spans = calculate_new_length(fiber.length)
|
||||
prev_node = fiber
|
||||
if n_spans > 1:
|
||||
next_nodes = [_ for _ in network.successors(fiber)]
|
||||
for next_node in next_nodes:
|
||||
network.remove_edge(fiber, next_node)
|
||||
if power_mode:
|
||||
node.dp_db = dp
|
||||
node.operational.gain_target = gain_target
|
||||
|
||||
new_params_length = new_length / UNITS[fiber.params.length_units]
|
||||
config = {'uid':fiber.uid, 'type': 'Fiber', 'metadata': fiber.__dict__['metadata'], \
|
||||
'params': fiber.__dict__['params']}
|
||||
fiber.uid = config['uid'] + '_1'
|
||||
fiber.length = new_length
|
||||
fiber.loss = fiber.loss_coef * fiber.length
|
||||
if node.params.type_variety == '':
|
||||
power_target = pref_total_db + dp
|
||||
edfa_variety = select_edfa(gain_target, power_target, equipment)
|
||||
extra_params = equipment['Edfa'][edfa_variety]
|
||||
node.params.update_params(extra_params._asdict())
|
||||
set_amplifier_voa(node, pref_total_db, power_mode)
|
||||
if isinstance(next_node, Roadm) or isinstance(next_node, Transceiver):
|
||||
break
|
||||
prev_dp = dp
|
||||
prev_node = node
|
||||
node = next_node
|
||||
# print(f'{node.uid}')
|
||||
next_node = next(n for n in network.successors(node))
|
||||
|
||||
for i in range(2, n_spans+1):
|
||||
new_config = dict(config)
|
||||
new_config['uid'] = new_config['uid'] + '_' + str(i)
|
||||
new_config['params'].length = new_params_length
|
||||
new_node = Fiber(new_config)
|
||||
network.add_node(new_node)
|
||||
network.add_edge(prev_node, new_node)
|
||||
network = add_egress_amplifier(network, prev_node)
|
||||
prev_node = new_node
|
||||
|
||||
for next_node in next_nodes:
|
||||
network.add_edge(prev_node, next_node)
|
||||
|
||||
network = add_egress_amplifier(network, prev_node)
|
||||
return network
|
||||
|
||||
def add_egress_amplifier(network, node):
|
||||
next_nodes = [n for n in network.successors(node)
|
||||
if not (isinstance(n, Edfa) or isinstance(n, Transceiver))]
|
||||
i = 1
|
||||
for next_node in next_nodes:
|
||||
if not (isinstance(n, Transceiver) or isinstance(n, Fused) or isinstance(n, Edfa))]
|
||||
#no amplification for fused spans or TRX
|
||||
for i, next_node in enumerate(next_nodes):
|
||||
network.remove_edge(node, next_node)
|
||||
amp = Edfa(
|
||||
uid = f'Edfa{i}_{node.uid}',
|
||||
params = {},
|
||||
operational = {
|
||||
'gain_target': 0,
|
||||
'tilt_target': 0,
|
||||
})
|
||||
network.add_node(amp)
|
||||
network.add_edge(node, amp)
|
||||
network.add_edge(amp, next_node)
|
||||
|
||||
uid = 'Edfa' + str(i)+ '_' + str(node.uid)
|
||||
metadata = next_node.metadata
|
||||
operational = {'gain_target': node.loss, 'tilt_target': 0}
|
||||
edfa_config_json = 'edfa_config.json'
|
||||
config = {'uid':uid, 'type': 'Edfa', 'metadata': metadata, \
|
||||
'config_from_json': edfa_config_json, 'operational': operational}
|
||||
new_edfa = Edfa(config)
|
||||
network.add_node(new_edfa)
|
||||
network.add_edge(node,new_edfa)
|
||||
network.add_edge(new_edfa, next_node)
|
||||
i +=1
|
||||
|
||||
return network
|
||||
def calculate_new_length(fiber_length, bounds, target_length):
|
||||
if fiber_length < bounds.stop:
|
||||
return fiber_length, 1
|
||||
|
||||
def build_network(network):
|
||||
fibers = [f for f in network.nodes() if isinstance(f, Fiber)]
|
||||
n_spans = int(fiber_length // target_length)
|
||||
|
||||
length1 = fiber_length / (n_spans+1)
|
||||
delta1 = target_length-length1
|
||||
result1 = (length1, n_spans+1)
|
||||
|
||||
length2 = fiber_length / n_spans
|
||||
delta2 = length2-target_length
|
||||
result2 = (length2, n_spans)
|
||||
|
||||
if (bounds.start<=length1<=bounds.stop) and not(bounds.start<=length2<=bounds.stop):
|
||||
result = result1
|
||||
elif (bounds.start<=length2<=bounds.stop) and not(bounds.start<=length1<=bounds.stop):
|
||||
result = result2
|
||||
else:
|
||||
result = result1 if delta1 < delta2 else result2
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def split_fiber(network, fiber, bounds, target_length, equipment):
|
||||
new_length, n_spans = calculate_new_length(fiber.length, bounds, target_length)
|
||||
if n_spans == 1:
|
||||
return
|
||||
|
||||
try:
|
||||
next_node = next(network.successors(fiber))
|
||||
prev_node = next(network.predecessors(fiber))
|
||||
except StopIteration:
|
||||
print(f'{repr(fiber)} is not properly connected, please check network topology')
|
||||
exit()
|
||||
|
||||
network.remove_edge(fiber, next_node)
|
||||
network.remove_edge(prev_node, fiber)
|
||||
network.remove_node(fiber)
|
||||
# update connector loss parameter with default values
|
||||
fiber_params = fiber.params._asdict()
|
||||
fiber_params['con_in'] = fiber.con_in
|
||||
fiber_params['con_out'] = fiber.con_out
|
||||
new_spans = [
|
||||
Fiber(
|
||||
uid = f'{fiber.uid}_({span}/{n_spans})',
|
||||
metadata = fiber.metadata,
|
||||
params = fiber_params
|
||||
) for span in range(n_spans)
|
||||
]
|
||||
for new_span in new_spans:
|
||||
new_span.length = new_length
|
||||
network.add_node(new_span)
|
||||
network.add_edge(prev_node, new_span)
|
||||
prev_node = new_span
|
||||
network.add_edge(prev_node, next_node)
|
||||
|
||||
def add_connector_loss(fibers, con_in, con_out, EOL):
|
||||
for fiber in fibers:
|
||||
network = split_fiber(network, fiber)
|
||||
if fiber.con_in is None: fiber.con_in = con_in
|
||||
if fiber.con_out is None:
|
||||
fiber.con_out = con_out #con_out includes EOL
|
||||
else:
|
||||
fiber.con_out = fiber.con_out+EOL
|
||||
|
||||
def add_fiber_padding(network, fibers, padding):
|
||||
"""last_fibers = (fiber for n in network.nodes()
|
||||
if not (isinstance(n, Fiber) or isinstance(n, Fused))
|
||||
for fiber in network.predecessors(n)
|
||||
if isinstance(fiber, Fiber))"""
|
||||
for fiber in fibers:
|
||||
this_span_loss = span_loss(network, fiber)
|
||||
next_node = next(network.successors(fiber))
|
||||
if this_span_loss < padding and not (isinstance(next_node, Fused)):
|
||||
#add a padding att_in at the input of the 1st fiber:
|
||||
#address the case when several fibers are spliced together
|
||||
first_fiber = find_first_node(network, fiber)
|
||||
if first_fiber.att_in is None:
|
||||
first_fiber.att_in = padding - this_span_loss
|
||||
else :
|
||||
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']
|
||||
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)
|
||||
target_length = max(min_length, 90_000)
|
||||
con_in = default_span_data.con_in
|
||||
con_out = default_span_data.con_out + default_span_data.EOL
|
||||
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)
|
||||
# don't group split fiber and add amp in the same loop
|
||||
# =>for code clarity (at the expense of speed):
|
||||
for fiber in fibers:
|
||||
split_fiber(network, fiber, bounds, target_length, equipment)
|
||||
|
||||
amplified_nodes = [n for n in network.nodes()
|
||||
if isinstance(n, Fiber) or isinstance(n, Roadm)]
|
||||
for node in amplified_nodes:
|
||||
add_egress_amplifier(network, node)
|
||||
|
||||
roadms = [r for r in network.nodes() if isinstance(r, Roadm)]
|
||||
for roadm in roadms:
|
||||
add_egress_amplifier(network, roadm)
|
||||
set_egress_amplifier(network, roadm, equipment, pref_total_db)
|
||||
|
||||
#support older json input topology wo Roadms:
|
||||
if len(roadms) == 0:
|
||||
trx = [t for t in network.nodes() if isinstance(t, Transceiver)]
|
||||
for t in trx:
|
||||
set_egress_amplifier(network, t, equipment, pref_total_db)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#! /bin/usr/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
gnpy.core.node
|
||||
@@ -17,45 +18,21 @@ This base class provides a mode convenient way to define a network element
|
||||
via subclassing.
|
||||
'''
|
||||
|
||||
|
||||
from uuid import uuid4
|
||||
from gnpy.core.utils import load_json
|
||||
|
||||
|
||||
class ConfigStruct:
|
||||
|
||||
def __init__(self, **config):
|
||||
if config is None:
|
||||
return None
|
||||
if 'config_from_json' in config:
|
||||
json_config = load_json(config['config_from_json'])
|
||||
self.set_config_attr(json_config)
|
||||
|
||||
self.set_config_attr(config)
|
||||
|
||||
def set_config_attr(self, config):
|
||||
for k, v in config.items():
|
||||
setattr(self, k, ConfigStruct(**v)
|
||||
if isinstance(v, dict) else v)
|
||||
|
||||
def __repr__(self):
|
||||
return f'{self.__dict__}'
|
||||
from collections import namedtuple
|
||||
|
||||
class Location(namedtuple('Location', 'latitude longitude city region')):
|
||||
def __new__(cls, latitude=0, longitude=0, city=None, region=None):
|
||||
return super().__new__(cls, latitude, longitude, city, region)
|
||||
|
||||
class Node:
|
||||
|
||||
def __init__(self, config=None):
|
||||
self.config = ConfigStruct(**config)
|
||||
if self.config is None or not hasattr(self.config, 'uid'):
|
||||
self.uid = uuid4()
|
||||
else:
|
||||
self.uid = self.config.uid
|
||||
if hasattr(self.config, 'params'):
|
||||
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
|
||||
def __init__(self, uid, name=None, params=None, metadata={'location':{}}, operational=None):
|
||||
if name is None:
|
||||
name = uid
|
||||
self.uid, self.name = uid, name
|
||||
if metadata and not isinstance(metadata.get('location'), Location):
|
||||
metadata['location'] = Location(**metadata.pop('location', {}))
|
||||
self.params, self.metadata, self.operational = params, metadata, operational
|
||||
|
||||
@property
|
||||
def coords(self):
|
||||
@@ -63,16 +40,15 @@ class Node:
|
||||
|
||||
@property
|
||||
def location(self):
|
||||
return self.config.metadata.location
|
||||
return self.metadata['location']
|
||||
loc = location
|
||||
|
||||
@property
|
||||
def loc(self): # Aliases .location
|
||||
return self.location
|
||||
def longitude(self):
|
||||
return self.location.longitude
|
||||
lng = longitude
|
||||
|
||||
@property
|
||||
def lng(self):
|
||||
return self.config.metadata.location.longitude
|
||||
|
||||
@property
|
||||
def lat(self):
|
||||
return self.config.metadata.location.latitude
|
||||
def latitude(self):
|
||||
return self.location.latitude
|
||||
lat = latitude
|
||||
|
||||
337
gnpy/core/request.py
Normal file
337
gnpy/core/request.py
Normal file
@@ -0,0 +1,337 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
gnpy.core.request
|
||||
=================
|
||||
|
||||
This module contains path request functionality.
|
||||
|
||||
This functionality allows the user to provide a JSON request
|
||||
file in accordance with a Yang model for requesting path
|
||||
computations and returns path results in terms of path
|
||||
and feasibility
|
||||
|
||||
See: draft-ietf-teas-yang-path-computation-01.txt
|
||||
"""
|
||||
|
||||
from collections import namedtuple
|
||||
from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO
|
||||
from networkx import (dijkstra_path, NetworkXNoPath)
|
||||
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
|
||||
from csv import writer
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+
|
||||
' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off')
|
||||
|
||||
class Path_request:
|
||||
def __init__(self, *args, **params):
|
||||
params = RequestParams(**params)
|
||||
self.request_id = params.request_id
|
||||
self.source = params.source
|
||||
self.destination = params.destination
|
||||
self.tsp = params.trx_type
|
||||
self.tsp_mode = params.trx_mode
|
||||
self.baud_rate = params.baud_rate
|
||||
self.nodes_list = params.nodes_list
|
||||
self.loose_list = params.loose_list
|
||||
self.spacing = params.spacing
|
||||
self.power = params.power
|
||||
self.nb_channel = params.nb_channel
|
||||
self.frequency = params.frequency
|
||||
self.format = params.format
|
||||
self.OSNR = params.OSNR
|
||||
self.bit_rate = params.bit_rate
|
||||
self.roll_off = params.roll_off
|
||||
|
||||
def __str__(self):
|
||||
return '\n\t'.join([ f'{type(self).__name__} {self.request_id}',
|
||||
f'source: {self.source}',
|
||||
f'destination: {self.destination}'])
|
||||
def __repr__(self):
|
||||
return '\n\t'.join([ f'{type(self).__name__} {self.request_id}',
|
||||
f'source: \t{self.source}',
|
||||
f'destination:\t{self.destination}',
|
||||
f'trx type:\t{self.tsp}',
|
||||
f'trx mode:\t{self.tsp_mode}',
|
||||
f'baud_rate:\t{self.baud_rate * 1e-9} Gbaud',
|
||||
f'bit_rate:\t{self.bit_rate * 1e-9} Gb/s',
|
||||
f'spacing:\t{self.spacing * 1e-9} GHz',
|
||||
f'power: \t{round(lin2db(self.power)+30,2)} dBm'
|
||||
'\n'])
|
||||
|
||||
class Result_element(Element):
|
||||
def __init__(self,path_request,computed_path):
|
||||
self.path_id = path_request.request_id
|
||||
self.path_request = path_request
|
||||
self.computed_path = computed_path
|
||||
hop_type = []
|
||||
for e in computed_path :
|
||||
if isinstance(e, Transceiver) :
|
||||
hop_type.append(' - '.join([path_request.tsp,path_request.tsp_mode]))
|
||||
else:
|
||||
hop_type.append('not recorded')
|
||||
self.hop_type = hop_type
|
||||
uid = property(lambda self: repr(self))
|
||||
@property
|
||||
def pathresult(self):
|
||||
if not self.computed_path:
|
||||
return {
|
||||
'path-id': self.path_id,
|
||||
'path-properties':{
|
||||
'path-metric': [
|
||||
{
|
||||
'metric-type': 'SNR@bandwidth',
|
||||
'accumulative-value': 'None'
|
||||
},
|
||||
{
|
||||
'metric-type': 'SNR@0.1nm',
|
||||
'accumulative-value': 'None'
|
||||
},
|
||||
{
|
||||
'metric-type': 'OSNR@bandwidth',
|
||||
'accumulative-value': 'None'
|
||||
},
|
||||
{
|
||||
'metric-type': 'OSNR@0.1nm',
|
||||
'accumulative-value': 'None'
|
||||
},
|
||||
{
|
||||
'metric-type': 'reference_power',
|
||||
'accumulative-value': self.path_request.power
|
||||
}
|
||||
],
|
||||
'path-srlgs': {
|
||||
'usage': 'not used yet',
|
||||
'values': 'not used yet'
|
||||
},
|
||||
'path-route-objects': [
|
||||
{
|
||||
'path-route-object': {
|
||||
'index': 0,
|
||||
'unnumbered-hop': {
|
||||
'node-id': self.path_request.source,
|
||||
'link-tp-id': self.path_request.source,
|
||||
'hop-type': ' - '.join([self.path_request.tsp, self.path_request.tsp_mode]),
|
||||
'direction': 'not used'
|
||||
},
|
||||
'label-hop': {
|
||||
'te-label': {
|
||||
'generic': 'not used yet',
|
||||
'direction': 'not used yet'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'path-route-object': {
|
||||
'index': 1,
|
||||
'unnumbered-hop': {
|
||||
'node-id': self.path_request.destination,
|
||||
'link-tp-id': self.path_request.destination,
|
||||
'hop-type': ' - '.join([self.path_request.tsp, self.path_request.tsp_mode]),
|
||||
'direction': 'not used'
|
||||
},
|
||||
'label-hop': {
|
||||
'te-label': {
|
||||
'generic': 'not used yet',
|
||||
'direction': 'not used yet'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
else:
|
||||
return {
|
||||
'path-id': self.path_id,
|
||||
'path-properties':{
|
||||
'path-metric': [
|
||||
{
|
||||
'metric-type': 'SNR@bandwidth',
|
||||
'accumulative-value': round(mean(self.computed_path[-1].snr),2)
|
||||
},
|
||||
{
|
||||
'metric-type': 'SNR@0.1nm',
|
||||
'accumulative-value': round(mean(self.computed_path[-1].snr+lin2db(self.path_request.baud_rate/12.5e9)),2)
|
||||
},
|
||||
{
|
||||
'metric-type': 'OSNR@bandwidth',
|
||||
'accumulative-value': round(mean(self.computed_path[-1].osnr_ase),2)
|
||||
},
|
||||
{
|
||||
'metric-type': 'OSNR@0.1nm',
|
||||
'accumulative-value': round(mean(self.computed_path[-1].osnr_ase_01nm),2)
|
||||
},
|
||||
{
|
||||
'metric-type': 'reference_power',
|
||||
'accumulative-value': self.path_request.power
|
||||
}
|
||||
],
|
||||
'path-srlgs': {
|
||||
'usage': 'not used yet',
|
||||
'values': 'not used yet'
|
||||
},
|
||||
'path-route-objects': [
|
||||
{
|
||||
'path-route-object': {
|
||||
'index': self.computed_path.index(n),
|
||||
'unnumbered-hop': {
|
||||
'node-id': n.uid,
|
||||
'link-tp-id': n.uid,
|
||||
'hop-type': self.hop_type[self.computed_path.index(n)],
|
||||
'direction': 'not used'
|
||||
},
|
||||
'label-hop': {
|
||||
'te-label': {
|
||||
'generic': 'not used yet',
|
||||
'direction': 'not used yet'
|
||||
}
|
||||
}
|
||||
}
|
||||
} for n in self.computed_path
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
return self.pathresult
|
||||
|
||||
def compute_constrained_path(network, req):
|
||||
trx = [n for n in network.nodes() if isinstance(n, Transceiver)]
|
||||
roadm = [n for n in network.nodes() if isinstance(n, Roadm)]
|
||||
edfa = [n for n in network.nodes() if isinstance(n, Edfa)]
|
||||
source = next(el for el in trx if el.uid == req.source)
|
||||
# start the path with its source
|
||||
# TODO : avoid loops due to constraints , guess name base on string,
|
||||
# avoid crashing if on req is not correct
|
||||
total_path = [source]
|
||||
for n in req.nodes_list:
|
||||
# print(n)
|
||||
try :
|
||||
node = next(el for el in trx if el.uid == n)
|
||||
except StopIteration:
|
||||
try:
|
||||
node = next(el for el in roadm if el.uid == f'roadm {n}')
|
||||
except StopIteration:
|
||||
try:
|
||||
node = next(el for el in edfa
|
||||
if el.uid.startswith(f'egress edfa in {n}'))
|
||||
except StopIteration:
|
||||
msg = f'could not find node : {n} in network topology: \
|
||||
not a trx, roadm, edfa or fused element'
|
||||
logger.critical(msg)
|
||||
raise ValueError(msg)
|
||||
# extend path list without repeating source -> skip first element in the list
|
||||
try:
|
||||
total_path.extend(dijkstra_path(network, source, node)[1:])
|
||||
source = node
|
||||
except NetworkXNoPath:
|
||||
# for debug
|
||||
# print(req.loose_list)
|
||||
# print(req.nodes_list.index(n))
|
||||
if req.loose_list[req.nodes_list.index(n)] == 'loose':
|
||||
print(f'could not find a path from {source.uid} to loose node : {n} in network topology')
|
||||
print(f'node {n} is skipped')
|
||||
else:
|
||||
msg = f'could not find a path from {source.uid} to node : {n} in network topology'
|
||||
logger.critical(msg)
|
||||
#raise ValueError(msg)
|
||||
print(msg)
|
||||
total_path = []
|
||||
|
||||
# preparing disjonction feature
|
||||
# for p in all_simple_paths(network,\
|
||||
# source=next(el for el in trx if el.uid == req.source),\
|
||||
# target=next(el for el in trx if el.uid == req.destination)):
|
||||
# print([e.uid for e in p if isinstance(e,Roadm)])
|
||||
|
||||
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.frequency['min'], req.roll_off,
|
||||
req.baud_rate, req.power, req.spacing, req.nb_channel)
|
||||
for el in path:
|
||||
si = el(si)
|
||||
if show :
|
||||
print(el)
|
||||
return path
|
||||
|
||||
|
||||
def jsontocsv(json_data,equipment,fileout):
|
||||
# read json path result file in accordance with:
|
||||
# Yang model for requesting Path Computation
|
||||
# draft-ietf-teas-yang-path-computation-01.txt.
|
||||
# and write results in an CSV file
|
||||
|
||||
mywriter = writer(fileout)
|
||||
mywriter.writerow(('path-id','source','destination','transponder-type',\
|
||||
'transponder-mode','baud rate (Gbaud)', 'input power (dBm)','path',\
|
||||
'OSNR@bandwidth','OSNR@0.1nm','SNR@bandwidth','SNR@0.1nm','Pass?'))
|
||||
tspjsondata = equipment['Transceiver']
|
||||
#print(tspjsondata)
|
||||
for p in json_data['path']:
|
||||
path_id = p['path-id']
|
||||
source = p['path-properties']['path-route-objects'][0]\
|
||||
['path-route-object']['unnumbered-hop']['node-id']
|
||||
destination = p['path-properties']['path-route-objects'][-1]\
|
||||
['path-route-object']['unnumbered-hop']['node-id']
|
||||
pth = ' | '.join([ e['path-route-object']['unnumbered-hop']['node-id']
|
||||
for e in p['path-properties']['path-route-objects']])
|
||||
|
||||
[tsp,mode] = p['path-properties']['path-route-objects'][0]\
|
||||
['path-route-object']['unnumbered-hop']['hop-type'].split(' - ')
|
||||
|
||||
# find the min acceptable OSNR, baud rate from the eqpt library based on tsp (tupe) and mode (format)
|
||||
try:
|
||||
[minosnr, baud_rate] = next([m['OSNR'] , m['baud_rate']]
|
||||
for m in equipment['Transceiver'][tsp].mode if m['format']==mode)
|
||||
|
||||
# for debug
|
||||
# print(f'coucou {baud_rate}')
|
||||
except IndexError:
|
||||
msg = f'could not find tsp : {self.tsp} with mode: {self.tsp_mode} in eqpt library'
|
||||
|
||||
raise ValueError(msg)
|
||||
output_snr = next(e['accumulative-value']
|
||||
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm')
|
||||
output_snrbandwidth = next(e['accumulative-value']
|
||||
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@bandwidth')
|
||||
output_osnr = next(e['accumulative-value']
|
||||
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@0.1nm')
|
||||
output_osnrbandwidth = next(e['accumulative-value']
|
||||
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@bandwidth')
|
||||
power = next(e['accumulative-value']
|
||||
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'reference_power')
|
||||
if isinstance(output_snr, str):
|
||||
isok = ''
|
||||
else:
|
||||
isok = output_snr >= minosnr
|
||||
mywriter.writerow((path_id,
|
||||
source,
|
||||
destination,
|
||||
tsp,
|
||||
mode,
|
||||
baud_rate*1e-9,
|
||||
round(lin2db(power)+30,2),
|
||||
pth,
|
||||
output_osnrbandwidth,
|
||||
output_osnr,
|
||||
output_snrbandwidth,
|
||||
output_snr,
|
||||
isok
|
||||
))
|
||||
216
gnpy/core/service_sheet.py
Normal file
216
gnpy/core/service_sheet.py
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
gnpy.core.service_sheet
|
||||
========================
|
||||
|
||||
XLS parser that can be called to create a JSON request file in accordance with
|
||||
Yang model for requesting path computation.
|
||||
|
||||
See: draft-ietf-teas-yang-path-computation-01.txt
|
||||
"""
|
||||
|
||||
from sys import exit
|
||||
try:
|
||||
from xlrd import open_workbook, XL_CELL_EMPTY
|
||||
except ModuleNotFoundError:
|
||||
exit('Required: `pip install xlrd`')
|
||||
from collections import namedtuple
|
||||
from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO
|
||||
from json import dumps
|
||||
from pathlib import Path
|
||||
from gnpy.core.equipment import load_equipment
|
||||
from gnpy.core.utils import db2lin, lin2db
|
||||
|
||||
SERVICES_COLUMN = 11
|
||||
#EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json'
|
||||
|
||||
all_rows = lambda sheet, start=0: (sheet.row(x) for x in range(start, sheet.nrows))
|
||||
logger = getLogger(__name__)
|
||||
|
||||
# Type for input data
|
||||
class Request(namedtuple('Request', 'request_id source destination trx_type mode \
|
||||
spacing power nb_channel disjoint_from nodes_list is_loose')):
|
||||
def __new__(cls, request_id, source, destination, trx_type, mode , spacing , power , nb_channel , disjoint_from ='' , nodes_list = None, is_loose = ''):
|
||||
return super().__new__(cls, request_id, source, destination, trx_type, mode, spacing, power, nb_channel, disjoint_from, nodes_list, is_loose)
|
||||
|
||||
# Type for output data: // from dutc
|
||||
class Element:
|
||||
def __eq__(self, other):
|
||||
return type(self) == type(other) and self.uid == other.uid
|
||||
def __hash__(self):
|
||||
return hash((type(self), self.uid))
|
||||
|
||||
class Request_element(Element):
|
||||
def __init__(self,Request,eqpt_filename):
|
||||
# request_id is str
|
||||
# excel has automatic number formatting that adds .0 on integer values
|
||||
# the next lines recover the pure int value, assuming this .0 is unwanted
|
||||
if not isinstance(Request.request_id,str):
|
||||
value = str(int(Request.request_id))
|
||||
if value.endswith('.0'):
|
||||
value = value[:-2]
|
||||
self.request_id = value
|
||||
else:
|
||||
self.request_id = Request.request_id
|
||||
self.source = Request.source
|
||||
self.destination = Request.destination
|
||||
self.srctpid = f'trx {Request.source}'
|
||||
self.dsttpid = f'trx {Request.destination}'
|
||||
# test that trx_type belongs to eqpt_config.json
|
||||
# if not replace it with a default
|
||||
equipment = load_equipment(eqpt_filename)
|
||||
try :
|
||||
if equipment['Transceiver'][Request.trx_type]:
|
||||
self.trx_type = Request.trx_type
|
||||
if [mode for mode in equipment['Transceiver'][Request.trx_type].mode]:
|
||||
self.mode = Request.mode
|
||||
except KeyError:
|
||||
msg = f'could not find tsp : {Request.trx_type} with mode: {Request.mode} in eqpt library \nComputation stopped.'
|
||||
#print(msg)
|
||||
logger.critical(msg)
|
||||
exit()
|
||||
# excel input are in GHz and dBm
|
||||
self.spacing = Request.spacing * 1e9
|
||||
self.power = db2lin(Request.power) * 1e-3
|
||||
self.nb_channel = int(Request.nb_channel)
|
||||
if not isinstance(Request.disjoint_from,str):
|
||||
value = str(int(Request.disjoint_from))
|
||||
if value.endswith('.0'):
|
||||
value = value[:-2]
|
||||
else:
|
||||
value = Request.disjoint_from
|
||||
self.disjoint_from = [n for n in value.split()]
|
||||
self.nodes_list = []
|
||||
if Request.nodes_list :
|
||||
self.nodes_list = Request.nodes_list.split(' | ')
|
||||
try :
|
||||
self.nodes_list.remove(self.source)
|
||||
msg = f'{self.source} removed from explicit path node-list'
|
||||
logger.info(msg)
|
||||
# print(msg)
|
||||
except ValueError:
|
||||
msg = f'{self.source} already removed from explicit path node-list'
|
||||
logger.info(msg)
|
||||
# print(msg)
|
||||
try :
|
||||
self.nodes_list.remove(self.destination)
|
||||
msg = f'{self.destination} removed from explicit path node-list'
|
||||
logger.info(msg)
|
||||
# print(msg)
|
||||
except ValueError:
|
||||
msg = f'{self.destination} already removed from explicit path node-list'
|
||||
logger.info(msg)
|
||||
# print(msg)
|
||||
|
||||
self.loose = 'loose'
|
||||
if Request.is_loose == 'no' :
|
||||
self.loose = 'strict'
|
||||
|
||||
uid = property(lambda self: repr(self))
|
||||
@property
|
||||
def pathrequest(self):
|
||||
return {
|
||||
'request-id':self.request_id,
|
||||
'source': self.source,
|
||||
'destination': self.destination,
|
||||
'src-tp-id': self.srctpid,
|
||||
'dst-tp-id': self.dsttpid,
|
||||
'path-constraints':{
|
||||
'te-bandwidth': {
|
||||
'technology': 'flexi-grid',
|
||||
'trx_type' : self.trx_type,
|
||||
'trx_mode' : self.mode,
|
||||
'effective-freq-slot':[{'n': 'null','m': 'null'}] ,
|
||||
'spacing' : self.spacing,
|
||||
'max-nb-of-channel' : self.nb_channel,
|
||||
'output-power' : self.power
|
||||
}
|
||||
},
|
||||
'optimizations': {
|
||||
'explicit-route-include-objects': [
|
||||
{
|
||||
'index': self.nodes_list.index(node),
|
||||
'unnumbered-hop':{
|
||||
'node-id': f'{node}',
|
||||
'link-tp-id': 'link-tp-id is not used',
|
||||
'hop-type': 'loose',
|
||||
'direction': 'direction is not used'
|
||||
},
|
||||
'label-hop':{
|
||||
'te-label': {
|
||||
'generic': 'generic is not used',
|
||||
'direction': 'direction is not used'
|
||||
}
|
||||
}
|
||||
}
|
||||
for node in self.nodes_list
|
||||
]
|
||||
|
||||
}
|
||||
}
|
||||
@property
|
||||
def pathsync(self):
|
||||
if self.disjoint_from :
|
||||
return {'synchonization-id':self.request_id,
|
||||
'svec': {
|
||||
'relaxable' : 'False',
|
||||
'link-diverse': 'True',
|
||||
'node-diverse': 'True',
|
||||
'request-id-number': [self.request_id]+ [n for n in self.disjoint_from]
|
||||
}
|
||||
}
|
||||
# TO-DO: avoid multiple entries with same synchronisation vectors
|
||||
@property
|
||||
def json(self):
|
||||
return self.pathrequest , self.pathsync
|
||||
|
||||
def convert_service_sheet(input_filename, eqpt_filename, output_filename='', filter_region=[]):
|
||||
service = parse_excel(input_filename)
|
||||
req = [Request_element(n,eqpt_filename) for n in service]
|
||||
# dumps the output into a json file with name
|
||||
# split_filename = [input_filename[0:len(input_filename)-len(suffix_filename)] , suffix_filename[1:]]
|
||||
if output_filename=='':
|
||||
output_filename = f'{str(input_filename)[0:len(str(input_filename))-len(str(input_filename.suffixes[0]))]}_services.json'
|
||||
# for debug
|
||||
# print(json_filename)
|
||||
data = {
|
||||
'path-request': [n.json[0] for n in req],
|
||||
'synchronisation': [n.json[1] for n in req
|
||||
if n.json[1] is not None]
|
||||
}
|
||||
with open(output_filename, 'w') as f:
|
||||
f.write(dumps(data, indent=2))
|
||||
return data
|
||||
|
||||
# to be used from dutc
|
||||
def parse_row(row, fieldnames):
|
||||
return {f: r.value for f, r in zip(fieldnames, row[0:SERVICES_COLUMN])
|
||||
if r.ctype != XL_CELL_EMPTY}
|
||||
#
|
||||
|
||||
def parse_excel(input_filename):
|
||||
with open_workbook(input_filename) as wb:
|
||||
service_sheet = wb.sheet_by_name('Service')
|
||||
services = list(parse_service_sheet(service_sheet))
|
||||
return services
|
||||
|
||||
def parse_service_sheet(service_sheet):
|
||||
logger.info(f'Validating headers on {service_sheet.name!r}')
|
||||
header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN]]
|
||||
expected = ['route id', 'Source', 'Destination', 'TRX type', \
|
||||
'Mode', 'System: spacing', 'System: input power (dBm)', 'System: nb of channels',\
|
||||
'routing: disjoint from', 'routing: path', 'routing: is loose?']
|
||||
if header != expected:
|
||||
msg = f'Malformed header on Service sheet: {header} != {expected}'
|
||||
logger.critical(msg)
|
||||
raise ValueError(msg)
|
||||
|
||||
service_fieldnames = 'request_id source destination trx_type mode spacing power nb_channel disjoint_from nodes_list is_loose'.split()
|
||||
# Important Note: it reads all colum on each row so that
|
||||
# it is not possible to write annotation in the excel sheet
|
||||
# outside the SERVICES_COLUMN ... TO BE IMPROVED
|
||||
# request_id should be unique for disjunction constraints (not used yet)
|
||||
for row in all_rows(service_sheet, start=5):
|
||||
yield Request(**parse_row(row[0:SERVICES_COLUMN], service_fieldnames))
|
||||
@@ -1,2 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
UNITS = {'m': 1,
|
||||
'km': 1E3}
|
||||
|
||||
@@ -12,6 +12,7 @@ This module contains utility functions that are used with gnpy.
|
||||
import json
|
||||
|
||||
import numpy as np
|
||||
from csv import writer
|
||||
from numpy import pi, cos, sqrt, log10
|
||||
from scipy import constants
|
||||
|
||||
@@ -24,8 +25,46 @@ def load_json(filename):
|
||||
|
||||
def save_json(obj, filename):
|
||||
with open(filename, 'w') as f:
|
||||
json.dump(obj, f)
|
||||
json.dump(obj, f, indent=2)
|
||||
|
||||
def write_csv(obj, filename):
|
||||
"""
|
||||
convert dictionary items to a csv file
|
||||
the dictionary format :
|
||||
|
||||
{'result category 1':
|
||||
[
|
||||
# 1st line of results
|
||||
{'header 1' : value_xxx,
|
||||
'header 2' : value_yyy},
|
||||
# 2nd line of results: same headers, different results
|
||||
{'header 1' : value_www,
|
||||
'header 2' : value_zzz}
|
||||
],
|
||||
'result_category 2':
|
||||
[
|
||||
{},{}
|
||||
]
|
||||
}
|
||||
|
||||
the generated csv file will be:
|
||||
result_category 1
|
||||
header 1 header 2
|
||||
value_xxx value_yyy
|
||||
value_www value_zzz
|
||||
result_category 2
|
||||
...
|
||||
"""
|
||||
with open(filename, 'w') as f:
|
||||
w = writer(f)
|
||||
for data_key, data_list in obj.items():
|
||||
#main header
|
||||
w.writerow([data_key])
|
||||
#sub headers:
|
||||
headers = [_ for _ in data_list[0].keys()]
|
||||
w.writerow(headers)
|
||||
for data_dict in data_list:
|
||||
w.writerow([_ for _ in data_dict.values()])
|
||||
|
||||
def c():
|
||||
"""
|
||||
@@ -64,6 +103,14 @@ def lin2db(value):
|
||||
def db2lin(value):
|
||||
return 10**(value / 10)
|
||||
|
||||
def round2float(number, step):
|
||||
step = round(step, 1)
|
||||
if step >= 0.01:
|
||||
number = round(number / step, 0)
|
||||
number = round(number * step, 1)
|
||||
else:
|
||||
number = round(number, 2)
|
||||
return number
|
||||
|
||||
wavelength2freq = constants.lambda2nu
|
||||
freq2wavelength = constants.nu2lambda
|
||||
|
||||
3
hooks/pre-commit
Executable file
3
hooks/pre-commit
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
exec pytest
|
||||
372
json_structure_description.rst
Normal file
372
json_structure_description.rst
Normal file
@@ -0,0 +1,372 @@
|
||||
*********************************************
|
||||
Equipment and Network description definitions
|
||||
*********************************************
|
||||
|
||||
1. Equipment description
|
||||
########################
|
||||
|
||||
Equipment description defines equipment types and those parameters.
|
||||
Description is made in JSON file with predefined structure. By default
|
||||
**transmission_main_example.py** uses **eqpt_config.json** file and that
|
||||
can be changed with **-e** or **--equipment** command line parameter.
|
||||
Parsing of JSON file is made with
|
||||
**gnpy.core.equipment.load_equipment(equipment_description)** and return
|
||||
value is a dictionary of format **dict[‘equipment
|
||||
type’][‘subtype’]=object**
|
||||
|
||||
1.1. Structure definition
|
||||
*************************
|
||||
|
||||
1.1.1. Equipment types
|
||||
*************************
|
||||
|
||||
Every equipment type is defined in JSON root with according name and
|
||||
array of parameters as value.
|
||||
|
||||
.. code-block::
|
||||
|
||||
{"Edfa": [...],
|
||||
"Fiber": [...]
|
||||
}
|
||||
|
||||
|
||||
1.1.2. Equipment parameters and subtypes
|
||||
*****************************************
|
||||
|
||||
|
||||
Array of parameters is a list of objects with unordered parameter name
|
||||
and its value definition. In case of multiple equipment subtypes each
|
||||
object contains **"type_variety":”type name”** name:value combination,
|
||||
if only one subtype exists **"type_variety"** name is not mandatory and
|
||||
it will be marked with **”default”** value.
|
||||
|
||||
.. code-block::
|
||||
|
||||
{"Edfa": [{
|
||||
"type_variety": "std_medium_gain",
|
||||
"gain_flatmax": 26,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"nf_min": 6,
|
||||
"nf_max": 10
|
||||
},
|
||||
{
|
||||
"type_variety": "std_low_gain",
|
||||
"gain_flatmax": 16,
|
||||
"gain_min": 8,
|
||||
"p_max": 21,
|
||||
"nf_min": 7,
|
||||
"nf_max": 11
|
||||
}
|
||||
],
|
||||
"Fiber": [{
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
1.2. Equipment parameters by type
|
||||
*********************************
|
||||
|
||||
1.2.1. EDFA element
|
||||
*******************
|
||||
|
||||
Two types of EDFA definition are possible. Description JSON file
|
||||
location is in **transmission_main_example.py** folder:
|
||||
|
||||
- Advanced – with JSON file describing gain/noise figure tilt and
|
||||
gain/noise figure ripple. **"advanced_config_from_json"** value
|
||||
contains filename.
|
||||
|
||||
.. code-block::
|
||||
|
||||
"Edfa":[{
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"advanced_config_from_json": "std_medium_gain_advanced_config.json"
|
||||
}
|
||||
]
|
||||
|
||||
- Default – with JSON file describing gain figure tilt and gain/noise
|
||||
figure ripple. **”default_edfa_config.json”** as source file.
|
||||
|
||||
.. code-block::
|
||||
|
||||
"Edfa":[{
|
||||
"gain_flatmax": 26,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"nf_min": 6,
|
||||
"nf_max": 10
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
1.2.2. Fiber element
|
||||
********************
|
||||
|
||||
Fiber element with its parameters:
|
||||
|
||||
.. code-block::
|
||||
|
||||
"Fiber":[{
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
1.2.3. Spans element
|
||||
********************
|
||||
|
||||
Spans element with its parameters:
|
||||
|
||||
.. code-block::
|
||||
|
||||
"Spans":[{
|
||||
"max_length": 150,
|
||||
"length_units": "km",
|
||||
"max_loss": 28,
|
||||
"padding": 10,
|
||||
"EOL": 1,
|
||||
"con_loss": 0.5
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
1.2.4. Spectral Information
|
||||
***************************
|
||||
|
||||
Spectral information with its parameters:
|
||||
|
||||
.. code-block::
|
||||
|
||||
"SI":[{
|
||||
"f_min": 191.3e12,
|
||||
"Nch": 80,
|
||||
"baud_rate": 32e9,
|
||||
"spacing": 75e9,
|
||||
"roll_off": 0.15,
|
||||
"power": 1.2589e-3
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
1.2.5. Transceiver element
|
||||
**************************
|
||||
|
||||
Transceiver element with its parameters. **”mode”** can contain multiple
|
||||
Transceiver operation formats.
|
||||
|
||||
.. code-block::
|
||||
|
||||
"Transceiver":[{
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
"format": "PS_SP64_1",
|
||||
"baudrate": 32e9,
|
||||
"OSNR": 9,
|
||||
"bit_rate": 100e9
|
||||
},
|
||||
{
|
||||
"format": "PS_SP64_2",
|
||||
"baudrate": 66e9,
|
||||
"OSNR": 10,
|
||||
"bit_rate": 200e9
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
***********************
|
||||
2. Network description
|
||||
***********************
|
||||
|
||||
Network description defines network elements with additional to
|
||||
equipment description parameters, metadata and elements interconnection.
|
||||
Description is made in JSON file with predefined structure. By default
|
||||
**transmission_main_example.py** uses **edfa_example_network.json** file
|
||||
and can be changed from command line. Parsing of JSON file is made with
|
||||
**gnpy.core.network.load_network(network_description,
|
||||
equipment_description)** and return value is **DiGraph** object which
|
||||
mimics network description.
|
||||
|
||||
2.1. Structure definition
|
||||
##########################
|
||||
|
||||
2.1.1. File root structure
|
||||
***************************
|
||||
|
||||
Network description JSON file root consist of three unordered parts:
|
||||
|
||||
- network_name – name of described network or service, is not used as
|
||||
of now
|
||||
|
||||
- elements - contains array of network element objects with their
|
||||
respective parameters
|
||||
|
||||
- connections – contains array of unidirectional connection objects
|
||||
|
||||
.. code-block::
|
||||
|
||||
{"network_name": "Example Network",
|
||||
"elements": [{...},
|
||||
{...}
|
||||
],
|
||||
"connections": [{...},
|
||||
{...}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
2.1.2. Elements parameters and subtypes
|
||||
****************************************
|
||||
|
||||
Array of network element objects consist of unordered parameter names
|
||||
and those values. In case of **"type_variety"** absence
|
||||
**"type_variety":”default”** name:value combination is used. As of the
|
||||
moment, existence of used **"type_variety"** in equipment description is
|
||||
obligatory.
|
||||
|
||||
2.2. Element parameters by type
|
||||
*********************************
|
||||
|
||||
2.2.1. Transceiver element
|
||||
***************************
|
||||
|
||||
Transceiver element with its parameters.
|
||||
|
||||
.. code-block::
|
||||
|
||||
{"uid": "trx Site_A",
|
||||
“metadata": {
|
||||
"location": {
|
||||
"city": "Site_A",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
}
|
||||
|
||||
|
||||
|
||||
2.2.2. ROADM element
|
||||
*********************
|
||||
|
||||
ROADM element with its parameters. **“params”** is optional, if not used
|
||||
default loss value of 20dB is used.
|
||||
|
||||
.. code-block::
|
||||
|
||||
{"uid": "roadm Site_A",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Site_A",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"loss": 17
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2.2.3. Fused element
|
||||
*********************
|
||||
|
||||
Fused element with its parameters. **“params”** is optional, if not used
|
||||
default loss value of 1dB is used.
|
||||
|
||||
.. code-block::
|
||||
|
||||
{"uid": "ingress fused spans in Site_B",
|
||||
"metadata": {
|
||||
"location": {
|
||||
“city": "Site_B",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 0.5
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2.2.4. Fiber element
|
||||
*********************
|
||||
|
||||
Fiber element with its parameters.
|
||||
|
||||
.. code-block::
|
||||
|
||||
{"uid": "fiber (Site_A \\u2192 Site_B)",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "",
|
||||
"region": "",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2.2.5. EDFA element
|
||||
********************
|
||||
|
||||
EDFA element with its parameters.
|
||||
|
||||
.. code-block::
|
||||
|
||||
{"uid": "Edfa1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 16,
|
||||
"tilt_target": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Site_A",
|
||||
"region": "",
|
||||
"latitude": 2,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2.3. Connections objects
|
||||
*************************
|
||||
|
||||
Each unidirectional connection object in connections array consist of
|
||||
two unordered **”from_node”** and **”to_node”** name pair with values
|
||||
corresponding to element **”uid”**
|
||||
|
||||
.. code-block::
|
||||
|
||||
{"from_node": "roadm Site_C",
|
||||
"to_node": "trx Site_C"
|
||||
}
|
||||
40
path_result_template.json
Normal file
40
path_result_template.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"paths": [
|
||||
{
|
||||
"path": {
|
||||
"path-id": null,
|
||||
"path-properties": {
|
||||
"path-metric": [
|
||||
{
|
||||
"metric-type": null,
|
||||
"accumulative-value": null
|
||||
}
|
||||
],
|
||||
"path-srlgs": {
|
||||
"usage": "not used yet",
|
||||
"values": ["not used yet"]
|
||||
},
|
||||
"path-route-objects": [
|
||||
{
|
||||
"path-route-object": {
|
||||
"index": null,
|
||||
"unnumbered-hop": {
|
||||
"node-id": null,
|
||||
"link-tp-id": null,
|
||||
"hop-type": null,
|
||||
"direction": "not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "not used yet",
|
||||
"direction": "not used yet"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
2
pytest.ini
Normal file
2
pytest.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
[pytest]
|
||||
addopts = -p no:warnings
|
||||
@@ -1,36 +1,40 @@
|
||||
alabaster==0.7.10
|
||||
attrs==17.4.0
|
||||
Babel==2.5.3
|
||||
certifi==2017.11.5
|
||||
alabaster==0.7.12
|
||||
atomicwrites==1.2.1
|
||||
attrs==18.2.0
|
||||
Babel==2.6.0
|
||||
certifi==2018.10.15
|
||||
chardet==3.0.4
|
||||
cycler==0.10.0
|
||||
decorator==4.1.2
|
||||
decorator==4.3.0
|
||||
docutils==0.14
|
||||
idna==2.6
|
||||
imagesize==0.7.1
|
||||
idna==2.7
|
||||
imagesize==1.1.0
|
||||
Jinja2==2.10
|
||||
kiwisolver==1.0.1
|
||||
latexcodec==1.0.5
|
||||
MarkupSafe==1.0
|
||||
matplotlib==2.1.0
|
||||
networkx==2.0
|
||||
numpy==1.13.3
|
||||
matplotlib==3.0.0
|
||||
more-itertools==4.3.0
|
||||
networkx==2.2
|
||||
numpy==1.15.2
|
||||
oset==0.1.3
|
||||
pluggy==0.6.0
|
||||
py==1.5.2
|
||||
packaging==18.0
|
||||
pluggy==0.7.1
|
||||
py==1.7.0
|
||||
pybtex==0.21
|
||||
pybtex-docutils==0.2.1
|
||||
Pygments==2.2.0
|
||||
pyparsing==2.2.0
|
||||
pytest==3.3.2
|
||||
python-dateutil==2.6.1
|
||||
pytz==2017.3
|
||||
PyYAML==3.12
|
||||
requests==2.18.4
|
||||
scipy==1.0.0
|
||||
pyparsing==2.2.2
|
||||
pytest==3.8.2
|
||||
python-dateutil==2.7.3
|
||||
pytz==2018.5
|
||||
PyYAML==3.13
|
||||
requests==2.19.1
|
||||
scipy==1.1.0
|
||||
six==1.11.0
|
||||
snowballstemmer==1.2.1
|
||||
Sphinx==1.6.6
|
||||
sphinxcontrib-bibtex==0.3.6
|
||||
sphinxcontrib-websupport==1.0.1
|
||||
urllib3==1.22
|
||||
Sphinx==1.8.1
|
||||
sphinxcontrib-bibtex==0.4.0
|
||||
sphinxcontrib-websupport==1.1.0
|
||||
urllib3==1.23
|
||||
xlrd==1.1.0
|
||||
|
||||
59
service-template.json
Normal file
59
service-template.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": null,
|
||||
"source": null,
|
||||
"destination": null,
|
||||
"src-tp-id": null,
|
||||
"dst-tp-id": null,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": null,
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": null,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": {
|
||||
"route-object-include-object": [
|
||||
{
|
||||
"index": null,
|
||||
"unnumbered-hop": {
|
||||
"node-id": null,
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": null,
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}],
|
||||
"synchronization": [
|
||||
{
|
||||
"synchronization-id": null,
|
||||
"svec": {
|
||||
"relaxable": "True",
|
||||
"link-diverse": "False",
|
||||
"node-diverse": "False",
|
||||
"request-id-number": [
|
||||
null ]
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
7
setup.py
7
setup.py
@@ -11,11 +11,13 @@ with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
|
||||
|
||||
setup(
|
||||
name='gnpy',
|
||||
version='0.1.0',
|
||||
version='0.1.3',
|
||||
description='route planning and optimization tool for mesh optical networks',
|
||||
long_description=long_description,
|
||||
long_description_content_type='text/x-rst; charset=UTF-8',
|
||||
url='https://github.com/Telecominfraproject/gnpy',
|
||||
author='Telecom Infra Project',
|
||||
author_email='james.powell@telecominfraproject.com',
|
||||
classifiers=[
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Intended Audience :: Developers',
|
||||
@@ -31,6 +33,7 @@ setup(
|
||||
'Topic :: Scientific/Engineering :: Physics',
|
||||
],
|
||||
keywords='optics network fiber communication route planning optimization',
|
||||
packages=find_packages(exclude=['examples', 'docs', 'tests']), # Required
|
||||
#packages=find_packages(exclude=['examples', 'docs', 'tests']), # Required
|
||||
packages=find_packages(exclude=['docs', 'tests']), # Required
|
||||
install_requires=list(open('requirements.txt'))
|
||||
)
|
||||
|
||||
261
tests/LinkforTest.json
Normal file
261
tests/LinkforTest.json
Normal file
@@ -0,0 +1,261 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx A",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "A",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx B",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "B",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx F",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "F",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "fiber (A \u2192 B)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": 1.00,
|
||||
"con_out": 1.00
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (B \u2192 C)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": 1.00,
|
||||
"con_out": 1.00
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (C \u2192 D)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": 1.00,
|
||||
"con_out": 1.00
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (D \u2192 E)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": 1.00,
|
||||
"con_out": 1.00
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (E \u2192 F)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": 1.00,
|
||||
"con_out": 1.00
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 18,
|
||||
"tilt_target": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"region": "",
|
||||
"latitude": 2,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa2",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 18,
|
||||
"tilt_target": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"region": "",
|
||||
"latitude": 2,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa3",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 18,
|
||||
"tilt_target": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"region": "",
|
||||
"latitude": 2,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa4",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 18,
|
||||
"tilt_target": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"region": "",
|
||||
"latitude": 2,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa5",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 18,
|
||||
"tilt_target": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"region": "",
|
||||
"latitude": 2,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "fiber (A \u2192 B)-",
|
||||
"to_node": "Edfa1"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa1",
|
||||
"to_node": "fiber (B \u2192 C)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (B \u2192 C)-",
|
||||
"to_node": "Edfa2"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa2",
|
||||
"to_node": "fiber (C \u2192 D)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (C \u2192 D)-",
|
||||
"to_node": "Edfa3"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa3",
|
||||
"to_node": "fiber (D \u2192 E)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (D \u2192 E)-",
|
||||
"to_node": "Edfa4"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa4",
|
||||
"to_node": "fiber (E \u2192 F)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (E \u2192 F)-",
|
||||
"to_node": "Edfa5"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa5",
|
||||
"to_node": "trx F"
|
||||
},
|
||||
{
|
||||
"from_node": "trx A",
|
||||
"to_node": "fiber (A \u2192 B)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa1",
|
||||
"to_node": "trx B"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from .test_utilities import *
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Author: Jean-Luc Auge
|
||||
# @Date: 2018-02-02 14:06:55
|
||||
|
||||
from gnpy.core.elements import Edfa
|
||||
import numpy as np
|
||||
from json import load
|
||||
import pytest
|
||||
from gnpy.core import network_from_json
|
||||
from gnpy.core.elements import Transceiver, Fiber, Edfa
|
||||
from gnpy.core.utils import lin2db, db2lin
|
||||
from gnpy.core.info import SpectralInformation, Channel, Power
|
||||
|
||||
network_file_name = 'tests/test_network.json'
|
||||
|
||||
@pytest.fixture(params=[(96, 0.05e12), (60, 0.075e12), (45, 0.1e12), (2, 0.1e12)],
|
||||
ids=['50GHz spacing', '75GHz spacing', '100GHz spacing', '2 channels'])
|
||||
# TODO in elements.py code: pytests doesn't pass with 1 channel: interpolate fail
|
||||
def nch_and_spacing(request):
|
||||
"""parametrize channel count vs channel spacing (Hz)"""
|
||||
yield request.param
|
||||
|
||||
@pytest.fixture()
|
||||
def bw():
|
||||
"""parametrize signal bandwidth (Hz)"""
|
||||
return 45e9
|
||||
|
||||
@pytest.fixture()
|
||||
def setup_edfa():
|
||||
"""init edfa class by reading test_network.json file
|
||||
remove all gain and nf ripple"""
|
||||
with open(network_file_name) as network_file:
|
||||
network_json = load(network_file)
|
||||
network = network_from_json(network_json)
|
||||
edfa = [n for n in network.nodes() if isinstance(n, Edfa)][0]
|
||||
|
||||
#edfa.params.dgt = np.zeros(96)
|
||||
edfa.params.gain_ripple = np.zeros(96)
|
||||
edfa.params.nf_ripple = np.zeros(96)
|
||||
yield edfa
|
||||
|
||||
@pytest.fixture()
|
||||
def setup_trx():
|
||||
"""init transceiver class to access snr and osnr calculations"""
|
||||
with open(network_file_name) as network_file:
|
||||
network_json = load(network_file)
|
||||
network = network_from_json(network_json)
|
||||
trx = [n for n in network.nodes() if isinstance(n, Transceiver)][0]
|
||||
return trx
|
||||
|
||||
@pytest.fixture()
|
||||
def si(nch_and_spacing, bw):
|
||||
"""parametrize a channel comb with nch, spacing and signal bw"""
|
||||
nch, spacing = nch_and_spacing
|
||||
si = SpectralInformation()
|
||||
si = si.update(carriers=tuple(Channel(f, 191.3e12+spacing*f,
|
||||
bw, 0.15, Power(1e-6, 0, 0)) for f in range(1,nch+1)))
|
||||
return si
|
||||
|
||||
@pytest.mark.parametrize("enabled", [True, False])
|
||||
@pytest.mark.parametrize("gain, nf_expected", [(10, 15), (15, 10), (25, 5.8)])
|
||||
def test_nf_calc(gain, nf_expected, enabled, setup_edfa, si):
|
||||
""" compare the 2 amplifier models (polynomial and estimated from nf_min and max)
|
||||
=> nf_model vs nf_poly_fit for boundary gain values: gain_min (and below) & gain_flatmax
|
||||
same values are expected between the 2 models
|
||||
=> unitary test for Edfa._calc_nf() (and Edfa.interpol_params)"""
|
||||
edfa = setup_edfa
|
||||
frequencies = np.array([c.frequency for c in si.carriers])
|
||||
pin = np.array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers])
|
||||
baud_rates = np.array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
edfa.params.nf_model_enabled = enabled
|
||||
edfa.interpol_params(frequencies, pin, baud_rates)
|
||||
|
||||
nf = edfa.nf
|
||||
dif = abs(nf[0] - nf_expected)
|
||||
assert dif < 0.01
|
||||
|
||||
@pytest.mark.parametrize("gain", [17, 19, 21, 23])
|
||||
def test_compare_nf_models(gain, setup_edfa, si):
|
||||
""" compare the 2 amplifier models (polynomial and estimated from nf_min and max)
|
||||
=> nf_model vs nf_poly_fit for intermediate gain values:
|
||||
between gain_min and gain_flatmax some discrepancy is expected but target < 0.5dB
|
||||
=> unitary test for Edfa._calc_nf (and Edfa.interpol_params)"""
|
||||
edfa = setup_edfa
|
||||
frequencies = np.array([c.frequency for c in si.carriers])
|
||||
pin = np.array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers])
|
||||
baud_rates = np.array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
edfa.params.nf_model_enabled = True
|
||||
edfa.interpol_params(frequencies, pin, baud_rates)
|
||||
nf_model = edfa.nf[0]
|
||||
|
||||
edfa.params.nf_model_enabled = False
|
||||
edfa.interpol_params(frequencies, pin, baud_rates)
|
||||
nf_poly = edfa.nf[0]
|
||||
dif = abs(nf_model - nf_poly)
|
||||
assert dif < 0.5
|
||||
|
||||
def test_si(si, nch_and_spacing):
|
||||
"""basic total power check of the channel comb generation"""
|
||||
nch = nch_and_spacing[0]
|
||||
pin = np.array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers])
|
||||
p_tot = np.sum(pin)
|
||||
expected_p_tot = si.carriers[0].power.signal * nch
|
||||
dif = abs(lin2db(p_tot/expected_p_tot))
|
||||
assert dif < 0.01
|
||||
|
||||
@pytest.mark.parametrize("gain", [13, 15, 17, 19, 21, 23, 25, 27])
|
||||
def test_ase_noise(gain, si, setup_edfa, setup_trx, bw):
|
||||
"""testing 3 different ways of calculating osnr:
|
||||
1-pin-edfa.nf+58 vs
|
||||
2-pout/pase afet propagate
|
||||
3-Transceiver osnr_ase_01nm
|
||||
=> unitary test for Edfa.noise_profile (Edfa.interpol_params, Edfa.propagate)"""
|
||||
edfa = setup_edfa
|
||||
frequencies = np.array([c.frequency for c in si.carriers])
|
||||
pin = np.array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers])
|
||||
baud_rates = np.array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
edfa.params.nf_model_enabled = False
|
||||
edfa.interpol_params(frequencies, pin, baud_rates)
|
||||
nf = edfa.nf
|
||||
pin = lin2db(pin[0]*1e3)
|
||||
osnr_expected = pin - nf[0] + 58
|
||||
|
||||
si = edfa(si)
|
||||
pout = np.array([c.power.signal for c in si.carriers])
|
||||
pase = np.array([c.power.ase for c in si.carriers])
|
||||
osnr = lin2db(pout[0] / pase[0]) - lin2db(12.5e9/bw)
|
||||
dif = abs(osnr - osnr_expected)
|
||||
|
||||
trx = setup_trx
|
||||
si = trx(si)
|
||||
osnr = trx.osnr_ase_01nm[0]
|
||||
dif = dif + abs(osnr - osnr_expected)
|
||||
|
||||
assert dif < 0.01
|
||||
120
tests/compare.py
Normal file
120
tests/compare.py
Normal file
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python3
|
||||
from json import load, dump
|
||||
from pathlib import Path
|
||||
from argparse import ArgumentParser
|
||||
from collections import namedtuple
|
||||
|
||||
class Results(namedtuple('Results', 'missing extra different expected actual')):
|
||||
def _asdict(self):
|
||||
return {'missing': self.missing,
|
||||
'extra': self.extra,
|
||||
'different': self.different}
|
||||
def __str__(self):
|
||||
rv = []
|
||||
if self.missing:
|
||||
rv.append('Missing: {len(self.missing)}/{len(self.expected)}')
|
||||
rv.extend(f'\t{x}' for x in sorted(self.missing))
|
||||
if self.extra:
|
||||
rv.append('Extra: {len(self.extra)}/{len(self.expected)}')
|
||||
rv.extend(f'\t{x}' for x in sorted(self.extra))
|
||||
if self.different:
|
||||
rv.append('Different: {len(self.different)}/{len(self.expected)}')
|
||||
rv.extend(f'\tExpected: {x}\n\tActual: {y}' for x, y in self.different)
|
||||
if not self.missing and not self.extra and not self.different:
|
||||
rv.append('All match!')
|
||||
return '\n'.join(rv)
|
||||
|
||||
class NetworksResults(namedtuple('NetworksResult', 'elements connections')):
|
||||
def _asdict(self):
|
||||
return {'elements': self.elements._asdict(),
|
||||
'connections': self.connections._asdict()}
|
||||
def __str__(self):
|
||||
return '\n'.join([
|
||||
'Elements'.center(40, '='),
|
||||
str(self.elements),
|
||||
'Connections'.center(40, '='),
|
||||
str(self.connections),
|
||||
])
|
||||
|
||||
class ServicesResults(namedtuple('ServicesResult', 'requests synchronizations')):
|
||||
def _asdict(self):
|
||||
return {'requests': self.requests.asdict(),
|
||||
'synchronizations': self.synchronizations.asdict()}
|
||||
def __str__(self):
|
||||
return '\n'.join([
|
||||
'Requests'.center(40, '='),
|
||||
str(self.requests),
|
||||
'Synchronizations'.center(40, '='),
|
||||
str(self.synchronizations),
|
||||
])
|
||||
|
||||
class PathsResults(namedtuple('PathsResults', 'paths')):
|
||||
def _asdict(self):
|
||||
return {'paths': self.paths.asdict()}
|
||||
def __str__(self):
|
||||
return '\n'.join([
|
||||
'Paths'.center(40, '='),
|
||||
str(self.paths),
|
||||
])
|
||||
|
||||
def compare(expected, actual, key=lambda x: x):
|
||||
expected = {key(el): el for el in expected}
|
||||
actual = {key(el): el for el in actual}
|
||||
missing = set(expected) - set(actual)
|
||||
extra = set(actual) - set(expected)
|
||||
different = [(expected[x], actual[x]) for
|
||||
x in set(expected) & set(actual)
|
||||
if expected[x] != actual[x]]
|
||||
return Results(missing, extra, different, expected, actual)
|
||||
|
||||
def compare_networks(expected, actual):
|
||||
elements = compare(expected['elements'], actual['elements'],
|
||||
key=lambda el: el['uid'])
|
||||
connections = compare(expected['connections'], actual['connections'],
|
||||
key=lambda el: (el['from_node'], el['to_node']))
|
||||
return NetworksResults(elements, connections)
|
||||
|
||||
def compare_services(expected, actual):
|
||||
requests = compare(expected['path-request'], actual['path-request'],
|
||||
key=lambda el: el['request-id'])
|
||||
synchronizations = compare(expected['synchronisation'], actual['synchronisation'],
|
||||
key=lambda el: el['synchonization-id'])
|
||||
return ServicesResults(requests, synchronizations)
|
||||
|
||||
def compare_paths(expected_output, actual_output):
|
||||
paths = compare(expected['path'], actual['path'], key=lambda el: el['path-id'])
|
||||
return PathsResults(paths)
|
||||
|
||||
COMPARISONS = {
|
||||
'networks': compare_networks,
|
||||
'services': compare_services,
|
||||
'paths': compare_paths,
|
||||
}
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('expected_output', type=Path, metavar='FILE')
|
||||
parser.add_argument('actual_output', type=Path, metavar='FILE')
|
||||
parser.add_argument('-o', '--output', default=None)
|
||||
parser.add_argument('-c', '--comparison', choices=COMPARISONS, default='networks')
|
||||
|
||||
def encode_sets(obj):
|
||||
if isinstance(obj, set):
|
||||
return list(obj)
|
||||
raise TypeError(f'{obj!r} is not JSON serializable!')
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.expected_output) as f:
|
||||
expected = load(f)
|
||||
|
||||
with open(args.actual_output) as f:
|
||||
actual = load(f)
|
||||
|
||||
result = COMPARISONS[args.comparison](expected, actual)
|
||||
|
||||
if args.output:
|
||||
with open(args.output, 'w') as f:
|
||||
dump(result, f, default=encode_sets, indent=2)
|
||||
else:
|
||||
print(str(result))
|
||||
BIN
tests/data/CORONET_Global_Topology.xls
Normal file
BIN
tests/data/CORONET_Global_Topology.xls
Normal file
Binary file not shown.
8318
tests/data/CORONET_Global_Topology_expected.json
Normal file
8318
tests/data/CORONET_Global_Topology_expected.json
Normal file
File diff suppressed because it is too large
Load Diff
296
tests/data/default_edfa_config.json
Normal file
296
tests/data/default_edfa_config.json
Normal file
@@ -0,0 +1,296 @@
|
||||
{
|
||||
"nf_ripple": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"gain_ripple": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"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
|
||||
]
|
||||
}
|
||||
124
tests/data/eqpt_config.json
Normal file
124
tests/data/eqpt_config.json
Normal file
@@ -0,0 +1,124 @@
|
||||
{ "Edfa":[{
|
||||
"type_variety": "CienaDB_medium_gain",
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "std_medium_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 26,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"nf_min": 6,
|
||||
"nf_max": 10,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "std_low_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 16,
|
||||
"gain_min": 8,
|
||||
"p_max": 21,
|
||||
"nf_min": 7,
|
||||
"nf_max": 11,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "test",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"nf_min": 5.8,
|
||||
"nf_max": 10,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "test_fixed_gain",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 21,
|
||||
"gain_min": 20,
|
||||
"p_max": 21,
|
||||
"nf0": 5,
|
||||
"allowed_for_design": true
|
||||
}
|
||||
],
|
||||
"Fiber":[{
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127
|
||||
}
|
||||
],
|
||||
"Spans":[{
|
||||
"power_mode": true,
|
||||
"delta_power_range_db": [0,0,1],
|
||||
"max_length": 150,
|
||||
"length_units": "km",
|
||||
"max_loss": 28,
|
||||
"padding": 10,
|
||||
"EOL": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
}
|
||||
],
|
||||
"Roadms":[{
|
||||
"gain_mode_default_loss": 20,
|
||||
"power_mode_pref": -20
|
||||
}],
|
||||
"SI":[{
|
||||
"f_min": 191.3e12,
|
||||
"f_max":196.1e12,
|
||||
"baud_rate": 32e9,
|
||||
"spacing": 50e9,
|
||||
"power_dbm": 0,
|
||||
"power_range_db": [0,0.5,0.5],
|
||||
"roll_off": 0.15,
|
||||
"OSNR": 15,
|
||||
"bit_rate":100e9
|
||||
}],
|
||||
"Transceiver":[
|
||||
{
|
||||
"type_variety": "vendorA_trx-type1",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
"format": "PS_SP64_1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 11,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15
|
||||
},
|
||||
{
|
||||
"format": "PS_SP64_2",
|
||||
"baud_rate": 64e9,
|
||||
"OSNR": 15,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type_variety": "Voyager_16QAM",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
"format": "16QAM",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 19,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
BIN
tests/data/excelTestFile.xls
Normal file
BIN
tests/data/excelTestFile.xls
Normal file
Binary file not shown.
694
tests/data/excelTestFile_expected.json
Normal file
694
tests/data/excelTestFile_expected.json
Normal file
@@ -0,0 +1,694 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Corlay)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Loudeac)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Lorient_KMA)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F01",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Rennes_STA)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Morlaix)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Brest_KLA)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Lannion_CAS)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Corlay)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Loudeac)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F01",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Rennes_STA \u2192 Stbrieuc)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Lannion_CAS)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Brest_KLA \u2192 Morlaix)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress edfa in Lannion_CAS to Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"tilt_target": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress edfa in Lannion_CAS to Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"tilt_target": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress edfa in Lannion_CAS to Stbrieuc",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"tilt_target": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress edfa in Corlay to Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"tilt_target": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "egress edfa in Lannion_CAS to Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "egress edfa in Lannion_CAS to Corlay",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Corlay)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Lannion_CAS)-",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "egress edfa in Lannion_CAS to Stbrieuc"
|
||||
},
|
||||
{
|
||||
"from_node": "egress edfa in Lannion_CAS to Stbrieuc",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "egress edfa in Lannion_CAS to Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "egress edfa in Lannion_CAS to Morlaix",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Morlaix)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Lannion_CAS)-",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Corlay)-",
|
||||
"to_node": "ingress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Loudeac)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Corlay)-",
|
||||
"to_node": "egress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Lannion_CAS)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Loudeac)-",
|
||||
"to_node": "ingress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Lorient_KMA)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Loudeac)-",
|
||||
"to_node": "egress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Corlay)-"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Loudeac)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Lorient_KMA)-",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F01"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F01",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F01"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F01",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Morlaix)-",
|
||||
"to_node": "fiber (Morlaix \u2192 Brest_KLA)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Brest_KLA \u2192 Morlaix)-",
|
||||
"to_node": "fiber (Morlaix \u2192 Lannion_CAS)-"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "fiber (Brest_KLA \u2192 Morlaix)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Brest_KLA)-",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lannion_CAS",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "trx Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lorient_KMA",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "trx Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Vannes_KBE",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "trx Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Rennes_STA",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "trx Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Brest_KLA",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "trx Brest_KLA"
|
||||
}
|
||||
]
|
||||
}
|
||||
82
tests/data/excelTestFile_services_expected.json
Normal file
82
tests/data/excelTestFile_services_expected.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "0",
|
||||
"source": "BREST_KLA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx BREST_KLA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.001
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "1",
|
||||
"source": "Lorient_KMA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.001
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronisation": [
|
||||
{
|
||||
"synchonization-id": "0",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"0",
|
||||
"1"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchonization-id": "1",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"1",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
tests/data/meshTopologyExampleV2.xls
Normal file
BIN
tests/data/meshTopologyExampleV2.xls
Normal file
Binary file not shown.
BIN
tests/data/meshTopologyExampleV2Eqpt.xls
Normal file
BIN
tests/data/meshTopologyExampleV2Eqpt.xls
Normal file
Binary file not shown.
804
tests/data/meshTopologyExampleV2Eqpt_expected.json
Normal file
804
tests/data/meshTopologyExampleV2Eqpt_expected.json
Normal file
@@ -0,0 +1,804 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 4.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 4.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx toto",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "toto",
|
||||
"region": "",
|
||||
"latitude": 6.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx tata",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "tata",
|
||||
"region": "",
|
||||
"latitude": 7.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 4.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 4.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm toto",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "toto",
|
||||
"region": "",
|
||||
"latitude": 6.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm tata",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "tata",
|
||||
"region": "",
|
||||
"latitude": 7.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Morlaix",
|
||||
"region": "RLD",
|
||||
"latitude": 3.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Morlaix",
|
||||
"region": "RLD",
|
||||
"latitude": 3.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Corlay)-F061",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Loudeac)-F010",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Brest_KLA)-F060",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 3.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (toto \u2192 tata)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 6.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Lannion_CAS)-F061",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Corlay)-F010",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Brest_KLA \u2192 Morlaix)-F060",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 3.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (tata \u2192 toto)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 6.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress edfa in Lannion_CAS to Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"tilt_target": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress edfa in Lorient_KMA to Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"tilt_target": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "egress edfa in Lannion_CAS to Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "egress edfa in Lannion_CAS to Corlay",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Corlay)-F061"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Lannion_CAS)-F061",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Corlay)-F061",
|
||||
"to_node": "ingress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Loudeac)-F010"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Corlay)-F010",
|
||||
"to_node": "egress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Lannion_CAS)-F061"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Loudeac)-F010",
|
||||
"to_node": "ingress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
|
||||
"to_node": "egress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Corlay)-F010"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "egress edfa in Lorient_KMA to Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "egress edfa in Lorient_KMA to Loudeac",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
|
||||
"to_node": "ingress fused spans in Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Morlaix",
|
||||
"to_node": "fiber (Morlaix \u2192 Brest_KLA)-F060"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Brest_KLA \u2192 Morlaix)-F060",
|
||||
"to_node": "egress fused spans in Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Morlaix",
|
||||
"to_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "fiber (Brest_KLA \u2192 Morlaix)-F060"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Brest_KLA)-F060",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm toto",
|
||||
"to_node": "fiber (toto \u2192 tata)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (tata \u2192 toto)-",
|
||||
"to_node": "roadm toto"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm tata",
|
||||
"to_node": "fiber (tata \u2192 toto)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (toto \u2192 tata)-",
|
||||
"to_node": "roadm tata"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lannion_CAS",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "trx Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lorient_KMA",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "trx Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Vannes_KBE",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "trx Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Rennes_STA",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "trx Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Brest_KLA",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "trx Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx toto",
|
||||
"to_node": "roadm toto"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm toto",
|
||||
"to_node": "trx toto"
|
||||
},
|
||||
{
|
||||
"from_node": "trx tata",
|
||||
"to_node": "roadm tata"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm tata",
|
||||
"to_node": "trx tata"
|
||||
}
|
||||
]
|
||||
}
|
||||
219
tests/data/meshTopologyExampleV2Eqpt_services_expected.json
Normal file
219
tests/data/meshTopologyExampleV2Eqpt_services_expected.json
Normal file
@@ -0,0 +1,219 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "0",
|
||||
"source": "Lorient_KMA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "1",
|
||||
"source": "Brest_KLA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx Brest_KLA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": [
|
||||
{
|
||||
"index": 0,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "Lannion_CAS",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "Lorient_KMA",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "3",
|
||||
"source": "Lannion_CAS",
|
||||
"destination": "Rennes_STA",
|
||||
"src-tp-id": "trx Lannion_CAS",
|
||||
"dst-tp-id": "trx Rennes_STA",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "4",
|
||||
"source": "Rennes_STA",
|
||||
"destination": "Lannion_CAS",
|
||||
"src-tp-id": "trx Rennes_STA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": 64,
|
||||
"output-power": 0.0019952623149688794
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "5",
|
||||
"source": "Lorient_KMA",
|
||||
"destination": "Lannion_CAS",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": [
|
||||
{
|
||||
"index": 0,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "toto",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronisation": [
|
||||
{
|
||||
"synchonization-id": "0",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"0",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchonization-id": "3",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"3",
|
||||
"4"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchonization-id": "5",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"5",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
762
tests/data/meshTopologyExampleV2_expected.json
Normal file
762
tests/data/meshTopologyExampleV2_expected.json
Normal file
@@ -0,0 +1,762 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 4.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 4.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx toto",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "toto",
|
||||
"region": "",
|
||||
"latitude": 6.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx tata",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "tata",
|
||||
"region": "",
|
||||
"latitude": 7.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 4.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 4.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm toto",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "toto",
|
||||
"region": "",
|
||||
"latitude": 6.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm tata",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "tata",
|
||||
"region": "",
|
||||
"latitude": 7.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Morlaix",
|
||||
"region": "RLD",
|
||||
"latitude": 3.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Morlaix",
|
||||
"region": "RLD",
|
||||
"latitude": 3.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Corlay)-F061",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Loudeac)-F010",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Brest_KLA)-F060",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 3.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (toto \u2192 tata)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 6.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Lannion_CAS)-F061",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Corlay)-F010",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Brest_KLA \u2192 Morlaix)-F060",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 3.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (tata \u2192 toto)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 6.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Corlay)-F061"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Lannion_CAS)-F061",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Corlay)-F061",
|
||||
"to_node": "ingress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Loudeac)-F010"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Corlay)-F010",
|
||||
"to_node": "egress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Lannion_CAS)-F061"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Loudeac)-F010",
|
||||
"to_node": "ingress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
|
||||
"to_node": "egress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Corlay)-F010"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
|
||||
"to_node": "ingress fused spans in Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Morlaix",
|
||||
"to_node": "fiber (Morlaix \u2192 Brest_KLA)-F060"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Brest_KLA \u2192 Morlaix)-F060",
|
||||
"to_node": "egress fused spans in Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Morlaix",
|
||||
"to_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "fiber (Brest_KLA \u2192 Morlaix)-F060"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Brest_KLA)-F060",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm toto",
|
||||
"to_node": "fiber (toto \u2192 tata)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (tata \u2192 toto)-",
|
||||
"to_node": "roadm toto"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm tata",
|
||||
"to_node": "fiber (tata \u2192 toto)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (toto \u2192 tata)-",
|
||||
"to_node": "roadm tata"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lannion_CAS",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "trx Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lorient_KMA",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "trx Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Vannes_KBE",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "trx Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Rennes_STA",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "trx Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Brest_KLA",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "trx Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx toto",
|
||||
"to_node": "roadm toto"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm toto",
|
||||
"to_node": "trx toto"
|
||||
},
|
||||
{
|
||||
"from_node": "trx tata",
|
||||
"to_node": "roadm tata"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm tata",
|
||||
"to_node": "trx tata"
|
||||
}
|
||||
]
|
||||
}
|
||||
219
tests/data/meshTopologyExampleV2_services_expected.json
Normal file
219
tests/data/meshTopologyExampleV2_services_expected.json
Normal file
@@ -0,0 +1,219 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "0",
|
||||
"source": "Lorient_KMA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "1",
|
||||
"source": "Brest_KLA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx Brest_KLA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": [
|
||||
{
|
||||
"index": 0,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "Lannion_CAS",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "Lorient_KMA",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "3",
|
||||
"source": "Lannion_CAS",
|
||||
"destination": "Rennes_STA",
|
||||
"src-tp-id": "trx Lannion_CAS",
|
||||
"dst-tp-id": "trx Rennes_STA",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "4",
|
||||
"source": "Rennes_STA",
|
||||
"destination": "Lannion_CAS",
|
||||
"src-tp-id": "trx Rennes_STA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": 64,
|
||||
"output-power": 0.0019952623149688794
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "5",
|
||||
"source": "Lorient_KMA",
|
||||
"destination": "Lannion_CAS",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": [
|
||||
{
|
||||
"index": 0,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "toto",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronisation": [
|
||||
{
|
||||
"synchonization-id": "0",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"0",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchonization-id": "3",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"3",
|
||||
"4"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchonization-id": "5",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"5",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
301
tests/data/std_medium_gain_advanced_config.json
Normal file
301
tests/data/std_medium_gain_advanced_config.json
Normal file
@@ -0,0 +1,301 @@
|
||||
{ "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
|
||||
],
|
||||
"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
|
||||
]
|
||||
}
|
||||
117
tests/data/test_network.json
Normal file
117
tests/data/test_network.json
Normal file
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"network_name": "EDFA Example Network - P2P",
|
||||
"elements": [{
|
||||
"uid": "Site_A",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Site A",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Span1",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km"
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"region": "",
|
||||
"latitude": 1,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 16,
|
||||
"tilt_target": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"region": "",
|
||||
"latitude": 2,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Span2",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km"
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"region": "",
|
||||
"latitude": 1,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa2",
|
||||
"type": "Edfa",
|
||||
"type_variety": "test_fixed_gain",
|
||||
"operational": {
|
||||
"gain_target": 16,
|
||||
"tilt_target": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"region": "",
|
||||
"latitude": 2,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Site_B",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Site B",
|
||||
"region": "",
|
||||
"latitude": 3,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
],
|
||||
"connections": [{
|
||||
"from_node": "Site_A",
|
||||
"to_node": "Span1"
|
||||
},
|
||||
{
|
||||
"from_node": "Span1",
|
||||
"to_node": "Edfa1"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa1",
|
||||
"to_node": "Span2"
|
||||
},
|
||||
{
|
||||
"from_node": "Span2",
|
||||
"to_node": "Edfa2"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa2",
|
||||
"to_node": "Site_B"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
@@ -1,313 +0,0 @@
|
||||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
}
|
||||
153
tests/test_amplifier.py
Normal file
153
tests/test_amplifier.py
Normal file
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Author: Jean-Luc Auge
|
||||
# @Date: 2018-02-02 14:06:55
|
||||
|
||||
from gnpy.core.elements import Edfa
|
||||
from numpy import zeros, array
|
||||
from json import load, dumps
|
||||
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
|
||||
from gnpy.core.network import build_network, load_network, set_roadm_loss
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
TEST_DIR = Path(__file__).parent
|
||||
DATA_DIR = TEST_DIR / 'data'
|
||||
test_network = DATA_DIR / 'test_network.json'
|
||||
eqpt_library = DATA_DIR / 'eqpt_config.json'
|
||||
|
||||
# TODO in elements.py code: pytests doesn't pass with 1 channel: interpolate fail
|
||||
@pytest.fixture(
|
||||
params=[(96, 0.05e12), (60, 0.075e12), (45, 0.1e12), (2, 0.1e12)],
|
||||
ids=['50GHz spacing', '75GHz spacing', '100GHz spacing', '2 channels'])
|
||||
def nch_and_spacing(request):
|
||||
"""parametrize channel count vs channel spacing (Hz)"""
|
||||
yield request.param
|
||||
|
||||
@pytest.fixture()
|
||||
def bw():
|
||||
"""parametrize signal bandwidth (Hz)"""
|
||||
return 45e9
|
||||
|
||||
@pytest.fixture()
|
||||
def setup_edfa_variable_gain():
|
||||
"""init edfa class by reading test_network.json file
|
||||
remove all gain and nf ripple"""
|
||||
equipment = load_equipment(eqpt_library)
|
||||
network = load_network(test_network, equipment)
|
||||
build_network(network, equipment,0, 20)
|
||||
edfa = [n for n in network.nodes() if isinstance(n, Edfa)][0]
|
||||
edfa.gain_ripple = zeros(96)
|
||||
edfa.interpol_nf_ripple = zeros(96)
|
||||
yield edfa
|
||||
|
||||
@pytest.fixture()
|
||||
def setup_edfa_fixed_gain():
|
||||
"""init edfa class by reading the 2nd edfa in test_network.json file"""
|
||||
equipment = load_equipment(eqpt_library)
|
||||
network = load_network(test_network, equipment)
|
||||
build_network(network, equipment, 0, 20)
|
||||
edfa = [n for n in network.nodes() if isinstance(n, Edfa)][1]
|
||||
yield edfa
|
||||
|
||||
@pytest.fixture()
|
||||
def setup_trx():
|
||||
"""init transceiver class to access snr and osnr calculations"""
|
||||
equipment = load_equipment(eqpt_library)
|
||||
network = load_network(test_network, equipment)
|
||||
build_network(network, equipment, 0, 20)
|
||||
trx = [n for n in network.nodes() if isinstance(n, Transceiver)][0]
|
||||
return trx
|
||||
|
||||
@pytest.fixture()
|
||||
def si(nch_and_spacing, bw):
|
||||
"""parametrize a channel comb with nb_channel, spacing and signal bw"""
|
||||
nb_channel, spacing = nch_and_spacing
|
||||
return create_input_spectral_information(191.3e12, 0.15, bw, 1e-3, spacing, nb_channel)
|
||||
|
||||
@pytest.mark.parametrize("gain, nf_expected", [(10, 15), (15, 10), (25, 5.8)])
|
||||
def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si):
|
||||
"""=> unitary test for variable gain model Edfa._calc_nf() (and Edfa.interpol_params)"""
|
||||
edfa = setup_edfa_variable_gain
|
||||
frequencies = array([c.frequency for c in si.carriers])
|
||||
pin = array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers])
|
||||
pin = pin/db2lin(gain)
|
||||
baud_rates = array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
pref = Pref(0, -gain)
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
result = edfa.nf
|
||||
assert pytest.approx(nf_expected, abs=0.01) == result[0]
|
||||
|
||||
@pytest.mark.parametrize("gain, nf_expected", [(15, 10), (20, 5), (25, 5)])
|
||||
def test_fixed_gain_nf(gain, nf_expected, setup_edfa_fixed_gain, si):
|
||||
"""=> unitary test for fixed gain model Edfa._calc_nf() (and Edfa.interpol_params)"""
|
||||
edfa = setup_edfa_fixed_gain
|
||||
frequencies = array([c.frequency for c in si.carriers])
|
||||
pin = array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers])
|
||||
pin = pin/db2lin(gain)
|
||||
baud_rates = array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
pref = Pref(0, -gain)
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
|
||||
assert pytest.approx(nf_expected, abs=0.01) == edfa.nf[0]
|
||||
|
||||
def test_si(si, nch_and_spacing):
|
||||
"""basic total power check of the channel comb generation"""
|
||||
nb_channel = nch_and_spacing[0]
|
||||
pin = array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers])
|
||||
p_tot = pin.sum()
|
||||
expected_p_tot = si.carriers[0].power.signal * nb_channel
|
||||
assert pytest.approx(expected_p_tot, abs=0.01) == p_tot
|
||||
|
||||
@pytest.mark.parametrize("gain", [17, 19, 21, 23])
|
||||
def test_compare_nf_models(gain, setup_edfa_variable_gain, si):
|
||||
""" compare the 2 amplifier models (polynomial and estimated from nf_min and max)
|
||||
=> nf_model vs nf_poly_fit for intermediate gain values:
|
||||
between gain_min and gain_flatmax some discrepancy is expected but target < 0.5dB
|
||||
=> unitary test for Edfa._calc_nf (and Edfa.interpol_params)"""
|
||||
edfa = setup_edfa_variable_gain
|
||||
frequencies = array([c.frequency for c in si.carriers])
|
||||
pin = array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers])
|
||||
pin = pin/db2lin(gain)
|
||||
baud_rates = array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
pref = Pref(0, -gain)
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
nf_model = edfa.nf[0]
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
nf_poly = edfa.nf[0]
|
||||
assert pytest.approx(nf_model, abs=0.5) == nf_poly
|
||||
|
||||
@pytest.mark.parametrize("gain", [13, 15, 17, 19, 21, 23, 25, 27])
|
||||
def test_ase_noise(gain, si, setup_edfa_variable_gain, setup_trx, bw):
|
||||
"""testing 3 different ways of calculating osnr:
|
||||
1-pin-edfa.nf+58 vs
|
||||
2-pout/pase afet propagate
|
||||
3-Transceiver osnr_ase_01nm
|
||||
=> unitary test for Edfa.noise_profile (Edfa.interpol_params, Edfa.propagate)"""
|
||||
edfa = setup_edfa_variable_gain
|
||||
frequencies = array([c.frequency for c in si.carriers])
|
||||
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)
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
nf = edfa.nf
|
||||
pin = lin2db(pin[0]*1e3)
|
||||
osnr_expected = pin - nf[0] + 58
|
||||
|
||||
si = edfa(si)
|
||||
pout = array([c.power.signal for c in si.carriers])
|
||||
pase = array([c.power.ase for c in si.carriers])
|
||||
osnr = lin2db(pout[0] / pase[0]) - lin2db(12.5e9/bw)
|
||||
assert pytest.approx(osnr_expected, abs=0.01) == osnr
|
||||
|
||||
trx = setup_trx
|
||||
si = trx(si)
|
||||
osnr = trx.osnr_ase_01nm[0]
|
||||
assert pytest.approx(osnr_expected, abs=0.01) == osnr
|
||||
@@ -1,63 +0,0 @@
|
||||
{
|
||||
"network_name": "EDFA Example Network - P2P",
|
||||
"elements": [{
|
||||
"uid": "Site A",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Span1",
|
||||
"type": "Fiber",
|
||||
"params": {
|
||||
"length": 80,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"dispersion": 16.7E-6,
|
||||
"gamma": 1.27E-3
|
||||
},
|
||||
"metadata": {
|
||||
"latitude": 1,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa1",
|
||||
"type": "Edfa",
|
||||
"operational": {
|
||||
"gain_target": 16,
|
||||
"tilt_target": 0
|
||||
},
|
||||
"config_from_json": "tests/edfa_config.json",
|
||||
"metadata": {
|
||||
"latitude": 2,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Site B",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"latitude": 3,
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
|
||||
],
|
||||
"connections": [{
|
||||
"from_node": "Site A",
|
||||
"to_node": "Span1"
|
||||
},
|
||||
{
|
||||
"from_node": "Span1",
|
||||
"to_node": "Edfa1"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa1",
|
||||
"to_node": "Site B"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
81
tests/test_parser.py
Normal file
81
tests/test_parser.py
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Author: Esther Le Rouzic
|
||||
# @Date: 2018-06-15
|
||||
|
||||
from gnpy.core.elements import Edfa
|
||||
import numpy as np
|
||||
from json import load
|
||||
import pytest
|
||||
from gnpy.core import network_from_json
|
||||
from gnpy.core.elements import Transceiver, Fiber, Edfa
|
||||
from gnpy.core.utils import lin2db, db2lin
|
||||
from gnpy.core.info import SpectralInformation, Channel, Power
|
||||
from tests.compare import compare_networks, compare_services
|
||||
from gnpy.core.convert import convert_file
|
||||
from gnpy.core.service_sheet import convert_service_sheet
|
||||
from pathlib import Path
|
||||
import filecmp
|
||||
from os import unlink
|
||||
|
||||
TEST_DIR = Path(__file__).parent
|
||||
DATA_DIR = TEST_DIR / 'data'
|
||||
eqpt_filename = DATA_DIR / 'eqpt_config.json'
|
||||
|
||||
# adding tests to check the parser non regression
|
||||
# convention of naming of test files:
|
||||
#
|
||||
# - ..._expected.json for the reference output
|
||||
|
||||
@pytest.mark.parametrize('xls_input,expected_json_output', {
|
||||
DATA_DIR / 'excelTestFile.xls': DATA_DIR / 'excelTestFile_expected.json',
|
||||
DATA_DIR / 'CORONET_Global_Topology.xls': DATA_DIR / 'CORONET_Global_Topology_expected.json',
|
||||
DATA_DIR / 'meshTopologyExampleV2.xls': DATA_DIR / 'meshTopologyExampleV2_expected.json',
|
||||
DATA_DIR / 'meshTopologyExampleV2Eqpt.xls': DATA_DIR / 'meshTopologyExampleV2Eqpt_expected.json',
|
||||
}.items())
|
||||
def test_excel_json_generation(xls_input, expected_json_output):
|
||||
convert_file(xls_input)
|
||||
|
||||
actual_json_output = xls_input.with_suffix('.json')
|
||||
with open(actual_json_output) as f:
|
||||
actual = load(f)
|
||||
unlink(actual_json_output)
|
||||
|
||||
with open(expected_json_output) as f:
|
||||
expected = load(f)
|
||||
|
||||
results = compare_networks(expected, actual)
|
||||
assert not results.elements.missing
|
||||
assert not results.elements.extra
|
||||
assert not results.elements.different
|
||||
assert not results.connections.missing
|
||||
assert not results.connections.extra
|
||||
assert not results.connections.different
|
||||
|
||||
# assume json entries
|
||||
# test that the build network gives correct results
|
||||
# TODO !!
|
||||
|
||||
@pytest.mark.parametrize('xls_input,expected_json_output', {
|
||||
DATA_DIR / 'excelTestFile.xls': DATA_DIR / 'excelTestFile_services_expected.json',
|
||||
DATA_DIR / 'meshTopologyExampleV2.xls': DATA_DIR / 'meshTopologyExampleV2_services_expected.json',
|
||||
DATA_DIR / 'meshTopologyExampleV2Eqpt.xls': DATA_DIR / 'meshTopologyExampleV2Eqpt_services_expected.json',
|
||||
}.items())
|
||||
def test_excel_service_json_generation(xls_input, expected_json_output):
|
||||
convert_service_sheet(xls_input, eqpt_filename)
|
||||
|
||||
actual_json_output = f'{str(xls_input)[:-4]}_services.json'
|
||||
with open(actual_json_output) as f:
|
||||
actual = load(f)
|
||||
unlink(actual_json_output)
|
||||
|
||||
with open(expected_json_output) as f:
|
||||
expected = load(f)
|
||||
|
||||
results = compare_services(expected, actual)
|
||||
assert not results.requests.missing
|
||||
assert not results.requests.extra
|
||||
assert not results.requests.different
|
||||
assert not results.synchronizations.missing
|
||||
assert not results.synchronizations.extra
|
||||
assert not results.synchronizations.different
|
||||
101
tests/test_propagation.py
Normal file
101
tests/test_propagation.py
Normal file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Author: Jean-Luc Auge
|
||||
# @Date: 2018-02-02 14:06:55
|
||||
|
||||
from gnpy.core.elements import Edfa
|
||||
import numpy as np
|
||||
from json import load, dumps
|
||||
import pytest
|
||||
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
|
||||
from gnpy.core.equipment import load_equipment
|
||||
from gnpy.core.network import build_network, load_network
|
||||
from pathlib import Path
|
||||
from networkx import dijkstra_path
|
||||
from numpy import mean
|
||||
|
||||
#network_file_name = 'tests/test_network.json'
|
||||
network_file_name = Path(__file__).parent.parent / 'tests/LinkforTest.json'
|
||||
#network_file_name = Path(__file__).parent.parent / 'examples/edfa_example_network.json'
|
||||
eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json'
|
||||
|
||||
@pytest.fixture(params=[(96, 0.05e12), (60, 0.075e12), (45, 0.1e12), (2, 0.1e12)],
|
||||
ids=['50GHz spacing', '75GHz spacing', '100GHz spacing', '2 channels'])
|
||||
# TODO in elements.py code: pytests doesn't pass with 1 channel: interpolate fail
|
||||
def nch_and_spacing(request):
|
||||
"""parametrize channel count vs channel spacing (Hz)"""
|
||||
yield request.param
|
||||
|
||||
def propagation(input_power, con_in, con_out,dest):
|
||||
equipment = load_equipment(eqpt_library_name)
|
||||
network = load_network(network_file_name,equipment)
|
||||
build_network(network, equipment, 0, 20)
|
||||
|
||||
# parametrize the network elements with the con losses and adapt gain
|
||||
# (assumes all spans are identical)
|
||||
for e in network.nodes():
|
||||
if isinstance(e, Fiber):
|
||||
loss = e.loss_coef * e.length
|
||||
e.con_in = con_in
|
||||
e.con_out = con_out
|
||||
if isinstance(e, Edfa):
|
||||
e.operational.gain_target = loss + con_in + con_out
|
||||
|
||||
transceivers = {n.uid: n for n in network.nodes() if isinstance(n, Transceiver)}
|
||||
|
||||
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)
|
||||
])
|
||||
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)
|
||||
for el in path:
|
||||
si = el(si)
|
||||
print(el) # remove this line when sweeping across several powers
|
||||
edfa_sample = next(el for el in path if isinstance(el, Edfa))
|
||||
nf = mean(edfa_sample.nf)
|
||||
|
||||
print(f'pw: {input_power} conn in: {con_in} con out: {con_out}',
|
||||
f'OSNR@0.1nm: {round(mean(sink.osnr_ase_01nm),2)}',
|
||||
f'SNR@bandwitdth: {round(mean(sink.snr),2)}')
|
||||
return sink , nf
|
||||
|
||||
test = {'a':(-1,1,0),'b':(-1,1,1),'c':(0,1,0),'d':(1,1,1)}
|
||||
expected = {'a':(-2,0,0),'b':(-2,0,1),'c':(-1,0,0),'d':(0,0,1)}
|
||||
|
||||
@pytest.mark.parametrize("dest",['trx B','trx F'])
|
||||
@pytest.mark.parametrize("osnr_test", ['a','b','c','d'])
|
||||
def test_snr(osnr_test, dest):
|
||||
pw = test[osnr_test][0]
|
||||
conn_in = test[osnr_test][1]
|
||||
conn_out =test[osnr_test][2]
|
||||
sink,nf = propagation(pw,conn_in,conn_out,dest)
|
||||
osnr = round(mean(sink.osnr_ase),3)
|
||||
nli = 1.0/db2lin(round(mean(sink.snr),3)) - 1.0/db2lin(osnr)
|
||||
pw = expected[osnr_test][0]
|
||||
conn_in = expected[osnr_test][1]
|
||||
conn_out = expected[osnr_test][2]
|
||||
sink,exp_nf = propagation(pw,conn_in,conn_out,dest)
|
||||
expected_osnr = round(mean(sink.osnr_ase),3)
|
||||
expected_nli = 1.0/db2lin(round(mean(sink.snr),3)) - 1.0/db2lin(expected_osnr)
|
||||
# compare OSNR taking into account nf change of amps
|
||||
osnr_diff = abs(osnr - expected_osnr + nf - exp_nf)
|
||||
nli_diff = abs((nli-expected_nli)/nli)
|
||||
assert osnr_diff <0.01 and nli_diff<0.01
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from logging import getLogger, basicConfig, INFO
|
||||
logger = getLogger(__name__)
|
||||
basicConfig(level=INFO)
|
||||
|
||||
for a in test :
|
||||
test_snr(a,'trx F')
|
||||
print('\n')
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
import pytest
|
||||
from gnpy.core.utils import db2lin
|
||||
def test_db2lin():
|
||||
assert pytest.approx(10.0)==db2lin(10.0)
|
||||
|
||||
def test_db2lin():
|
||||
assert pytest.approx(10.0) == db2lin(10.0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
from logging import getLogger, basicConfig, INFO
|
||||
@@ -13,4 +13,3 @@ if __name__ == '__main__':
|
||||
|
||||
logger.info(f'Running {test}')
|
||||
test_db2lin()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user