Utilities functions

Use this module for utility functions.

>>> import opticomlib.utils as ut

Functions

dec2bin(num[, digits])

Converts an integer to its binary representation.

str2array(string[, dtype])

Converts a string to array of numbers.

get_time(line_of_code, n)

Get the average time of execution of a line of code.

tic()

Start a timer.

toc()

Stop a timer.

db(x)

Calculates the logarithm in base 10 of the input x and multiplies it by 10.

dbm(x)

Calculates dBm from Watts.

idb(x)

Calculates the number value from a dB value.

idbm(x)

Calculates the power value in Watts from a dBm value.

gaus(x[, mu, std])

Gaussian function.

Q(x)

Q-function.

phase(H)

Calculate the unwrapped phase of a frequency response.

tau_g(H, fs)

Calculate the group delay of a frequency response.

dispersion(H, fs, f0)

Calculate the dispersion of a frequency response.

bode(H, fs[, f0, xaxis, disp, yscale, ret, ...])

Plot the Bode plot of a given transfer function H (magnitude, phase and group delay).

rcos(x, alpha, T)

Raised cosine spectrum function.

si(x[, unit, k])

Unit of measure classifier.

norm(x)

Normalize an array by dividing each element by the maximum value in the array.

nearest(x, a)

Find the nearest value in an array.

theory_BER(P_avg, modulation[, M, decision, ...])

This function calculates the bit error rate (BER) for an OPTICAL RECEIVER, based on a PIN photodetector, from the average input power.

p_ase([amplify, wavelength, G, NF, BW_opt])

Calculate the ASE noise power [Watts].

average_voltages(P_avg, modulation[, M, ER, ...])

Calculate the average voltages of the ON and OFF slots [Voltages].

noise_variances(P_avg, modulation[, M, ER, ...])

Calculate the theoretical noise variances for OFF and ON slots, include sig-ase, ase-ase, thermal and shot noises [V^2].

optimum_threshold(mu0, mu1, S0, S1, modulation)

Calculate the optimum threshold for binary modulation formats.

shortest_int(data[, percent])

Estimation of the shortest interval containing percent of the samples in 'data'.

opticomlib.utils.dec2bin(num: int, digits: int = 8)[source]

Converts an integer to its binary representation.

Parameters:
  • num (int) – Integer to convert.

  • digits (int, default: 8) – Number of bits of the binary representation.

Returns:

Binary representation of num of length digits.

Return type:

binary_sequence

Raises:

ValueError – If num is too large to be represented with digits bits.

Example

>>> dec2bin(5, 4)
array([0, 1, 0, 1], dtype=uint8)
opticomlib.utils.str2array(string: str, dtype: bool | int | float | complex | None = None)[source]

Converts a string to array of numbers. Use comma (,) or whitespace (`` ) as element separators and semicolon (;``) as row separator. Also, i or j can be used to represent the imaginary unit.

Parameters:
  • string (str) – String to convert.

  • dtype (type, optional) – Data type of the output array. If dtype is not given, the data type is determined from the input string. If dtype is given, the data output is cast to the given type. Allowed values are bool, int, float and complex.

Returns:

arr – Numeric array.

Return type:

np.ndarray

Raises:

ValueError – If the string contains invalid characters.

Example

For binary numbers, string must contain only 0 and 1. Only in this case, sequence don’t need to be separated by commas or spaces although it is allowed.

>>> str2array('101')
array([ True, False, True])
>>> str2array('1 0 1; 0 1 0')
array([[ True, False,  True],
       [False,  True, False]])

Special case >>> str2array(‘1 0 1 10’) array([True, False, True, True, False]) >>> str2array(‘1 0 1 10’, dtype=int) array([ 1, 0, 1, 10]) >>> str2array(‘1 0 1 10’, dtype=float) array([ 1., 0., 1., 10.]) >>> str2array(‘1 0 1 10’, dtype=complex) array([ 1.+0.j, 0.+0.j, 1.+0.j, 10.+0.j])

