def test_level2bits(self): self.assertEqual( list(map(misc.level2bits, range(1, 20))), [1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5]) # Test if an exception is raised for a value of n lower then 1 with self.assertRaises(ValueError): misc.level2bits(0) with self.assertRaises(ValueError): misc.level2bits(-2)
def plotConstellation(self): # pragma: no cover """Plot the constellation (in a scatter plot). """ fig = plt.figure() ax = fig.add_subplot(1, 1, 1) # one row, one column, first plot # circlePatch = patches.Circle((0,0),1) # circlePatch.set_fill(False) # ax.add_patch(circlePatch) ax.scatter(self.symbols.real, self.symbols.imag) ax.axis('equal') ax.grid() formatString = "{0:0=" + str(level2bits(self._M)) + "b} ({0})" index = 0 for symbol in self.symbols: ax.text( symbol.real, # Coordinate X symbol.imag + 0.03, # Coordinate Y formatString.format(index, format_spec="0"), # Text verticalalignment='bottom', # From now on, text properties horizontalalignment='center') index += 1 plt.show()
def _calculateGrayMappingIndexQAM(L): """ 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 : np.ndarray indexes that should be applied to the constellation created by _createConstellation in order to correspond to Gray mapping """ # Row vector with the column variation (second half of the index in # binary form) column = binary2gray(np.arange(0, L, dtype=int)) # Column vector with the row variation # # Column vector with the row variation (first half of the index in # binary form) row = column.reshape(L, 1) columns = np.tile(column, (L, 1)) rows = np.tile(row, (1, L)) # Shift the first part by half the number of bits and sum with the # second part to form each element in the index matrix index_matrix = (rows << (level2bits(L**2) // 2)) + columns # Return the indexes as a vector (row order, which is the default # in numpy) return np.reshape(index_matrix, L**2)
def _calculateGrayMappingIndexQAM(L): """ 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 .. aafig:: 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 : np.ndarray indexes that should be applied to the constellation created by _createConstellation in order to correspond to Gray mapping """ # Row vector with the column variation (second half of the index in # binary form) column = binary2gray(np.arange(0, L, dtype=int)) # Column vector with the row variation # # Column vector with the row variation (first half of the index in # binary form) row = column.reshape(L, 1) columns = np.tile(column, (L, 1)) rows = np.tile(row, (1, L)) # Shift the first part by half the number of bits and sum with the # second part to form each element in the index matrix index_matrix = (rows << (level2bits(L ** 2) // 2)) + columns # Return the indexes as a vector (row order, which is the default # in numpy) return np.reshape(index_matrix, L ** 2)
def calcTheoreticalBER(self, SNR): """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 : float | np.ndarray The theoretical bit error rate. """ # $P_b = \frac{1}{k}P_s$ # Number of bits per symbol k = level2bits(self._M) return 1.0 / k * self.calcTheoreticalSER(SNR)
def calcTheoreticalBER(self, SNR): """Calculates the theoretical (approximation) bit error rate for the QAM scheme. Parameters ---------- SNR : float or array like Signal-to-noise-value (in dB). Returns ------- BER : float or array like The theoretical bit error rate. """ # For higher SNR values and gray mapping, each symbol error # corresponds to approximately a single bit error. The BER is then # given by the probability of error of a single carrier in the QAM # system divided by the number of bits transported in that carrier. k = level2bits(self._M) Psc = self._calcTheoreticalSingleCarrierErrorRate(SNR) ber = (2. * Psc) / k return ber
def calcTheoreticalBER(self, SNR): """ Calculates the theoretical (approximation) bit error rate for the QAM scheme. Parameters ---------- SNR : float | np.ndarray Signal-to-noise-value (in dB). Returns ------- BER : float | np.ndarray The theoretical bit error rate. """ # For higher SNR values and gray mapping, each symbol error # corresponds to approximately a single bit error. The BER is then # given by the probability of error of a single carrier in the QAM # system divided by the number of bits transported in that carrier. k = level2bits(self._M) Psc = self._calcTheoreticalSingleCarrierErrorRate(SNR) ber = (2. * Psc) / k return ber