Open In Colab

[1]:
if 'google.colab' in str(get_ipython()):
    ! git clone -b main https://github.com/edsonportosilva/OptiCommPy
    from os import chdir as cd
    cd('/content/OptiCommPy/')
    ! pip install .
[2]:
from IPython.display import Image

Getting started

In this notebook, a basic illustrative example of the usage of the OptiCommPy package is presented.

Defining the model to be simulated

To demonstrate the basic features of OptiCommPy, in this notebook a simulation of a On-Off Keying (OOK) transmission system based on intensity modulation and direct-detection (IM/DD) is presented. A schematic for the transmission system to be simulated is shown in the figure below.

[3]:
Image("./figures/BasicIMDD.jpg", width=600)
[3]:
_images/getting_started_4_0.jpg

It consists of a 10 Gb/s NRZ OOK transmission over 100 km of optical fiber with direct detection at the receiver assuming a photodiode with 10 GHz of bandwidth. The fiber channel is modeled as a linear propagation medium exhibiting loss and chromatic dispersion. All the simulation parameters are listed in the table below.

Parameter

Value

Order of the modulation format (\(M\))

2

Symbol rate (\(R_s\))

10 Gb/s

Pulse shape

NRZ

Laser output power (\(P_i\))

\(3\) dBm

MZM \(V_\pi\) voltage

2.0 V

MZM bias voltage (\(V_b\))

-1.0 V

Total link distance (\(L\))

100 km

Fiber loss parameter (\(\alpha\))

0.2 dB/km

Fiber dispersion parameter (\(D\))

16 ps/nm/km

Central optical frequency (\(F_c\))

\(193.1\) THz

Photodiode bandwidth (\(B\))

\(10\) GHz

Simulation sampling rate (\(F_s\))

160 GSamples/s

Simulating a basic IM/DD model

To build the corresponding simulation setup with OptiCommPy, first the necessary functions need to be imported as

[4]:
import numpy as np
from optic.models.devices import mzm, photodiode
from optic.models.channels import linearFiberChannel
from optic.comm.modulation import modulateGray
from optic.comm.metrics import bert
from optic.dsp.core import firFilter, pulseShape, upsample, pnorm
from optic.utils import parameters, dBm2W
from scipy.special import erfc
import matplotlib.pyplot as plt

Given that each element in the simulation requires a specific set of parameters, the subsequent step involves defining all necessary parameters to establish the desired simulation setup, as illustrated in the next cell.

In this particular example, we assume that the electrical signal driving the optical modulator is a binary non-return-to-zero (NRZ) signal. The optical modulator is modeled as an ideal Mach-Zehnder Modulator (MZM). The fiber channel is considered linear and affected only by loss and chromatic dispersion. Lastly, at the receiver, a photodiode with a frequency response limited to 10 GHz is configured, subject to the influence of thermal and shot noise.

[5]:
# simulation parameters
SpS = 16  # samples per symbol
M = 2  # order of the modulation format
Rs = 10e9  # Symbol rate
Fs = SpS * Rs  # Signal sampling frequency (samples/second)
Pi_dBm = 3  # laser optical power at the input of the MZM in dBm
Pi = dBm2W(Pi_dBm)  # convert from dBm to W

# typical NRZ pulse
pulse = pulseShape("nrz", SpS)
pulse = pulse / max(abs(pulse))  # normalize to 1 Vpp

# MZM parameters
paramMZM = parameters()
paramMZM.Vpi = 2
paramMZM.Vb = -paramMZM.Vpi / 2

# linear fiber optical channel parameters
paramCh = parameters()
paramCh.L = 100        # total link distance [km]
paramCh.alpha = 0.2    # fiber loss parameter [dB/km]
paramCh.D = 16         # fiber dispersion parameter [ps/nm/km]
paramCh.Fc = 193.1e12  # central optical frequency [Hz]
paramCh.Fs = Fs