For integer and float numbers >>> str2array(‘1 2 3 4’) array([1, 2, 3, 4]) >>> str2array(‘1.1 2.2 3.3 4.4’) array([1.1, 2.2, 3.3, 4.4])

For complex numbers >>> str2array(‘1+2j 3-4i’) array([1.+2.j, 3.-4.j])

opticomlib.utils.get_time(line_of_code: str, n: int)[source]

Get the average time of execution of a line of code.

Parameters:
  • line_of_code (str) – Line of code to execute.

  • n (int) – Number of iterations.

Returns:

time – Average time of execution, in seconds.

Return type:

float

Example

>>> get_time('for i in range(1000): pass', 1000)
1.1955300000010993e-05
opticomlib.utils.tic()[source]

Start a timer. Create a global variable with the current time. Then you can use toc() to get the elapsed time.

Example

>>> tic() # wait some time
>>> toc()
2.687533378601074
opticomlib.utils.toc()[source]

Stop a timer. Get the elapsed time since the last call to tic().

Returns:

time – Elapsed time, in seconds.

Return type:

float

Example

>>> tic() # wait some time
>>> toc()
2.687533378601074
opticomlib.utils.db(x)[source]

Calculates the logarithm in base 10 of the input x and multiplies it by 10.

\[\text{db} = 10\log_{10}{x}\]
Parameters:

x (Number or Array_Like) – Input value (x>=0).

Returns:

out – dB value.

Return type:

float or np.ndarray

Raises:
  • TypeError – If x is not a number, list, tuple or ndarray.

  • ValueError – If x or any(x) < 0.

Example

>>> db(1)
0.0
>>> db([1,2,3,4])
array([0.        , 3.01029996, 4.77121255, 6.02059991])
opticomlib.utils.dbm(x)[source]

Calculates dBm from Watts.

\[\text{dbm} = 10\log_{10}{x}+30\]
Parameters:

x (Number or Array_Like) – Input value (x>=0).

Returns:

out – dBm value. If x is a number, then the output is a float. If x is an array_like, then the output is an np.ndarray.

Return type:

float or np.ndarray

Raises:
  • TypeError – If x is not a number, list, tuple or ndarray.

  • ValueError – If x or any(x) < 0.

Example

>>> dbm(1)
30.0
>>> dbm([1,2,3,4])
array([30.        , 33.01029996, 34.77121255, 36.02059991])
opticomlib.utils.idb(x)[source]

Calculates the number value from a dB value.

\[y = 10^{\frac{x}{10}}\]
Parameters:

x (Number or Array_Like) – Input value.

Returns:

out – Number value. If x is a number, then the output is a float. If x is an array_like, then the output is an np.ndarray.

Return type:

float or np.ndarray

Example

>>> idb(3)
1.9952623149688795
>>> idb([0,3,6,9])
array([1.        , 1.99526231, 3.98107171, 7.94328235])
opticomlib.utils.idbm(x)[source]

Calculates the power value in Watts from a dBm value.

\[y = 10^{(\frac{x}{10}-3)}\]
Parameters:

x (Number or Array_Like) – Input value.

Returns:

out – Power value in Watts. If x is a number, then the output is a float. If x is an array_like, then the output is an np.ndarray.

Return type:

float or np.ndarray

Example

>>> idbm(0)
0.001
>>> idbm([0,3,6,9])
array([0.001     , 0.00199526, 0.00398107, 0.00794328])
opticomlib.utils.gaus(x, mu: float = None, std: float = None)[source]

Gaussian function.

\[\text{gaus}(x) = \frac{1}{\sigma\sqrt{2\pi}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}\]
Parameters:
  • x (Number or Array_Like) – Input value.

  • mu (float, default: 0) – Mean.

  • std (float, default: 1) – Standard deviation.

Returns:

out – Gaussian function value. If x is a number, then the output is a float. If x is an array_like, then the output is an np.ndarray.

Return type:

float or np.ndarray

Examples

