SNR Module
Overview
At a Glance
- Purpose:
Signal-to-Noise Ratio measurement and validation for optical networks
- Location:
fusion/modules/snr/- Key Files:
registry.py,snr.py,utils.py- Depends On:
fusion.interfaces.snr,fusion.core.properties- Used By:
fusion.core(via adapters),fusion.pipelines
The SNR module provides signal quality assessment for optical network simulations. It calculates whether proposed spectrum assignments meet the physical layer constraints required for successful data transmission.
What this module does:
Calculates SNR for paths across multiple links
Models noise components: ASE, nonlinear (SCI, XCI), and cross-talk
Validates modulation format feasibility based on SNR thresholds
Supports multi-core fiber (MCF) with inter-core cross-talk modeling
Exposes a registry pattern for algorithm discovery and selection
When you would work here:
Adding a new SNR calculation algorithm
Implementing advanced noise models (e.g., EGN model refinements)
Supporting new fiber types or modulation formats
Optimizing physical layer calculations for performance
Understanding SNR in Optical Networks
Important
SNR (Signal-to-Noise Ratio) determines whether a lightpath can successfully transmit data. Higher-order modulation formats (e.g., 64-QAM) require higher SNR but use spectrum more efficiently. Lower-order formats (e.g., BPSK) tolerate lower SNR but require more spectrum.
Key Concepts
- Signal-to-Noise Ratio (SNR)
The ratio of signal power to noise power, measured in decibels (dB). Higher SNR means better signal quality.
- OSNR (Optical SNR)
SNR measured in the optical domain before receiver processing. Typically higher than electrical SNR due to receiver noise.
- ASE Noise (Amplified Spontaneous Emission)
Linear noise introduced by optical amplifiers (EDFAs). Accumulates with each amplifier in the path. Dominant noise source for long paths.
- Nonlinear Noise
Noise from fiber nonlinearities that increases with signal power:
SCI (Self-Channel Interference): Signal interfering with itself
XCI (Cross-Channel Interference): Interference from adjacent channels
XPM (Cross-Phase Modulation): Phase distortion from other channels
FWM (Four-Wave Mixing): New frequencies generated by channel interaction
- Cross-talk (XT)
In multi-core fibers, light leaks between adjacent cores. Significant for space-division multiplexing (SDM) systems.
- Modulation Threshold
Each modulation format has a minimum required SNR:
Modulation
Required SNR
Use Case
BPSK
6.0 dB
Very long reach, low capacity
QPSK
9.0 dB
Long reach, moderate capacity
8-QAM
12.0 dB
Medium reach
16-QAM
15.0 dB
Metro networks
32-QAM
18.0 dB
Short reach, high capacity
64-QAM
21.0 dB
Data center interconnects
Legacy vs. Orchestrator Architecture
Like the routing module, SNR algorithms are used by both architecture paths:
+===========================================================================+
| SNR MODULE USAGE |
+===========================================================================+
| |
| use_orchestrator = False use_orchestrator = True |
| (Legacy Path) (Orchestrator Path) |
| |
| +------------------+ +------------------+ |
| | SDNController | | SDNOrchestrator | |
| +--------+---------+ +--------+---------+ |
| | | |
| | Direct instantiation | Via SNRAdapter |
| v v |
| +------------------+ +------------------+ |
| | fusion/core/ | | fusion/core/ | |
| | snr_measurements | | adapters/ | |
| | (legacy class) | | snr_adapter.py | |
| +--------+---------+ +--------+---------+ |
| | | |
| | Uses | Wraps |
| v v |
| +-------------------------------------------------------+ |
| | fusion/modules/snr/ | |
| | | |
| | StandardSNRMeasurer | |
| | (future: EGNSNRMeasurer, etc.) | |
| +-------------------------------------------------------+ |
| |
+===========================================================================+
Key insight: The algorithms in fusion/modules/snr/ are shared by both paths.
The difference is only in how they are invoked and what proxy objects wrap them.
How Adapters Work with SNR
Component |
Location |
Role |
|---|---|---|
SNRAdapter |
|
Wraps legacy SNR for orchestrator. Converts |
SnrMeasurements |
|
Legacy implementation (1600+ lines). Contains EGN model, multi-band calculations, GSNR lookups. |
StandardSNRMeasurer |
|
Clean modular implementation following interface contract. |
Architecture
Module Structure
fusion/modules/snr/
|-- __init__.py # Public API exports (11 items)
|-- registry.py # Algorithm discovery and creation
|-- snr.py # StandardSNRMeasurer implementation
|-- utils.py # Helper functions (file I/O, slot indexing)
|-- README.md # Module documentation
|
|-- visualization/ # Visualization plugin (BETA)
| |-- __init__.py
| `-- snr_plugin.py # Plugin with 3 renderers, 7 metrics
|
`-- tests/ # Unit tests
|-- __init__.py
|-- test_snr.py
|-- test_registry.py
`-- test_utils.py
Data Flow
1. SPECTRUM ASSIGNMENT PROPOSED
|
| path + spectrum_info (start_slot, end_slot, core, band, modulation)
v
2. SNR ALGORITHM SELECTION (via registry)
|
| create_snr_algorithm("standard_snr", engine_props, ...)
v
3. PER-LINK SNR CALCULATION
|
| For each link in path:
| - Calculate ASE noise
| - Calculate nonlinear noise (SCI + XCI)
| - Calculate cross-talk (if multi-core)
| - Combine: link_snr = signal_power / total_noise
v
4. PATH SNR AGGREGATION
|
| total_snr = 1 / sum(1/link_snr for each link)
v
5. THRESHOLD VALIDATION
|
| required_snr = get_required_snr_threshold(modulation, reach)
| acceptable = calculated_snr >= required_snr + margin
v
6. RESULT RETURNED
|
| SNR value (dB) + acceptable (bool)
Components
registry.py
- Purpose:
Centralized registry for SNR algorithm discovery and instantiation
- Key Classes:
SNRRegistry,SNR_ALGORITHMS- Key Functions:
create_snr_algorithm(),list_snr_algorithms(),get_multicore_snr_algorithms()
The registry enables dynamic algorithm selection based on configuration:
from fusion.modules.snr import (
SNRRegistry,
create_snr_algorithm,
list_snr_algorithms,
get_multicore_snr_algorithms,
)
# List available algorithms
algorithms = list_snr_algorithms()
# ['standard_snr']
# Get only multi-core capable algorithms
mc_algorithms = get_multicore_snr_algorithms()
# ['standard_snr']
# Create an algorithm instance
snr_measurer = create_snr_algorithm(
"standard_snr",
engine_props,
sdn_props,
spectrum_props,
route_props,
)
# Calculate SNR
snr_db = snr_measurer.calculate_snr(path, spectrum_info)
Registered Algorithms:
Name |
Multi-core |
Description |
|---|---|---|
|
Yes |
Standard SNR with ASE, nonlinear, and cross-talk noise |
snr.py (StandardSNRMeasurer)
- Purpose:
Primary SNR calculation implementation
- Key Class:
StandardSNRMeasurer
The StandardSNRMeasurer implements the AbstractSNRMeasurer interface
with comprehensive noise modeling:
from fusion.modules.snr import StandardSNRMeasurer
measurer = StandardSNRMeasurer(engine_props, sdn_props, spectrum_props, route_props)
# Calculate SNR for a path
spectrum_info = {
"start_slot": 10,
"end_slot": 18,
"core_num": 0,
"band": "c",
"modulation": "16-QAM",
}
snr_db = measurer.calculate_snr(path=[0, 1, 2, 3], spectrum_info=spectrum_info)
# Check if SNR meets requirements
required_snr = measurer.get_required_snr_threshold("16-QAM", reach=500.0)
is_valid = measurer.is_snr_acceptable(snr_db, required_snr, margin=1.0)
Key Methods:
calculate_snr(path, spectrum_info) -> floatEntry point. Calculates total path SNR by aggregating per-link SNR values using the reciprocal formula:
1/SNR_total = sum(1/SNR_link).calculate_link_snr(source, destination, spectrum_info) -> floatCalculates SNR for a single link by combining noise components.
_calculate_ase_noise(link_length) -> floatComputes ASE noise based on amplifier count (one per ~80 km span).
_calculate_nonlinear_noise(source, dest, spectrum_info) -> dictReturns
{"sci": float, "xci": float}for self-channel and cross-channel interference._calculate_crosstalk_noise(source, dest, spectrum_info) -> floatComputes inter-core cross-talk for multi-core fibers.
Noise Model:
The total noise power per link is:
N_total = N_ase + N_sci + N_xci + N_xt
Where:
- N_ase = h * f * NF * B * N_amps (ASE noise)
- N_sci = G_sci * P^3 (Self-channel interference)
- N_xci = sum(G_xci_i * P_i * P^2) (Cross-channel interference)
- N_xt = XT_coeff * N_adj * P (Cross-talk, multi-core only)
utils.py
- Purpose:
Utility functions for file I/O and slot indexing
- Key Functions:
get_loaded_files(),get_slot_index(),compute_response()
get_loaded_files(core_num, cores_per_link, file_mapping_dict, network)Loads pre-computed modulation and GSNR data from files in
data/pre_calc/{network}/. Used for lookup-based SNR validation.get_slot_index(current_band, start_slot, engine_props)Converts band-relative slot index to absolute index. Handles L, C, S bands with configurable offsets.
compute_response(mod_format, snr_props, spectrum_props, sdn_props)Validates that a modulation format can satisfy the bandwidth request. Returns
Trueif valid,Falseotherwise.
visualization/ (BETA)
- Purpose:
Visualization plugin for SNR analysis plots
- Status:
BETA - API may change in future releases
The visualization submodule provides a plugin that extends FUSION’s core visualization system with SNR-specific plots:
Registered Metrics (7):
snr- Signal-to-Noise Ratio (dB)osnr- Optical SNR (dB)q_factor- Quality factor (dB)ber- Bit Error Rateosnr_margin- Margin from required threshold (dB)ase_noise- ASE noise power (dBm)signal_power- Optical signal power (dBm)
Plot Types (3):
snr_vs_distanceShows SNR degradation along transmission distance with confidence intervals and threshold line.
q_factor_plotTwo-panel visualization: Q-factor vs distance (left) and BER vs SNR (right).
osnr_margin_plotBar chart showing OSNR margin for different configurations. Color-coded: green (safe, >3 dB), orange (warning, >1 dB), red (critical, <0 dB).
See Visualization Submodule (BETA) for usage details.
Configuration
The SNR algorithm is configured via engine_props:
Parameter |
Default |
Description |
|---|---|---|
|
12.5e9 |
Bandwidth per spectrum slot (Hz) |
|
1e-3 |
Launch power per channel (W) |
|
0.2 |
Fiber loss (dB/km) |
|
16.7 |
Chromatic dispersion (ps/nm/km) |
|
1.3e-3 |
Kerr nonlinearity (1/W/km) |
|
4.5 |
EDFA noise figure (dB) |
|
80.0 |
Distance between amplifiers (km) |
Parameter |
Default |
Description |
|---|---|---|
|
1 |
Number of cores (1 = single-core, 7 = MCF) |
|
-40 |
Inter-core cross-talk coefficient (dB) |
INI Configuration Example:
[physical_layer]
snr_algorithm = standard_snr
fiber_attenuation = 0.2
fiber_dispersion = 16.7
edfa_noise_figure = 4.5
span_length = 80.0
[multicore]
cores_per_link = 7
xt_coefficient = -40
Development Guide
Adding a New SNR Algorithm
Step 1: Create the algorithm file
# fusion/modules/snr/my_snr.py
"""My custom SNR measurement algorithm."""
from typing import Any
from fusion.interfaces.snr import AbstractSNRMeasurer
class MySNRMeasurer(AbstractSNRMeasurer):
"""
My custom SNR measurement algorithm.
Implements [describe what makes it special].
"""
@property
def algorithm_name(self) -> str:
return "my_snr"
@property
def supports_multicore(self) -> bool:
return True # or False
def calculate_snr(
self, path: list[Any], spectrum_info: dict[str, Any]
) -> float:
# YOUR SNR CALCULATION LOGIC
total_snr_linear = 0.0
for i in range(len(path) - 1):
link_snr = self.calculate_link_snr(
path[i], path[i + 1], spectrum_info
)
total_snr_linear += 10 ** (-link_snr / 10)
return -10 * math.log10(total_snr_linear)
def calculate_link_snr(
self, source: Any, destination: Any, spectrum_info: dict[str, Any]
) -> float:
# YOUR LINK SNR CALCULATION
pass
def calculate_crosstalk(
self, path: list[Any], core_num: int, spectrum_info: dict[str, Any]
) -> float:
# YOUR CROSSTALK CALCULATION
pass
def calculate_nonlinear_noise(
self, path: list[Any], spectrum_info: dict[str, Any]
) -> dict[str, float]:
return {"sci": 0.0, "xci": 0.0, "xpm": 0.0, "fwm": 0.0}
def get_required_snr_threshold(self, modulation: str, reach: float) -> float:
thresholds = {
"BPSK": 6.0, "QPSK": 9.0, "8-QAM": 12.0,
"16-QAM": 15.0, "32-QAM": 18.0, "64-QAM": 21.0,
}
base = thresholds.get(modulation, 9.0)
reach_penalty = 0.1 * (reach / 100)
return base + reach_penalty
def is_snr_acceptable(
self, calculated_snr: float, required_snr: float, margin: float = 0.0
) -> bool:
return calculated_snr >= (required_snr + margin)
def update_link_state(
self, source: Any, destination: Any, spectrum_info: dict[str, Any]
) -> None:
pass # Override if algorithm maintains state
def get_metrics(self) -> dict[str, Any]:
return {"algorithm": self.algorithm_name}
Step 2: Register in registry.py
from .my_snr import MySNRMeasurer
# In SNRRegistry._register_default_algorithms()
self.register("my_snr", MySNRMeasurer)
Step 3: Export in __init__.py
from .my_snr import MySNRMeasurer
__all__ = [
# ... existing exports
"MySNRMeasurer",
]
Step 4: Add tests
Create tests/test_my_snr.py following the AAA pattern.
Multi-core Fiber Support
When implementing multi-core support:
Set
supports_multicore = TrueHandle
core_numinspectrum_infoImplement
calculate_crosstalk()properlyConsider adjacent core interference
def calculate_crosstalk(self, path, core_num, spectrum_info):
"""Calculate inter-core cross-talk."""
# Determine number of adjacent cores (7-core hex: center=6, edge=3)
if core_num == 0: # Center core
num_adjacent = 6
else:
num_adjacent = 3
# Cross-talk power
xt_linear = 10 ** (self.xt_coefficient / 10)
xt_power = xt_linear * num_adjacent * self.signal_power
return xt_power
Testing
- Test Location:
fusion/modules/snr/tests/- Run Tests:
pytest fusion/modules/snr/tests/ -v
Test Files:
test_snr.py: Core algorithm tests (SNR calculation, noise components)test_registry.py: Registry functionality (registration, creation)test_utils.py: Utility function tests (file loading, slot indexing)
Example Test:
def test_snr_decreases_with_distance(snr_measurer, mock_topology):
"""Test that SNR decreases for longer paths."""
spectrum_info = {"start_slot": 10, "end_slot": 18, "core_num": 0, "band": "c"}
short_path = [0, 1]
long_path = [0, 1, 2, 3, 4]
snr_short = snr_measurer.calculate_snr(short_path, spectrum_info)
snr_long = snr_measurer.calculate_snr(long_path, spectrum_info)
assert snr_short > snr_long # Longer path = more noise = lower SNR
Troubleshooting
Issue: SNR always returns very low values
- Symptom:
All paths fail SNR validation
- Cause:
Input power may be too low or fiber parameters incorrect
- Solution:
Check
input_power(should be ~1 mW) andfiber_attenuation
Issue: Cross-talk not being calculated
- Symptom:
Multi-core allocations show no cross-talk penalty
- Cause:
cores_per_linkmay be set to 1 (single-core mode)- Solution:
Set
cores_per_link = 7for multi-core fiber
Issue: Modulation always fails validation
- Symptom:
High-order modulations (64-QAM) always rejected
- Cause:
Path length exceeds modulation reach
- Solution:
Use lower-order modulation for long paths, or check reach penalty
Visualization Submodule (BETA)
Note
Status: BETA
The visualization submodule is in BETA and actively being developed. The API may evolve in future releases.
Usage:
from fusion.visualization.plugins import get_global_registry
# Load the plugin
registry = get_global_registry()
registry.discover_plugins()
registry.load_plugin("snr")
# Generate plots via standard API
from fusion.visualization.application.use_cases.generate_plot import generate_plot
# SNR vs distance plot
result = generate_plot(
config_path="my_experiment.yml",
plot_type="snr_vs_distance",
output_path="plots/snr_distance.png",
)
# OSNR margin analysis
result = generate_plot(
config_path="my_experiment.yml",
plot_type="osnr_margin_plot",
output_path="plots/osnr_margin.png",
)