# photodiode parameters
paramPD = parameters()
paramPD.ideal = False
paramPD.B = Rs
paramPD.Fs = Fs

After defining all the required parameters, the core simulation code in the next cell establishes the signal flow through each OptiCommPy model, extending from the initial bit source to the direct-detection optical receiver. Starting with a pseudorandom bit sequence, the signal undergoes upsampling and pulse shaping, resulting in a series of binary non-return-to-zero (NRZ) pulses. The electrical NRZ signal then drives the MZM, biased at the quadrature point. An ideal laser generates the optical carrier, free from phase and intensity noise. This optical signal, upon exiting the MZM, traverses the linear fiber channel, where it is subjected to losses and chromatic dispersion. Finally, the signal is detected by a photodiode.

[6]:
## Simulation
print("\nStarting simulation...", end="")

# generate pseudo-random bit sequence
np.random.seed(seed=123)  # fixing the seed to get reproducible results
bitsTx = np.random.randint(2, size=100000)

# generate 2-PAM modulated symbol sequence
symbTx = modulateGray(bitsTx, M, "pam")

# upsampling
symbolsUp = upsample(symbTx, SpS)

# pulse shaping
sigTx = firFilter(pulse, symbolsUp)

# optical modulation
Ai = np.sqrt(Pi)  # ideal cw laser constant envelope
sigTxo = mzm(Ai, sigTx, paramMZM)

# linear fiber channel model
sigCh = linearFiberChannel(sigTxo, paramCh)

# noisy PD (thermal noise + shot noise + bandwidth limit)
I_Rx = photodiode(sigCh, paramPD)

# capture samples in the middle of signaling intervals
I_Rx = I_Rx[0::SpS]

print("simulation completed.")

Starting simulation...simulation completed.

After detecting the optical signal and getting the corresponding photocurrent I_Rx, signal quality metrics can then be evaluated in order to quantify the transmission performance. For OOK optical transmission systems, the most common metrics are the BER and the quality-factor (Q-factor), which can be calculated from the I_Rx using the function bert available from optic.comm.metrics.

[7]:
# calculate the BER and Q-factor
BER, Q = bert(I_Rx)

print("\nTransmission performance metrics:")
print(f"Q-factor = {Q:.2f} ")
print(f"BER = {BER:.2e}")

# theoretical error probability from Q-factor
Pb = 0.5 * erfc(Q / np.sqrt(2))
print(f"Pb = {Pb:.2e}\n")

Transmission performance metrics:
Q-factor = 3.49
BER = 2.80e-04
Pb = 2.44e-04

Generating BER vs \(P_{in}\), Q vs \(P_{in}\) curves

Starting from this basic setup, we can conduct a comprehensive analysis via Monte Carlo simulations to characterize transmission performance across various system parameters. For instance, by repeating the same simulation with different optical launch powers and transmission distances, we can generate performance curves that depict BER and Q-factor as functions of received optical power at varying transmission distances. The corresponding code is shown in the next cell.

[8]:
from tqdm.notebook import tqdm

# simulation parameters
SpS = 16        # Samples per symbol
M = 2           # order of the modulation format
Rs = 10e9       # Symbol rate (for the OOK case, Rs = Rb)
Fs = SpS*Rs     # Signal sampling frequency (samples/second)
Ts = 1/Fs       # Sampling period

# MZM parameters
paramMZM = parameters()
paramMZM.Vpi = 2
paramMZM.Vb = -paramMZM.Vpi/2

# typical NRZ pulse
pulse = pulseShape('nrz', SpS)
pulse = pulse/max(abs(pulse))

# linear fiber optical channel parameters
paramCh = parameters()
paramCh.L = 100  # total link distance [km]
paramCh.alpha = 0.2  # fiber loss parameter [dB/km]
paramCh.D = 16  # fiber dispersion parameter [ps/nm/km]
paramCh.Fc = 193.1e12  # central optical frequency [Hz]
paramCh.Fs = Fs

