Skip to content

Commit a97d45a

Browse files
ehsun-shclaude
andcommitted
Put the coherent transceiver on real fiber
Every coherent example was back to back, so the impairment that dominates any real span had never been in the loop. Measured: 5 km of ordinary fiber costs 20 dB of SNR, and at 80 km 3698 of 3968 symbols are wrong — 93%, against the 93.75% that blind guessing gives on 16-QAM. Not degraded, gone. DispersionCompensator is one static all-pass filter on the received baseband. It calls the fiber's own propagator with a negated distance rather than writing the inverse transfer function out, because two copies of a beta2 sign only stay equal until someone edits one of them. Spans from 5 km to 1000 km return to back-to-back EVM with no penalty that grows with distance, which is the whole argument for coherent detection: an all-pass phase removes nothing, so a receiver that measures the field still holds all of it. Parametrised by accumulated D*L in ps/nm, not by D and length. A receiver cannot know how the span was built and two links with the same total behave identically; asking for both would invent a distinction that does not exist on that side of the fiber. Kept separate from the butterfly equaliser, and that ordering is measured rather than quoted: on the dual-pol link over 80 km, growing the butterfly from 7 taps to 65 leaves EVM at 2941% against 3030%, while the static block ahead of a 7-tap filter restores 2.67%. A blind modulus criterion has no gradient to follow once the constellation is a Gaussian blob. Seven sabotages, seven caught: compensation sign, hardcoded wavelength, ps/nm conversion off by 1000x, parameter ignored, compensation skipped, I/Q swapped, symbol rate dropped from dispersive_spread. README: the front page claimed equalisers and coherent detection were not implemented, months after both landed. Corrected, along with the roadmap and four new validation rows. Mockup: regenerated from the new export, so the demonstrated link now runs over the span too. Two defects found by rebuilding against real output — the sensitivity sweep's 256-QAM curve never crossed the FEC threshold and so was the one curve with no label, and the sweep overrode only one of the two mappers. 17 blocks, 22 links, 33 components, contrast clean in both grounds across all four dock tabs. 517 tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent efd8b7e commit a97d45a

11 files changed

Lines changed: 5715 additions & 4583 deletions

File tree

DESIGN.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ edit time is meaningless if the types are invisible.
7979
The shown project is chosen so that **all five types appear on the canvas at
8080
once**. That is why it is a coherent link rather than an on-off-keyed one: only
8181
a coherent chain carries binary into symbols, symbols into two electrical
82-
drives, an optical field, two photocurrents back, and symbols out again.
82+
drives, an optical field, two photocurrents back, and symbols out again. It runs
83+
over 80 km of real fibre, because a simulator whose demonstration link has no
84+
fibre in it is showing the wrong thing.
8385

8486
Accent is `--optical`, because the tool is an optical simulator. Semantic colour
8587
(`--good` / `--warn` / `--bad`) is kept separate from it and does not count as
@@ -124,6 +126,13 @@ the action taken dozens of times an hour.
124126
sized by the dock's *height*, not its width. A shorter dock wastes the width it
125127
has.
126128

129+
**The canvas viewBox is 1000 x 420**, seven columns of 138px at a node width of
130+
116. The seventh column arrived with the span and its compensator; the
131+
alternative was to keep six and squeeze the spacing to 120, which leaves four
132+
pixels between adjacent blocks and reads as cramped. Widening also moved the
133+
aspect ratio to 2.38, closer to the 2.29 the canvas region actually has at
134+
1440px than the 2.10 it had before.
135+
127136
Below 940px the palette and inspector collapse and a note says so. This is a
128137
desktop tool; the real build would dock them as overlays rather than dropping
129138
them, and the note says that too rather than pretending the narrow layout is the
@@ -174,6 +183,10 @@ found engine defects that the test suite missed:
174183
zero** however bad the link was, because that block emits decisions. The
175184
mockup now carries two analysers — soft measurement before the decoder, error
176185
count after — which is also how a bench does it.
186+
- The sensitivity sweep's 256-QAM curve **never crossed the FEC threshold** in
187+
the swept range, so it was the one curve with no label — quietly contradicting
188+
the rule in section 6 that each is labelled where it crosses. The range now
189+
extends far enough that all four cross.
177190

178191
---
179192

@@ -225,7 +238,8 @@ Two findings worth keeping:
225238
- The mockup shows a single-carrier link. The dual-polarization link is in
226239
[`examples/dualpol_link.py`](examples/dualpol_link.py) and is not on the
227240
canvas: at ~20 blocks the node text stops being readable at this canvas size,
228-
and a schematic nobody can read is not a better demonstration.
241+
and a schematic nobody can read is not a better demonstration. At 17 blocks
242+
the current graph is already close to that ceiling.
229243
- No motion beyond the run pulse and the control transitions.
230244
- No empty, loading or error states — there is no session server yet to produce
231245
them.

