#!/usr/bin/env python3 # -*- coding: utf-8 -*- ''' 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 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 linspace, 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 logger = getLogger(__name__) 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() kwargs = {'figure': fig, 'pos': pos} 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: p_start, p_stop, p_step = equipment['SI']['default'].power_range_db p_num = abs(int(round((p_stop - p_start)/p_step))) + 1 if p_step != 0 else 1 power_range = list(linspace(p_start, p_stop, p_num)) 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) #print(f'\n !!!!!!!!!!!!!!!!! TEST POINT !!!!!!!!!!!!!!!!!!!!!') #print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}') # => use "in" or "out" parameter # => use "nli" or "ase" or "signal" or "total" parameter simulation_data.append({ 'Pch_dBm' : pref_ch_db + dp_db, 'OSNR_ASE_0.1nm' : round(mean(destination.osnr_ase_01nm),2), 'OSNR_ASE_signal_bw' : round(mean(destination.osnr_ase),2), 'SNR_nli_signal_bw' : round(mean(destination.osnr_nli),2), 'SNR_total_signal_bw' : round(mean(destination.snr),2) }) write_csv(result_dicts, 'simulation_result.csv') return path parser = ArgumentParser() parser.add_argument('-e', '--equipment', type=Path, default=Path(__file__).parent / 'eqpt_config.json') parser.add_argument('-pl', '--plot', action='store_true') parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence') parser.add_argument('-l', '--list-nodes', action='store_true', help='list all transceiver nodes') parser.add_argument('-po', '--power', default=0, help='channel ref power in dBm') parser.add_argument('-names', '--names-matching', action='store_true', help='display network names that are closed matches') #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() basicConfig(level={0: ERROR, 1: INFO, 2: DEBUG}.get(args.verbose, DEBUG)) equipment = load_equipment(args.equipment) # logger.info(equipment) # print(args.filename) network = load_network(args.filename, equipment, args.names_matching) # 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() #First try to find exact match if source/destination provided if args.source: source = transceivers.pop(args.source, None) valid_source = True if source else False else: source = None logger.info('No source node specified: picking random transceiver') if args.destination: destination = transceivers.pop(args.destination, None) valid_destination = True if destination else False else: destination = None logger.info('No destination node specified: picking random transceiver') #If no exact match try to find partial match if args.source and not source: #TODO code a more advanced regex to find nodes match source = next((transceivers.pop(uid) for uid in transceivers \ if args.source.lower() in uid.lower()), None) if args.destination and not destination: #TODO code a more advanced regex to find nodes match destination = next((transceivers.pop(uid) for uid in transceivers \ if args.destination.lower() in uid.lower()), None) #If no partial match or no source/destination provided pick random if not source: source = list(transceivers.values())[0] del transceivers[source.uid] if not destination: destination = list(transceivers.values())[0] 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'] = '' params['path_bandwidth'] = 0 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 not args.source: print(f'\n(No source node specified: picked {source.uid})') elif not valid_source: print(f'\n(Invalid source node {args.source!r} replaced with {source.uid})') if not args.destination: print(f'\n(No destination node specified: picked {destination.uid})') elif not valid_destination: print(f'\n(Invalid destination node {args.destination!r} replaced with {destination.uid})') if args.plot: plot_results(network, path, source, destination)