def transmission_main_example(args=None): parser = argparse.ArgumentParser( description= 'Send a full spectrum load through the network from point A to point B', epilog=_help_footer, formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) _add_common_options(parser, network_default=_examples_dir / 'edfa_example_network.json') parser.add_argument('--show-channels', action='store_true', help='Show final per-channel OSNR summary') parser.add_argument('-pl', '--plot', action='store_true') 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('source', nargs='?', help='source node') parser.add_argument('destination', nargs='?', help='destination node') args = parser.parse_args(args if args is not None else sys.argv[1:]) _setup_logging(args) (equipment, network) = load_common_data(args.equipment, args.topology, args.sim_params, args.save_network_before_autodesign) if args.plot: plot_baseline(network) transceivers = { n.uid: n for n in network.nodes() if isinstance(n, Transceiver) } if not transceivers: sys.exit('Network has no transceivers!') if len(transceivers) < 2: sys.exit('Network has only one transceiver!') if args.list_nodes: for uid in transceivers: print(uid) sys.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['bidir'] = False params['nodes_list'] = [destination.uid] params['loose_list'] = ['strict'] params['format'] = '' params['path_bandwidth'] = 0 trx_params = trx_mode_params(equipment) # Randomly generate input power input_power = round(random.random(), 2) input_power = -2 + (input_power * (8)) args.power = input_power if args.power: trx_params['power'] = db2lin(float(args.power)) * 1e-3 params.update(trx_params) req = PathRequest(**params) power_mode = equipment['Span']['default'].power_mode print('\n'.join([ f'Power mode is set to {power_mode}', f'=> it can be modified in eqpt_config.json - Span' ])) pref_ch_db = lin2db(req.power * 1e3) # reference channel power / span (SL=20dB) pref_total_db = pref_ch_db + lin2db( req.nb_channel) # reference total power / span (SL=20dB) try: build_network(network, equipment, pref_ch_db, pref_total_db) except exceptions.NetworkTopologyError as e: print( f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {e}' ) sys.exit(1) except exceptions.ConfigurationError as e: print( f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {e}') sys.exit(1) path = compute_constrained_path(network, req) spans = [ s.params.length for s in path if isinstance(s, RamanFiber) or isinstance(s, Fiber) ] print( f'\nThere are {len(spans)} fiber spans over {sum(spans)/1000:.0f} km between {source.uid} ' f'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] if not power_mode: # power cannot be changed in gain mode power_range = [0] for dp_db in power_range: req.power = db2lin(pref_ch_db + dp_db) * 1e-3 if power_mode: print( f'\nPropagating with input power = {ansi_escapes.cyan}{lin2db(req.power*1e3):.2f} dBm{ansi_escapes.reset}:' ) else: print( f'\nPropagating in {ansi_escapes.cyan}gain mode{ansi_escapes.reset}: power cannot be set manually' ) infos = propagate(path, req, equipment) if len(power_range) == 1: for elem in path: print(elem) if power_mode: print( f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f} dBm:' ) else: print(f'\nTransmission results:') # print('-------------') # print(destination.snr_01nm) # print('-------------') print( f' Final SNR total (0.1 nm): {ansi_escapes.cyan}{mean(destination.snr_01nm):.02f} dB{ansi_escapes.reset}' ) else: print(path[-1]) if args.save_network is not None: save_network(network, args.save_network) print( f'{ansi_escapes.blue}Network (after autodesign) saved to {args.save_network}{ansi_escapes.reset}' ) if args.show_channels: print('\nThe total SNR per channel at the end of the line is:') print('{:>5}{:>26}{:>26}{:>28}{:>28}{:>28}'.format( 'Ch. #', 'Channel frequency (THz)', 'Channel power (dBm)', 'OSNR ASE (signal bw, dB)', 'SNR NLI (signal bw, dB)', 'SNR total (signal bw, dB)')) # print(dir(info)) for final_carrier, ch_osnr, ch_snr_nl, ch_snr in zip( infos.carriers, path[-1].osnr_ase, path[-1].osnr_nli, path[-1].snr): ch_freq = final_carrier.frequency * 1e-12 ch_power = lin2db(final_carrier.power.signal * 1e3) print('{:5}{:26.2f}{:26.2f}{:28.2f}{:28.2f}{:28.2f}'.format( final_carrier.channel_number, round(ch_freq, 2), round(ch_power, 2), round(ch_osnr, 2), round(ch_snr_nl, 2), round(ch_snr, 2))) 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) # MY ADDITION # just to see what the different contributions of ASE and NLI are # return input_power, path[-1].osnr_ase, path[-1].osnr_nli, path[-1].snr # to test Raman return input_power, destination.snr_01nm, mean(destination.snr_01nm)
def test_roadm_target_power(prev_node_type, effective_pch_out_db, power_dbm): ''' Check that egress power of roadm is equal to target power if input power is greater than target power else, that it is equal to input power. Use a simple two hops A-B-C topology for the test where the prev_node in ROADM B is either an amplifier or a fused, so that the target power can not be met in this last case. ''' equipment = load_equipment(EQPT_LIBRARY_NAME) json_network = load_json(TEST_DIR / 'data/twohops_roadm_power_test.json') prev_node = next(n for n in json_network['elements'] if n['uid'] == 'west edfa in node B to ila2') json_network['elements'].remove(prev_node) if prev_node_type == 'edfa': prev_node = {'uid': 'west edfa in node B to ila2', 'type': 'Edfa'} elif prev_node_type == 'fused': prev_node = {'uid': 'west edfa in node B to ila2', 'type': 'Fused'} prev_node['params'] = {'loss': 0} json_network['elements'].append(prev_node) network = network_from_json(json_network, equipment) p_total_db = power_dbm + lin2db( automatic_nch(equipment['SI']['default'].f_min, equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) build_network(network, equipment, power_dbm, p_total_db) params = { 'request_id': 0, 'trx_type': '', 'trx_mode': '', 'source': 'trx node A', 'destination': 'trx node C', 'bidir': False, 'nodes_list': ['trx node C'], 'loose_list': ['strict'], 'format': '', 'path_bandwidth': 100e9, 'effective_freq_slot': None, } trx_params = trx_mode_params(equipment) params.update(trx_params) req = PathRequest(**params) req.power = db2lin(power_dbm - 30) path = compute_constrained_path(network, req) si = create_input_spectral_information(req.f_min, req.f_max, req.roll_off, req.baud_rate, req.power, req.spacing) for i, el in enumerate(path): if isinstance(el, Roadm): carriers_power_in_roadm = min([ c.power.signal + c.power.nli + c.power.ase for c in si.carriers ]) si = el(si, degree=path[i + 1].uid) if el.uid == 'roadm node B': print('input', carriers_power_in_roadm) # if previous was an EDFA, power level at ROADM input is enough for the ROADM to apply its # target power (as specified in equipment ie -20 dBm) # if it is a Fused, the input power to the ROADM is smaller than the target power, and the # ROADM cannot apply this target. In this case, it is assumed that the ROADM has 0 dB loss # so the output power will be the same as the input power, which for this particular case # corresponds to -22dBm + power_dbm # next step (for ROADM modelling) will be to apply a minimum loss for ROADMs ! if prev_node_type == 'edfa': assert el.effective_pch_out_db == effective_pch_out_db if prev_node_type == 'fused': # then output power == input_power == effective_pch_out_db + power_dbm assert effective_pch_out_db + power_dbm == \ pytest.approx(lin2db(carriers_power_in_roadm * 1e3), rel=1e-3) assert el.effective_pch_out_db == effective_pch_out_db + power_dbm for carrier in si.carriers: print(carrier.power.signal + carrier.power.nli + carrier.power.ase) power = carrier.power.signal + carrier.power.nli + carrier.power.ase if prev_node_type == 'edfa': # edfa prev_node sets input power to roadm to a high enough value: # Check that egress power of roadm is equal to target power assert power == pytest.approx( db2lin(effective_pch_out_db - 30), rel=1e-3) elif prev_node_type == 'fused': # fused prev_node does reamplfy power after fiber propagation, so input power # to roadm is low. # Check that egress power of roadm is equalized to the min carrier input power. assert power == pytest.approx(carriers_power_in_roadm, rel=1e-3) else: si = el(si)