pyphysim.modulators package¶
Submodules¶
pyphysim.modulators.fundamental module¶
Module with class for some fundamental modulators, such as PSK and M-QAM.
All fundamental modulators inherit from the Modulator class and should call the self.setConstellation method in their __init__ method, as well as implement the calcTheoreticalSER and calcTheoreticalBER methods.
-
class
pyphysim.modulators.fundamental.
BPSK
[source]¶ Bases:
pyphysim.modulators.fundamental.Modulator
BPSK Class
-
calcTheoreticalBER
(SNR: NumberOrArray) → NumberOrArray[source]¶ Calculates the theoretical (approximation) bit error rate for the BPSK scheme.
- Parameters
SNR (float | np.ndarray) – Signal-to-noise-value (in dB).
- Returns
BER – The theoretical bit error rate.
- Return type
float | np.ndarray
-
calcTheoreticalSER
(SNR: NumberOrArray) → NumberOrArray[source]¶ Calculates the theoretical (approximation) symbol error rate for the BPSK scheme.
- Parameters
SNR (float | np.ndarray) – Signal-to-noise-value (in dB).
- Returns
SER – The theoretical symbol error rate.
- Return type
float | np.ndarray
-
demodulate
(receivedData: numpy.ndarray) → numpy.ndarray[source]¶ Demodulate the data.
- Parameters
receivedData (np.ndarray) – Data to be demodulated.
- Returns
demodulated_data – The demodulated data.
- Return type
np.ndarray
-
modulate
(inputData: numpy.ndarray) → numpy.ndarray[source]¶ Modulate the input data (decimal data).
- Parameters
inputData (np.ndarray) – Data to be modulated.
- Returns
modulated_data – The modulated data
- Return type
np.ndarray
- Raises
ValueError – If inputData has any invalid value such as values greater than self._M - 1. Note that inputData should not have negative values but no check is done for this.
-
-
class
pyphysim.modulators.fundamental.
Modulator
[source]¶ Bases:
object
Base class for digital modulators.
The derived classes need to at least call setConstellation to set the constellation in their constructors as well as implement calcTheoreticalSER and calcTheoreticalBER.
Examples
>>> np.set_printoptions(linewidth=70) >>> constellation = np.array([1 + 1j, - 1 + 1j, - 1 - 1j, 1 - 1j]) >>> m=Modulator() >>> m.setConstellation(constellation) >>> m.symbols array([ 1.+1.j, -1.+1.j, -1.-1.j, 1.-1.j]) >>> m.M 4 >>> m.K 2.0 >>> m 4-Modulator object >>> m.modulate(np.array([0, 0, 3, 3, 1, 3, 3, 3, 2, 2])) array([ 1.+1.j, 1.+1.j, 1.-1.j, 1.-1.j, -1.+1.j, 1.-1.j, 1.-1.j, 1.-1.j, -1.-1.j, -1.-1.j])
>>> m.demodulate(np.array([ 1. + 1.j, 1. + 1.j, 1. - 1.j, 1. - 1.j, - 1. + 1.j, 1. - 1.j, 1. - 1.j, 1. - 1.j, - 1. - 1.j, - 1. - 1.j])) array([0, 0, 3, 3, 1, 3, 3, 3, 2, 2])
-
property
K
¶ Get method for the K property.
The K property corresponds to the number of bits represented by each symbol in the constellation. It is equal to log2(M), where M is the constellation size.
See also
-
property
M
¶ Get method for the M property.
The M property corresponds to the number of symbols in the constellation.
See also
-
calcTheoreticalBER
(SNR: NumberOrArray) → NumberOrArray[source]¶ Calculates the theoretical bit error rate.
- Parameters
SNR (float | np.ndarray) – Signal-to-noise-value (in dB).
- Returns
BER – The theoretical bit error rate.
- Return type
float | np.ndarray
See also
Notes
This function should be implemented in the derived classes
-
calcTheoreticalPER
(SNR: NumberOrArray, packet_length: int) → NumberOrArray[source]¶ Calculates the theoretical package error rate.
A package is a group of bits, where if a single bit is in error then the whole package is considered to be in error.
The package error rate (PER) is a direct mapping of the bit error rate (BER), such that
\[PER = 1 - (1 - BER)^{L}\]where \(L\) is the package_length.
- Parameters
SNR (float | np.ndarray) – Signal-to-noise-value (in dB).
packet_length (int) – The package length. That is, the number of bits in each package.
- Returns
PER – The theoretical package error rate.
- Return type
float | np.ndarray
-
calcTheoreticalSER
(SNR: NumberOrArray) → NumberOrArray[source]¶ Calculates the theoretical symbol error rate.
- Parameters
SNR (float | np.ndarray) – Signal-to-noise-value (in dB).
- Returns
SER – The theoretical symbol error rate.
- Return type
float | np.ndarray
See also
Notes
This function should be implemented in the derived classes
-
calcTheoreticalSpectralEfficiency
(SNR: NumberOrArray, packet_length: Optional[int] = None) → NumberOrArray[source]¶ Calculates the theoretical spectral efficiency.
If there was no error in the transmission, the spectral efficiency would be equal to the K property, that is, equal to the number of bits represented by each symbol in the constellation. However, due to bit errors the effective spectral efficiency will be lower.
The calcTheoreticalSpectralEfficiency method calculates the effective spectral efficiency from the K property and the package error rate (PER) for the given SNR and packet_length ‘L’, such that
\[se = K * (1 - PER)\]- Parameters
SNR (float | np.ndarray) – Signal-to-noise-value (in dB).
packet_length (int, optional) – The package length. That is, the number of bits in each package.
- Returns
se – The theoretical spectral efficiency.
- Return type
float | np.ndarray
See also
-
demodulate
(receivedData: numpy.ndarray) → numpy.ndarray[source]¶ Demodulate the data.
- Parameters
receivedData (np.ndarray) – Data to be demodulated.
- Returns
demodulated_data – The demodulated data.
- Return type
np.ndarray
-
modulate
(inputData: Union[int, numpy.ndarray]) → numpy.ndarray[source]¶ Modulate the input data (decimal data).
- Parameters
inputData (np.ndarray | int) – Data to be modulated.
- Returns
modulated_data – The modulated data
- Return type
np.ndarray
- Raises
ValueError – If inputData has any invalid value such as values greater than self._M - 1. Note that inputData should not have negative values but no check is done for this.
-
property
name
¶ Get method for the ‘name’ property.
- Returns
The name of the modulator.
- Return type
-
setConstellation
(symbols: numpy.ndarray) → None[source]¶ Set the constellation of the modulator.
This function should be called in the constructor of the derived classes.
- Parameters
symbols (np.ndarray) – A an numpy array with the symbol table.
-
property
-
class
pyphysim.modulators.fundamental.
PSK
(M: int, phaseOffset: float = 0)[source]¶ Bases:
pyphysim.modulators.fundamental.Modulator
PSK Class
-
static
_createConstellation
(M: int, phaseOffset: float) → numpy.ndarray[source]¶ Generates the Constellation for the PSK modulation scheme.
-
calcTheoreticalBER
(SNR: NumberOrArray) → NumberOrArray[source]¶ Calculates the theoretical (approximation) bit error rate for the M-PSK scheme using Gray coding.
- Parameters
SNR (float | np.ndarray) – Signal-to-noise-value (in dB).
- Returns
BER – The theoretical bit error rate.
- Return type
float | np.ndarray
-
calcTheoreticalSER
(SNR: NumberOrArray) → NumberOrArray[source]¶ Calculates the theoretical (approximation for high M and high SNR) symbol error rate for the M-PSK scheme.
- Parameters
SNR (float | np.ndarray) – Signal-to-noise-value (in dB).
- Returns
SER – The theoretical symbol error rate.
- Return type
float | np.ndarray
-
static
-
class
pyphysim.modulators.fundamental.
QAM
(M: int)[source]¶ Bases:
pyphysim.modulators.fundamental.Modulator
QAM Class
-
_calcTheoreticalSingleCarrierErrorRate
(SNR: NumberOrArray) → NumberOrArray[source]¶ Calculates the theoretical (approximation) error rate of a single carrier in the QAM system (QAM has two carriers).
- Parameters
SNR (float | np.ndarray) – Signal-to-noise-value (in dB).
- Returns
Psc – The theoretical single carrier error rate.
- Return type
float | np.ndarray
Notes
This method is used in the
calcTheoreticalSER()
implementation.See also
-
static
_calculateGrayMappingIndexQAM
(L: int) → numpy.ndarray[source]¶ Calculates the indexes that should be applied to the constellation created by _createConstellation in order to correspond to Gray mapping.
Notice that the square M-QAM constellation is a matrix of dimension L x L, where L is the square root of M. Since the constellation was generated without taking into account the Gray mapping, then we need to reorder the generated constellation and this function calculates the indexes that can be applied to the original constellation in order to do exactly that.
As an example, for the 16-QAM modulation the indexes can be organized (row order) in the matrix below
/
00
01
11
10
00
0000
0001
0011
0010
01
0100
0101
0111
0110
11
1100
1101
1111
1110
10
1000
1001
1011
1010
This is equivalent to concatenate a Gray mapping for the row with a Gray mapping for the column, and the corresponding indexes are [0, 1, 3, 2, 4, 5, 7, 6, 12, 13, 15, 14, 8, 9, 11, 10]
- Parameters
L (int) – Square root of the modulation cardinality (must be an integer).
- Returns
indexes – indexes that should be applied to the constellation created by _createConstellation in order to correspond to Gray mapping
- Return type
np.ndarray
-
static
_createConstellation
(M: int) → numpy.ndarray[source]¶ Generates the Constellation for the (SQUARE) M-QAM modulation scheme.
- Parameters
M (int) – The modulation cardinality
- Returns
symbols – The QAM constellation with the desired cardinality.
- Return type
np.ndarray
-
-
class
pyphysim.modulators.fundamental.
QPSK
[source]¶ Bases:
pyphysim.modulators.fundamental.PSK
QPSK Class
pyphysim.modulators.ofdm module¶
Module implementing OFDM modulation and demodulation.
-
class
pyphysim.modulators.ofdm.
OFDM
(fft_size: int, cp_size: int, num_used_subcarriers: Optional[int] = None)[source]¶ Bases:
object
OFDM class.
-
_add_CP
(input_data: numpy.ndarray) → numpy.ndarray[source]¶ Add the Cyclic prefix to the input data.
- Parameters
input_data (np.ndarray) – OFDM modulated data (after the IFFT). This must be a 2D numpy array with shape (Number of OFDM symbols, IFFT size).
- Returns
output – The input_data with the cyclic prefix added. The shape of the output is (Number of OFDM symbols, IFFT size + CP Size).
- Return type
np.ndarray
-
_calc_zeropad
(input_data_size: int) → Tuple[int, int][source]¶ Calculates the number of zeros that must be added to the input data to make it a multiple of the OFDM size.
The number of zeros that must be added to the input data is returned along with the number of OFDM symbols that will be generated.
- Parameters
input_data_size (int) – Size the the data that will be modulated by the OFDM object.
- Returns
(zeropad, num_ofdm_symbols) – A tuple with zeropad and num_ofdm_symbols. Zeropad is the number of zeros added to the input data to make the total number of elements a multiple of the number of used subcarriers. Num_ofdm_symbols is the number of OFDM symbols required to transmit input_data_size symbols.
- Return type
-
_calculate_power_scale
() → float[source]¶ Calculate the power scale that needs to be applied in the modulator and removed in the demodulate methods.
The power is applied in the modulator method so that the total power of the OFDM samples is similar to the total power of the symbols modulated by OFDM.
Note that this total power is shared among useful samples and the cyclic prefix in one OFDM symbol. Therefore, the larger the cyclic prefix size the lower is this power scale to account energy loss due to sending the cyclic prefix.
- Returns
power_scale – The calculated power scale. You should take the square root of this before multiplying by the samples.
- Return type
-
_get_subcarrier_numbers
() → numpy.ndarray[source]¶ Get the indexes of all subcarriers, including the negative, the DC and the positive subcarriers.
Note that these indexes are not suitable for indexing in python. They are the actual indexes of the subcarriers in an OFDM symbol. For instance, an OFDM symbol with 16 subcarriers will have indexes from -8 to 7. However, due to the way the fft is implemented in numpy the indexes here are actually from 0 to 7 followed by -8 to -1.
- Returns
Numbers of all subcarriers, including the negative, the DC and the positive subcarriers
- Return type
np.ndarray
Examples
>> ofdm_obj = OFDM(16, 4, 16) >> ofdm_obj._get_subcarrier_numbers() array([ 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1])
-
_get_used_subcarrier_numbers
() → numpy.ndarray[source]¶ Get the subcarrier indexes of the actually used subcarriers.
Note that these indexes are not suitable for indexing in python. They are the actual indexes of the subcarriers in an OFDM symbol. See the documentation of the _get_subcarrier_numbers function.
- Returns
Number of the actually used subcarriers.
- Return type
np.ndarray
Examples
>> ofdm_obj = OFDM(16, 4, 10) >> ofdm_obj._get_used_subcarrier_numbers() array([ 1, 2, 3, 4, 5, -5, -4, -3, -2, -1]) >> ofdm_obj = OFDM(16, 4, 14) >> ofdm_obj._get_used_subcarrier_numbers() array([ 1, 2, 3, 4, 5, 6, 7, -7, -6, -5, -4, -3, -2, -1])
-
_prepare_decoded_signal
(decoded_signal: numpy.ndarray) → numpy.ndarray[source]¶ Prepare the decoded signal that was processed by the FFT in the demodulate function.
This is equivalent of reversing the indexing that was done by the _prepare_input_signal method.
- Parameters
decoded_signal (np.ndarray) – Signal that was decoded by the FFT in the OFDM demodulate method.
- Returns
demodulated_samples – Demodulated samples of the symbols that were modulated by the OFDM object (for instance the PSK or M-QAM symbols passed to OFDM).
- Return type
np.ndarray
Notes
This method should be called AFTER the Cyclic Prefix was removed and the FFT was performed.
Also, because the number of zeropad was not saved, then _prepare_decoded_signal has no way to remove them.
See also
-
_prepare_input_signal
(input_signal: numpy.ndarray) → numpy.ndarray[source]¶ Prepare the input signal to be passed to the IFFT in the modulate function.
The input signal must be prepared before it is passed to the IFFT in the OFDM modulate function.
First, zeros must be added so that the input signal size is multiple of the number of used subcarriers.
After that the input signal must be allocated to subcarriers in the center of the spectrum (except for the DC component). That is, zeros will be allocated to the lower and higher subcarriers such that only num_used_subcarriers are used from fft_size subcarriers.
This preparation is performed by the _prepare_input_signal function.
- Parameters
input_signal (np.ndarray) – Input signal that must be modulated by the OFDM modulate function.
- Returns
input_ifft – Signal suitable to be passed to the IFFT function to actually perform the OFDM modulation.
- Return type
np.ndarray
See also
-
_remove_CP
(received_data: numpy.ndarray) → numpy.ndarray[source]¶ Remove the Cyclic prefix of the received data.
- Parameters
received_data (np.ndarray) – Data that must be demodulated by the OFDM object.
- Returns
output – Received data without the Cyclic prefix.
- Return type
np.ndarray
Notes
The _remove_CP method will also change the shape so that it is suitable to be passed to the FFT function.
-
demodulate
(received_signal: numpy.ndarray) → numpy.ndarray[source]¶ Perform the OFDM demodulation of the received_signal.
- Parameters
received_signal (np.ndarray) – An array with the samples of the received OFDM symbols.
- Returns
demodulated_data – Demodulated symbols.
- Return type
np.ndarray
-
get_used_subcarrier_indexes
() → numpy.ndarray[source]¶ Get the subcarrier indexes of the subcarriers actually used in a way suitable for python indexing (going from 0 to fft_size-1).
- Returns
indexes – Subcarrier indexes of the subcarriers actually used in a way suitable for python indexing.
- Return type
np.ndarray
Notes
This is the function actually used in the modulate function.
Examples
Consider the example below where we have 16 subcarriers and only 10 subcarriers are used. The lower and higher subcarrier as well as the DC subcarrier will not be used. The index of the used subcarriers should go then from 11 to 15 (5 subcarriers), skip subcarrier 0, and then go from 1 to 5 (the other 5 subcarriers).
>>> ofdm_obj = OFDM(16, 4, 10) >>> ofdm_obj.get_used_subcarrier_indexes() array([11, 12, 13, 14, 15, 1, 2, 3, 4, 5]) >>> ofdm_obj = OFDM(16,4,14) >>> ofdm_obj.get_used_subcarrier_indexes() array([ 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7])
-
modulate
(input_signal: numpy.ndarray) → numpy.ndarray[source]¶ Perform the OFDM modulation of the input_signal.
- Parameters
input_signal (np.ndarray) – Input signal that must be modulated by the OFDM modulate function.
- Returns
output – An array with the samples of the modulated OFDM symbols.
- Return type
np.ndarray
-
-
class
pyphysim.modulators.ofdm.
OfdmOneTapEqualizer
(ofdm_obj: pyphysim.modulators.ofdm.OFDM)[source]¶ Bases:
object
The OfdmOneTapEqualizer class performs the one-tap equalization often required in OFDM transmissions to compensate the effect of the channel at each subcarrier.
- Parameters
ofdm_obj (OFDM) – The OFDM object used to modulate/demodulate the data.
-
_equalize_data
(data_reshaped: numpy.ndarray, mean_freq_response: numpy.ndarray) → numpy.ndarray[source]¶ Perform the one-tap equalization and return data after the channel compensation.
- Parameters
data_reshaped (np.ndarray) – The data to be equalized. If must be a 2D numpy array, where different rows correspond to different OFDM symbols and the different columns correspond to the USED subcarriers. Dimension: num OFDM symbols x num Used subcarriers
mean_freq_response (np.ndarray) – The frequency response for each OFDM symbol. Dimension: num OFDM symbols x FFT size
- Returns
The received data after the one-tap equalization to compensate the channel effect. Dimension: num OFDM symbols x num Used subcarriers
- Return type
np.ndarray
-
equalize_data
(data: numpy.ndarray, impulse_response: pyphysim.channels.fading.TdlImpulseResponse) → numpy.ndarray[source]¶ Perform the one-tap equalization and return data after the channel compensation.
- Parameters
data (np.ndarray) – The data to be equalized.
impulse_response (fading.TdlImpulseResponse) – The impulse response of the channel.
- Returns
The received data after the one-tap equalization to compensate the channel effect.
- Return type
np.ndarray
Module contents¶
Package containing modulators.