In short

  • An OSNR budget is just noise bookkeeping: every amplifier and the transmitter itself add noise, and you add them up in linear units, never in dB.
  • One formula does most of the work: OSNR per amplifier ≈ 58 + Pin − NF (dB, 0.1 nm reference bandwidth, around 1550 nm).
  • On a short 400ZR link the transmitter’s own OSNR is often the biggest single noise source, so raising launch power gives diminishing returns.

Context

400ZR pluggables put coherent optics straight into routers, and the IP team now owns links that used to belong to the optical team. The first question on any new DCI route is “will it close?” Vendor planning tools answer that, but if you can’t reproduce the number on a napkin, you can’t sanity-check the tool, argue about margin, or troubleshoot a link that misbehaves. This post builds the budget by hand for a realistic two-span link.

How it works

OSNR is the ratio of signal power to amplified spontaneous emission (ASE) noise, measured in a 0.1 nm (≈12.5 GHz) reference bandwidth. Each EDFA adds ASE; attenuation before the amplifier makes it worse, because the amplifier boosts a weaker signal with the same noise floor.

The standard single-amplifier approximation:

OSNR_amp [dB] ≈ 58 + P_in [dBm per channel] − NF [dB]

The 58 comes from −10·log10(h·ν·Δν) with Δν = 12.5 GHz at ~1550 nm, which is about −58 dBm. That term is the quantum noise floor in the reference bandwidth.

Noise sources are independent, so the total is the “parallel resistor” sum in linear units:

1/OSNR_total = 1/OSNR_tx + 1/OSNR_amp1 + 1/OSNR_amp2 + …   (all linear, not dB)

Our link:

flowchart LR
    A[Router A<br/>400ZR Tx] --> M[Mux] --> B[Booster EDFA]
    B -- "Span 1: 60 km, 16.2 dB" --> I[In-line EDFA]
    I -- "Span 2: 60 km, 16.2 dB" --> P[Pre-amp EDFA]
    P --> D[Demux] --> R[Router B<br/>400ZR Rx]

Assumptions

ParameterValueNote
FiberG.652.D, 0.22 dB/kmtypical installed plant
Span length2 × 60 km120 km total
Span loss13.2 + 1 (connectors/splices) + 2 (ageing margin) = 16.2 dB
Amplifier NF5.5 dBall three EDFAs ⚠️NOTE: confirm against the actual amp datasheet
Booster input−12 dBm per channel400ZR Tx output after mux loss ⚠️ NOTE: check module Tx power and mux insertion loss
Launch power into each span+1 dBm per channel
Tx OSNR36 dB (0.1 nm)⚠️ NOTE:verify against the module datasheet and the OIF 400ZR IA minimum
Required OSNR at Rx26 dB (0.1 nm)incl. implementation penalties ⚠️ NOTE: confirm the OIF 400ZR IA value for amplified links

The calculation

Step 1 — per-amplifier OSNR

ElementPin per channelOSNR contribution
Transmitter—36.0 dB
Booster−12 dBm58 − 12 − 5.5 = 40.5 dB
In-line amp+1 − 16.2 = −15.2 dBm58 − 15.2 − 5.5 = 37.3 dB
Pre-amp+1 − 16.2 = −15.2 dBm37.3 dB

Step 2 — add in linear

1/10^3.60 + 1/10^4.05 + 2 × 1/10^3.73
= 2.51e-4 + 0.89e-4 + 3.72e-4
= 7.13e-4
OSNR_total = −10·log10(7.13e-4) ≈ 31.5 dB

Step 3 — margin

Margin = 31.5 − 26.0 = 5.5 dB

The link closes comfortably. The more interesting result is where the noise comes from:

SourceShare of total noise
Transmitter35 %
Booster13 %
Two span amplifiers52 %

Over a third of the noise is created before the light even leaves Router A.

What does more launch power buy you?

Launch per channelTotal OSNRMargin
−1 dBm30.3 dB4.3 dB
0 dBm30.9 dB4.9 dB
+1 dBm31.5 dB5.5 dB
+2 dBm32.0 dB6.0 dB

Three extra dB of launch buy only 1.7 dB of OSNR, because the transmitter term doesn’t move. Past a point, extra power also triggers nonlinear penalties this linear model ignores. Adding a third 60 km span at +1 dBm drops the total to about 30.5 dB, still above 26 dB.

Code: reproduce it

Python 3.11+, no dependencies:

import math

def lin(db): return 10 ** (db / 10)
def db(x): return 10 * math.log10(x)

def amp_osnr(p_in_dbm, nf_db):
    """OSNR (0.1 nm) of one EDFA, per-channel input power."""
    return 58 + p_in_dbm - nf_db

def total_osnr(contribs_db):
    """Combine independent noise sources (dB in, dB out)."""
    return db(1 / sum(1 / lin(o) for o in contribs_db))

TX_OSNR, REQ_OSNR, NF = 36.0, 26.0, 5.5
SPAN_LOSS, LAUNCH, BOOSTER_IN, SPANS = 16.2, 1.0, -12.0, 2

parts = [TX_OSNR, amp_osnr(BOOSTER_IN, NF)]
parts += [amp_osnr(LAUNCH - SPAN_LOSS, NF)] * SPANS   # in-line + pre-amp

osnr = total_osnr(parts)
print(f"Total OSNR: {osnr:.1f} dB, margin: {osnr - REQ_OSNR:.1f} dB")

Run it with python osnr_budget.py. Expected output: Total OSNR: 31.5 dB, margin: 5.5 dB.

Verification

In the field, compare the budget with what the receiver reports:

  • Read the estimated OSNR and pre-FEC BER from the 400ZR module’s PM counters on the router.
  • Compare per-channel power at each amplifier input with the budget values above.
  • A measured OSNR within 1–2 dB of the budget is normal. A gap of 3 dB or more means something in your assumptions is wrong. ⚠️ NOTE: confirm this rule of thumb against field experience.

Common pitfalls

  1. Adding dB values directly. 36 dB and 37 dB do not combine to 73 dB, or to 36.5 dB. Always convert to linear first.
  2. Using total power instead of per-channel power. A booster at +19 dBm total with 64 channels is +1 dBm per channel. Put total power into the formula and you’ll overstate OSNR by 18 dB.
  3. Forgetting the transmitter. Budgets that start at the booster look great on paper and fail on short 400ZR links.
  4. Mixing reference bandwidths. Some tools report OSNR in the signal bandwidth instead of 0.1 nm. For a ~60 GBd 400ZR signal that’s about a 7 dB difference (10·log10(60/12.5)) for the same link.
  5. No ageing margin. Fiber repairs add splices over the years. A link with 1 dB of margin on day one won’t have it in year five.

If you want the theory behind amplifiers, dispersion and OSNR in more depth before working on a live line system:

Key takeaways

  • OSNR ≈ 58 + Pin − NF per amplifier. Combine contributions in linear units.
  • Every dB of span loss costs a dB of OSNR at the next amplifier.
  • On short coherent links the transmitter often dominates, so more launch power gives little back.
  • Keep your assumptions in a table. Most disagreements with a planning tool are about an assumption, not the math.