# photodiode parameters
paramPD = parameters()
paramPD.ideal = False
paramPD.B = Rs
paramPD.Fs = Fs

powerValues = np.arange(-30, -9) # power values at the input of the pin receiver
distances = np.arange(100, 160,10) # distances

BER = np.zeros((len(distances), len(powerValues)))
Q = np.zeros((len(distances), len(powerValues)))

for indL, L in enumerate(tqdm(distances)):
    paramCh.L = L  # total link distance [km]
    for indPi, Pi_dBm in enumerate(powerValues):
        Pi = dBm2W(Pi_dBm + L*paramCh.alpha + 3) # optical signal power in W at the MZM input

        # generate pseudo-random bit sequence
        np.random.seed(seed=123)  # fixing the seed to get reproducible results
        bitsTx = np.random.randint(2, size=10**5)
        n = np.arange(0, bitsTx.size)

        # generate ook modulated symbol sequence
        symbTx = modulateGray(bitsTx, M, 'pam')
        symbTx = pnorm(symbTx) # power normalization

        # upsampling
        symbolsUp = upsample(symbTx, SpS)

        # pulse formatting
        sigTx = firFilter(pulse, symbolsUp)

        # optical modulation
        Ai = np.sqrt(Pi)
        sigTxo = mzm(Ai, sigTx, paramMZM)

        # linear fiber channel model
        sigCh = linearFiberChannel(sigTxo, paramCh)

        # pin receiver
        I_Rx = photodiode(sigCh.real, paramPD)
        I_Rx = I_Rx/np.std(I_Rx)

        # capture samples in the middle of signaling intervals
        I_Rx = I_Rx[0::SpS]

        # calculate the BER and Q-factor
        BER[indL, indPi], Q[indL, indPi] = bert(I_Rx)

#         print(f'\nPin = {Pi_dBm:.2f} dBm')
#         print('\nReceived signal parameters:')
#         print(f'Q-factor = {Q[indL, indPi]:.2f} ')
#         print(f'BER = {BER[indL, indPi]:.2e}')
[9]:
markers = ['o','>','x','*','d','^']

Prx = powerValues #- paramCh.L*paramCh.alpha

plt.figure()
for indL, L in enumerate(distances):
   # Prx = powerValues - L*paramCh.alpha
    plt.plot(Prx, np.log10(BER[indL,:]),'--', marker=markers[indL], label=f'{L} km')

plt.grid()
plt.ylabel('log10(BER)')
plt.xlabel('$P_{RX}$ [dBm]')
plt.title('BER vs input power at the receiver')
plt.legend()
plt.ylim(-4,0)
plt.xlim(min(Prx), max(Prx))
plt.show()

plt.figure()
for indL, L in enumerate(distances):
   # Prx = powerValues - L*paramCh.alpha
    plt.plot(Prx, 10*np.log10(Q[indL,:]),'--', marker=markers[indL], label=f'{L} km')

plt.grid()
plt.ylabel('Q-factor [dB]')
plt.xlabel('$P_{RX}$ [dBm]')
plt.title('Q-factor vs input power at the receiver')
plt.legend()
#plt.ylim(-10,0)
plt.xlim(min(Prx), max(Prx))
plt.show()
C:\Users\edson.DESKTOP-54IJM4F\AppData\Local\Temp\ipykernel_5396\2042705777.py:8: RuntimeWarning: divide by zero encountered in log10
  plt.plot(Prx, np.log10(BER[indL,:]),'--', marker=markers[indL], label=f'{L} km')
_images/getting_started_16_1.png
_images/getting_started_16_2.png

These plots clearly illustrate that system performance deteriorates with longer transmission distances, which is primarily attributed to the cumulative impact of chromatic dispersion and the noise sources at the photodiode, resulting in increased signal degradation.

References

[1] G. P. Agrawal, Fiber-Optic Communication Systems. John Wiley & Sons, 2021.