Skip to main content

Link budget

A wavelength either closes or it does not. The link budget is the arithmetic that decides, and in this demo it is a computation over loaded plant rather than a spreadsheet somebody maintains manually.

Three independent gates, all evaluated, all reported:

GateQuestionPasses when
OSNRIs there enough signal against the noise the amplifiers add?osnr_total - required_osnr - system_margin >= 0
Chromatic dispersionCan the receiver still recover the pulse shape?accumulated <= mode.cd_tolerance
GainCan each amplifier restore the power the element ahead of it took?gain >= loss ahead, aging included

The engine is src/infrahub_demo_otn/budget.py. It imports no Infrahub SDK, so its tests need no server, and every number crossing its boundary is a scaled integer.

Two directions, and two chains of amplifiers​

An optical multiplex section runs from one ROADM to the other, and light travels both ways over it. Amplification only works one way. An erbium-doped amplifier has an input and an output, and it restores the power of light entering its input. An amplifier hut therefore holds two amplifiers, one facing each way, sharing a building and nothing else.

The section holds one relationship per chain, amplifiers_a2b and amplifiers_b2a, and which one holds an amplifier is which way it faces. Nothing on the amplifier repeats that. What the amplifier does have is an oms_sequence counting along the direction its own chain amplifies. A section with N spans has N+1 amplifiers each way, 2N+2 in total, and the two chains are budgeted separately.

Here is oms-ams-bru, three spans between Amsterdam and Brussels:

An amplifier name is a position and nothing else. Huts count from the A end and each holds two amplifiers. Hut 0 is the Amsterdam building with 01 and 02, hut 1 is the first roadside hut with 03 and 04, and so on. Read the two columns above side by side and 03 and 04 are the two amplifiers in one building, facing opposite ways. Their sequence numbers differ because the two chains reach that building from opposite ends.

Sort either chain on its sequence and it is already in the order the light meets it. Nothing has to parse a name to get there. The chain comes from the relationship and the order comes from the sequence. Whether an amplifier is a booster, an inline or a pre-amplifier is the role on its own input and output ports.

Walking a section the other way swaps the chains. It does not reverse one. Reversing a chain models the same amplifiers running backwards, which is not what is installed. The distinction is invisible on this dataset, where all 306 amplifiers have the same noise figure and the same gain. It stops being invisible the first time a section is built from mixed equipment.

Two chains produce two margins for one section. Where a report shows a single margin, it shows the worse of the two, because that is the direction a service over the section is limited by. Both figures are listed underneath, so the choice is visible rather than assumed.

A worked example over the shortest section​

The shortest section in the network, 220 km over three spans, carrying a 400G wavelength on DP-16QAM 64GBd 400G from Amsterdam towards Brussels. Every number below comes out of objects/.

The inputs. Three G.652.D spans of 73,334, 73,333 and 73,333 metres. Each has 18 fusion splices at 0.05 dB, two mated connector pairs at 0.3 dB, and a 1.5 dB aging allowance. A ROADM at each end at 7.0 dB. Four amplifiers in the Amsterdam-to-Brussels chain, each 4.0 dB noise figure and 22.0 dB gain, and four more facing the other way.

Per-span loss. The four-term span formula, on the first span:

(73334 / 1000) * 200 = 14667 mdB attenuation
18 * 50 = 900 mdB splices
2 * 300 = 600 mdB connectors
1500 mdB aging allowance
------------
17667 mdB = 17.667 dB

Per-stage OSNR. Launch power is +3.0 dBm per channel. The loss ahead of an amplifier is the loss of whatever sits immediately before it. The aging allowance is not charged here. It is a reserve against loss that has not happened yet, so it belongs to the power budget and to the gain requirement, not to the noise. That leaves 16.167 dB of real fiber loss on each span.

booster, behind the 7.0 dB ROADM:
3000 - 7000 - 4000 + 58000 = 50000 mdB = 50.000 dB
each line amplifier, behind 16.167 dB of fiber:
3000 - 16167 - 4000 + 58000 = 40833 mdB = 40.833 dB

The 58.0 dB is the reference-bandwidth constant, -10*log10(h * nu * delta_nu) for 12.5 GHz, which is 0.1 nm at 1550 nm. It is a named constant in the engine, never a literal in the formula.