>>> gaus(0, 0, 1)
0.3989422804014327
>>> gaus([0,1,2,3], 0, 1)
array([0.39894228, 0.24197072, 0.05399097, 0.00443185])
from opticomlib import gaus
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-5, 5, 1000)
y = gaus(x, 0, 1)

plt.figure(figsize=(8, 5))
plt.plot(x, y, 'r', lw=2)
plt.ylabel('y')
plt.xlabel('x')
plt.grid(alpha=0.3)
plt.show()

(Source code, png, hires.png, pdf)

Gaussian function
opticomlib.utils.Q(x)[source]

Q-function.

\[Q(x) = \frac{1}{2}\text{erfc}\left( \frac{x}{\sqrt{2}} \right)\]
Parameters:

x (Numper or Array_Like) – Input value.

Returns:

out – Q(x) values. If x is a number, then the output is a float. If x is an array_like, then the output is an np.ndarray.

Return type:

float or np.ndarray

Examples

>>> Q(0)
0.5
>>> Q([0,1,2,3])
array([0.5       , 0.15865525, 0.02275013, 0.0013499 ])
from opticomlib import Q
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-5, 5, 1000)

plt.figure(figsize=(8, 5))
plt.plot(x, Q(x), 'r', lw=3, label='Q(x)')
plt.plot(x, Q(-x), 'b', lw=3, label='Q(-x)')
plt.ylabel('y')
plt.xlabel('x')
plt.legend()
plt.grid()
plt.show()

(Source code, png, hires.png, pdf)

Gaussian function
opticomlib.utils.phase(H: ndarray)[source]

Calculate the unwrapped phase of a frequency response.

Parameters:

H (np.ndarray) – Frequency response of a system.

Returns:

phase – Unwrapped phase in radians.

Return type:

np.ndarray

Examples

from opticomlib import phase
import matplotlib.pyplot as plt
import numpy as np

t = np.linspace(-5, 5, 1000)
y = np.exp(1j*t**2)
phi = phase(y)

plt.figure(figsize=(8, 5))
plt.plot(t, phi, 'r', lw=2)
plt.ylabel('phase [rad]')
plt.xlabel('t')
plt.grid(alpha=0.3)
plt.show()

(Source code, png, hires.png, pdf)

Gaussian function
opticomlib.utils.tau_g(H: ndarray, fs: float)[source]

Calculate the group delay of a frequency response.

Parameters:
  • H (np.ndarray) – Frequency response of a system.

  • fs (float) – Sampling frequency of the system.

Returns:

tau – Group delay of the system, in [ps].

Return type:

np.ndarray

Examples

from opticomlib import tau_g
import matplotlib.pyplot as plt
import numpy as np

t = np.linspace(-5, 5, 1000)
y = np.exp(1j*t**2)
phi = tau_g(y, 1e2)

plt.figure(figsize=(8, 5))
plt.plot(t[:-1], phi, 'r', lw=2)
plt.ylabel(r'$\tau_g$ [ps]')
plt.xlabel('t')
plt.grid(alpha=0.3)
plt.show()

(Source code, png, hires.png, pdf)

Gaussian function
opticomlib.utils.dispersion(H: ndarray, fs: float, f0: float)[source]

Calculate the dispersion of a frequency response.

Parameters:
  • H (np.ndarray) – Frequency response of a system.

  • fs (float) – Sampling frequency of the system.

  • f0 (float) – Center frequency of the system.

Returns:

D – Cumulative dispersion of the system, in [ps/nm].

Return type:

np.ndarray

opticomlib.utils.bode(H: ndarray, fs: float, f0: float = None, xaxis: Literal['f', 'w', 'lambda'] = 'f', disp: bool = False, yscale: Literal['linear', 'db'] = 'linear', ret: bool = False, retAxes: bool = False, show_: bool = True, style: Literal['dark', 'light'] = 'dark', xlim: tuple = None)[source]

Plot the Bode plot of a given transfer function H (magnitude, phase and group delay).

