Source code for Modulators.DiscSpecModulator
# This file is part of NFDMLab.
#
# NFDMLab is free software; you can redistribute it and/or
# modify it under the terms of the version 2 of the GNU General
# Public License as published by the Free Software Foundation.
#
# NFDMLab 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 NFDMLab; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# 02111-1307 USA
#
# Contributors:
# Sander Wahls (TU Delft) 2018
# Shrinivas Chimmalgi (TU Delft) 2018
import numpy as np
import matplotlib.pyplot as plt
from FNFTpy.fnft_nsev_inverse_wrapper import nsev_inverse_wrapper, nsev_inverse_xi_wrapper
from FNFTpy.options_handling import fnft_nsev_inverse_default_options_wrapper
from FNFTpy.fnft_nsev_wrapper import nsev_wrapper
from FNFTpy.options_handling import fnft_nsev_default_options_wrapper
from Modulators import BaseModulator
from Helpers import NFSpectrum
from Helpers import next_pow2
[docs]class DiscSpecModulator(BaseModulator):
"""This modulator embeds symbols in residues of a multi-soliton with fixed
eigenvalue pattern."""
[docs] def __init__(self, eigenvalues, residues_amplitude, normalized_distance,
T, required_normalized_dt):
"""Constructor.
Parameters
----------
eigenvalues : numpy.array(complex)
Vector of eigenvalues used to generate fiber inputs.
residues_amplitude : numpy.array(complex)
Vector of scaling factors. The final residues for the eigenvalue is
obtained by multiplying these factors with the symbols.
normalized_distance : float
Fiber length in normalized units.
T : numpy.array(float)
Vector of length two with T[0]<T[1]. Specifies the time interval, in
normalized units, used for the generation of the fiber inputs.
required_normalized_dt : float
The modulator will choose the number of time domain samples such
that the time step, in normalized units, does not exceed this value.
"""
# Save some parameters for later use.
self._eigenvalues = eigenvalues
self._residues_amplitude = residues_amplitude
self._normalized_distance = normalized_distance
self._n_symbols_per_block = np.size(eigenvalues)
assert np.size(residues_amplitude) == self.n_symbols_per_block
self._T = T
# Determine number of samples and time step from time step requirements
self._n_samples = next_pow2((T[1] - T[0])/required_normalized_dt + 1)
self._normalized_dt = (T[1] - T[0])/(self.n_samples - 1)
# Setup options for the forward and inverse NFT
self._opts_inv = fnft_nsev_inverse_default_options_wrapper()
self._opts_fwd = fnft_nsev_default_options_wrapper()
self._opts_inv.discspec_type = 1 # residues
self._opts_fwd.discspec_type = 1 # residues
self._opts_fwd.contspec_type = 3 # skip computation of continuous spectrum
def _new_nfspec(self):
"""Generates an empty NFSpectrum object with the right discspec_type and
plotting hints."""
nfspec = NFSpectrum("none", "b/a'")
nfspec.bound_state_plot_range = np.zeros(4)
nfspec.bound_state_plot_range[0] = np.max(np.real(self._eigenvalues))
nfspec.bound_state_plot_range[1] = np.min(np.real(self._eigenvalues))
nfspec.bound_state_plot_range[3] = np.max(np.imag(self._eigenvalues))
nfspec.bound_state_plot_range *= 1.5
return nfspec
[docs] def modulate(self, symbols):
# Docstring is inherited from base class.
# Determine residues
nc = np.size(symbols)
assert nc == self.n_symbols_per_block
residues = self._residues_amplitude*symbols
# Call inverse NFT to generate the multi-soliton
rdict = nsev_inverse_wrapper(0,
[],
-1,
1,
nc,
self._eigenvalues,
residues,
self.n_samples,
self._T[0],
self._T[1],
+1,
self._opts_inv)
if rdict['return_value'] != 0:
raise Exception("FNFT failed")
# Save generated nonlinear Fourier spectrum for the user
nfspec = self._new_nfspec()
nfspec.bound_states = self._eigenvalues
nfspec.normconsts = residues
return rdict['q'], nfspec
[docs] def demodulate(self, q):
# Docstring is inherited from base class.
# Call NFT to obtain discrete spectrum
assert np.size(q) == self.n_samples
rdict = nsev_wrapper(self.n_samples,
q,
self._T[0],
self._T[1],
-1,
1,
0,
self.n_symbols_per_block,
+1,
self._opts_fwd)
if rdict['return_value'] != 0:
raise Exception("FNFT failed")
# Save nonlinear Fourier spectrum for the user, add plotting hints
nfspec = self._new_nfspec()
nfspec.normconsts = rdict['disc_res']
nfspec.bound_states = rdict['bound_states']
# Recover symbols from residues
symbols = np.zeros(self.n_symbols_per_block, dtype=complex)
phase_shifts = np.exp(-2j*nfspec.bound_states**2*self._normalized_distance)
if rdict['bound_states_num']>0:
for i in range(0, self.n_symbols_per_block):
dists = np.abs(self._eigenvalues[i] - nfspec.bound_states)
idx = np.argmin(dists)
symbols[i] = phase_shifts[idx]*nfspec.normconsts[idx]/self._residues_amplitude[i]
return symbols, nfspec