The hop table. This is what the budget report transform renders.

#HopKindLossStage OSNRCumulative lossCumulative OSNRCumulative delay
1roadm-ams-01node7.000 dB7.000 dB0.1 us
2amp-ams-bru-01amplifier50.000 dB7.000 dB49.500 dB0.2 us
3span-ams-bru-01span17.667 dB24.667 dB49.500 dB359.3 us
4amp-ams-bru-03amplifier40.833 dB24.667 dB39.836 dB359.4 us
5span-ams-bru-02span17.667 dB42.334 dB39.836 dB718.5 us
6amp-ams-bru-05amplifier40.833 dB42.334 dB37.067 dB718.6 us
7span-ams-bru-03span17.667 dB60.001 dB37.067 dB1077.7 us
8amp-ams-bru-07amplifier40.833 dB60.001 dB35.390 dB1077.8 us
9roadm-bru-01node7.000 dB67.001 dB34.890 dB1078.0 us

Two numbers in that table need explanation.

The cumulative OSNR after the booster is 49.500 dB, not the 50.000 dB the stage delivers. Each ROADM traversed costs a 0.5 dB filtering penalty on top of its insertion loss, and the head ROADM has already been crossed.

The cumulative OSNR drops 9.664 dB at the first line amplifier and only 2.769 dB at the second. Stages cascade in the linear domain, 1 / OSNR_total = sum(1 / OSNR_stage), so the first bad stage does most of the damage and each identical one after it does less. Two equal stages land exactly 3.010 dB below either of them.

The verdict.

QuantityValue
Total length220.000 km
Total loss67.001 dB
OSNR delivered34.890 dB
Required by DP-16QAM 64GBd 400G24.500 dB
System margin1.000 dB
OSNR margin+9.390 dB
Accumulated dispersion3740.0 ps/nm
Tolerated by the mode50000.0 ps/nm
Dispersion margin+46260.0 ps/nm
Latency in this direction1082.0 us

Both gates pass, with room. On the shortest section in the network that is the expected answer. The interesting answers are on the long routes.

Brussels to Amsterdam produces the same numbers. The two ROADMs have the same insertion loss, the two chains hold the same equipment, and no span here has a Raman pump, so nothing distinguishes the directions. That is the ordinary case. It is what makes the few sections where the two differ worth looking at.

The model, and where it is tuned​

Five constants set the whole model. Two of them are tuned towards the favourable end of realistic, and the table marks which two.

ConstantValueNote
OSNR reference bandwidth58.0 dBPhysics. 12.5 GHz, which is 0.1 nm at 1550 nm.
Per-channel launch power+3.0 dBmTuned. Across 96 channels that is +22.8 dBm composite, a high-power booster rather than a typical one.
Amplifier noise figure4.0 dBTuned. A good low-noise erbium-doped amplifier, not an average one.
ROADM filtering penalty0.5 dB per nodeRepresentative.
System margin1.0 dBImplementation penalty and end-of-life degradation.

The span lengths, the amplifier spacing, and the reach and OSNR figures in the optical mode catalog are illustrative in exactly the same sense. None of them is sourced from a vendor data sheet or from published plant records.

The node and amplifier latency terms, 150 and 100 nanoseconds, are illustrative too. They sit three orders of magnitude below propagation and change no verdict; they are modelled because zero is a stronger claim than small.

Beginning of life, and why the aging allowance is not in the OSNR​

Every span has an aging_margin_mdb, 1.5 dB by default. It is a reserve against repairs and degradation that have not happened yet.

The engine keeps two loss numbers for every span:

  • span_loss_mdb includes the allowance. It determines the power budget and the amplifier gain requirement, because an amplifier has to still restore the launch power at end of life.
  • span_fiber_loss_mdb excludes it. It determines the OSNR, because that is the loss the light meets today.

Charging the allowance to the OSNR as well would compute an end-of-life figure and then subtract a system margin on top of it, which is the same pessimism counted twice. Over the 17 spans of the longest route the allowance alone is 25.5 dB.

So the OSNR this demo reports is a beginning-of-life figure, and the 1.0 dB system margin is what covers implementation penalty and aging on top.

