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

Integration Points

Component

Location

Role

SNRAdapter

fusion/core/adapters/snr_adapter.py

Wraps legacy SNR for orchestrator. Converts NetworkState to SDNPropsProxyForSNR, calls legacy SNR, returns SNRResult.

SnrMeasurements

fusion/core/snr_measurements.py

Legacy implementation (1600+ lines). Contains EGN model, multi-band calculations, GSNR lookups.

StandardSNRMeasurer

fusion/modules/snr/snr.py

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

standard_snr

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) -> float

Entry 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) -> float

Calculates SNR for a single link by combining noise components.

_calculate_ase_noise(link_length) -> float

Computes ASE noise based on amplifier count (one per ~80 km span).

_calculate_nonlinear_noise(source, dest, spectrum_info) -> dict

Returns {"sci": float, "xci": float} for self-channel and cross-channel interference.

_calculate_crosstalk_noise(source, dest, spectrum_info) -> float

Computes 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 True if valid, False otherwise.

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 Rate

  • osnr_margin - Margin from required threshold (dB)

  • ase_noise - ASE noise power (dBm)

  • signal_power - Optical signal power (dBm)

Plot Types (3):

snr_vs_distance

Shows SNR degradation along transmission distance with confidence intervals and threshold line.

q_factor_plot

Two-panel visualization: Q-factor vs distance (left) and BER vs SNR (right).

osnr_margin_plot

Bar 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:

Physical Layer Parameters

Parameter

Default

Description

bw_per_slot

12.5e9

Bandwidth per spectrum slot (Hz)

input_power

1e-3

Launch power per channel (W)

fiber_attenuation

0.2

Fiber loss (dB/km)

fiber_dispersion

16.7

Chromatic dispersion (ps/nm/km)

nonlinear_coefficient

1.3e-3

Kerr nonlinearity (1/W/km)

edfa_noise_figure

4.5

EDFA noise figure (dB)

span_length

80.0

Distance between amplifiers (km)

Multi-core Fiber Parameters

Parameter

Default

Description

cores_per_link

1

Number of cores (1 = single-core, 7 = MCF)

xt_coefficient

-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:

  1. Set supports_multicore = True

  2. Handle core_num in spectrum_info

  3. Implement calculate_crosstalk() properly

  4. Consider 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) and fiber_attenuation

Issue: Cross-talk not being calculated

Symptom:

Multi-core allocations show no cross-talk penalty

Cause:

cores_per_link may be set to 1 (single-core mode)

Solution:

Set cores_per_link = 7 for 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",
)