Parameters:
  • H (np.ndarray) – The transfer function.

  • fs (float) – The sampling frequency.

  • f0 (float, default: None) – The center frequency. If not None, dispersion are also plotted.

  • xaxis (str, default: ‘f’) – The x-axis (frequency, angular velocity, wavelength).

  • disp (bool, default: False) – Whether to plot the dispersion.

  • ret (bool, default: False) – Whether to return the plotted data.

  • show (bool, default: True) – Whether to display the plot.

  • style (str, default: ‘dark’) – The plot style.

Returns:

(f, H, phase, tau_g) – A tuple containing the frequency, magnitude, phase, and group delay if ret=True.

Return type:

np.ndarray

Raises:

ValueError – If style is not “dark” or “light”.

Example

>>> from opticomlib import bode
>>> H, phase, tau_g = bode(H, fs, ret=True, show_=False)
opticomlib.utils.rcos(x, alpha, T)[source]

Raised cosine spectrum function.

Parameters:
  • x (Number or Array_Like) – Input values.

  • alpha (float) – Roll-off factor.

  • T (float) – Symbol period.

Returns:

Raised cosine function.

Return type:

np.ndarray

Example

https://en.wikipedia.org/wiki/Raised-cosine_filter

from opticomlib import rcos
import matplotlib.pyplot as plt
import numpy as np

T = 1
x = np.linspace(-1.5/T, 1.5/T, 1000)

plt.figure(figsize=(8, 5))

for alpha in [0, 0.25, 0.5, 1]:
    plt.plot(x, rcos(x, alpha, T), label=r'$\alpha$ = {}'.format(alpha))

plt.ylabel('y')
plt.xlabel('x')
plt.legend()
plt.grid(alpha=0.3)
plt.show()

(Source code, png, hires.png, pdf)

Raised cosine function
opticomlib.utils.si(x, unit: Literal['m', 's'] = 's', k: int = 1)[source]

Unit of measure classifier.

Parameters:
  • x (int | float) – Number to classify.

  • unit (str, default: 's') – Unit of measure. Valid options are {‘s’, ‘m’, ‘Hz’, ‘rad’, ‘bit’, ‘byte’, ‘W’, ‘V’, ‘A’, ‘F’, ‘H’, ‘Ohm’}.

  • k (int, default: 1) – Precision of the output.

Returns:

String with number and unit.

Return type:

str

Example

>>> si(0.002, 's')
'2.0 ms'
>>> si(1e9, 'Hz')
'1.0 GHz'
opticomlib.utils.norm(x)[source]

Normalize an array by dividing each element by the maximum value in the array.

Parameters:

x (Array_Like) – Input array to be normalized.

Returns:

out – Normalized array.

Return type:

np.ndarray

Raises:

ValueError – If x is not an array_like.

opticomlib.utils.nearest(x, a)[source]

Find the nearest value in an array.

Parameters:
  • x (Array_Like) – Input array.

  • a (Number) – Value to find.

Returns:

out – Nearest value in the array.

Return type:

Number

Raises:

ValueError – If x is not an array_like. If a is not a number.

opticomlib.utils.p_ase(amplify=True, wavelength=1.55e-06, G=None, NF=None, BW_opt=None)[source]

Calculate the ASE noise power [Watts].

Parameters:
  • amplify (bool, default: True) – If use an EDFA or not at the receiver (before PIN).

  • wavelength (float, default: 1550e-9) – Wavelength of the signal.

  • G (float) – Gain of EDFA, in [dB]. Only used if amplify=True. This parameter is mandatory.

  • NF (float) – Noise Figure of EDFA, in [dB]. Only used if amplify=True. Mandatory.

  • BW_opt (float) – Bandwidth of optical filter that is placed after the EDFA, in [Hz]. Only used if amplify=True. Mandatory.

Returns:

p_ase – ASE optical noise power, in [W].

Return type:

float

opticomlib.utils.average_voltages(P_avg, modulation: Literal['ook', 'ppm'], M=None, ER=inf, amplify=True, wavelength=1.55e-06, G=None, NF=None, BW_opt=None, r=1.0, R_L=50)[source]

Calculate the average voltages of the ON and OFF slots [Voltages].