Raman amplification puts the gain inside the fiber. A pump laser injects light about a hundred nanometres below the signal wavelength. Stimulated Raman scattering transfers power from the pump to the signal while the two travel through the same glass. No gain block appears on the path. The signal reaches the far end of the span with more power in it than the loss of the fiber predicts.

The quantity that describes this is on-off gain: the received signal power with the pump running, less the received signal power with the pump switched off. It is stored on OtnRamanPump.on_off_gain_mdb, and the engine credits it as reduced effective span loss rather than as another stage in the cascade:

effective fiber loss = fiber loss - on-off gain + combiner insertion loss

The gain is credited one way and the combiner is charged both ways. A pump amplifies one direction of travel and does nothing at all for light going the other way. The wavelength combiner that couples the pump onto the fiber sits in line on the glass, and light going either way passes through it and pays its insertion loss. A pumped span is therefore asymmetric: it costs less one way, by the gain minus the combiner, and more the other way, by the combiner alone.

Which direction a pump amplifies​

The pump does not store the answer. It stores two facts about the hardware, and the direction follows from them:

  • injection_end, which end of the span the pump is spliced in at, named against the span's own site_a and site_b.
  • propagation, whether the laser fires with the signal or against it.

A counter-propagating pump fires back up the fiber from the far end, so one at the B end amplifies the A to B signal. A co-propagating pump fires along with the signal from the near end, so one at the A end amplifies A to B as well. Those are two ways of reaching the same result, and all four cases are:

Injected atLaser firesAmplifies
B endAgainst the signalA to B
A endWith the signalA to B
A endAgainst the signalB to A
B endWith the signalB to A

Every pump in the loaded network is counter-propagating, so only the first and third rows occur here. The table shows all four because a reader who sees only the shipped cases will assume counter-propagating is the only kind there is. Firing against the signal means gain builds where the signal is weakest, which is the useful place to put it.

Storing the conclusion alongside the two facts that fix it would be a third value that can contradict them. A counter-propagating pump recorded at the A end and marked as amplifying A to B is a self-contradictory object, and a model that accepts one will eventually hold one.

What that is worth on this network​

Nine spans have a pump on the default branch, all of them on Vienna to Milan, all injected at the Milan end and firing against the signal, which amplifies Vienna towards Milan. Each pump quotes 10.0 dB of on-off gain, and each combiner costs 0.5 dB.

DirectionEffective span loss against the bare fiber
Vienna towards Milan9.5 dB lower
Milan towards Vienna0.5 dB higher

Nine spans are pumped, so the section total is nine times each figure. This is the one place in the network where the two directions of a section produce different answers, and it is why the reports show both.

The budget report shows the arithmetic on the rows it applies to and nowhere else. A pumped span row shows its on-off gain, its combiner loss, and what the span would have cost unpumped, so the improvement is traceable to its source. The other 123 spans have no Raman columns at all, because a column of zeros across the rest of the network reads as a fault rather than as a correct answer.

Where the treatment stops being predictive​

Crediting the on-off gain as reduced loss is a first-order engineering approximation, and it is worth being exact about what it leaves out.

A real distributed Raman amplifier contributes noise of its own: amplified spontaneous emission from the distributed gain, double Rayleigh backscattering, and pump relative intensity noise transferred onto the signal. This model charges none of it. It credits the gain, takes the combiner loss, and stops.

So every OSNR figure on this page that involves a pumped span is slightly optimistic against a full distributed model. The direction of the error is known and its size is not modelled here. A decision that depends on a few tenths of a decibel over a pumped span needs a Raman simulation, not this arithmetic.

The same statement sits in the engine module beside the code that produces the number, because someone reading the code will not have this page open.

What the model does not include​

AbsentWhy
Nonlinear penaltiesCredible modelling needs a fiber effective-area and channel-count model this demo does not have.
Polarisation-mode dispersionSame reason.
Regeneration and 3R placementThe network is transparent end to end by design.
Flexgrid media channelsAnchors are quantised onto the 50 GHz grid. A carrier's occupied width is modelled and checked against the band. A 128 GBd wavelength is 150,000 MHz wide rather than one channel, but its centre may only sit on a grid position. See the spectral model.
Raman pump noiseThe on-off gain is credited and the pump's own noise is not charged, so a pumped span reads slightly better than it would in a full distributed model.
Any CWDM linkOtnFiberType.attenuation_mdb_per_km is a 1550 nm figure, and applying it to a 1471 nm wavelength understates the loss. This repository holds no per-band attenuation figure, so it cannot say by how much. The coarse tail therefore belongs to no optical multiplex section, which is what keeps it out of the engine. See optical plant concepts.

