Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion optic/comm/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ def monteCarloMI(rx, tx, M, constType, px=None):

for k in range(nModes):
σ2 = noiseVar[k]
MI[k] = calcMI(rx[:, k], tx[:, k], σ2, constSymb, px)
MI[k] = calcMI(rx[:, k], tx[:, k], σ2, constSymb, px)[0]
return MI


Expand Down
28 changes: 28 additions & 0 deletions tests/test_dsp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
"""
Test functions in dsp.py

"""

from optic.dsp.core import finddelay
import numpy as np


def test_finddelay_for_arrays_of_real_values():
delay = 35

a = np.arange(0, 100)
b = np.roll(a, -delay)

assert delay == finddelay(a, b)
assert delay == -finddelay(b, a)


def test_finddelay_for_arrays_of_complex_values():
delay = 57

a = np.arange(0, 100) + 1j * np.arange(0, 100)
b = np.roll(a, -delay)

assert delay == finddelay(a, b)
assert delay == -finddelay(b, a)
97 changes: 97 additions & 0 deletions tests/test_metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import unittest
import numpy as np
from optic.dsp.core import pnorm
from optic.models.channels import awgn
from optic.comm.modulation import modulateGray
from optic.comm.metrics import (
fastBERcalc,
calcLLR,
monteCarloGMI,
monteCarloMI,
Qfunc,
calcEVM,
theoryBER,
calcLinOSNR,
GN_Model_NyquistWDM,
ASE_NyquistWDM,
GNmodel_OSNR,
)

class TestCommunicationMetrics(unittest.TestCase):

def setUp(self):
# Set up common parameters or fixtures if needed
pass

def test_fastBERcalc(self):
# Add test cases for fastBERcalc

# Run BER vs Ebn0 Monte Carlo simulation in the AWGN channel

qamOrder = [4, 16, 64, 256] # Modulation order

EbN0dB_ = np.arange(10, 25.5, 0.5)
BER = np.zeros((len(EbN0dB_),len(qamOrder)))
BER_theory = np.zeros((len(EbN0dB_),len(qamOrder)))

for ii, M in enumerate(qamOrder):

for indSNR in range(EbN0dB_.size):

EbN0dB = EbN0dB_[indSNR]

# generate random bits
bitsTx = np.random.randint(2, size=int(np.log2(M)*1e5))

# Map bits to constellation symbols
symbTx = modulateGray(bitsTx, M, 'qam')

# Normalize symbols energy to 1
symbTx = pnorm(symbTx)

# AWGN channel
snrdB = EbN0dB + 10*np.log10(np.log2(M))
symbRx = awgn(symbTx, snrdB)

# BER calculation
BER[indSNR, ii] = fastBERcalc(symbRx, symbTx, M, 'qam')[0][0]
BER_theory[indSNR, ii] = theoryBER(M, EbN0dB, 'qam')

if BER[indSNR, ii] == 0:
break

np.testing.assert_array_almost_equal(BER, BER_theory, decimal=3)

def test_monteCarlo_MI_and_GMI(self):
# Run GMI vs SNR Monte Carlo simulation
qamOrder = [4, 16, 64] # Modulation order

SNR = np.arange(15, 26, 1)
MI = np.zeros((len(SNR),len(qamOrder)))
GMI = np.zeros((len(SNR),len(qamOrder)))

for ii, M in enumerate(qamOrder):
for indSNR in range(SNR.size):

snrdB = SNR[indSNR]

# generate random bits
bitsTx = np.random.randint(2, size=int(np.log2(M)*1e5))

# Map bits to constellation symbols
symbTx = modulateGray(bitsTx, M, 'qam')

# Normalize symbols energy to 1
symbTx = pnorm(symbTx)

# AWGN channel
symbRx = awgn(symbTx, snrdB)

# GMI estimation
MI[indSNR, ii] = monteCarloMI(symbRx, symbTx, M, 'qam')[0]
GMI[indSNR, ii] = monteCarloGMI(symbRx, symbTx, M, 'qam')[0][0]

np.testing.assert_array_almost_equal(MI, GMI, decimal=1)

if __name__ == '__main__':
unittest.main()
56 changes: 56 additions & 0 deletions tests/test_modulation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import unittest
import numpy as np
from optic.utils import bitarray2dec, dec2bitarray
from optic.comm.modulation import grayCode, grayMapping, minEuclid, demap, modulateGray, demodulateGray

class TestModulationFunctions(unittest.TestCase):
def test_GrayCode(self):
# Test GrayCode function for different values of n
for n in range(1, 6):
with self.subTest(n=n):
result = grayCode(n)
self.assertEqual(len(result), 2**n)
self.assertEqual(len(result[0]), n)

def test_GrayMapping(self):
# Test GrayMapping function for different values of M and constType
M_values = [4, 16, 64]
constTypes = ['qam', 'psk', 'pam']
for M in M_values:
for constType in constTypes:
with self.subTest(M=M, constType=constType):
result = grayMapping(M, constType)
self.assertEqual(len(result), M)

def test_minEuclid(self):
# Test minEuclid function with some example inputs
symb = np.array([1+1j, 2+2j, 3+3j])
const = np.array([1+1j, 3+3j, 2+2j])
result = minEuclid(symb, const)
np.testing.assert_array_equal(result, np.array([0, 2, 1]))

def test_demap(self):
# Test demap function with some example inputs
indSymb = np.array([0, 2, 1])
bitMap = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
result = demap(indSymb, bitMap)
np.testing.assert_array_equal(result, np.array([0, 0, 1, 0, 0, 1]))

def test_modulateGray(self):
# Test modulateGray function with some example inputs
bits = np.array([0, 1, 0, 0, 1, 0])
M = 4
constType = 'qam'
result = modulateGray(bits, M, constType)
self.assertEqual(len(result), len(bits) // int(np.log2(M)))

def test_demodulateGray(self):
# Test demodulateGray function with some example inputs
symb = np.array([1+1j, 3+3j, 2+2j])
M = 4
constType = 'qam'
result = demodulateGray(symb, M, constType)
self.assertEqual(len(result), len(symb) * int(np.log2(M)))

if __name__ == '__main__':
unittest.main()