Parameters:
  • P_avg (float) – Average Received input optical Power (in [dBm]).

  • modulation (Literal['ook', 'ppm']) – Kind of modulation format {‘ook’, ‘ppm’}, more modulations in future…

  • M (int) – Order of M-ary PPM (a power of 2). Only needed if modulation=’ppm’.

  • ER (float, default: np.inf) – Extinction Ratio of the input optical signal, in [dB].

  • amplify (bool, default: True) – If use an EDFA or not at the receiver (before PIN).

  • wavelength (float, default: 1550e-9) – Wavelength of the signal.

  • G (float) – Gain of EDFA, in [dB]. Only used if amplify=True. This parameter is mandatory.

  • NF (float) – Noise Figure of EDFA, in [dB]. Only used if amplify=True. Mandatory.

  • BW_opt (float) – Bandwidth of optical filter that is placed after the EDFA, in [Hz]. Only used if amplify=True. Mandatory.

  • r (float, default: 1.0) – Responsivity of photo-detector.

  • R_L (float, default: 50) – Load resistance of photo-detector, in [Ω].

Returns:

  • mu (np.ndarray) – Average voltage of ON and OFF slots. mu[0] is the OFF slot and mu[1] is the ON slot.

  • mu_ASE (float) – ASE voltage offset.

opticomlib.utils.noise_variances(P_avg, modulation: Literal['ook', 'ppm'], M=None, ER=inf, amplify=True, wavelength=1.55e-06, G=None, NF=None, BW_opt=None, r=1.0, BW_el=5000000000.0, R_L=50, T=300, NF_el=0)[source]

Calculate the theoretical noise variances for OFF and ON slots, include sig-ase, ase-ase, thermal and shot noises [V^2]. If amplify=False only thermal and shot are calculated.

Parameters:
  • P_avg (float) – Average Received input optical Power (in [dBm]).

  • modulation (Literal['ook', 'ppm']) – Kind of modulation format {‘ook’, ‘ppm’}, more modulations in future…

  • M (int) – Order of M-ary PPM (a power of 2). Only needed if modulation=’ppm’.

  • ER (float) – Extinction Ratio of the input optical signal, in [dB].

  • amplify (bool) – If use an EDFA or not at the receiver (before PIN). Default: False.

  • wavelength (float) – Central frequency of communication, in [Hz]. Only used if amplify=True. Default: 1550 nm.

  • G (float) – Gain of EDFA, in [dB]. Only used if amplify=True. This parameter is mandatory.

  • NF (float) – Noise Figure of EDFA, in [dB]. Only used if amplify=True. Mandatory.

  • BW_opt (float) – Bandwidth of optical filter that is placed after the EDFA, in [Hz]. Only used if amplify=True. Mandatory.

  • r (float) – Responsivity of photo-detector. Default: 1.0 [A/W]

  • BW_el (float) – Bandwidth of photo-detector or electrical filter, in [Hz]. Default: 5e9 [Hz].

  • R_L (float) – Load resistance of photo-detector, in [Ω]. Default: 50 [Ω].

  • T (float) – Temperature of photo-detector, in [K]. Default: 300 [K].

  • NF_el (float) – Equivalent Noise Figure of electric circuit, in [dB]. Default: 0 [dB]

opticomlib.utils.optimum_threshold(mu0, mu1, S0, S1, modulation: Literal['ook', 'ppm'], M=None)[source]

Calculate the optimum threshold for binary modulation formats.

Parameters:
  • mu0 (float) – Average voltage of OFF slot.

  • mu1 (float) – Average voltage of ON slot.

  • S0 (float) – Noise variance of OFF slot.

  • S1 (float) – Noise variance of ON slot.

  • modulation (str) – Modulation format

  • M (int) – PPM order

Returns:

threshold – Optimum threshold value.

Return type:

float

opticomlib.utils.theory_BER(P_avg, modulation: Literal['ook', 'ppm'], M=None, decision=None, threshold=None, ER=inf, amplify=False, f0=193414500000000.0, G=None, NF=None, BW_opt=None, r=1.0, BW_el=5000000000.0, R_L=50, T=300, NF_el=0)[source]