Three findings the budget produced​

The demo reports negative results. These are the three the engine found, and each one is asserted by tests/unit/test_budget_claims.py, recomputed from objects/ on every test run.

Route length does not order signal quality​

Berlin to Amsterdam has four routes. Each is budgeted at DP-16QAM 64GBd 400G from Berlin, so the amplifier count is the chain that carries the light that way.

RoutekmAmplifiersROADMsOSNRMargin
Berlin - Hamburg - Amsterdam80012327.784 dB+2.284 dB
Berlin - Frankfurt - Amsterdam101014326.007 dB+0.507 dB
Berlin - Prague - Frankfurt - Amsterdam122018425.241 dB-0.259 dB
Berlin - Copenhagen - Hamburg - Amsterdam133020425.378 dB-0.122 dB

The last two rows are the finding. The Copenhagen route is 110 km longer than the Prague route and has 0.137 dB more margin, because its sections are built from shorter spans. Span loss enters the cascade exponentially; route length enters it linearly.

A planner ranking routes by distance would pick the Prague route as the better fallback. The budget says otherwise, and no map shows it.

All four routes carry 400G at DP-QPSK 128GBd, so the split above is a modulation choice rather than a corridor nobody can use.

The dispersion gate fires once, on one site pair, at 400G only​

Madrid to Warsaw, routed through Paris, Frankfurt and Prague, is 2970 km. At 17 ps/nm/km that accumulates 50,490 ps/nm against the 50,000 ps/nm both 400G modes tolerate. It fails by one percent.

At DP-QPSK 128GBd 400G that pair fails the OSNR gate as well, by 0.021 dB. Two independent constraints landing within a quarter of a decibel of each other on the same site pair is a coincidence, and it is worth naming as one. At 100G the pair passes both gates comfortably.

A second constraint that never fires proves only that the engine has two branches. One that fires once, marginally, at the highest modulation and nowhere else gives a planner a real answer. Madrid to Warsaw at 400G needs dispersion compensation or a lower modulation, and no other pair does.

The spectrally efficient 400G mode misses exactly one section​

DP-16QAM 64GBd 400G closes on 40 of the 42 section evaluations, which is 21 sections taken singly in both directions. The two it misses are the two directions of Paris to Madrid, 1250 km over 14 spans, each short by 0.535 dB. That is the longest single section in the network, and it is the only place the higher-order modulation does not fit.

The coherent pluggables, whose 120 km reach clears nothing on a network whose shortest section is 220 km, complete the reach picture. Pluggables are out, 16QAM 400G covers every section except the longest one, and QPSK 400G covers every route except the longest one.

Paris to Madrid does not close, and the check says so​

Run the OSNR check against the default branch and it fails. That is the state the network is in, not a fault in the demo, and the route it names is Paris to Madrid in both directions.

1250 km of fiber over 14 spans, with 15 amplifiers in each direction, is a demanding section, and DP-16QAM 64GBd 400G misses it by 0.535 dB. Madrid sits at the end of the longest single section here, and half a decibel is what a section that long costs at that modulation. The number is a property of the route, not of the dataset.

An engineer looking at half a decibel reaches for three answers. Only two of them are available on this route.

AnswerWhat it costs
Drop to DP-QPSK 128GBd 400GThe section closes at +4.965 dB, at twice the spectrum for every wavelength on it.
Regenerate part way alongNot available here. See below.
Put Raman on the sectionThe loss comes back without spending spectrum or a building.

The middle answer is not available on this route, and that was measured rather than assumed. Cutting a route in half at a regenerator needs a site in the middle to put one at. oms-par-mad is a single multiplex section carrying all 14 spans, and Madrid appears in no other section, so it hangs off Paris as a leaf. The boundaries between those 14 spans are amplifier huts, and this model gives every span its section's own endpoints as site_a and site_b rather than the huts it runs between. There is no object between Paris and Madrid to attach a device to.

