def test_link_performance(): # Apply link_performance to SISO QPSK and AWGN channel QPSK = QAMModem(4) def receiver(y, h, constellation): return QPSK.demodulate(y, 'hard') model = LinkModel(QPSK.modulate, SISOFlatChannel(fading_param=(1 + 0j, 0)), receiver, QPSK.num_bits_symbol, QPSK.constellation, QPSK.Es) BERs = link_performance(model, range(0, 9, 2), 600e4, 600) desired = erfc(sqrt(10**(arange(0, 9, 2) / 10) / 2)) / 2 assert_allclose(BERs, desired, rtol=0.25, err_msg='Wrong performance for SISO QPSK and AWGN channel') # Apply link_performance to MIMO 16QAM and 4x4 Rayleigh channel QAM16 = QAMModem(16) RayleighChannel = MIMOFlatChannel(4, 4) RayleighChannel.uncorr_rayleigh_fading(complex) def receiver(y, h, constellation): return QAM16.demodulate(kbest(y, h, constellation, 16), 'hard') model = LinkModel(QAM16.modulate, RayleighChannel, receiver, QAM16.num_bits_symbol, QAM16.constellation, QAM16.Es) SNRs = arange(0, 21, 5) + 10 * log10(QAM16.num_bits_symbol) BERs = link_performance(model, SNRs, 600e4, 600) desired = (2e-1, 1e-1, 3e-2, 2e-3, 4e-5) # From reference assert_allclose(BERs, desired, rtol=1.25, err_msg='Wrong performance for MIMO 16QAM and 4x4 Rayleigh channel')
def setupMappingTable(self): """Setup mapping table, depending on the choosen mapping scheme.""" # Get number of levels for mapping M = self.mapping_config[1] if self.mapping_type == "QAM": # QAM object self.m_qam_obj = QAMModem(M) # List of binary representations for M-QAM list_of_binary = [ ','.join(str(np.binary_repr(number, width=int(np.log2(M))))) for number in range(0, M) ] # Creates the mapping table self.mapping_table = { eval(f"({binary})"): self.m_qam_obj.modulate(eval(binary))[0] for binary in list_of_binary } elif self.mapping_type == "PSK": # BPSK = 2-PSK if M == 2: self.mapping_table = {(1, ): +1 + 0j, (0, ): -1 + 0j} else: # PSK object self.m_psk_obj = PSKModem(M) # List of binary representations for M-PSK list_of_binary = [ ','.join(str(np.binary_repr(number, width=int(np.log2(M))))) for number in range(0, M) ] # Creates the mapping table self.mapping_table = { eval(f"({binary})"): self.m_psk_obj.modulate(eval(binary))[0] for binary in list_of_binary } # Also, setup the demapping table self.demapping_table = {v: k for k, v in self.mapping_table.items()}
def test_bit_lvl_repr(): qam = QAMModem(4) nb_rx = 2 nb_tx = 2 RayleighChannel = MIMOFlatChannel(nb_tx, nb_rx) RayleighChannel.fading_param = (zeros((nb_rx, nb_tx), complex), identity(nb_tx), identity(nb_rx)) SNR = arange(10, 16, 5) def receiver_with_blr(y, H, cons): beta = int(log2(len(cons))) # creation de w reel = [pow(2, i) for i in range(beta // 2 - 1, -1, -1)] im = [1j * pow(2, i) for i in range(beta // 2 - 1, -1, -1)] w = concatenate((reel, im), axis=None) A = bit_lvl_repr(H, w) mes = array(mimo_ml(y, A, [-1, 1])) mes[mes == -1] = 0 return mes def receiver_without_blr(y, H, cons): return qam.demodulate(mimo_ml(y, H, cons), 'hard') my_model_without_blr = \ LinkModel(qam.modulate, RayleighChannel, receiver_without_blr, qam.num_bits_symbol, qam.constellation, qam.Es) my_model_with_blr = \ LinkModel(qam.modulate, RayleighChannel, receiver_with_blr, qam.num_bits_symbol, qam.constellation, qam.Es) ber_without_blr = link_performance(my_model_without_blr, SNR, 300e4, 300) ber_with_blr = link_performance(my_model_with_blr, SNR, 300e4, 300) assert_allclose(ber_without_blr, ber_with_blr, rtol=0.5, err_msg='bit_lvl_repr changes the performance')
def __init__(self): # Create a custom Modem custom_constellation = [ re + im * 1j for re, im in product((-3.5, -0.5, 0.5, 3.5), repeat=2) ] self.custom_modems = [Modem(custom_constellation)] # Add to custom modems a QAM modem with modified constellation QAM_custom = QAMModem(16) QAM_custom.constellation = custom_constellation self.custom_modems.append(QAM_custom) self.modems += self.custom_modems # Assert that error is raised when the contellation length is not a power of 2 with assert_raises(ValueError): QAM_custom.constellation = (0, 0, 0)
class ModemTestcase: qam_modems = [QAMModem(4), QAMModem(16), QAMModem(64)] psk_modems = [PSKModem(4), PSKModem(16), PSKModem(64)] modems = qam_modems + psk_modems def __init__(self): # Create a custom Modem custom_constellation = [ re + im * 1j for re, im in product((-3.5, -0.5, 0.5, 3.5), repeat=2) ] self.custom_modems = [Modem(custom_constellation)] # Add to custom modems a QAM modem with modified constellation QAM_custom = QAMModem(16) QAM_custom.constellation = custom_constellation self.custom_modems.append(QAM_custom) self.modems += self.custom_modems # Assert that error is raised when the contellation length is not a power of 2 with assert_raises(ValueError): QAM_custom.constellation = (0, 0, 0) def test(self): for modem in self.modems: self.do(modem) for modem in self.qam_modems: self.do_qam(modem) for modem in self.psk_modems: self.do_psk(modem) for modem in self.custom_modems: self.do_custom(modem) # Default methods for TestClasses that not implement a specific test def do(self, modem): pass def do_qam(self, modem): pass def do_psk(self, modem): pass def do_custom(self, modem): pass
def build_modulator(modulation_scheme): """Construct a modulator. Arguments: ---------- modulation_scheme: a string - name of a modulation scheme. Returns: -------- QAMModem - represents modulator @ particular modulation scheme. """ # @TODO: add more modulation schemes if str.lower(modulation_scheme) == 'qpsk': return QAMModem(m=4) elif str.lower(modulation_scheme) == 'qam16': return QAMModem(m=16) elif str.lower(modulation_scheme) == 'qam64': return QAMModem(m=64) elif str.lower(modulation_scheme) == 'qam128': return QAMModem(m=128) else: raise ValueError( 'Modulation scheme {} is not supported'.format(modulation_scheme))
def test_bit_lvl_repr(): # Set seed seed(17121996) # Test the BLR by comparing the performance of a receiver with and without it. qam = QAMModem(4) nb_rx = 2 nb_tx = 2 RayleighChannel = MIMOFlatChannel(nb_tx, nb_rx) RayleighChannel.fading_param = (zeros( (nb_rx, nb_tx), complex), identity(nb_tx), identity(nb_rx)) SNR = arange(10, 16, 5) def receiver_with_blr(y, H, cons, noise_var): # Create w beta = int(log2(len(cons))) reel = [pow(2, i) for i in range(beta // 2 - 1, -1, -1)] im = [1j * pow(2, i) for i in range(beta // 2 - 1, -1, -1)] w = concatenate((reel, im), axis=None) # Compute bit level representation A = bit_lvl_repr(H, w) mes = array(mimo_ml(y, A, [-1, 1])) mes[mes == -1] = 0 return mes def receiver_without_blr(y, H, cons, noise_var): return qam.demodulate(mimo_ml(y, H, cons), 'hard') my_model_without_blr = \ LinkModel(qam.modulate, RayleighChannel, receiver_without_blr, qam.num_bits_symbol, qam.constellation, qam.Es) my_model_with_blr = \ LinkModel(qam.modulate, RayleighChannel, receiver_with_blr, qam.num_bits_symbol, qam.constellation, qam.Es) ber_without_blr = link_performance(my_model_without_blr, SNR, 300e4, 300) ber_with_blr = link_performance(my_model_with_blr, SNR, 300e4, 300) assert_allclose(ber_without_blr, ber_with_blr, rtol=0.5, err_msg='bit_lvl_repr changes the performance') # Test error raising with assert_raises(ValueError): bit_lvl_repr(RayleighChannel.channel_gains[0], array((2, 4, 6)))
def decode(pt): return QAMModem(4).demodulate(pt, 'hard')
class Mapping(object): def __init__(self, bitstream_frame, mapping_config, sync_obj): """Constructor of Mapping, that parallelizes and maps the input stream.""" # Create sync object, and set debug and simulation path self.sync_obj = sync_obj self.DEBUG = self.sync_obj.getDebug( "Mapping") or self.sync_obj.getDebug("all") self.sync_obj.appendToSimulationPath("Mapping") if self.DEBUG: print('Running Mapping...') # Mapping config to be applied. self.mapping_config = mapping_config # Bitstream info for transmission, depending on number of frames. self.bitstream_frame = bitstream_frame # Number of bits per symbol, before mapping. self.bits_per_symbol = int(np.log2(mapping_config[1])) # Size of current frame to be transmitted. if self.bitstream_frame is not None: self.frame_size = len(bitstream_frame) # Array with all parallelized symbols from the input bitstream. self.parallelized_info = np.zeros([ self.frame_size // self.bits_per_symbol, self.bits_per_symbol ], dtype=np.uint8) else: self.frame_size = None self.parallelized_info = None # Type of mapping to be applied in parallelized data. self.mapping_type = mapping_config[0] # self.parallelized_info = np.zeros([self.frame_size//self.bits_per_symbol, self.bits_per_symbol], dtype=np.bool_) # Bitstream info after receiveing, depending on number of frames. self.rx_bitstream_frame = None # Array with all symbols converted from the input bitstream, when mapping # Or the output mapped, when demapping self.mapped_info = None # Has the shape of the input mapped info. self.mapped_shape = None # Stores the number of data carriers, i.e., the number of quadrature symbols for each transmission self.number_of_data_carriers = None pass @sync_track def setupMappingTable(self): """Setup mapping table, depending on the choosen mapping scheme.""" # Get number of levels for mapping M = self.mapping_config[1] if self.mapping_type == "QAM": # QAM object self.m_qam_obj = QAMModem(M) # List of binary representations for M-QAM list_of_binary = [ ','.join(str(np.binary_repr(number, width=int(np.log2(M))))) for number in range(0, M) ] # Creates the mapping table self.mapping_table = { eval(f"({binary})"): self.m_qam_obj.modulate(eval(binary))[0] for binary in list_of_binary } elif self.mapping_type == "PSK": # BPSK = 2-PSK if M == 2: self.mapping_table = {(1, ): +1 + 0j, (0, ): -1 + 0j} else: # PSK object self.m_psk_obj = PSKModem(M) # List of binary representations for M-PSK list_of_binary = [ ','.join(str(np.binary_repr(number, width=int(np.log2(M))))) for number in range(0, M) ] # Creates the mapping table self.mapping_table = { eval(f"({binary})"): self.m_psk_obj.modulate(eval(binary))[0] for binary in list_of_binary } # Also, setup the demapping table self.demapping_table = {v: k for k, v in self.mapping_table.items()} @sync_track def serialToParallel(self): """Converts each 'bitstream_frame' into 2D numpy array, as [bits_per_symbol, len(bitstream_info)/bits_per_symbol]""" if self.number_of_data_carriers is not None: self.parallelized_info = self.bitstream_frame.reshape( (self.number_of_data_carriers, self.bits_per_symbol)) else: raise ValueError( f"\n\n***Error --> Please first setup the number of data carriers 'number_of_data_carriers'.\nUse setNumberOfDataCarriers(value)\n" ) pass @sync_track def createPilotTable(self): """Create the mapping table for given pilot config.""" # Supported modulation if self.mapping_type in ["QAM", "PSK"]: self.setupMappingTable() max_imag = None max_real = None for key in self.mapping_table.keys(): if max_imag is None: max_imag = self.mapping_table[key].imag elif max_imag < self.mapping_table[key].imag: max_imag = self.mapping_table[key].imag if max_real is None: max_real = self.mapping_table[key].real elif max_real < self.mapping_table[key].real: max_real = self.mapping_table[key].real # get pilot value self.pilot_value = max_real + 1j * max_imag return self.pilot_value else: raise ValueError( f"\n\n***Error --> Not supported mapping_type: <{self.mapping_type}>!\n" ) @sync_track def applyMapping(self): """Given 'bits_per_symbol' and 'mapping_type', generates the 'mapped_info'.""" # Supported modulation if self.mapping_type in ["QAM", "PSK"]: self.setupMappingTable() self.serialToParallel() # calculate the mapped info, depending on the modulations scheme self.mapped_info = np.array([ self.mapping_table[tuple(symbol)] for symbol in self.parallelized_info ]) else: raise ValueError( f"\n\n***Error --> Not supported mapping_type: <{self.mapping_type}>!\n" ) @sync_track def applyDemapping(self, mapped_output): """Given 'mapped_output', returns the closest values for the demapping.""" printDebug(self.mapping_type) if self.mapping_type != "QA": self.setupMappingTable() # Given the demapping table, get all possible constellation points self.constellation = np.array( [x for x in self.demapping_table.keys()]) # calculates what is the distance between each received data, and each constellation point euclidean_dist = abs( mapped_output.reshape((-1, 1)) - self.constellation.reshape((1, -1))) # Get the minimum distance index min_distance = euclidean_dist.argmin(axis=1) # get back the real constellation point self.found_constellation = self.constellation[min_distance] # Do the de-mapping transofrmation, back to bit list values self.rx_bitstream_frame = np.vstack( [self.demapping_table[C] for C in self.found_constellation]) # printDebug(self.found_constellation) # printDebug(self.rx_bitstream_frame) # printDebug(mapped_output) self.ParallelToserial() # printDebug(self.rx_bitstream_frame) else: raise ValueError( f"\n\n***Error --> Not supported mapping_type: <{self.mapping_type}>!\n" ) @sync_track def ParallelToserial(self): """Converts each parllelized constellation into a serial stream of data 'rx_bitstream_frame'.""" self.rx_bitstream_frame = self.rx_bitstream_frame.reshape((-1, )) @sync_track def getPilotValue(self): """Returns value of self.pilot_value""" return self.pilot_value @sync_track def setPilotValue(self, pilot_value): """Set new value for self.pilot_value""" self.pilot_value = pilot_value @sync_track def getBitstreamFrame(self): """Returns value of self.bitstream_frame""" return self.bitstream_frame @sync_track def setBitstreamFrame(self, bitstream_frame): """Set new value for self.bitstream_frame""" self.bitstream_frame = bitstream_frame @sync_track def getBitsPerSymbol(self): """Returns value of self.bits_per_symbol""" return self.bits_per_symbol @sync_track def setBitsPerSymbol(self, bits_per_symbol): """Set new value for self.bits_per_symbol""" self.bits_per_symbol = bits_per_symbol @sync_track def getFrameSize(self): """Returns value of self.frame_size""" return self.frame_size @sync_track def setFrameSize(self, frame_size): """Set new value for self.frame_size""" self.frame_size = frame_size @sync_track def getMappedInfo(self): """Returns value of self.mapped_info""" return self.mapped_info @sync_track def setMappedInfo(self, mapped_info): """Set new value for self.mapped_info""" self.mapped_info = mapped_info @sync_track def getMappingType(self): """Returns value of self.mapping_type""" return self.mapping_type @sync_track def setMappingType(self, mapping_type): """Set new value for self.mapping_type""" self.mapping_type = mapping_type @sync_track def getRxBitstreamFrame(self): """Returns value of self.rx_bitstream_frame""" return self.rx_bitstream_frame @sync_track def setRxBitstreamFrame(self, rx_bitstream_frame): """Set new value for self.rx_bitstream_frame""" self.rx_bitstream_frame = rx_bitstream_frame @sync_track def getConstellation(self): """Returns value of self.constellation""" return self.constellation @sync_track def setConstellation(self, constellation): """Set new value for self.constellation""" self.constellation = constellation @sync_track def getFoundConstellation(self): """Returns value of self.found_constellation""" return self.found_constellation @sync_track def setFoundConstellation(self, found_constellation): """Set new value for self.found_constellation""" self.found_constellation = found_constellation @sync_track def getNumberOfDataCarriers(self): """Returns value of self.number_of_data_carriers""" return self.number_of_data_carriers @sync_track def setNumberOfDataCarriers(self, number_of_data_carriers): """Set new value for self.number_of_data_carriers""" self.number_of_data_carriers = number_of_data_carriers def getSyncObj(self): """Returns value of self.sync_obj""" return self.sync_obj def setSyncObj(self, sync_obj): """Set new value for self.sync_obj""" self.sync_obj = sync_obj
def test_link_performance(): # Set seed seed(8071996) ###################################### # Build models & desired solutions ###################################### models = [] desired_bers = [] snr_range = [] labels = [] rtols = [] code_rates = [] # SISO QPSK and AWGN channel QPSK = QAMModem(4) def receiver(y, h, constellation, noise_var): return QPSK.demodulate(y, 'hard') models.append( LinkModel(QPSK.modulate, SISOFlatChannel(fading_param=(1 + 0j, 0)), receiver, QPSK.num_bits_symbol, QPSK.constellation, QPSK.Es)) snr_range.append(arange(0, 9, 2)) desired_bers.append(erfc(sqrt(10**(snr_range[-1] / 10) / 2)) / 2) labels.append('SISO QPSK and AWGN channel') rtols.append(.25) code_rates.append(1) # MIMO 16QAM, 4x4 Rayleigh channel and hard-output K-Best QAM16 = QAMModem(16) RayleighChannel = MIMOFlatChannel(4, 4) RayleighChannel.uncorr_rayleigh_fading(complex) def receiver(y, h, constellation, noise_var): return QAM16.demodulate(kbest(y, h, constellation, 16), 'hard') models.append( LinkModel(QAM16.modulate, RayleighChannel, receiver, QAM16.num_bits_symbol, QAM16.constellation, QAM16.Es)) snr_range.append(arange(0, 21, 5) + 10 * log10(QAM16.num_bits_symbol)) desired_bers.append((2e-1, 1e-1, 3e-2, 2e-3, 4e-5)) # From reference labels.append('MIMO 16QAM, 4x4 Rayleigh channel and hard-output K-Best') rtols.append(1.25) code_rates.append(1) # MIMO 16QAM, 4x4 Rayleigh channel and soft-output best-first QAM16 = QAMModem(16) RayleighChannel = MIMOFlatChannel(4, 4) RayleighChannel.uncorr_rayleigh_fading(complex) ldpc_params = get_ldpc_code_params( 'commpy/channelcoding/designs/ldpc/wimax/1440.720.txt', True) def modulate(bits): return QAM16.modulate( triang_ldpc_systematic_encode(bits, ldpc_params, False).reshape(-1, order='F')) def decoder(llrs): return ldpc_bp_decode(llrs, ldpc_params, 'MSA', 15)[0][:720].reshape(-1, order='F') def demode(symbs): return QAM16.demodulate(symbs, 'hard') def receiver(y, h, constellation, noise_var): return best_first_detector(y, h, constellation, (1, 3, 5), noise_var, demode, 500) models.append( LinkModel(modulate, RayleighChannel, receiver, QAM16.num_bits_symbol, QAM16.constellation, QAM16.Es, decoder, 0.5)) snr_range.append(arange(17, 20, 1)) desired_bers.append((1.7e-1, 1e-1, 2.5e-3)) # From reference labels.append( 'MIMO 16QAM, 4x4 Rayleigh channel and soft-output best-first') rtols.append(2) code_rates.append(.5) ###################################### # Make tests ###################################### for test in range(len(models)): BERs = link_performance(models[test], snr_range[test], 5e5, 200, 720, models[test].rate) assert_allclose(BERs, desired_bers[test], rtol=rtols[test], err_msg='Wrong performance for ' + labels[test]) full_metrics = models[test].link_performance_full_metrics( snr_range[test], 2500, 200, 720, models[test].rate) assert_allclose(full_metrics[0], desired_bers[test], rtol=rtols[test], err_msg='Wrong performance for ' + labels[test])
title('Binary PSK wave of Odd-numbered bits of input sequence') subplot(3, 1, 2) plot(S2) title('Binary PSK wave of Even-numbered bits of input sequence') subplot(3, 1, 3) plot(S) title('QPSK waveform') show() M = 4 i = range(0, M) t = arange(0, 0.001+1, 0.001) s1 = ones([len(i), len(t)]) s2 = ones([len(i), len(t)]) for i in range(0, M): s1[i, :] = [cos(2*pi*2*tt)*cos((2*i-1)*pi/4) for tt in t] s2[i, :] = [-sin(2*pi*2*tt)*sin((2*i-1)*pi/4) for tt in t] plot(s1+s2) show() from commpy.modulation import QAMModem from numpy.random import randint # msg_bits = randint(0, 2, 50) # l = QAMModem(4).modulate(msg_bits) l = QAMModem(4) plot(l) show()
def qamdemod(self, received_data): X = QAMModem(self.mod_order) data_qamdemod = X.demodulate(received_data * self.modulation_power(),'hard',1) return data_qamdemod
def qammod(self): data = np.random.randint(2, size=self.pack_size) X = QAMModem(self.mod_order) Y = self.modulation_power() data_qam = X.modulate(data) / Y # normalization return [data_qam, data]
def modulation_power(self): X = QAMModem(self.mod_order) x = np.reshape(np.fliplr(int2binlist(np.arange(0, self.mod_order, 1), int(np.sqrt(self.mod_order))).T), (1, self.mod_order * int(np.sqrt(self.mod_order)))) xx = X.modulate(x[0]) mod_power = np.sqrt(np.mean((xx) * (xx.T).conj().T)) return mod_power
def __init__(self, input1): assert input1 <= 256, "existing inherited modulator does not go over QAM256" QAMModem.__init__(self, input1) self.bitarrayprecalc()
# Authors: CommPy contributors # License: BSD 3-Clause from commpy.modulation import PSKModem, QAMModem # ============================================================================= # Example constellation plot of Modem # ============================================================================= # Constellation corresponding to PSKModem for 4 bits per symbols psk = PSKModem(16) psk.plot_constellation() # Constellation corresponding to QAMModem for 2 bits per symbols qam = QAMModem(4) qam.plot_constellation()
import numpy as np from random import randint from commpy.modulation import QAMModem from commpy.filters import rrcosfilter from commpy.utilities import bitarray2dec, dec2bitarray np.set_printoptions(threshold=np.nan) N = 1024 # output size M = 16 mod1 = QAMModem(M) # QAM16 sB = dec2bitarray(randint(0, 2**(mod1.num_bits_symbol * N * M / 4)), (mod1.num_bits_symbol * N * M / 4)) # Random bit stream # print np.array2string(sB) sQ = mod1.modulate(sB) # Modulated baud points print np.array2string(np.abs(sQ)) sPSF = rrcosfilter(N * 4, 0.2, 1, 4000)[1] qW = np.convolve(sPSF, sQ) # Waveform with PSF #print np.array2string(qW)