anomaly-detection-material-parameters-calibration

Sionna param calibration (research proj)
git clone https://git.ea.contact/anomaly-detection-material-parameters-calibration
Log | Files | Refs | README

edfa.py (6476B)


      1 #
      2 # SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
      3 # SPDX-License-Identifier: Apache-2.0
      4 #
      5 
      6 """
      7 This module defines a model for an Erbium-Doped Fiber Amplifier.
      8 """
      9 import tensorflow as tf
     10 from tensorflow.keras.layers import Layer
     11 import sionna
     12 
     13 class EDFA(Layer):
     14     # pylint: disable=line-too-long
     15     r"""EDFA(g=4.0,f=7.0,f_c=193.55e12,dt=1e-12,with_dual_polarization=False,dtype=tf.complex64,**kwargs)
     16 
     17     Layer implementing a model of an Erbium-Doped Fiber Amplifier
     18 
     19     Amplifies the optical input signal by a given gain and adds
     20     amplified spontaneous emission (ASE) noise.
     21 
     22     The noise figure including the noise due to beating of signal and
     23     spontaneous emission is :math:`F_\mathrm{ASE,shot} =\frac{\mathrm{SNR}
     24     _\mathrm{in}}{\mathrm{SNR}_\mathrm{out}}`,
     25     where ideally the detector is limited by shot noise only, and
     26     :math:`\text{SNR}` is the signal-to-noise-ratio. Shot noise is
     27     neglected here but is required to derive the noise power of the amplifier, as
     28     otherwise the input SNR is infinitely large. Hence, for the input SNR,
     29     it follows [A2012]_ that
     30     :math:`\mathrm{SNR}_\mathrm{in}=\frac{P}{2hf_cW}`, where :math:`h` denotes
     31     Planck's constant, :math:`P` is the signal power, and :math:`W` the
     32     considered bandwidth.
     33     The output SNR is decreased by ASE noise induced by the amplification.
     34     Note that shot noise is applied after the amplifier and is hence not
     35     amplified. It results that :math:`\mathrm{SNR}_\mathrm{out}=\frac{GP}{\left
     36     (4\rho_\mathrm{ASE}+2hf_c\right)W}`, where :math:`G` is the
     37     parametrized gain.
     38     Hence, one can write the former equation as :math:`F_\mathrm{ASE,shot} = 2
     39     n_\mathrm{sp} \left(1-G^{-1}\right) + G^{-1}`.
     40     Dropping shot noise again results in :math:`F = 2 n_\mathrm{sp} \left(1-G^
     41     {-1}\right)=2 n_\mathrm{sp} \frac{G-1}{G}`.
     42 
     43     For a transparent link, e.g., the required gain per span is :math:`G =
     44     \exp\left(\alpha \ell \right)`.
     45     The spontaneous emission factor is :math:`n_\mathrm{sp}=\frac{F}
     46     {2}\frac{G}{G-1}`.
     47     According to [A2012]_ and [EKWFG2010]_ combined with [BGT2000]_ and [GD1991]_,
     48     the noise power spectral density of the EDFA per state of
     49     polarization is obtained as :math:`\rho_\mathrm{ASE}^{(1)} = n_\mathrm{sp}\left
     50     (G-1\right) h f_c=\frac{1}{2}G F h f_c`.
     51     At simulation frequency :math:`f_\mathrm{sim}`, the noise has a power of
     52     :math:`P_\mathrm{ASE}^{(1)}=\sigma_\mathrm{n,ASE}^2=2\rho_\mathrm{ASE}^{(1)}
     53     \cdot f_\mathrm{sim}`,
     54     where the factor :math:`2` accounts for the unpolarized noise (for dual
     55     polarization the factor is :math:`1` per polarization).
     56     Here, the notation :math:`()^{(1)}` means that this is the noise introduced by a
     57     single EDFA.
     58 
     59     This class inherits from the Keras `Layer` class and can be used as layer in
     60     a Keras model.
     61 
     62     Example
     63     --------
     64 
     65     Setting-up:
     66 
     67     >>> edfa = EDFA(
     68     >>>     g=4.0,
     69     >>>     f=2.0,
     70     >>>     f_c=193.55e12,
     71     >>>     dt=1.0e-12,
     72     >>>     with_dual_polarization=False)
     73 
     74     Running:
     75 
     76     >>> # x is the optical input signal
     77     >>> y = EDFA(x)
     78 
     79     Parameters
     80     ----------
     81         g : float
     82             Amplifier gain (linear domain). Defaults to 4.0.
     83 
     84         f : float
     85             Noise figure (linear domain). Defaults to 7.0.
     86 
     87         f_c : float
     88             Carrier frequency :math:`f_\mathrm{c}` in :math:`(\text{Hz})`.
     89             Defaults to 193.55e12.
     90 
     91         dt : float
     92             Time step :math:`\Delta_t` in :math:`(\text{s})`.
     93             Defaults to 1e-12.
     94 
     95         with_dual_polarization : bool
     96             Considers axis [-2] as x- and y-polarization and applies the noise
     97             per polarization. Defaults to `False`.
     98 
     99         dtype : tf.complex
    100             Defines the datatype for internal calculations and the output
    101             dtype. Defaults to `tf.complex64`.
    102 
    103     Input
    104     -----
    105         x : Tensor, tf.complex
    106             Optical input signal
    107 
    108     Output
    109     -------
    110         y : Tensor with same shape as ``x``, ``dtype``
    111             Amplifier output
    112     """
    113     def __init__(
    114             self,
    115             g=4.0,
    116             f=7.0,
    117             f_c=193.55e12,
    118             dt=1e-12,
    119             with_dual_polarization=False,
    120             dtype=tf.complex64,
    121             **kwargs):
    122 
    123         super().__init__(dtype=dtype, **kwargs)
    124 
    125         self._dtype = dtype
    126         self._cdtype = tf.as_dtype(dtype)
    127         self._rdtype = tf.as_dtype(dtype).real_dtype
    128 
    129         self._g = tf.cast(g, self._rdtype)
    130         self._f = tf.cast(f, self._rdtype)
    131         self._f_c = tf.cast(f_c, self._rdtype)
    132         self._dt = tf.cast(dt, self._rdtype)
    133 
    134         assert isinstance(with_dual_polarization, bool), \
    135                             "with_dual_polarization must be bool."
    136         self._with_dual_polarization = with_dual_polarization
    137 
    138         # Spontaneous emission factor
    139         if self._g == 1.0:
    140             self._n_sp = tf.cast(0.0, self._rdtype)
    141         else:
    142             self._n_sp = self._f / tf.cast(
    143                 2.0, self._rdtype) * self._g / (
    144                                  self._g - tf.cast(1.0, self._rdtype))
    145 
    146         self._rho_n_ase = tf.cast(
    147             self._n_sp * (self._g - tf.cast(1.0, self._rdtype)) *
    148             sionna.constants.H * self._f_c,
    149             self._rdtype)  # Noise density in (W/Hz)
    150         self._p_n_ase = tf.cast(
    151             2.0, self._rdtype) * self._rho_n_ase * tf.cast(
    152             1.0, self._rdtype) / (self._dt)  # Noise power in (W)
    153 
    154         if self._with_dual_polarization:
    155             self._p_n_ase = self._p_n_ase / tf.cast(2.0, self._rdtype)
    156 
    157     def call(self, inputs):
    158         if self._with_dual_polarization:
    159             tf.assert_equal(tf.shape(inputs)[-2], 2)
    160 
    161         x = tf.cast(inputs, self._cdtype)
    162 
    163         # Calculate noise signal with given noise power
    164         n = tf.complex(
    165             sionna.config.tf_rng.normal(
    166                 tf.shape(x),
    167                 tf.cast(0.0, self._rdtype),
    168                 tf.sqrt(self._p_n_ase / tf.cast(2.0, self._rdtype)),
    169                 self._rdtype),
    170             sionna.config.tf_rng.normal(
    171                 tf.shape(x),
    172                 tf.cast(0.0, self._rdtype),
    173                 tf.sqrt(self._p_n_ase / tf.cast(2.0, self._rdtype)),
    174                 self._rdtype))
    175 
    176         # Amplify signal
    177         x = x * tf.cast(tf.sqrt(self._g), self._cdtype)
    178 
    179         # Add noise signal
    180         y = x + n
    181 
    182         return y