Channels#
- class PhaseNoise(sigma2: float, *, per: Literal['pair', 'row', 'signal'] = 'pair', seed: int | None = None, name: str = 'phase noise')[source]#
Wiener (random-walk) phase noise channel.
Signal Model#
\[y[n] = x[n] \, e^{j \phi[n]}, \qquad \phi[n] = \sum_{k=0}^{n} \delta\phi_k, \qquad \delta\phi_k \sim \mathcal{N}\left(0, \sigma^2\right)\]where \(\sigma^2\) is the variance of the independent phase increments \(\delta\phi_k\) (a Wiener process accumulated along the samples). The magnitude \(|y[n]| = |x[n]|\) is preserved.
Axes: axis -1 – the phase random walk accumulates along the samples. What shares one walk is declared by
per, which names the event – the slice of the array that sees one laser:"pair"(default): one walk per trailing(2, N)block – the two rows of a polarization pair come from one laser, and every leading axis is batch: independent draws per trial. A 2-D or deeper input whose axis -2 is not 2 is refused, because it cannot be read both ways: use"row"for independent rows or"signal"for one shared walk."row": one independent walk per 1-D row."signal": one walk broadcast over the whole array.
A 1-D input sees one walk under every setting.
- param sigma2:
Variance \(\sigma^2\) of the per-sample phase increments, in rad^2.
- type sigma2:
float
- param per:
The event that shares one laser. Default is
"pair".- type per:
{“pair”, “row”, “signal”}, optional, keyword-only
- param seed:
Local RNG seed.
- type seed:
int, optional, keyword-only
- param name:
Name of the channel instance. Default is
"phase noise".- type name:
str, optional, keyword-only
References
T. Pfau, S. Hoffmann, R. Noe, “Hardware-Efficient Coherent Digital Receiver Concept With Feedforward Carrier Recovery for M-QAM Constellations,” Journal of Lightwave Technology, vol. 27, no. 8, pp. 989-999, 2009.
Examples
>>> x = np.ones(3, dtype=complex) >>> y = PhaseNoise(0.01, seed=0)(x) >>> print(np.round(np.abs(y), 6)) [1. 1. 1.]
- class ChromaticDispersion(z: float, *, fs: float = 1, alpha_dB: float = 0, direction: int = 1, name: str = 'cd', lamb: float = 1550, D: float = 17, c: float = 299792458)[source]#
Chromatic dispersion (CD) of a fiber span, applied exactly in the frequency domain.
Signal Model#
The block solves the linear part of the nonlinear Schroedinger equation (NLSE) for the complex field envelope \(A(z, t)\):
\[\frac{\partial A(z, t)}{\partial z} = -j \frac{\beta_2}{2} \frac{\partial^2 A(z, t)}{\partial t^2} - \frac{\alpha}{2} A(z, t)\]whose exact solution over a fiber of length \(z\) is applied in the frequency domain:
\[\hat{A}(z, \omega) = \hat{A}(0, \omega) \, e^{j \frac{\beta_2}{2} z \omega^2} \, e^{-\frac{\alpha}{2} z}\]with the group velocity dispersion \(\beta_2\) (ps^2/km) derived from the dispersion coefficient \(D\), the wavelength \(\lambda\) and the speed of light \(c\), and the attenuation \(\alpha = \frac{\ln 10}{10} \, \alpha_{dB}\) (Np/km):
\[\beta_2 = -\frac{10^3 \, D \lambda^2}{2 \pi c}\]With
direction=-1the phase shift and the attenuation are both inverted (backward propagation, used for CD compensation).Axes: axis -1 – the transfer function is applied along the last axis; leading axes are batch, every row through the same fibre. \(\beta_2\) is given in ps^2/km and converted internally to s^2/km (factor \(10^{-24}\)), so the exponent is dimensionless.
- param z:
Fiber length \(z\) in km.
- type z:
float
- param fs:
Sampling frequency in Hz; sets the discrete pulsation grid \(\omega\). Default is 1.
- type fs:
float, keyword-only
- param alpha_dB:
Attenuation \(\alpha_{dB}\) in dB/km. Default is 0 (lossless).
- type alpha_dB:
float, keyword-only
- param direction:
Propagation direction, 1 for forward and -1 for backward. Default is 1.
- type direction:
int, keyword-only
- param lamb:
Wavelength \(\lambda\) in nm. Default is
WAVELENGTH.- type lamb:
float, keyword-only
- param D:
Dispersion coefficient \(D\) in ps/nm/km. Default is
CD_COEFFICIENT.- type D:
float, keyword-only
- param c:
Speed of light \(c\) in m/s. Default is
SPEED_OF_LIGHT.- type c:
float, keyword-only
- param name:
Identifier for the dispersion instance. Default is
"cd".- type name:
str, optional, keyword-only
References
G. P. Agrawal, Nonlinear Fiber Optics, 5th ed., Academic Press, 2013, Section 3.2.
S. J. Savory, “Digital filters for coherent optical receivers,” Optics Express, vol. 16, no. 2, pp. 804-817, 2008.
A. Shahkarami, “Complexity reduction over bi-RNN-based Kerr nonlinearity equalization in dual-polarization fiber-optic communications via a CRNN-based approach,” Dissertation, Institut polytechnique de Paris, 2022. URL: https://www.theses.fr/2022IPPAT034.
Examples
>>> rng = np.random.default_rng(0) >>> x = rng.normal(size=64) + 1j * rng.normal(size=64) >>> cd = ChromaticDispersion(80.0, fs=10e9) >>> cd_back = ChromaticDispersion(80.0, fs=10e9, direction=-1) >>> bool(np.allclose(cd_back(cd(x)), x)) True
- class KerrNonLinearity(z: float, *, direction: int = 1, name: str = 'nl', gamma: float = 1.3, gain: float = 1)[source]#
Kerr nonlinearity (self-phase modulation) of a fiber segment.
Signal Model#
The block solves the nonlinear part of the nonlinear Schroedinger equation (NLSE) for the complex field envelope \(A(z, t)\):
\[\frac{\partial A(z, t)}{\partial z} = j \gamma |A(z, t)|^2 A(z, t)\]whose exact solution over a segment of length \(z\) is the intensity-dependent phase rotation
\[A(z, t) = g \, A(0, t) \, e^{j \gamma z |A(0, t)|^2}\]where \(\gamma\) is the Kerr coefficient (rad/W/km) and \(g\) an optional amplitude gain. With
direction=-1the phase rotation is conjugated (backward propagation, used in DBP).Axes: element-wise – the rotation is pointwise, leading axes are batch, and the shape is not validated. The signal must be the full field: a pointwise Kerr step applied to separate channels would model SPM only (no XPM, no FWM).
- param z:
Segment length \(z\) in km.
- type z:
float
- param direction:
Propagation direction, 1 for forward and -1 for backward. Default is 1.
- type direction:
int, keyword-only
- param gamma:
Kerr coefficient \(\gamma\) in rad/W/km. Default is
KERR_COEFFICIENT.- type gamma:
float, keyword-only
- param gain:
Amplitude gain \(g\) (linear). Default is 1.
- type gain:
float, keyword-only
- param name:
Identifier for the nonlinearity instance. Default is
"nl".- type name:
str, optional, keyword-only
References
G. P. Agrawal, Nonlinear Fiber Optics, 5th ed., Academic Press, 2013, Section 4.1.
C. Häger and H. D. Pfister, “Physics-based deep learning for fiber-optic communication systems,” IEEE Journal on Selected Areas in Communications, vol. 39, no. 1, pp. 280-294, 2021.
Examples
>>> x = np.array([1.0 + 0j]) >>> y = KerrNonLinearity(10, gamma=1.3)(x) >>> print(round(float(np.angle(y[0])), 4)) 0.4336
- class PMDEmulator(dgd: float, *, n_sections: int = 8, fs: float = 1.0, seed: int | None = None, name: str = 'pmd')[source]#
Polarization rotation and first-order PMD, as a section emulator.
Signal Model#
The fibre’s random birefringence is emulated by \(K\) sections, each a random unitary Jones rotation followed by a differential group delay (DGD) of \(\tau / \sqrt{K}\) between the two principal states, applied exactly in the frequency domain:
\[\mathbf{Y}(\omega) = \prod_{k=1}^{K} \mathbf{D}_k(\omega) \, \mathbf{R}_k \; \mathbf{X}(\omega), \qquad \mathbf{D}_k(\omega) = \mathrm{diag}\!\left( e^{+j \omega \tau / 2\sqrt{K}}, \; e^{-j \omega \tau / 2\sqrt{K}} \right)\]Each \(\mathbf{R}_k\) is drawn Haar-uniform from the seeded local generator, so the emulator is unitary – it conserves energy exactly – and reproducible. With
n_sections=1anddgd=0it reduces to one random rotation of the state of polarization, which is the part every polarization demultiplexer must undo even before PMD enters.Randomly oriented sections add their DGDs in quadrature, hence the \(1/\sqrt{K}\) per section: the declared
dgdis the RMS DGD of the ensemble. Over section draws the DGD is Maxwellian, with mean \(\sqrt{8/3\pi}\,\tau \approx 0.921\,\tau\) (Poole & Wagner). Withn_sections=1the DGD is deterministic and equals \(\tau\) exactly. The concatenation of sections also makes the DGD frequency-dependent, which is what distinguishes PMD from a wavelength-flat rotation.Axes: polarization pair – expects
(..., 2, N); the Jones matrices act on the polarization axis, the delays on the last one.- param dgd:
RMS differential group delay \(\tau\) in seconds.
- type dgd:
float
- param n_sections:
Number of concatenated sections \(K\). Default is 8.
- type n_sections:
int, optional, keyword-only
- param fs:
Sampling frequency in Hz. Default is 1.0.
- type fs:
float, optional, keyword-only
- param seed:
Local RNG seed for the section rotations. Same seed, same fibre.
- type seed:
int, optional, keyword-only
- param name:
Block name. Default
"pmd".- type name:
str, optional, keyword-only
- raises ShapeError:
If the input does not carry a polarization pair
(..., 2, N).
References
C. D. Poole and R. E. Wagner, “Phenomenological approach to polarisation dispersion in long single-mode fibres,” Electronics Letters, vol. 22, no. 19, pp. 1029-1030, 1986. S. J. Savory, “Digital filters for coherent optical receivers,” Optics Express, vol. 16, no. 2, pp. 804-817, 2008.
Examples
>>> x = np.zeros((2, 8), dtype=complex); x[0] = 1.0 >>> y = PMDEmulator(0.0, n_sections=1, seed=1)(x) >>> bool(np.abs(np.sum(np.abs(y) ** 2) - 8.0) < 1e-9) # unitary True >>> bool(np.abs(y[1, 0]) > 0) # the rotation mixes the two rows True