This function calculates the bit error rate (BER) for an OPTICAL RECEIVER, based on a PIN photodetector, from the average input power. It also allows consider the effects of an EDFA amplifier in the results.

If amplify==False, thermal and shot noise of PIN will be consider in BER calculation. If amplify==True, signal-ase and ase-ase beating noises generated by the EDFA are consider too.

In addition, parameter NF_el != 0 (electrical noise figure) can be used to represent another sources of noise after detection, like electrical amplifiers, etc.

Parameters:
  • P_avg (float) – Average Received input optical Power (in [dBm]).

  • modulation (Literal['ook', 'ppm']) – Kind of modulation format {‘ook’, ‘ppm’}, more modulations in future…

  • M (int) – Order of M-ary PPM (a power of 2). Only needed if modulation=’ppm’.

  • decision (Literal['hard', 'soft']) – Kind of PPM decision. ‘hard’ decision use the optimum threshold to separate ON and OFF slots. ‘soft’ decision use the Maximum a Posteriori (MAP) and it outperform ‘hard’ decision. Only needed if modulation=’ppm’.

  • threshold (float) – Threshold for decision. Only needed if (modulation=’ook’) or (modulation=’ppm and decision=’hard’). Value must be in (0, 1), without include edges. By default optimum threshold is used.

  • ER (float) – Extinction Ratio of the input optical signal, in [dB].

  • amplify (bool) – If use an EDFA or not at the receiver (before PIN). Default: False.

  • f0 (float) – Central frequency of communication, in [Hz]. Only used if amplify=True. Default: 193.4 THz corresponding to 1550 nm.

  • G (float) – Gain of EDFA, in [dB]. Only used if amplify=True. This parameter is mandatory.

  • NF (float) – Noise Figure of EDFA, in [dB]. Only used if amplify=True. Mandatory.

  • BW_opt (float) – Bandwidth of optical filter that is placed after the EDFA, in [Hz]. Only used if amplify=True. Mandatory.

  • r (float) – Responsivity of photo-detector. Default: 1.0 [A/W]

  • BW_el (float) – Bandwidth of photo-detector or electrical filter, in [Hz]. Default: 5e9 [Hz].

  • R_L (float) – Load resistance of photo-detector, in [Ω]. Default: 50 [Ω].

  • T (float) – Temperature of photo-detector, in [K]. Default: 300 [K].

  • NF_el (float) – Equivalent Noise Figure of electric circuit, in [dB]. Default: 0 [dB]

Returns:

BER – Theoretical Bit Error rate of the system specified.

Return type:

float

Notes

The bandwidths are used only for the determination of the noise contribution to the BER value, i.e. the signal distortion due to these bandwidth is not taken into account. Therefore, the bandwidths BW_el and BW_opt are not considered to affect the transmitted signal.

Example

from opticomlib import theory_BER
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-40, -20, 1000)  # Average input optical power [dB]

plt.figure(figsize=(8, 6))

plt.semilogy(x, theory_BER(P_avg=x, modulation='ook'), label='OOK')
plt.semilogy(x, theory_BER(P_avg=x, modulation='ppm', M=4, decision='soft'), label='4-PPM (soft)')
plt.semilogy(x, theory_BER(P_avg=x, modulation='ppm', M=4, decision='hard'), label='4-PPM (hard)')

plt.xlabel(r'$P_{avg}$')
plt.ylabel('BER')
plt.legend()
plt.grid(alpha=0.3)
plt.ylim(1e-9,)
plt.show()

(Source code, png, hires.png, pdf)

Raised cosine function
opticomlib.utils.shortest_int(data: ndarray, percent: float = 50) tuple[float, float][source]

Estimation of the shortest interval containing percent of the samples in ‘data’.

Parameters:

data (ndarray) – Array of data.

Returns:

The shortest interval containing 50% of the samples in ‘data’.

Return type:

tuple[float, float]