README.md

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
>
1414
> Two complete links run end to end and produce numbers that match theory.
1515
> **Direct detection:** PRBS → NRZ → CW laser → MZM → fiber (loss + dispersion) → PIN → filter →
16-
> eye/Q/BER. **Coherent:** PRBS → Gray-coded M-QAM → RRC shaping → IQ modulator → 90° hybrid with
17-
> balanced detection → carrier recovery → EVM/SNR and counted errors, up to 256-QAM, and dual
18-
> polarization at 256 Gb/s with a blind butterfly equaliser.
16+
> eye/Q/BER. **Coherent:** PRBS → Gray-coded M-QAM → RRC shaping → IQ modulator → **fiber**
17+
> 90° hybrid with balanced detection → dispersion compensation → carrier recovery → EVM/SNR and
18+
> counted errors, up to 256-QAM, and dual polarization at 256 Gb/s with a blind butterfly
19+
> equaliser. The coherent chain now runs over a real span: 1000 km of fiber leaves nothing
20+
> recoverable at the photodiode, and the receiver returns it to back-to-back quality.
1921
> Every physics block is validated against a closed-form result in CI.
2022
>
2123
> Projects save to versioned JSON and sweeps are first-class, so a curve is one call rather than
@@ -26,7 +28,7 @@
2628
> realisation with the right Maxwellian statistics.
2729
>
2830
> **Not implemented yet:** cross-phase modulation and four-wave mixing between channels,
29-
> equalisers, coherent detection, and the GUI. See the [roadmap](#roadmap).
31+
> wavelength-selective filtering, and the GUI. See the [roadmap](#roadmap).
3032
>
3133
> This is not yet a useful simulator. It is a foundation with the expensive decisions made and
3234
> tested. Criticism of those decisions is worth more right now than any feature —
@@ -146,6 +148,59 @@ subtracting a constant or a line, and it puts a ceiling on SNR that no power bud
146148
search of Pfau et al. Both halves of that claim are [asserted](tests/test_dsp.py) — the second
147149
would be meaningless without the first.
148150

151+
### Reaching past the bench
152+
153+
Every coherent example above was back to back. `python examples/dispersion_link.py` puts the same
154+
32 GBd 16-QAM link through real fiber, with loss and nonlinearity switched off so that chromatic
155+
dispersion is the only thing acting:
156+
157+
```
158+
accumulated spread uncompensated compensated
159+
span [ps/nm] [symbols] EVM SNR errors EVM SNR
160+
0 km 0 0.0 1.68% 35.51 dB 0/3968 1.68% 35.51 dB
161+
5 km 85 0.8 17.04% 15.37 dB 5/3968 1.67% 35.52 dB
162+
20 km 340 3.3 217.64% -6.75 dB 3336/3968 1.67% 35.56 dB
163+
80 km 1360 13.4 2542.45% -28.11 dB 3698/3968 1.68% 35.52 dB
164+
400 km 6800 67.0 2686.86% -28.58 dB 3692/3968 1.67% 35.55 dB
165+
1000 km 17000 167.4 3390.48% -30.61 dB 3697/3968 1.68% 35.51 dB
166+
```
167+
168+
Read the uncompensated column first, because it is the reason this block exists. **Five kilometres
169+
— a metro hop — costs twenty decibels.** At 80 km the link is not degraded, it is gone: 3698 of
170+
3968 symbols wrong is 93%, and blind guessing on 16-QAM gives 93.75%. The whole coherent phase had
171+
been validated without ever meeting the impairment that dominates every real span.
172+
173+
The compensated column is the argument for coherent detection in one line. **Back-to-back quality
174+
at every distance, with no penalty that grows with it.** Dispersion is an all-pass phase — it
175+
rearranges the field in time and removes nothing — so a receiver that measures the field still
176+
holds all of it, and one static filter puts it back. That is not error correction; it is inverting
177+
an invertible operation. A direct-detection receiver squares the field at the photodiode, destroys
178+
the phase, and can never do this at all, which is why it has to carry dispersion-compensating fiber
179+
in the line instead.
180+
181+
The setting is sharp, and that is worth seeing rather than being told:
182+
183+
| compensator set to | error | EVM | SNR |
184+
| ---: | ---: | ---: | ---: |
185+
| 76 km | −68 ps/nm | 13.64% | 17.30 dB |
186+
| 79 km | −17 ps/nm | 3.78% | 28.46 dB |
187+
| **80 km** | **0** | **1.68%** | **35.52 dB** |
188+
| 81 km | +17 ps/nm | 3.77% | 28.47 dB |
189+
| 84 km | +68 ps/nm | 13.64% | 17.30 dB |
190+
191+
Being one kilometre out costs 7 dB. The symmetry of those flanks is also the sign check: a
192+
compensator applying its correction the wrong way round would put the nominal setting at *twice*
193+
the span, and the two sides would not match to three digits.
194+
195+
**Why this is a separate stage from the butterfly equaliser.** Both are linear filters, so one
196+
adaptive filter could in principle do both jobs. It does not work. On the dual-polarization link
197+
over 80 km, growing the butterfly from 7 taps to 65 — the longest the block allows, and nine times
198+
the cost — leaves the link just as dead, because a blind modulus criterion has no gradient to
199+
follow once the constellation is smeared into a Gaussian blob. The static block ahead of a 7-tap
200+
filter restores back-to-back quality outright. Dispersion is static and *long*; polarization
201+
mixing is fast and *short*; one filter serving both would have to be both, which is the worst of
202+
each. That ordering is [asserted](tests/test_cd_compensation.py), not quoted.
203+
149204
### Dual polarization
150205

151206
`python examples/dualpol_link.py` puts two independent 16-QAM tributaries on orthogonal
@@ -359,8 +414,8 @@ time window, and results are reproducible.
359414
| **0 — Foundations**| Signal model, context, port types, component base, registry, scheduler, `.oosim` project format, sweeps, CI | ~1 month |
360415
| **1 — MVP: linear link** *(essentially done)* | ✅ PRBS → NRZ → laser → MZM → fiber (α + CD) → PIN → filter → eye/Q/BER, validated end to end. **Python only, no GUI.** | ~2–3 months |
361416
| **1.5 — Nonlinear & amplified**| Adaptive-step SSFM, Kerr, EDFA with ASE, OSNR, PMD, APD | ~2 months |
362-
| **2 — Coherent transceiver**| Gray-coded M-QAM to 256, IQ modulator with bias and quadrature error, 90° hybrid, balanced detection, blind carrier phase recovery, dual polarization with a blind butterfly equaliser, root-raised-cosine shaping and matched filtering, differential quadrant encoding, EVM/MER, constellation diagram, validated against closed-form SER | ~3 months |
363-
| **3 — GUI & WDM** | Session server, React Flow graph editor, OSA · DWDM + XPM/FWM crosstalk, 400G/800G references, CuPy back-end | ~6 months |
417+
| **2 — Coherent transceiver**| Gray-coded M-QAM to 256, IQ modulator with bias and quadrature error, 90° hybrid, balanced detection, blind carrier phase recovery, dual polarization with a blind butterfly equaliser, root-raised-cosine shaping and matched filtering, differential quadrant encoding, receiver-side dispersion compensation over spans to 1000 km, EVM/MER, constellation diagram, validated against closed-form SER | ~3 months |
418+
| **3 — GUI & WDM** | Session server, React Flow graph editor, wavelength-selective filters and an OSA · DWDM + XPM/FWM crosstalk, 400G/800G references, CuPy back-end | ~6 months |
364419
| **4 — PIC** | Waveguides, ring resonators, MMI, MZI via integration with an existing S-matrix solver; PDK import ||
365420

366421
¹ One developer, part-time. Estimates, not commitments.
@@ -383,6 +438,10 @@ Every physics block ships with a test against a closed-form result, run in CI
383438
| Gaussian pulse, CD only | `T₁/T₀ = √(1 + (z/L_D)²)`, `L_D = T₀²/\|β₂\|` ||
384439
| Chirped Gaussian | `T₁/T₀ = √((1 + Cβ₂z/T₀²)² + (β₂z/T₀²)²)` — pins the sign of β₂ ||
385440
| Dispersion compensation | `+D` then `−D` restores the input sample-for-sample ||
441+
| Receiver-side CD removal | Compensator is the propagator inverted; round trip exact to 1e-9 ||
442+
| CD compensation is all-pass | Energy conserved; a wrong sign lands exactly on twice the span ||
443+
| β₂ ∝ λ² | Compensating 1550 nm as 1310 nm leaves the predicted `1 − λ₁²/λ₂²` residual ||
444+
| **Span recovery** | 5 km to 1000 km return to back-to-back EVM; uncompensated 80 km is at chance ||
386445
| GVD | Energy conserved (Parseval); β₂ = −Dλ²/2πc per band ||
387446
| PRBS | Period `2ⁿ−1`; `2ⁿ⁻¹` marks; every n-bit window appears once ||
388447
| Ideal push-pull MZM | `P_out/P_in = cos²(πV / 2V_π)`; null depth equals the declared ER ||

0 commit comments

Comments
 (0)