Impairments#

class IQImbalance(alpha: complex, beta: complex, *, name: str = 'iq_impairment')[source]#

Apply an IQ imbalance impairment.

Signal Model#

The output is a widely-linear combination of the input signal and its complex conjugate:

\[y[n] = \alpha x[n] + \beta x^*[n]\]

Axes: element-wise – applied pointwise, shape-agnostic.

param alpha:

Complex weight \(\alpha\) applied to the direct signal \(x[n]\).

type alpha:

complex

param beta:

Complex weight \(\beta\) applied to the conjugate signal \(x^*[n]\).

type beta:

complex

param name:

Name of the impairment instance. Default is "iq_impairment".

type name:

str, optional, keyword-only

References

I. Fatadin, S. J. Savory, D. Ives, “Compensation of Quadrature Imbalance in an Optical QPSK Coherent Receiver,” IEEE Photonics Technology Letters 20(20), 2008.

Examples

>>> iq = IQImbalance(alpha=1.0, beta=0.1)
>>> print(iq(np.array([1.0+1.0j, 1.0-1.0j])))
[1.1+0.9j 1.1-0.9j]
class CFO(cfo: float, *, name: str = 'cfo_impairment')[source]#

Apply a Carrier Frequency Offset (CFO).

Signal Model#

The input signal is multiplied by a complex exponential phase ramp:

\[y[n] = x[n] \, e^{j \omega_0 n}\]

where \(\omega_0\) is the normalized carrier frequency offset in rad/sample.

Axes: axis -1 – the time index \(n\) runs along the last axis; leading axes are batch, every row sees the same offset.

param cfo:

Normalized carrier frequency offset \(\omega_0\) in rad/sample.

type cfo:

float

param name:

Name of the impairment instance. Default is "cfo_impairment".

type name:

str, optional, keyword-only

References

J. G. Proakis, M. Salehi, Digital Communications, 5th ed., McGraw-Hill, 2008, Chapter 5.

Examples

>>> cfo = CFO(cfo=np.pi/2)
>>> y = cfo(np.ones(4, dtype=complex))
>>> print(np.round(y, 2) + 0.0)
[ 1.+0.j  0.+1.j -1.+0.j  0.-1.j]
class Delay(tau: int, *, pad_zeros: bool = True, name: str = 'delay_impairment')[source]#

Discard the first \(\tau\) samples of the input signal (delay removal).

Signal Model#

The block drops the first \(\tau\) samples, i.e. it advances the signal by \(\tau\) samples:

\[y[n] = x[n + \tau], \qquad 0 \le n < N - \tau\]

When pad_zeros is True, the output is zero-padded at the end to keep the input length \(N\); otherwise the output has length \(N - \tau\).

Axes: axis -1 – samples are dropped along the last axis; leading axes are batch.

The name is historical: the block models the receiver-side removal of a known delay – it advances the signal – which is why its model reads \(y[n] = x[n + \tau]\).

param tau:

Number of samples \(\tau\) to discard (non-negative integer).

type tau:

int

param pad_zeros:

If True (default), pads the output with trailing zeros to match the input size \(N\).

type pad_zeros:

bool, optional, keyword-only

param name:

Name of the impairment instance. Default is "delay_impairment".

type name:

str, optional, keyword-only

raises ValueError:

If tau is negative (validated at construction).

References

H. Meyr, M. Moeneclaey, S. Fechtel, Digital Communication Receivers, Wiley, 1998, Chapter 4 (timing offsets).

Examples

>>> delay = Delay(2)
>>> print(delay(np.array([1.0, 2.0, 3.0, 4.0])))
[3. 4. 0. 0.]