That sharpens the negative result instead of softening it. Regeneration is not the fix for Paris to Madrid, so the two answers that remain are the modulation and the Raman pumps, and the pumps are the cheaper one.

Raman is the standard answer to a section that is a fraction of a decibel short, and this network already models the equipment for it. The fix is a branch:

uv run invoke demo-raman

That puts two pumps on each of the first three spans out of Paris, one at each end, and runs the same check. It passes, in both directions: oms-par-mad moves from -0.535 dB to +0.361 dB each way.

Two pumped spans would also pass, at +0.041 dB, which is a pass on paper and nothing in the field. The pumps go in at both ends because a pump credits its on-off gain to the one direction it amplifies and its combiner charges both. Pumping one end only would fix one direction and make the other worse.

The proposed change then holds six pump objects, three fiber spans whose only change is the pump list now pointing back at them, and no edited margin figure anywhere. That is the point. The margin moved because the plant changed.

The default branch keeps the red check on purpose. A network whose every gate is green teaches nothing about what a gate is for. A shortfall hidden until someone tries to provision over it is a worse demonstration than one the pipeline reports on every run.

A regenerated circuit, and the two things it costs​

A route with intermediate sites has an answer Paris to Madrid does not. Put an O-E-O regenerator at one of them, and the circuit becomes two wavelengths joined electrically: the light is received, the payload is reframed, and a fresh transmitter starts the second half. Each half carries its own OSNR budget, so a route too long for any single wavelength can still be built.

OtnOduSwitch is the kind. Its carriers relationship names the wavelengths patched to the shelf, and src/infrahub_demo_otn/chains.py reads that to decide whether a junction is real. The two wavelengths must meet at a site, that site must host the device, and the device must be patched to both.

What terminates each half is a line port, on the regenerator itself. The shelf carries two, one facing each segment, and each names its wavelength. So the Madrid to Paris half ends on a Madrid transponder at one end and on the regenerator at the other, and the Paris to Warsaw half starts on the same shelf and ends on a Warsaw transponder. Ask either wavelength what terminates it and it answers with hardware at both ends, the same way a direct circuit does.

That is also what makes the split affordable. The inner ends need no transponder at the junction site, which matters because Paris and Frankfurt each hold one spare line port and a split needs two.

Madrid to Warsaw, where one regenerator is not enough​

2970 km over four sections, oms-par-mad, oms-par-fra, oms-prg-fra and oms-prg-waw. No single wavelength closes it on any of the ten modes in the catalog; the best is -0.021 dB at DP-QPSK 128GBd 400G. Three sites on that route can hold a regenerator, and demo/06_mad_waw_16qam.yml places one at each with the wavelength pair its split needs. Every figure below came back from a provisioning run.

AttemptSegment 1Segment 2Verdict
400G DP-16QAM, regenerated at Paris-0.535 dB-2.439 dBRefused
400G DP-16QAM, regenerated at Frankfurt-2.755 dB+0.240 dBRefused
400G DP-16QAM, regenerated at Prague-4.004 dB+2.782 dBRefused
400G DP-QPSK 128GBd, regenerated at Frankfurt+2.745 dB+5.740 dBCloses

Three of the four attempts fail, and that is the finding. Cutting a route in half does not on its own make it close. The fix is a regenerator and a different modulation, and demo/07_mad_waw_qpsk.yml is the second half of that pair. Prague is worth reading as the runner-up. It closes its second half at 16QAM with 2.782 dB to spare and fails its first by 4 dB, which shows the split point matters independently of the mode.

A route's verdict is a conjunction over its segments, not an average. One half short of OSNR refuses the whole circuit however much margin the other half has. The Paris split is refused on -0.535 dB while its second segment is worse still. RouteBudget offers no single margin figure for a regenerated route, deliberately. A scalar would have to be a minimum or a mean, and both read as a statement about a circuit that has no such property.

Why a direct wavelength is always preferred​

Two costs, and the model states both rather than leaving them implied.

A regeneration. The circuit needs a device at the junction, and that device is a transponder pair plus the framing hardware between them. It is a thing to buy, rack, power and maintain, at a site that has to be there already.

