ocspy-0.0.5/000777 000765 000024 00000000000 13462527144 014037 5ustar00huazhilunstaff000000 000000 ocspy-0.0.5/Ocspy/000777 000765 000024 00000000000 13462527144 015134 5ustar00huazhilunstaff000000 000000 ocspy-0.0.5/PKG-INFO000777 000765 000024 00000000500 13462527161 015131 0ustar00huazhilunstaff000000 000000 Metadata-Version: 1.0 Name: ocspy Version: 0.0.5 Summary: simulate optical communication Home-page: UNKNOWN Author: nigulasikaochuan Author-email: nigulasikaochuan@sjtu.edu.cn License: MIT Licence Description: simulate optical communication Keywords: pip,optical communication,coherent optical,ocspy,ocspy Platform: any ocspy-0.0.5/ocspy.egg-info/000777 000765 000024 00000000000 13462527144 016666 5ustar00huazhilunstaff000000 000000 ocspy-0.0.5/setup.cfg000777 000765 000024 00000000046 13462527161 015662 0ustar00huazhilunstaff000000 000000 [egg_info] tag_build = tag_date = 0 ocspy-0.0.5/setup.py000777 000765 000024 00000001027 13462527161 015553 0ustar00huazhilunstaff000000 000000 from setuptools import setup, find_packages setup( name = "ocspy", version = "0.0.5", keywords = ("pip", "optical communication","coherent optical", "ocspy", "ocspy"), description = "simulate optical communication", long_description = "simulate optical communication", license = "MIT Licence", packages=find_packages(), url = "", author = "nigulasikaochuan", author_email = "nigulasikaochuan@sjtu.edu.cn", include_package_data = True, platforms = "any", install_requires = [] ) ocspy-0.0.5/ocspy.egg-info/PKG-INFO000777 000765 000024 00000000500 13462527161 017760 0ustar00huazhilunstaff000000 000000 Metadata-Version: 1.0 Name: ocspy Version: 0.0.5 Summary: simulate optical communication Home-page: UNKNOWN Author: nigulasikaochuan Author-email: nigulasikaochuan@sjtu.edu.cn License: MIT Licence Description: simulate optical communication Keywords: pip,optical communication,coherent optical,ocspy,ocspy Platform: any ocspy-0.0.5/ocspy.egg-info/SOURCES.txt000777 000765 000024 00000001137 13462527161 020556 0ustar00huazhilunstaff000000 000000 setup.py Ocspy/__init__.py Ocspy/Base/SignalInterface.py Ocspy/Base/__init__.py Ocspy/Channel/__init__.py Ocspy/Channel/channel.py Ocspy/Filter/__init__.py Ocspy/Filter/designFilter.py Ocspy/Instrument/ElectricInstrument.py Ocspy/Instrument/OpticalInstrument.py Ocspy/Instrument/__init__.py Ocspy/ReceiverDsp/__init__.py Ocspy/ReceiverDsp/dsp_tools.py Ocspy/ReceiverDsp/receiver_dsp.py Ocspy/ReceiverDsp/receiver_metrics.py Ocspy/qamdata/__init__.py Ocspy/tool/__init__.py Ocspy/tool/tool.py ocspy.egg-info/PKG-INFO ocspy.egg-info/SOURCES.txt ocspy.egg-info/dependency_links.txt ocspy.egg-info/top_level.txtocspy-0.0.5/ocspy.egg-info/dependency_links.txt000777 000765 000024 00000000001 13462527161 022736 0ustar00huazhilunstaff000000 000000 ocspy-0.0.5/ocspy.egg-info/top_level.txt000777 000765 000024 00000000006 13462527161 021416 0ustar00huazhilunstaff000000 000000 Ocspy ocspy-0.0.5/Ocspy/Base/000777 000765 000024 00000000000 13462527144 016006 5ustar00huazhilunstaff000000 000000 ocspy-0.0.5/Ocspy/Channel/000777 000765 000024 00000000000 13462527144 016504 5ustar00huazhilunstaff000000 000000 ocspy-0.0.5/Ocspy/Filter/000777 000765 000024 00000000000 13462527144 016361 5ustar00huazhilunstaff000000 000000 ocspy-0.0.5/Ocspy/Instrument/000777 000765 000024 00000000000 13462527144 017304 5ustar00huazhilunstaff000000 000000 ocspy-0.0.5/Ocspy/ReceiverDsp/000777 000765 000024 00000000000 13462527144 017347 5ustar00huazhilunstaff000000 000000 ocspy-0.0.5/Ocspy/qamdata/000777 000765 000024 00000000000 13462527144 016544 5ustar00huazhilunstaff000000 000000 ocspy-0.0.5/Ocspy/tool/000777 000765 000024 00000000000 13462527144 016111 5ustar00huazhilunstaff000000 000000 ocspy-0.0.5/Ocspy/tool/__init__.py000777 000765 000024 00000000266 13462527161 020230 0ustar00huazhilunstaff000000 000000 from .tool import scatterplot from .tool import save_signal from .tool import load_signal from .tool import spectrum_analyzer from .tool import lamb2freq from .tool import freq2lamb ocspy-0.0.5/Ocspy/tool/tool.py000777 000765 000024 00000007175 13462527161 017454 0ustar00huazhilunstaff000000 000000 import pickle import zlib from scipy.constants import c from scipy.signal import welch import numpy as np def lamb2freq(lam): ''' :param lam: wavelength [m] :return: frequence [Hz] ''' return c / lam def freq2lamb(freq): ''' :param freq: frequence [Hz] :return: lambda:[m] ''' return c / freq def downsample(signal, sps): ''' downsample along row can receive siganl object or ndarray :param signal: Signal Object or ndarray :param sps: :return: signal ''' if not isinstance(signal, np.ndarray): sample = signal.data_sample_in_fiber after_process = np.zeros_like(signal.symbol) else: sample = np.atleast_2d(signal) length = divmod(sample.shape[1], sps) if length[1] != 0: raise ("after downsample the sample number should be integer") after_process = np.zeros((sample.shape[0], length[0]), dtype=sample.dtype) for i in range(sample.shape[0]): after_process[i, :] = sample[i, ::sps] return after_process def scatterplot_colorful(signal, down_sample=True): pass def scatterplot(signal, down_sample=True,backend='mplt'): ''' :param signal: signal object or ndarray :param down_sample: if down_sample is true , signal must be signal object :param backend: can choose mplt or pyqt :return: ''' if down_sample: assert hasattr(signal,'sps_in_fiber') symbol = downsample(signal,signal.sps_in_fiber) else: if hasattr(signal,'decision_symbol'): symbol = signal.decision_symbol else: symbol = signal pol_number = signal.shape[0] if backend == 'mplt': import matplotlib.pyplot as plt for i in range(pol_number): plt.subplot(1,pol_number, i+1) ibranch = symbol[i,:].real qbranch = symbol[i,:].imag plt.scatter(ibranch,qbranch,marker='o',color='b') plt.show() def spectrum_analyzer(signal,fs=None,backend='mplt'): ''' :param signal: signal object or ndarray :return: None ''' if isinstance(signal,np.ndarray): assert fs is not None sample = signal else: fs = signal.fs_in_fiber sample = signal[:] pol_number = sample.shape[0] if backend =='mplt': import matplotlib.pyplot as plt plt.figure(figsize=(20,6)) for i in range(pol_number): plt.subplot(1,pol_number,i+1) [f,pxx] = welch(sample[i,:],fs,nfft=2048,detrend=False,return_onesided=False) plt.plot(f/1e9,10*np.log10(np.abs(pxx))) plt.xlabel('Frequency [GHZ]') plt.ylabel('Power Spectrem Density [db/Hz]') plt.show() def eyedigram(signal): pass def save_signal(fn, signal, lvl=4, **kwargs): """ Save a signal object using zlib compression Parameters ---------- fn : basestring filename signal : SignalObject the signal to save lvl : int, optional the compression to use for zlib """ with open(fn, "wb") as fp: sc = zlib.compress( pickle.dumps({'signal': signal, 'simulation_information': kwargs}, protocol=pickle.HIGHEST_PROTOCOL), level=lvl) fp.write(sc) def load_signal(fn): """ Load a signal object from a zlib compressed pickle file. Parameters ---------- fn : basestring filename of the file Returns ------- sig : SignalObject The loaded signal object """ with open(fn, "rb") as fp: s = zlib.decompress(fp.read()) obj = pickle.loads(s) return obj ocspy-0.0.5/Ocspy/qamdata/4qam.mat000777 000765 000024 00000000302 13451130500 020067 0ustar00huazhilunstaff000000 000000 MATLAB 5.0 MAT-file, Platform: GLNXA64, Created on: Fri Sep 28 09:56:51 2018 IM:xc``b6 X|F fӌ`u@g-xhq&ocspy-0.0.5/Ocspy/qamdata/8qam.mat000777 000765 000024 00000000320 13451130501 020074 0ustar00huazhilunstaff000000 000000 MATLAB 5.0 MAT-file, Platform: GLNXA64, Created on: Thu Sep 27 23:16:30 2018 IMHxc``l@ABpHs)rFU0*a栉'AD3eocspy-0.0.5/Ocspy/qamdata/16qam.mat000777 000765 000024 00000000330 13451130500 020153 0ustar00huazhilunstaff000000 000000 MATLAB 5.0 MAT-file, Platform: GLNXA64, Created on: Thu Sep 27 23:21:37 2018 IMPxc```d``@"J1iF @<-7Gxnh~O$a+8i:{\4TNpqti-jocspy-0.0.5/Ocspy/qamdata/32qam.mat000777 000765 000024 00000000371 13451130500 020156 0ustar00huazhilunstaff000000 000000 MATLAB 5.0 MAT-file, Platform: GLNXA64, Created on: Thu Sep 27 23:22:08 2018 IMqxc```b``@"gb0P9 ɟCbʟvQgW|>G4L.f' SNt-?%`?pocspy-0.0.5/Ocspy/qamdata/64qam.mat000777 000765 000024 00000000375 13451130501 020170 0ustar00huazhilunstaff000000 000000 MATLAB 5.0 MAT-file, Platform: GLNXA64, Created on: Thu Sep 27 23:22:36 2018 IMuxc```a``@"gb0P9AL 'nx43߃ɦYD*%Þ\i?Ȧ FKCo?$ocspy-0.0.5/Ocspy/ReceiverDsp/dsp_tools.py000777 000765 000024 00000012625 13462527161 021737 0ustar00huazhilunstaff000000 000000 # -*- coding: utf-8 -*- # This file is part of QAMpy. # # QAMpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Foobar is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with QAMpy. If not, see . # # Copyright 2018 Jochen Schröder, Mikael Mazur # These two functions from QamPy library import warnings import numpy as np import numba N = np """ a number of convenience functions""" def normal_sample(samples): ''' :param samples: 1d array :return: normalized sample ''' samples = np.atleast_2d(samples)[0,:] samples = samples / np.sqrt(np.mean(samples.real ** 2 + samples.imag ** 2)) return samples @numba.jit(cache=True) def segment_axis(a, length, overlap, mode='cut', append_to_end=0): """ Generate a new array that chops the given array along the given axis into overlapping frames. example: >>> segment_axis(arange(10), 4, 2) array([[0, 1, 2, 3], [2, 3, 4, 5], [4, 5, 6, 7], [6, 7, 8, 9]]) arguments: a The array to segment must be 1d-array length The length of each frame overlap The number of array elements by which the frames should overlap end What to do with the last frame, if the array is not evenly divisible into pieces. Options are: 'cut' Simply discard the extra values 'pad' Pad with a constant value append_to_end: The value to use for end='pad' a new array will be returned. """ if a.ndim !=1: raise Exception("Error, input array must be 1d") if overlap > length: raise Exception("overlap cannot exceed the whole length") stride = length - overlap row = 1 total_number = length while True: total_number = total_number + stride if total_number > len(a): break else: row = row + 1 # 一共要分成row行 if total_number > len(a): if mode == 'cut': b = np.zeros((row, length), dtype=np.complex128) is_append_to_end = False else: b = np.zeros((row + 1, length), dtype=np.complex128) is_append_to_end = True else: b = np.zeros((row, length), dtype=np.complex128) is_append_to_end = False index = 0 for i in range(row): b[i, :] = a[index:index + length] index = index + stride if is_append_to_end: last = a[index:] b[row, 0:len(last)] = last b[row, len(last):] = append_to_end return b def bin2gray(value): """ Convert a binary value to an gray coded value see _[1]. This also works for arrays. ..[1] https://en.wikipedia.org/wiki/Gray_code#Constructing_an_n-bit_Gray_code """ return value ^ (value >> 1) @numba.jit(cache=True) def decision(symbol, constl): ''' constl must be 2d ''' if constl.ndim !=2: raise Exception("constl input to decision must be 2d") distance = np.abs(constl[0] - symbol) decision = constl[0, np.argmin(distance)] return decision def cal_symbols_qam(M): """ Generate the symbols on the constellation diagram for M-QAM """ if np.log2(M) % 2 > 0.5: return cal_symbols_cross_qam(M) else: return cal_symbols_square_qam(M) def cal_symbols_cross_qam(M): """ Generate the symbols on the constellation diagram for non-square (cross) M-QAM """ N = (np.log2(M) - 1) / 2 s = 2 ** (N - 1) rect = np.mgrid[-(2 ** (N + 1) - 1):2 ** (N + 1) - 1:1.j * 2 ** (N + 1), -( 2 ** N - 1):2 ** N - 1:1.j * 2 ** N] qam = rect[0] + 1.j * rect[1] idx1 = np.where((abs(qam.real) > 3 * s) & (abs(qam.imag) > s)) idx2 = np.where((abs(qam.real) > 3 * s) & (abs(qam.imag) <= s)) qam[idx1] = np.sign(qam[idx1].real) * ( abs(qam[idx1].real) - 2 * s) + 1.j * (np.sign(qam[idx1].imag) * (4 * s - abs(qam[idx1].imag))) qam[idx2] = np.sign(qam[idx2].real) * ( 4 * s - abs(qam[idx2].real)) + 1.j * (np.sign(qam[idx2].imag) * (abs(qam[idx2].imag) + 2 * s)) return qam.flatten() def cal_symbols_square_qam(M): """ Generate the symbols on the constellation diagram for square M-QAM """ qam = np.mgrid[-(2 * np.sqrt(M) / 2 - 1):2 * np.sqrt( M) / 2 - 1:1.j * np.sqrt(M), -(2 * np.sqrt(M) / 2 - 1):2 * np.sqrt(M) / 2 - 1:1.j * np.sqrt(M)] return (qam[0] + 1.j * qam[1]).flatten() def cal_scaling_factor_qam(M): """ Calculate the scaling factor for normalising MQAM symbols to 1 average Power """ bits = np.log2(M) if not bits % 2: scale = 2 / 3 * (M - 1) else: symbols = cal_symbols_qam(M) scale = (abs(symbols) ** 2).mean() return scale def get_power(x): return np.mean(x.real ** 2 + x.imag ** 2) ocspy-0.0.5/Ocspy/ReceiverDsp/receiver_dsp.py000777 000765 000024 00000031204 13462527161 022375 0ustar00huazhilunstaff000000 000000 import numpy as np import numba from scipy.fftpack import fft, ifft, fftfreq from scipy.signal import fftconvolve from scipy.signal import correlate from .dsp_tools import segment_axis, decision, cal_symbols_square_qam, cal_scaling_factor_qam, get_power, \ cal_symbols_qam from ..Base.SignalInterface import Signal from ..Instrument.ElectricInstrument import PulseShaping import os class MatchedFilter(object): def __init__(self, roll_off, span, sps): self.h = PulseShaping.rcosdesign(int(span * sps), roll_off, 1, int(sps)) self.delay = int(span / 2 * sps) def match_filter(self, signal): x = signal.data_sample_in_fiber[0, :] y = signal.data_sample_in_fiber[1, :] x = fftconvolve(x, self.h) y = fftconvolve(y, self.h) x = np.roll(x, -self.delay) y = np.roll(y, -self.delay) x = x[:signal.sample_number_in_fiber] y = y[:signal.sample_number_in_fiber] return x, y def inplace_match_filter(self, signal): x, y = self.match_filter(signal) signal.data_sample_in_fiber = np.array([x, y]) return signal def __call__(self, signal): self.inplace_match_filter(signal) return signal def syncsignal(symbol_tx, sample_rx, sps): ''' :param symbol_tx: 发送符号 :param sample_rx: 接收符号,会相对于发送符号而言存在滞后 :param sps: samples per symbol :return: 收端符号移位之后的结果 # 不会改变原信号 ''' assert sample_rx.ndim == 1 assert symbol_tx.ndim == 1 assert len(sample_rx) >= len(symbol_tx) symbol_tx = np.atleast_2d(symbol_tx)[0, :] sample_rx = np.atleast_2d(sample_rx)[0, :] res = correlate(sample_rx[::sps], symbol_tx) index = np.argmax(np.abs(res)) out = np.roll(sample_rx, sps * (-index - 1 + symbol_tx.shape[0])) return out def cd_compensation(signal: Signal, spans, inplace=False): ''' This function is used for chromatic dispersion compensation in frequency domain. The signal is Signal object, and a new sample is created from property data_sample_in_fiber :param signal: Signal object :param spans: Span object, the signal's has propagated through these spans :param inplace: if True, the compensated sample will replace the original sample in signal,or new ndarray will be r eturned :return: if inplace is True, the signal object will be returned; if false the ndarray will be returned ''' center_wavelength = signal.center_wave_length freq_vector = fftfreq(signal[0, :].shape[0], 1 / signal.fs_in_fiber) omeg_vector = 2 * np.pi * freq_vector sample = np.zeros_like(signal[:]) for i in range(signal.shape[0]): sample[i, :] = signal[i, :] if not isinstance(spans, list): spans = [spans] for span in spans: beta2 = -span.beta2(center_wavelength) dispersion = (-1j / 2) * beta2 * omeg_vector ** 2 * span.length for pol_index in range(sample.shape[0]): sample[pol_index, :] = ifft(fft(sample[pol_index, :]) * np.exp(dispersion)) if inplace: signal[:] = sample return signal else: return sample def super_scalar(): ''' implementing superscalar algorithm to recover signal's phase :return: ''' pass def __pll(): ''' using in super scalar, should not be called outside this file :return: ''' pass def __ml(): ''' using in super scalar, should not be called outside this file :return: ''' pass def over_lap_save(signal, h): ''' :param signal: 1d-array :param h: filter's taps implement linear convolution using over-lap-save method :return: ''' pass def dual_frequency_lms_equalizer_block(signal, ntaps, sps, constl=None, train_symbol=None, mu=0.001, niter=3): pass @numba.jit(cache=True) def dual_pol_time_domain_lms_equalizer_pll(signal, ntaps, sps, constl=None, train_symbol=None, mu=0.001, niter=3, g=0.01, symbol_length=65536): weight = __dual_pol_init_lms_weight(ntaps) # xx xy yx yy data_sample_in_fiber = signal[:] samplex = segment_axis(data_sample_in_fiber[0, :], ntaps, ntaps - sps) sampley = segment_axis(data_sample_in_fiber[1, :], ntaps, ntaps - sps) if train_symbol is not None: xsymbol = train_symbol[0, ntaps // 2 // sps:] ysymbol = train_symbol[1, ntaps // 2 // sps:] # xsymbol = np.roll(xsymbol,) xsymbols = np.zeros((1, symbol_length), dtype=np.complex128) ysymbols = np.zeros((1, symbol_length), dtype=np.complex128) errorsx = np.zeros((1, niter * samplex.shape[0]), dtype=np.float64) errorsy = np.zeros((1, niter * samplex.shape[0]), dtype=np.float64) pll_phase = np.zeros((2, samplex.shape[0]), dtype=np.complex128) pll_error = np.zeros((2, samplex.shape[0]), dtype=np.complex128) for j in range(niter): for i in range(samplex.shape[0]): xout = np.sum(samplex[i, ::-1] * weight[0][0]) + np.sum(sampley[i, ::-1] * weight[1][0]) yout = np.sum(samplex[i, ::-1] * weight[2][0]) + np.sum(sampley[i, ::-1] * weight[3][0]) xout_cpr = xout * np.exp(-1j * pll_phase[0, i]) yout_cpr = yout * np.exp(-1j * pll_phase[1, i]) pll_error[0, i] = (xout * np.conj(xout_cpr)).imag pll_error[1, i] = (yout * np.conj(yout_cpr)).imag pll_phase[0, i] = g * pll_error[0, i] + pll_phase[0, i] pll_phase[1, i] = g * pll_error[1, i] + pll_phase[1, i] # is not None and j == 0: if train_symbol is not None: errorx = __calc_error_train(xout_cpr, symbol=xsymbol[i]) errory = __calc_error_train(yout_cpr, symbol=ysymbol[i]) else: assert constl is not None # assert constl.ndim ==1 if constl.ndim != 2: raise Exception("constl must be 2d") errorx = __calc_error_dd(xout_cpr, constl) errory = __calc_error_dd(yout_cpr, constl) weight[0][0] = weight[0][0] + mu * errorx * np.conj(samplex[i, ::-1]) weight[1][0] = weight[1][0] + mu * errorx * np.conj(sampley[i, ::-1]) weight[2][0] = weight[2][0] + mu * errory * np.conj(samplex[i, ::-1]) weight[3][0] = weight[3][0] + mu * errory * np.conj(sampley[i, ::-1]) if j == niter - 1: xsymbols[0, i] = xout_cpr ysymbols[0, i] = yout_cpr errorsx[0, i] = np.abs(errorx) errorsy[0, i] = np.abs(errory) return xsymbols[0, :], ysymbols[0, :], weight, errorsx, errorsy @numba.jit(cache=True) def dual_pol_time_domain_lms_equalizer(signal, ntaps, sps, constl=None, train_symbol=None, mu=0.001, niter=3, symbol_length=65536): ''' :param signal: signal to be equalized, a numpy array, 2d-array,each row represent a polarizaiton :param ntaps: the tap of the filter :param sps: sample per symbol :param constl: the constellation, used in dd :param train_symbol: symbol to train :param mu: the learning rate, default to 0.001 :param niter: the number of equlizeing operation :return: equalized symbols, errors ''' data_sample_in_fiber = signal[:] samplex = segment_axis(data_sample_in_fiber[0, :], ntaps, ntaps - sps) sampley = segment_axis(data_sample_in_fiber[1, :], ntaps, ntaps - sps) if train_symbol is not None: xsymbol = train_symbol[0, ntaps // 2 // sps:] ysymbol = train_symbol[1, ntaps // 2 // sps:] # xsymbol = np.roll(xsymbol,) weight = __dual_pol_init_lms_weight(ntaps) # xx xy yx yy xsymbols = np.zeros((1, symbol_length), dtype=np.complex128) ysymbols = np.zeros((1, symbol_length), dtype=np.complex128) errorsx = np.zeros((1, niter * samplex.shape[0]), dtype=np.float64) errorsy = np.zeros((1, niter * samplex.shape[0]), dtype=np.float64) for j in range(niter): for i in range(samplex.shape[0]): xout = np.sum(samplex[i, ::-1] * weight[0][0]) + np.sum(sampley[i, ::-1] * weight[1][0]) yout = np.sum(samplex[i, ::-1] * weight[2][0]) + np.sum(sampley[i, ::-1] * weight[3][0]) if train_symbol is not None and j == 0: errorx = __calc_error_train(xout, symbol=xsymbol[i]) errory = __calc_error_train(yout, symbol=ysymbol[i]) else: assert constl is not None # assert constl.ndim ==1 if constl.ndim != 2: raise Exception("constl must be 2d") errorx = __calc_error_dd(xout, constl) errory = __calc_error_dd(yout, constl) weight[0][0] = weight[0][0] + mu * errorx * np.conj(samplex[i, ::-1]) weight[1][0] = weight[1][0] + mu * errorx * np.conj(sampley[i, ::-1]) weight[2][0] = weight[2][0] + mu * errory * np.conj(samplex[i, ::-1]) weight[3][0] = weight[3][0] + mu * errory * np.conj(sampley[i, ::-1]) if j == niter - 1: xsymbols[0, i] = xout ysymbols[0, i] = yout errorsx[0, i] = np.abs(errorx) errorsy[0, i] = np.abs(errory) return xsymbols, ysymbols, weight, errorsx, errorsy @numba.jit(cache=True) def __calc_error_dd(xout, constl): ''' only use in dual_pol_time_domain_lms_equalizer, should not be used outside this file xout: a symbol to be decision,one element constl: must be 2d array ''' symbol = decision(xout, constl) return symbol - xout @numba.jit(cache=True) def __calc_error_train(xout, symbol): ''' only use in dual_pol_time_domain_lms_equalizer, should not be used outside this file xout: symbol in ,one element symbol: True symbol,one element ''' error = symbol - xout return error @numba.jit(cache=True) def __dual_pol_init_lms_weight(ntaps): ''' only use in dual_pol_time_domain_lms_equalizer, should not be used outside this file implement filter taps initialization :param ntaps: :return: ''' # hxx = np.zeros((1, ntaps), dtype=np.complex128) # # hxy = np.zeros((1, ntaps), dtype=np.complex128) # hyx = np.zeros((1, ntaps), dtype=np.complex128) # hyy = np.zeros((1, ntaps), dtype=np.complex128) # # hxx[0, ntaps // 2] = 1 # hyy[0, ntaps // 2] = 1 hxx = np.zeros((1, ntaps), dtype=np.complex128) hxy = np.zeros((1, ntaps), dtype=np.complex128) hyx = np.zeros((1, ntaps), dtype=np.complex128) hyy = np.zeros((1, ntaps), dtype=np.complex128) hxy[0, ntaps // 2] = 1 hyx[0, ntaps // 2] = 1 return (hxx, hxy, hyx, hyy) def demap_to_msg(rx_symbols, order, do_normal=True): ''' :param rx_symbols:1d array or 2d array if 2d the shape[0] must be 1 :param do_normal: if True the rx_symbols will be normalized to 1 :return: 2d array msg, the shape[0] is 1 ''' from scipy.io import loadmat base_path = os.path.abspath(__file__) base_path = os.path.dirname(os.path.dirname(base_path)) if order == 8: raise NotImplementedError('Not implemented yet') constl = cal_symbols_qam(order) / np.sqrt(cal_scaling_factor_qam(order)) rx_symbols = np.atleast_2d(rx_symbols) assert rx_symbols.shape[0] == 1 rx_symbols = rx_symbols[0] qam_data = loadmat(f'{base_path}/qamdata/{order}qam.mat')['x'][0] msg = np.zeros((1, rx_symbols.shape[1])) if do_normal: rx_symbols = rx_symbols / np.sqrt(np.mean(rx_symbols.imag ** 2 + rx_symbols.real ** 2)) for index, symbol in enumerate(rx_symbols[0]): decision_symbol = decision(symbol, constl=np.atleast_2d(constl)) choose = np.abs(decision_symbol - qam_data) < np.spacing(1) # print(choose) # print(np.nonzero(choose)[0]) msg[0, index] = np.nonzero(choose)[0] # print(msg[0,index]) # break return msg def main(): import matplotlib.pyplot as plt from scipy.io import loadmat cc = loadmat('test2.mat') tx = cc['txsymbols'] sam = cc['rxSignalIn'] import time now = time.time() constl = cal_symbols_qam(16) constl = constl / np.sqrt(cal_scaling_factor_qam(16)) constl = np.atleast_2d(constl) xsym, ysym, wer, errorsx, errorsy = dual_pol_time_domain_lms_equalizer(sam[:], 13, 2, constl=constl, train_symbol=tx, mu=0.001, niter=3) plt.plot(errorsx) print(time.time() - now) if __name__ == '__main__': main() ocspy-0.0.5/Ocspy/ReceiverDsp/receiver_metrics.py000777 000765 000024 00000006051 13462527161 023257 0ustar00huazhilunstaff000000 000000 import numpy as np from .dsp_tools import cal_symbols_qam from .dsp_tools import cal_scaling_factor_qam from .dsp_tools import normal_sample import numba def biterror(order, receive_msg, tx_msg): assert receive_msg.ndim == 1 assert tx_msg.ndim == 1 assert len(receive_msg) == len(tx_msg) bitnumber = int(np.log2(order)) cnt = 0 index = receive_msg != tx_msg to_find_recv = receive_msg[index] to_find_tx = tx_msg[index] for i in range(len(to_find_recv)): temp_recv = np.binary_repr(to_find_recv[i], bitnumber) temp_tx = np.binary_repr(to_find_tx[i], bitnumber) for index in range(len(temp_recv)): if temp_recv[index] != temp_tx[index]: cnt = cnt + 1 return cnt / len(receive_msg) / bitnumber def Q(biterr): pass def evm(receive_symbol, tx_symbol): pass def theory_ber_from_snr(M,is_pdm): pass def snr2osnr(is_pdm): pass def osnr2snr(is_pdm,signal_bandwidth): pass def estimate_snr_using_tx(recive_symbol, tx_symbol, to_normalize=True,head=1024): recive_symbol = np.atleast_2d(recive_symbol) pol_number = recive_symbol.shape[0] tx_symbol = np.atleast_2d(tx_symbol) assert recive_symbol.shape == tx_symbol.shape recive_symbol = recive_symbol[:,head:-1-head] tx_symbol = tx_symbol[:,head:-1-head] snr = [] noise_powers = [] if to_normalize: for i in range(pol_number): recive_symbol[i, :] = normal_sample(recive_symbol[i, :]) tx_symbol[i, :] = normal_sample(tx_symbol[i, :]) else: print("please ensure the rx and tx are normalized") noise = recive_symbol - tx_symbol for i in range(pol_number): noise_power = noise[i, :].real ** 2 + noise[i, :].imag ** 2 noise_power = np.mean(noise_power) noise_powers.append(noise_power) snr.append(10*np.log10(1 / noise_power)) if pol_number == 2: total_noise_power = np.sum(noise_powers) total_snr = 2 / total_noise_power total_snr = 10*np.log10(total_snr) snr.append(total_snr) return snr[0],snr[1],snr[2] else: return snr[0] def estimate_snr(E, M): """Calculate the signal to noise ratio SNR according to formula given in _[1] Parameters: ---------- E : array_like input field M: : int order of the QAM constallation Returns: ------- S0/N: : float linear SNR estimate References: ---------- ...[1] Gao and Tepedelenlioglu in IEEE Trans in Signal Processing Vol 53, pg 865 (2005). """ gamma = _cal_gamma(M) r2 = np.mean(abs(E) ** 2) r4 = np.mean(abs(E) ** 4) S1 = 1 - 2 * r2 ** 2 / r4 - np.sqrt( (2 - gamma) * (2 * r2 ** 4 / r4 ** 2 - r2 ** 2 / r4)) S2 = gamma * r2 ** 2 / r4 - 1 return S1 / S2 def _cal_gamma(M): """Calculate the gamma factor for SNR estimation.""" A = abs(cal_symbols_qam(M)) / np.sqrt(cal_scaling_factor_qam(M)) uniq, counts = np.unique(A, return_counts=True) return np.sum(uniq ** 4 * counts / M) ocspy-0.0.5/Ocspy/Instrument/ElectricInstrument.py000777 000765 000024 00000016044 13462527161 023510 0ustar00huazhilunstaff000000 000000 from scipy.signal import convolve, fftconvolve, resample_poly from scipy.signal import resample import numpy as np class ADC(object): def __call__(self, signal): print("ad will be implemented") print("after ad, the sample in fiber will be resampled to signal.sps") print("and the signal.sps_in_fiber will be set to signal.sps") sps_in_fiber = signal.sps_in_fiber sps = signal.sps N = 1/(sps / sps_in_fiber) N = int(N) tempx = resample_poly(signal[0], 1, N) tempy = resample_poly(signal[1], 1, N) new_sample = np.array([tempx, tempy]) signal.data_sample_in_fiber = new_sample signal.sps_in_fiber = signal.sps return signal class DAC(object): def __call__(self, signal): sps_in_fiber = np.ceil(signal.sps_in_fiber) N = sps_in_fiber / signal.sps N = int(N) from scipy.signal import resample_poly # tempx = resample(signal.data_sample[0, :], N) # tempy = resample(signal.data_sample[1, :], N) tempx = resample_poly(signal.data_sample, up=N, down=1, axis=1) signal.data_sample_in_fiber = tempx # signal.sps_in_fiber = sps_in_fiber class PulseShaping(object): ''' Perform Pluse Shaping. need a dict to construct, the construct should contain: key:pulse_shaping span sps alpha ''' def __init__(self, **kwargs): self.kind = kwargs['kind'] self.span = kwargs['span'] self.sps = kwargs['sps'] self.alpha = kwargs['alpha'] assert divmod(self.span * self.sps, 2)[1] == 0 self.number_of_sample = self.span * self.sps self.delay = self.span / 2 * self.sps self.filter_tap = self.__design_filter() def __design_filter(self): if self.kind == 'rrc': h = PulseShaping.rcosdesign(self.number_of_sample, self.alpha, 1, self.sps) return np.atleast_2d(h) if self.kind == 'rc': print( 'error, why do you want to design rc filter,the practical use should be two rrc filters,on in transimit' 'side,and one in receiver side, rrc filter will be designed') @staticmethod def rcosdesign(N, alpha, Ts, Fs): """ Generates a root raised cosine (RRC) filter (FIR) impulse response. Parameters ---------- N : int Length of the filter in samples. alpha : float Roll off factor (Valid values are [0, 1]). Ts : float Symbol period in seconds. Fs : float Sampling Rate in Hz. Returns --------- time_idx : 1-D ndarray of floats Array containing the time indices, in seconds, for the impulse response. h_rrc : 1-D ndarray of floats Impulse response of the root raised cosine filter. """ T_delta = 1 / float(Fs) sample_num = np.arange(N + 1) h_rrc = np.zeros(N + 1, dtype=float) for x in sample_num: t = (x - N / 2) * T_delta if t == 0.0: h_rrc[x] = 1.0 - alpha + (4 * alpha / np.pi) elif alpha != 0 and t == Ts / (4 * alpha): h_rrc[x] = (alpha / np.sqrt(2)) * (((1 + 2 / np.pi) * (np.sin(np.pi / (4 * alpha)))) + ( (1 - 2 / np.pi) * (np.cos(np.pi / (4 * alpha))))) elif alpha != 0 and t == -Ts / (4 * alpha): h_rrc[x] = (alpha / np.sqrt(2)) * (((1 + 2 / np.pi) * (np.sin(np.pi / (4 * alpha)))) + ( (1 - 2 / np.pi) * (np.cos(np.pi / (4 * alpha))))) else: h_rrc[x] = (np.sin(np.pi * t * (1 - alpha) / Ts) + 4 * alpha * (t / Ts) * np.cos(np.pi * t * (1 + alpha) / Ts)) / \ (np.pi * t * (1 - (4 * alpha * t / Ts) * (4 * alpha * t / Ts)) / Ts) return h_rrc / np.sqrt(np.sum(h_rrc * h_rrc)) def rrcfilter(self, signal_interface): ''' :param signal_interface: signal object to be pulse shaping,because a reference of signal object is passed so the filter is in place :return: None ''' # print("---begin pulseshaping ---,the data sample will be set") # upsample by insert zeros signal_interface.data_sample = np.zeros( (signal_interface.symbol.shape[0], signal_interface.sps * signal_interface.symbol_length)) signal_interface.data_sample = np.asarray(signal_interface.data_sample, dtype=np.complex) for i in range(signal_interface.symbol.shape[0]): signal_interface.data_sample[i, :] = signal_interface.upsample(signal_interface.symbol[i, :], signal_interface.sps)[0, :] temp = [] for i in range(signal_interface.data_sample.shape[0]): temp.append(convolve(self.filter_tap[0, :], signal_interface.data_sample[i, :])) # tempy = convolve(self.filter_tap[0, :], signal_interface.data_sample[1, :]) # temp_signal = np.array([tempx, tempy]) temp_signal = np.array(temp) # compensate group delay temp_signal = np.roll(temp_signal, -int(self.delay), axis=1) signal_interface.data_sample = temp_signal[:, :signal_interface.sps * signal_interface.symbol_length] def __call__(self, signal): self.rrcfilter(signal) class AWG(object): def __init__(self, sps=2, alpha=0.02, span=1024): ''' :param sps: pulse shaping parameters :param alpha: reference of the signal object,change signal_interface's property will change all :param span This function will be used to pulse shaping, the sps is default to 2, it should correspond to signal's sps, then the siganl will be resampled to sps_in_fiber automatically ''' # self.signal_interface = signal_interface self.roll_off = alpha self.span = span self.pulse_shaping_filter = PulseShaping(kind='rrc', alpha=alpha, sps=sps, span=span) self.dac = DAC() def __call__(self, signal_interface): self.pulse_shaping_filter.rrcfilter(signal_interface) self.dac(signal_interface) return signal_interface def __str__(self): string = f"the pulse shaping filter: roll_off:{self.pulse_shaping_filter.alpha}," \ f" the sps of pulse shaping filter : {self.signal_interface.sps}, the adc sample per symbol: {self.signal_interface.sps_in_fiber} " return string def __repr__(self): return self.__str__() if __name__ == '__main__': # h = PulseShaping.rcosdesign(32 * 4, 0.2, 1, 4) import progressbar import time bar = progressbar.ProgressBar(max_value=16000) for i in range(16000): bar.update(i + 1) time.sleep(0.01) bar.update(16000) bar.finish() ocspy-0.0.5/Ocspy/Instrument/OpticalInstrument.py000777 000765 000024 00000023325 13462527161 023351 0ustar00huazhilunstaff000000 000000 ''' This file contains optical instrument along the signal's propagation ''' import numpy as np from numpy.fft import fftfreq from scipy.constants import h, c from scipy.fftpack import fft, ifft from ..Base.SignalInterface import QamSignal, Signal, WdmSignal from ..Filter.designFilter import LowPassFilter from typing import List from scipy.special import erf import warnings class Demultiplex(object): @staticmethod def demux_signal(wdm_signal, signal_index=None, roll_off=0.02, nonlinear_fiber=None): ''' This static function is used to demultiplex a wdm_signal, the signal_index of wdm_signal will be obtained, it will be shift to center_frequency, an ideal low pass filter will be used to remove other signal that is not interested in. :param wdm_signal: the wdm signal to be demulitplexed :param signal_index: which signal want to get,return signal objectl,if None.all channel will be returned :return: ''' frequences = wdm_signal.relative_frequence tarray = np.arange(0, wdm_signal[:].shape[1]) * (1 / wdm_signal.fs_in_fiber) # temp = wdm_signal[:] temp = np.zeros_like(wdm_signal[:]) for i in range(temp.shape[0]): temp[i, :] = wdm_signal[i, :] * np.exp(-1j * 2 * np.pi * frequences[signal_index] * tarray) # center_frequence = wdm_signal.relative_frequence[signal_index] pos_freq = wdm_signal.signals[signal_index].symbol_rate / 2 * (1 + roll_off) neg_freq = -pos_freq # LowPassFilter.inplace_ideal_lowpass(temp,pos_freq,neg_freq,fs_in_fiber=wdm_signal.fs_in_fiber) signal = QamSignal(symbol_rate=wdm_signal.signals[signal_index].symbol_rate, sps=wdm_signal.signals[signal_index].sps, sps_in_fiber=wdm_signal.signals[signal_index].sps_in_fiber, is_dp=wdm_signal.signals[signal_index].is_dp, is_from_array=True, is_from_demux=True, mf=wdm_signal.signals[signal_index].mf, symbol_length=wdm_signal.signals[signal_index].symbol_length) signal.data_sample_in_fiber = temp # signal.symbol = wdm_signal.signals[signal_index].symbol signal.msg = wdm_signal.signals[signal_index].msg # the symbol and msg should not be changed, it is the tx information signal.center_frequence = wdm_signal.center_frequence LowPassFilter.inplace_ideal_lowpass(signal, pos_freq, neg_freq) # signal.symbol = wdm_signal.signals[signal_index].symbol return signal class Multiplex(object): ''' This class implements signal multiplex ''' @staticmethod def mux_signal(signals: List[Signal], grid_size): ''' :param signals: signals that will be :param grid_size: the grid size of the wdm signal :return: ''' fs_in_fiber = [] for signal in signals: fs_in_fiber.append(signal.fs_in_fiber) if np.any(np.diff(np.array(fs_in_fiber))): assert "the sampling frequence of each signal in fiber should be the same" absolute_frequences = [] for signal in signals: absolute_frequences.append(signal.center_frequence) absolute_frequences = np.array(absolute_frequences) freq = np.min(absolute_frequences) + np.max(absolute_frequences) freq = freq / 2 relative_frequence = np.array(absolute_frequences) - freq # 从左到右开始复用 # 每个wdm信道的采样频率必须保持一致 # 每个wdm信道的样本点个数也要保持一致,截掉长的采样序列的尾部 sample_length = [] for signal in signals: sample_length.append(signal.sample_number_in_fiber) if np.any(np.diff(sample_length)): warnings.warn("the number of sample in fiber are not the same, the longer will be " "trancted") min_length = min(sample_length) for signal in signals: number_to_del = signal.sample_number_in_fiber - min_length if number_to_del == 0: continue else: signal.data_sample_in_fiber = signal.data_sample_in_fiber[:, 0: - number_to_del] sample_number = signals[0].sample_number_in_fiber fs = signals[0].fs_in_fiber t_array = np.arange(0, sample_number) * (1 / fs) wdm_data_sample = 0 for index, signal in enumerate(signals): wdm_data_sample += signal.data_sample_in_fiber * np.exp( 1j * 2 * np.pi * relative_frequence[index] * t_array) wdm_signal = WdmSignal(signals, grid_size, wdm_data_sample) return wdm_signal class Edfa: def __init__(self, gain_db, nf, is_ase=True, mode='ConstantGain', expected_power=0): ''' :param gain_db: :param nf: :param is_ase: 是否添加ase噪声 :param mode: ConstantGain or ConstantPower :param expected_power: 当mode为ConstantPoower 时候,此参数有效 ''' self.gain_db = gain_db self.nf = nf self.is_ase = is_ase self.mode = mode self.expected_power = expected_power def one_ase(self, signal,gain_lin=None): ''' :param signal: :return: ''' lamb = signal.center_wave_length if gain_lin is None: one_ase = (h * c / lamb) * (self.gain_lin * 10 ** (self.nf_lin / 10) - 1) / 2 else: one_ase = (h * c / lamb) * (gain_lin * 10 ** (self.nf_lin / 10) - 1) / 2 return one_ase @property def gain_lin(self): return 10 ** (self.gain_db / 10) @property def nf_lin(self): return 10 ** (self.nf / 10) def traverse(self, signal): if self.is_ase: noise = self.one_ase(signal) * signal.fs_in_fiber noise_sample = np.random.randn(*(signal[:].shape)) + 1j * np.random.randn(*(signal[:].shape)) each_pol_power = noise noise_sample = np.sqrt(each_pol_power / 2) * noise_sample else: noise_sample = 0 if self.mode == 'ConstantGain': signal[:] = np.sqrt(self.gain_lin) * signal[:] + noise_sample return if self.mode == 'ConstantPower': # raise NotImplementedError("Not implemented") signal_power = np.mean(np.abs(signal.data_sample[0, :]) ** 2) + np.mean( np.abs(signal.data_sample[1, :]) ** 2) desired_power_linear = (10 ** (self.expected_power / 10)) / 1000 linear_gain = desired_power_linear / signal_power # # noise = self.one_ase(signal,linear_gain)*signal.fs_in_fiber noise_sample = np.random.randn(*(signal[:].shape))+1j*np.random.randn(*(signal[:].shape)) noise_sample = np.sqrt(noise/2) * noise_sample signal[:] = np.sqrt(linear_gain) * signal[:] + noise_sample return signal def __call__(self, signal): self.traverse(signal) return signal def __str__(self): string = f"Model is {self.mode}\n" \ f"Gain is {self.gain_db} db\n" \ f"ase is {self.is_ase}\n" \ f"noise figure is {self.nf}" return string def __repr__(self): return self.__str__() class WSS(object): def __init__(self, frequency_offset, bandwidth, oft): ''' :param frequency_offset: value away from center [GHz] :param bandwidth: 3-db Bandwidth [Ghz] :param oft:GHZ ''' self.frequency_offset = frequency_offset self.bandwidth = bandwidth self.otf = oft self.H = None self.freq = None def traverse(self, signal): sample = np.zeros_like(signal[:]) for i in range(sample.shape[0]): sample[i, :] = signal[i, :] freq = fftfreq(len(sample[0, :]), 1 / signal.fs_in_fiber) freq = freq / 1e9 self.freq = freq self.__get_transfer_function(freq) for i in range(sample.shape[0]): sample[i, :] = ifft(fft(sample[i, :]) * self.H) return sample def __call__(self, signal): sample = self.traverse(signal) signal[:] = sample return signal def __get_transfer_function(self, freq_vector): delta = self.otf / 2 / np.sqrt(2 * np.log(2)) H = 0.5 * delta * np.sqrt(2 * np.pi) * ( erf((self.bandwidth / 2 - (freq_vector - self.frequency_offset)) / np.sqrt(2) / delta) - erf( (-self.bandwidth / 2 - (freq_vector - self.frequency_offset)) / np.sqrt(2) / delta)) H = H / np.max(H) self.H = H def plot_transfer_function(self, freq=None): import matplotlib.pyplot as plt if self.H is None: self.__get_transfer_function(freq) self.freq = freq index = self.H >0.001 plt.figure(figsize=(20,6)) plt.subplot(121) plt.scatter(self.freq[index], np.abs(self.H[index]),color='b',marker='o') plt.xlabel('GHz') plt.ylabel('Amplitude') plt.title("without log") plt.subplot(122) plt.scatter(self.freq[index], 10 * np.log10(np.abs(self.H[index])),color='b',marker='o') plt.xlabel('GHz') plt.ylabel('Amplitude') plt.title("with log") plt.show() def __str__(self): string = f'the center_frequency is {0 + self.frequency_offset}[GHZ] \t\n' \ f'the 3-db bandwidth is {self.bandwidth}[GHz]\t\n' \ f'the otf is {self.otf} [GHz] \t\n' return string def __repr__(self): return self.__str__() class MZM(object): pass class IQ(object): pass class LaserSource(object): pass ocspy-0.0.5/Ocspy/Instrument/__init__.py000777 000765 000024 00000000425 13462527161 021420 0ustar00huazhilunstaff000000 000000 from .ElectricInstrument import AWG from .ElectricInstrument import ADC from .ElectricInstrument import PulseShaping from .ElectricInstrument import DAC from .OpticalInstrument import Edfa from .OpticalInstrument import Multiplex from .OpticalInstrument import Demultiplex,WSS ocspy-0.0.5/Ocspy/Filter/__init__.py000777 000765 000024 00000000047 13462527161 020475 0ustar00huazhilunstaff000000 000000 from .designFilter import LowPassFilterocspy-0.0.5/Ocspy/Filter/designFilter.py000777 000765 000024 00000004750 13462527161 021362 0ustar00huazhilunstaff000000 000000 from ..Base.SignalInterface import Signal from scipy.fftpack import fftfreq from scipy.fftpack import fft from scipy.fftpack import ifft import numpy as np class LowPassFilter(object): @staticmethod def inplace_ideal_lowpass(signal, pos_cutoff_frquence, neg_cutoff_frequence, fs_in_fiber=None): ''' :param signal: in place filter :return: ''' samples = LowPassFilter.ideal_lowpass(signal, pos_cutoff_frquence, neg_cutoff_frequence, fs_in_fiber) if fs_in_fiber is None: # means signal is an signal object pol_number = signal.pol_number else: # means signal is a ndarray pol_number = signal.shape[0] for i in range(pol_number): signal[i, :] = samples[i, :] # signal.data_sample[0, :] = sample_x # signal.data_sample[1, :] = sample_y return signal @staticmethod def ideal_lowpass(signal: Signal, pos_cutoff_frequence, neg_cutoff_frequence, fs_in_fiber=None): ''' :param signal: the sampled sample will be returned, original sample of signal will not be changed :return: ''' if fs_in_fiber is None: fs_in_fiber = signal.fs_in_fiber pol_number = signal.pol_number sample_number_in_fiber = signal.sample_number_in_fiber else: sample_number_in_fiber = signal.shape[1] pol_number = signal.shape[0] samples = np.zeros_like(signal[:]) for i in range(pol_number): samples[i, :] = signal[i, :] # sample_x = signal[0, :] # sample_y = signal[1, :] freq = fftfreq(sample_number_in_fiber, 1 / fs_in_fiber) # sample_x_fourier_transform = fft(sample_x) # sample_y_fourier_transform = fft(sample_y) fft_sample = fft(samples, axis=1) # 超过滤波器带宽的频率点直接设置为0 for i in range(pol_number): fft_sample[i, freq > pos_cutoff_frequence] = 0 fft_sample[i, freq < neg_cutoff_frequence] = 0 # sample_x_fourier_transform[freq > pos_cutoff_frequence ] = 0 # sample_x_fourier_transform[freq < neg_cutoff_frequence] = 0 # sample_y_fourier_transform[abs(freq) > self.bw] = 0 samples = ifft(fft_sample, axis=1) # sample_x = ifft(sample_x_fourier_transform) # sample_y = ifft(sample_y_fourier_transform) return samples @staticmethod def bessel_filter(signal:Signal): pass ocspy-0.0.5/Ocspy/Channel/__init__.py000777 000765 000024 00000000211 13462527161 020611 0ustar00huazhilunstaff000000 000000 from .channel import NonlinearFiber from .channel import LinearFiber from .channel import Awgn from .channel import NonlinearFiberWithPMDocspy-0.0.5/Ocspy/Channel/channel.py000777 000765 000024 00000034623 13462527161 020500 0ustar00huazhilunstaff000000 000000 from numpy.fft import fftfreq from scipy.fftpack import fft, ifft try: import cupy as cp from cupy.fft import fft as cfft from cupy.fft import ifft as icfft except Exception as e: print('cupy can not be used') from ..Base import SignalInterface import numpy as np from scipy.constants import c import math # import progressbar # import arrayfire as af class Awgn(object): def __init__(self, signal_power, request_snr, sps): self.signal_power = signal_power self.request_snr = request_snr self.sps = sps def __call__(self, shape): noise_power = self.signal_power / self.request_snr_lin noise_power = noise_power * self.sps noise_x = np.sqrt(noise_power / 2 / 2) * ( np.random.randn(shape) + 1j * np.random.randn(shape)) noise_y = np.sqrt(noise_power / 2 / 2) * ( np.random.randn(shape) + 1j * np.random.randn(shape)) return noise_x, noise_y @property def request_snr_lin(self): return 10 ** (self.request_snr / 10) class LinearFiber(object): ''' property: self.alpha [db/km] self.D [ps/nm/km] self.length [km] self.wave_length:[nm] self.beta2: caculate beta2 from D,s^2/km self.slope: derivative of self.D ps/nm^2/km self.beta3_reference: s^3/km method: __call__: the signal will ''' def __init__(self, alpha, D, length, slope=0, reference_wave_length=1550): self.alpha = alpha self.D = D self.length = length self.reference_wave_length = reference_wave_length self.slope = slope @property def beta3_reference(self): res = (self.reference_wave_length * 1e-12 / 2 / np.pi / c / 1e-3) ** 2 * ( 2 * self.reference_wave_length * 1e-12 * self.D + ( self.reference_wave_length * 1e-12) ** 2 * self.slope * 1e12) return res @property def alpha_lin(self): # [1/km] return 0.1 * self.alpha * np.log(10) def leff(self, length): ''' :param length: the length of a fiber [km] :return: the effective length [km] ''' effective_length = 1 - np.exp(-self.alpha_lin * length) effective_length = effective_length / self.alpha_lin return effective_length @property def beta2_reference(self): return -self.D * (self.reference_wave_length * 1e-12) ** 2 / 2 / np.pi / c / 1e-3 def beta2(self, wave_length): ''' :param wave_length: [m] :return: beta2 at wave_length [s^2/km] ''' dw = 2 * np.pi * c * (1 / wave_length - 1 / (self.reference_wave_length * 1e-9)) return self.beta2_reference + self.beta3_reference * dw def _prop(self, signal: SignalInterface.Signal): ''' :param signal: signal object to propagation across this fiber :return: ndarray ''' center_lambda = signal.center_wave_length after_prop = np.zeros_like(signal[:]) for pol in range(0, signal.pol_number): sample = signal[pol, :] sample_fft = fft(sample) freq = fftfreq(signal.sample_number_in_fiber, 1 / signal.fs_in_fiber) omeg = 2 * np.pi * freq after_prop[pol, :] = sample_fft * np.exp(-self.alpha_lin * self.length / 2) after_prop[pol, :] = ifft(after_prop[pol, :]) disp = np.exp(-1j / 2 * self.beta2(center_lambda) * omeg ** 2 * self.length) after_prop[pol, :] = ifft(fft(after_prop[pol, :]) * disp) return np.atleast_2d(after_prop) def inplace_prop(self, signal: SignalInterface.Signal): after_prop = self._prop(signal) for i in range(signal.pol_number): signal[i, :] = after_prop[i, :] return signal def __call__(self, signal): self.inplace_prop(signal) return signal def __str__(self): string = f"alpha is {self.alpha} [db/km]\n" \ f"beta2 is {self.beta2_reference} [s^2/km]\n" \ f"beta3 is {self.beta3_reference} []\n" \ f"D is {self.D} ps/nm/km\n" \ f"length is {self.length} km\n" \ f"reference wave length is {self.reference_wave_length * 1e9} [nm]" return string def __repr__(self): return self.__str__() class NonlinearFiber(LinearFiber): def __init__(self, alpha, D, length, gamma, slope=0, step_length=5 / 1000, reference_wave_length=1550): super().__init__(alpha, D, length, slope=slope, reference_wave_length=reference_wave_length) self.gamma = gamma self.step_length = step_length @property def step_length_eff(self): return (1 - np.exp(-self.alpha_lin * self.step_length)) / self.alpha_lin def cupy_prop(self, signal): # print("cupy is used") if signal.pol_number != 2: raise Exception("only dp signal supported at this time") step_number = self.length / self.step_length step_number = int(np.floor(step_number)) temp = np.zeros_like(signal[:]) freq = fftfreq(signal.sample_number_in_fiber, 1 / signal.fs_in_fiber) freq_gpu = cp.asarray(freq) omeg = 2 * np.pi * freq_gpu D = -1j / 2 * self.beta2(signal.center_wave_length) * omeg ** 2 N = 8 / 9 * 1j * self.gamma atten = -self.alpha_lin / 2 time_x = cp.asarray(signal[0, :]) time_y = cp.asarray(signal[1, :]) # print('*' * 20 + "begin simulation" + '*' * 20) # bar = progressbar.ProgressBar(max_value=step_number) for i in range(step_number): # bar.update(i + 1) fftx = cfft(time_x) ffty = cfft(time_y) time_x, time_y = self.linear_prop_cupy(D, fftx, ffty, self.step_length / 2) time_x, time_y = self.nonlinear_prop_cupy(N, time_x, time_y) time_x = time_x * math.exp(atten * self.step_length) time_y = time_y * math.exp(atten * self.step_length) fftx = cfft(time_x) ffty = cfft(time_y) time_x, time_y = self.linear_prop_cupy(D, fftx, ffty, self.step_length / 2) # bar.finish() # print('*' * 20 + "end simulation" + '*' * 20) last_step = self.length - self.step_length * step_number last_step_eff = (1 - np.exp(-self.alpha_lin * last_step)) / self.alpha_lin if last_step == 0: time_x = cp.asnumpy(time_x) time_y = cp.asnumpy(time_y) temp[0, :] = time_x temp[1, :] = time_y return temp fftx = cfft(time_x) ffty = cfft(time_y) time_x, time_y = self.linear_prop_cupy(D, fftx, ffty, last_step / 2) time_x, time_y = self.nonlinear_prop_cupy(N, time_x, time_y, last_step_eff) time_x = time_x * math.exp(atten * last_step) time_y = time_y * math.exp(atten * last_step) fftx = cfft(time_x) ffty = cfft(time_y) time_x, time_y = self.linear_prop_cupy(D, fftx, ffty, last_step / 2) temp[0, :] = cp.asnumpy(time_x) temp[1, :] = cp.asnumpy(time_y) return temp def nonlinear_prop_cupy(self, N, time_x, time_y, step_length=None): if step_length is None: time_x = time_x * cp.exp( N * self.step_length_eff * (cp.abs(time_x) ** 2 + cp.abs( time_y) ** 2)) time_y = time_y * cp.exp( N * self.step_length_eff * (cp.abs(time_x) ** 2 + cp.abs(time_y) ** 2)) else: time_x = time_x * cp.exp( N * step_length * (cp.abs(time_x) ** 2 + cp.abs( time_y) ** 2)) time_y = time_y * cp.exp( N * step_length * (cp.abs(time_x) ** 2 + cp.abs(time_y) ** 2)) return time_x, time_y def linear_prop_cupy(self, D, fftx, ffty, length): time_x = icfft(fftx * cp.exp(D * length)) time_y = icfft(ffty * cp.exp(D * length)) return time_x, time_y def inplace_prop(self, signal): after_prop = self.cupy_prop(signal) signal.data_sample_in_fiber = after_prop return signal def __call__(self, signal): self.inplace_prop(signal) return signal def __str__(self): string = super(NonlinearFiber, self).__str__() string = f"{string}" \ f"the step length is {self.step_length} [km]\n" return string def __repr__(self): return self.__str__() class NonlinearFiberWithPMD(LinearFiber): def __init__(self, alpha, D, length, gamma, reference_length=1550, step_length=5 / 1000, is_dgd=True, **kwargs): ''' :param alpha: [db/km] :param D: [ps/nm/km] :param length: [km] :param gamma: [] :param reference_length: :param is_vector: :param kwargs: if pmd, dgd_coef should be specified,ps/sqrt(km) step_length [km] the step length of split step fourier method ''' super(NonlinearFiberWithPMD, self).__init__(alpha, D, length, reference_length) self.gamma = gamma if is_dgd: try: self.dgd_coef = kwargs['dgd_coef'] except KeyError as e: self.dgd_coef = 0.1 else: self.dgd_coef = 0 self.step_length = step_length self.nplates = self.length / self.step_length self.nplates = int(np.floor(self.nplates)) if self.nplates * self.step_length < self.length: self.last_step = self.length - self.nplates * self.step_length else: self.last_step = 0 self.dgd = self.dgd_coef * np.sqrt(self.step_length * self.nplates) * 1e-12 # s if self.last_step != 0: self.last_dgd = self.dgd_coef * np.sqrt(self.last_step) else: self.last_dgd = 0 self.dgdrms = np.sqrt((3 * np.pi) / 8) * self.dgd / np.sqrt(self.nplates) if self.last_dgd != 0: self.dgdrms_last = np.sqrt((3 * np.pi) / 8) * self.last_dgd / np.sqrt(1) else: self.dgdrms_last = 0 @property def betat(self): return 1 def vector_prop(self, signal: SignalInterface.WdmSignal): print("The Mankov equation of will be solved using split step fourier method") sample_x = signal[0, :] sample_y = signal[1, :] sample_x_gpu = cp.asarray(sample_x) sample_y_gpu = cp.asarray(sample_y) wave_length = signal.center_wave_length beta2 = self.beta2(wave_length=wave_length) freq = fftfreq(signal.sample_number_in_fiber, 1 / signal.fs_in_fiber) omeg = 2 * np.pi * freq betat = -0.5 * omeg ** 2 * beta2 db1 = self.dgdrms * omeg db1_last = self.dgdrms_last * omeg sample_x_gpu, sample_y_gpu = self.__vector_prop(sample_x_gpu, sample_y_gpu, betat, self.gamma, db1, db1_last) sample_x_cpu = cp.asnumpy(sample_x_gpu) sample_y_cpu = cp.asnumpy(sample_y_gpu) return sample_x_cpu, sample_y_cpu def __call__(self, signal): signal[0, :], signal[1, :] = self.vector_prop(signal) return signal def __vector_prop(self, ux, uy, betat, gamma, db1, db1_last): betat = cp.asarray(betat) db1 = cp.asarray(db1) db1_last = cp.asarray(db1_last) gamma = gamma * 8 / 9 halfalpha = 0.5 * self.alpha_lin # bar = progressbar.ProgressBar(max_value=self.nplates) for i in range(self.nplates): ux, uy = self.__vector_nonlinear_step(ux, uy, gamma, self.step_length) ux, uy = self.__vector_linear_step(ux, uy, betat, db1, self.step_length) ux = ux * math.exp(-halfalpha * self.step_length) uy = uy * math.exp(-halfalpha * self.step_length) # bar.update(i + 1) if self.last_step: print(f"the last step {self.last_step}m is going to be propagated") ux, uy = self.__vector_nonlinear_step(ux, uy, gamma, self.last_step) ux, uy = self.__vector_linear_step(ux, uy, betat, db1_last, self.last_step) ux = ux * math.exp(-halfalpha * self.last_step) uy = uy * math.exp(-halfalpha * self.last_step) return ux, uy def __vector_linear_step(self, ux, uy, betat, db1, step_length): ux = cfft(ux) uy = cfft(uy) phi = np.random.rand(1, 1) * np.pi - 0.5 * np.pi kapa = 0.5 * np.arcsin(2 * np.random.rand(1, 1) - 1) rotation_matrix = np.array([[np.cos(phi) * np.cos(kapa) + 1j * np.sin(phi) * np.sin(kapa), np.sin(phi) * np.cos(kapa) - 1j * np.cos(phi) * np.sin(kapa)], [-np.sin(phi) * np.cos(kapa) - 1j * np.cos(phi) * np.sin(kapa), np.cos(phi) * np.cos(kapa) - 1j * np.sin(phi) * np.sin(kapa)]]) rotation_matrix_conj = rotation_matrix.T.conj() # rotate to principle state to of the fiber uux = rotation_matrix[0, 0] * ux + rotation_matrix[0, 1] * uy uuy = rotation_matrix[1, 0] * ux + rotation_matrix[1, 1] * uy # betat = -1/2*beta2*omeg**2 disp = betat * step_length # linear_operator_xpol = (0.5 * db1 + disp) * 1j linear_operator_ypol = (disp - 0.5 * db1) * 1j # linear_operator_xpol = disp * 1j # linear_operator_ypol = disp * 1j uux = cp.exp(linear_operator_xpol) * uux uuy = cp.exp(linear_operator_ypol) * uuy ux = rotation_matrix_conj[0, 0] * uux + rotation_matrix_conj[0, 1] * uuy uy = rotation_matrix_conj[1, 0] * uux + rotation_matrix_conj[1, 1] * uuy ux = cp.ifft(ux) uy = cp.ifft(uy) return ux, uy def __vector_nonlinear_step(self, ux, uy, gamma_mankorv, step_length): ''' :param ux: samples of x-pol :param uy: samples of y-pol :param gamma_mankorv: 8/9*gamma,represent average over 那个啥球上 :return: samples after nonlinear propagation ''' leff = self.leff(step_length) power = cp.abs(ux) ** 2 + cp.abs(uy) ** 2 gamma_mankorv = gamma_mankorv * leff ux = ux * cp.exp(1j * gamma_mankorv * power) uy = uy * cp.exp(1j * gamma_mankorv * power) return ux, uy def __str__(self): string = super().__str__() string = f'{string}\n' \ f'step length for split fourier method is {self.step_length}' return string def __repr__(self): return self.__str__() if __name__ == "__main__": pass ocspy-0.0.5/Ocspy/Base/SignalInterface.py000777 000765 000024 00000033476 13462527161 021435 0ustar00huazhilunstaff000000 000000 import numpy as np from scipy.io import loadmat from scipy.constants import c from typing import List from ..tool.tool import freq2lamb import os base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) class Signal(object): ''' Signal: This Signal is the base class for single carrier signal, the QamSignal inherit this class: the property of Signal: 1. symbol_rate: GHZ 2. mf: the modulation format 3. symbol_length: the length of symbol 4. signal_power: the launch power of signal,the waveform will be set to this signal,when the signal pass laser object 5. sps: the sample per symbol for dsp 6. sps in fiber: the sample per symbol for waveform in fiber,because of nonlinear effect of fiber, the sample rate shuould greater than 2 time highest frequence of signal. 7.lam: the wavelength of the signal: [nm] @property ''' def __init__(self, **kwargs): ''' :param kwargs: 包含单信道信号的各项参数,包括: 1. symbol_rate 符号速率 【Hz】 2. mf 调制格式 3. symbol length 符号长度 4. signal_power 信号功率 【dbm】 5. sps: 发端dsp的采样率 6. sps_in_fiber: 光纤中采样率 7. lam: 信号波长,单位为国际单位 m,property修饰符修饰 8. center_frequence: Hz 所有单位全部使用国际标准单位 ''' self._symbol = None self.symbol_rate = kwargs['symbol_rate'] self.mf = kwargs['mf'] self.symbol_length = kwargs['symbol_length'] self.sps_in_fiber = kwargs['sps_in_fiber'] self.sps = kwargs['sps'] self.decision_symbol = None self.data_sample = None self.data_sample_in_fiber = None self.center_frequence = kwargs['frequence'] @property def symbol(self): return self._symbol def set_signal_power(self, power, unit="dbm"): if unit == 'dbm': power_lin = power / 10 power_lin = 10 ** power_lin power_lin = power_lin / 1000 else: power_lin = power self.inplace_normalize_power() for i in range(self.pol_number): self[i, :] = self[i, :] * np.sqrt(power_lin / self.pol_number) @property def sample_number_in_fiber(self): import warnings if self.sps_in_fiber * self.symbol_length != self.data_sample_in_fiber.shape[1]: warnings.warn("the sps_in_fiber * symbol length not equal the sample number stored in sample_number_fiber") return self.data_sample_in_fiber.shape[1] return int(self.sps_in_fiber * self.symbol_length) @property def sample_number(self): return self.sps * self.symbol_length @property def center_wave_length(self): return c / self.center_frequence def tonumpy(self): ''' :return: reference to self.data_sample_in_fiber if function not return a new ndarrya change it outside will change the data_sample_in_fiber property of the object ''' data_sample_in_fiber = np.zeros_like(self.data_sample_in_fiber) for i in range(self.pol_number): data_sample_in_fiber[i, :] = self.data_sample_in_fiber[i, :] return data_sample_in_fiber def __getitem__(self, index): ''' :param index: Slice Object :return: ndarray of the signal, change this ndarray will change the object ''' assert self.data_sample_in_fiber is not None return self.data_sample_in_fiber[index] def __setitem__(self, index, value): ''' :param index: SLICE Object :param value: the value that to be set,must be ndarray :return: ''' if self.data_sample_in_fiber is None: self.data_sample_in_fiber = np.atleast_2d(value) else: self.data_sample_in_fiber[index] = value def measure_power_in_fiber(self, unit='w'): ''' :return: signal power in the fiber, in unit [W] ''' sample_in_fiber = np.atleast_2d(self.data_sample_in_fiber) signal_power = 0 for i in range(sample_in_fiber.shape[0]): signal_power += np.mean(np.abs(sample_in_fiber[i, :]) ** 2) if unit == "w": return signal_power else: return 10 * np.log10(signal_power * 1000) @property def fs(self): if self.sps is None: return 0 return self.symbol_rate * self.sps @property def fs_in_fiber(self): return self.symbol_rate * self.sps_in_fiber def inplace_normalize_power(self): ''' in place operation :return: ''' self[:] = self.normalize_power() def remove_dc(self): for i in range(self.pol_number): self[i, :] = self[i, :] - np.mean(self[i, :]) @property def pol_number(self): return self.data_sample_in_fiber.shape[0] def inplace_normalize_dsp_sample(self): temp = self.normalize_dsp_sample() self.data_sample = temp def normalize_dsp_sample(self): temp = np.zeros_like(self.data_sample) for i in range(self.pol_number): temp[i, :] = self.data_sample[i, :] / np.sqrt(np.mean(np.abs(self.data_sample[i, :]) ** 2)) return temp def normalize_power(self): ''' new ndarray will be returned , the signal object itself is not changed :param signal: :return: ndarray and signal object will not be changed ''' temp = np.zeros_like(self.data_sample_in_fiber) for i in range(self.data_sample_in_fiber.shape[0]): temp[i, :] = self.data_sample_in_fiber[i, :] / np.sqrt( np.mean(np.abs(self.data_sample_in_fiber[i, :]) ** 2)) return temp def upsample(self, symbol_x, sps): ''' :param symbol_x: 1d array :param sps: sample per symbol :return: 2-d array after inserting zeroes between symbols ''' assert symbol_x.ndim == 1 symbol_x.shape = -1, 1 symbol_x = np.tile(symbol_x, (1, sps)) symbol_x[:, 1:] = 0 symbol_x.shape = 1, -1 return symbol_x def __str__(self): string = f'\n\tSymbol rate:{self.symbol_rate / 1e9}[Ghz]\n\tfs_in_fiber:{self.fs_in_fiber / 1e9}[Ghz]\n\t' \ f'signal_power_in_fiber:{self.measure_power_in_fiber()} [W]\n\t' \ f'signal_power_in_fiber:{self.measure_power_in_fiber("dbm")} [dbm]\n\t' \ f'wave_length is {self.center_wave_length * 1e9} [nm]\n\t' \ f'center_frequence is {self.center_frequence * 1e-12}[Thz]\n\t' return string def __repr__(self): return self.__str__() @property def shape(self): return self.data_sample_in_fiber.shape class SignalFromNumpyArray(Signal): def __init__(self, array_sample, symbol_rate, mf, symbol_length, sps_in_fiber, sps, **kwargs): ''' :param array_sample: nd arrary :param symbol_rate: GHZ :param mf: modulation format :param symbol_length: the length of transimitted symbol :param sps_in_fiber: the samples per symbol in fiber :param sps: the sampes per symbol in DSP This function will read samples from array_sample, the array_sample should be propogated in fiber The center frequence is the center frequence, and the absolute frequence is from left to right in wdm comb The relative frequence i.e base band equivalent is set a property ''' super(SignalFromNumpyArray, self).__init__( **dict(symbol_rate=symbol_rate, mf=mf, symbol_length=symbol_length, sps_in_fiber=sps_in_fiber, sps=sps)) self.data_sample_in_fiber = array_sample if 'tx_symbol' in kwargs: self.tx_symbol = kwargs['tx_symbol'] if 'rx_symbol' in kwargs: self.rx_symbol = kwargs['rx_symbol'] if 'center_frequence' in kwargs: self.center_frequence = kwargs['center_frequence'] if 'aboslute_frequence' in kwargs: self.absolute_frequence = kwargs['absolute_frequence'] @property def relative_frequence(self): if hasattr(self, 'center_frequence') and hasattr(self, 'absolute_frequence'): return self.absolute_frequence - self.center_frequence else: raise Exception( 'The frequence not provided enough, please ensure the center_frequency and the absolute_frequenc are all provided') class WdmSignal(object): ''' WdmSignal Class ''' def __init__(self, signals: List[Signal], grid_size, multiplexed_filed): ''' :param signals: list of signal :param grid_size: [GHz],if conventional grid_size,this will be a number or a list of same value if the elestical wdm, the grid size of each channel is different. The WdmSignal class should not be used outside usually, in mux function, it will be created automatically. ''' self.signals = signals self.grid_size = grid_size self.center_frequence = (np.min(self.absolute_frequences) + np.max(self.absolute_frequences)) / 2 self.center_wave_length = freq2lamb(self.center_frequence) self.__multiplexed_filed = np.atleast_2d(multiplexed_filed) def __setitem__(self, slice, value): assert self.__multiplexed_filed is not None self.__multiplexed_filed[slice] = value def __getitem__(self, slice): assert self.__multiplexed_filed is not None return self.__multiplexed_filed[slice] # @property # def center_frequence(self): # frequences = self.absolute_frequences() # return (np.max(frequences)+np.min(frequences))/2 @property def pol_number(self): return self.__multiplexed_filed.shape[0] @property def shape(self): return self.__multiplexed_filed.shape @property def fs_in_fiber(self): return self.signals[0].fs_in_fiber @property def sample_number_in_fiber(self): return self.__multiplexed_filed.shape[1] @property def lam(self): lambdas = [] for signal in self.signals: lambdas.append(signal.center_wave_length) return lambdas @property def data_sample_in_fiber(self): return self.__multiplexed_filed @data_sample_in_fiber.setter def data_sample_in_fiber(self, value): self.__multiplexed_filed = value @property def mfs(self): mfs = [] for sig in self.signals: mfs.append(sig.mf) return mfs @property def absolute_frequences(self): ''' :return: ''' return np.array([signal.center_frequence for signal in self.signals]) @property def relative_frequences(self): ''' :return: frequences centered in zero frequence , an ndarray ''' return self.absolute_frequences - self.center_frequence def __str__(self): string = f"center_frequence is {self.center_frequence * 1e-12} [THZ]\t\n" \ f"power is {self.measure_power_in_fiber()} [dbm] \t\n" \ f"power is {self.measure_power_in_fiber(unit='w')} [w]\t\n" return string def measure_power_in_fiber(self, unit='dbm'): power = 0 for i in range(self.pol_number): power += np.mean(np.abs(self[i, :]) ** 2) if unit == 'dbm': power = power * 1000 power = 10 * np.log10(power) return power class QamSignal(Signal): def __init__(self, symbol_rate=35e9, mf="16-qam", symbol_length=2 ** 16, sps=2, sps_in_fiber=4, is_dp=True, frequence=193.1e12, is_from_array=False, is_from_demux=False): ''' :param symbol_rate: [hz] 符号速率 :param mf: 调制格式,16-qam,32-qam,64-qam,qpsk :param signal_power: [dbm] 信号功率 :param symbol_length: 符号长度 :param sps: 发端dsp的过采样率 :param sps_infiber: 在光纤中传输时的过采样率 ''' super().__init__( **dict(symbol_length=symbol_length, symbol_rate=symbol_rate, mf=mf, sps=sps, sps_in_fiber=sps_in_fiber, frequence=frequence) ) self.is_from_demux = is_from_demux if self.mf == 'qpsk': order = 4 else: order = self.mf.split('-')[0] order = int(order) if not is_from_array: if is_dp: self.message = np.random.randint(low=0, high=order, size=(2, self.symbol_length)) else: self.message = np.random.randint(low=0, high=order, size=(1, self.symbol_length)) self.is_dp = is_dp # self.symbol = None self.init(order) def init(self, order): qam_data = (f'{base_path}/' + 'qamdata/' + str(order) + 'qam.mat') qam_data = loadmat(qam_data)['x'] symbol = np.zeros(self.message.shape, dtype=np.complex) symbol = np.atleast_2d(symbol) for i in range(symbol.shape[0]): for msg in np.unique(self.message[i, :]): symbol[i, self.message[i, :] == msg] = qam_data[0, msg] self._symbol = symbol def _main(): symbol_rate = 35e9 mf = '16-qam' signal_power = 0 symbol_length = 5 sps = 2 sps_infiber = 4 parameter = dict(symbol_rate=symbol_rate, mf=mf, symbol_length=symbol_length, sps=sps, sps_in_fiber=sps_infiber, is_dp=False) signal = QamSignal(**parameter) signal[:] = np.array([1, 2, 3, 4, 5]) signal[0, :] = np.array([4, 5, 6, 7, 8]) # signal._normalize_power() print(signal[0, :]) print(signal.measure_power_in_fiber()) print("heiehi") ocspy-0.0.5/Ocspy/Base/__init__.py000777 000765 000024 00000000244 13462527161 020121 0ustar00huazhilunstaff000000 000000 from .SignalInterface import Signal from .SignalInterface import WdmSignal from .SignalInterface import SignalFromNumpyArray from .SignalInterface import QamSignal