Latency. The delay is the sum of both segments plus what the junction charges for reframing, which framing_latency_ns holds on the device. The Madrid to Warsaw circuit takes 14558.963 us end to end at 3000 ns of framing delay, against 9163.620 us for the Madrid to Frankfurt half on its own. For the AI and HPC payloads that treat round-trip time as the budget, that is the term that matters.

So the routing engine ranks a direct wavelength above a chain wherever both serve the route. Madrid to Warsaw takes the chain because it has no direct wavelength that closes on any mode, which is the only condition under which a chain wins.

Re-validation on a proposed change​

The arithmetic above runs once. The check runs it again on every change.

checks/osnr_margin.py runs on every proposed change. It reads the whole plant once and then runs two loops over it.

The carrier loop budgets all 40 pre-provisioned wavelengths against their own modes and logs an error for each one that fails any of the three gates. Lengthen a span on a branch and every wavelength crossing it is re-evaluated before the change can merge.

The section sweep budgets every section on its own, in both directions, against one reference mode, DP-16QAM 64GBd 400G. That is 42 evaluations over 21 sections, and it exists because the carrier loop cannot reach most of the network. Only five sections carry a wavelength, so sixteen of the twenty-one were never evaluated by anything. Paris to Madrid was one of the sixteen. The sweep is also the only place the N+1-amplifiers-per-direction rule becomes a gate rather than a unit test, because nothing in the graph enforces how many amplifiers a section holds.

A failure from either loop names the direction, not only the section.

uv run invoke check --name osnr_margin

On the default branch the sweep fails and the carrier loop passes:

ERROR osnr_margin::OsnrMarginCheck: FAILED
ERROR Re-validated 40 carriers over 21 sections. Worst OSNR margin
is +1.894 dB, on oc-ch047-fra-mil
ERROR oms-par-mad a_to_b is short of OSNR by 0.535 dB on DP-16QAM
64GBd 400G: the section delivers 24.965 dB over 1250 km and
15 amplifiers, and the mode needs 24.500 dB plus 1.000 dB of
system margin
ERROR oms-par-mad b_to_a is short of OSNR by 0.535 dB on DP-16QAM
64GBd 400G: the section delivers 24.965 dB over 1250 km and
15 amplifiers, and the mode needs 24.500 dB plus 1.000 dB of
system margin
ERROR Swept 42 section evaluations over 21 sections in two
directions against DP-16QAM 64GBd 400G. Worst standalone OSNR
margin is -0.535 dB, on oms-par-mad a_to_b. Short of OSNR:
oms-par-mad

That worst carrier is one of the 40 wavelengths on the congested Frankfurt to Milan corridor. It runs 780 km at 16QAM. It is the tightest margin of any wavelength on the network, and it is unaffected by Raman: the pumps are on Vienna to Milan and this carrier does not cross that section.

Stretch one span of the Frankfurt to Milan corridor from 87 km to 200 km on a branch. The carrier loop says so, per wavelength and per gate:

oc-ch047-fra-mil on DP-16QAM 64GBd 400G is short of OSNR by 11.385 dB:
the path delivers 14.115 dB over 893 km and 10 amplifiers, and the mode
needs 24.500 dB plus 1.000 dB of system margin

oc-ch047-fra-mil crosses amp-fra-mil-03, whose gain cannot recover
the loss ahead of its input at end of life

A wavelength the check cannot evaluate is an error, never a skip. A check that passes silently over an object it could not read reports success for a network it never read. The sweep is the same principle applied to the plant: a section nobody has provisioned over is still a section that has to close.

Reading the budget as a table​

transforms/budget_report.py renders the same computation as structured output: one row per hop with running totals, plus a verdict block, for every wavelength on the branch. Every scaled integer is accompanied by its rendered unit, so no row asks the reader to divide by a thousand.

uv run invoke demo-budget

Each wavelength is budgeted in both directions. The headline margin is the worse of the two, and both are listed with the ROADM each walk started from, so a reader can tell an asymmetric section from a symmetric one. Any pumped span on the path is listed separately with what it costs each way. For a span pumped one way only, the worse direction is the unpumped one. A report that showed the reported walk alone would show a Raman span with no Raman on it.

The wavelengths come back sorted by OSNR margin, worst first, because the worst one is the answer to "how much room is left".