```python
def STBC_4x1(Num_Bit, BER_start, BER_end, Error_limit, mod, demod):
Nt = 4 # The number of transmit antennas
Nr = 1 # The number of receive antenna
Num_Bit = Num_Bit # It depends on what the modulation is
BER = []
BitPerNoiseDB_iter = range(BER_start, BER_end) # Set the range of BER you wanna see
for BitPerNoiseDB in BitPerNoiseDB_iter:
SymbolPerNoiseRATIO = Num_Bit*10**(BitPerNoiseDB/10) # It contains 'Num_Bit' number of bits per symbol, so we have to product Num_Bit
AWGN_sigma = math.sqrt(1/(2*SymbolPerNoiseRATIO)) # When the SymbolPerNoiseRATIO increases, the variance of AWGN decreases
Error_Count = 0
Total_Error_Count = 0
Bit_Count = 0
while Total_Error_Count < Error_limit: # Set the limit number of error count. if you don't have an enough time to observe the result of the simulation,
# you can reduce it. But to get more elaborate results, I recommand you to set the number at least more than 100.
### ---------------------------------------------------------------- ###
### Define some matrixs and arrays what we're gonna use at the below ###
Information_Generator = np.zeros([Nt, Num_Bit], dtype='int')
Detected_Array = np.zeros([Nt, Num_Bit], dtype='int')
Bit = np.zeros([Nt, Num_Bit], dtype='int')
symbol = []
AWGN = []
h = []
signal = np.zeros([4, 1], dtype='complex')
STBC_symbol = np.zeros([4, 4], dtype='complex')
h_matrix = np.zeros([4, 4], dtype='complex')
### ---------------------------------------------------------------- ###
for a in range(0, Nt):
for generator in range(0, Num_Bit):
Information_Generator[a][generator] = randrange(2) # Generate the information bits!! The value of bits has to be 0 or 1
Bit = Information_Generator
#print('Bit:', Bit)
#print('------------------')
for i in range(0, Nt):
symbol.append(QPSK_mod(Bit[i])) # Modulate the Bits to symbols
# As you can see, you can change the type of modulation by changing the name of it
for i in range(0, Nt):
AWGN_gen = np.random.normal(size = (1,2))
AWGN.append(complex(AWGN_gen[0][0], AWGN_gen[0][1]) * AWGN_sigma) # By producting AWGN_sigma with AWGN, we can adjust the size of AWGN
h.append(np.random.randn(1)*np.sqrt(0.5) + 1j*np.random.randn(1)*np.sqrt(0.5)) # Rayleigh fading
h_matrix[0][0] = h[0] # For computing the process of demodulation of STBC, we have to have channel gain matrix (h_matrix)
h_matrix[0][1] = h[1]
h_matrix[0][2] = h[2]
h_matrix[0][3] = h[3]
h_matrix[1][0] = h[1].conjugate()
h_matrix[1][1] = -h[0].conjugate()
h_matrix[1][2] = h[3].conjugate()
h_matrix[1][3] = -h[2].conjugate()
h_matrix[2][0] = h[2]
h_matrix[2][1] = h[3]
h_matrix[2][2] = h[0]
h_matrix[2][3] = h[1]
h_matrix[3][0] = h[3].conjugate()
h_matrix[3][1] = -h[2].conjugate()
h_matrix[3][2] = h[1].conjugate()
h_matrix[3][3] = -h[0].conjugate()
STBC_symbol[0][0] = symbol[0] # STBC Encoding
STBC_symbol[0][1] = symbol[1]
STBC_symbol[0][2] = symbol[2]
STBC_symbol[0][3] = symbol[3]
STBC_symbol[1][0] = -symbol[1].conjugate()
STBC_symbol[1][1] = symbol[0].conjugate()
STBC_symbol[1][2] = -symbol[3].conjugate()
STBC_symbol[1][3] = symbol[2].conjugate()
STBC_symbol[2][0] = symbol[2]
STBC_symbol[2][1] = symbol[3]
STBC_symbol[2][2] = symbol[0]
STBC_symbol[2][3] = symbol[1]
STBC_symbol[3][0] = -symbol[3].conjugate()
STBC_symbol[3][1] = symbol[2].conjugate()
STBC_symbol[3][2] = -symbol[1].conjugate()
STBC_symbol[3][3] = symbol[0].conjugate()
STBC_symbol = STBC_symbol/np.sqrt(Nt) # To make the energy of symbols one, devide the symbols by sqrt of the number of transmit antennas
STBC_symbol = np.matmul(STBC_symbol, h) # y = X*h
signal = [STBC_symbol[i][0] + AWGN[i] for i in range(len(AWGN))] # y = X*h + n ::: SHOTING!!! :::
#--------------Demodulation-------------#
signal[1] = signal[1].conjugate()
signal[3] = signal[3].conjugate()
h_hermitian_mul_signal = np.matmul(h_matrix.conj().T, signal)
Inverse_of_H_matrix = np.linalg.inv(np.matmul(h_matrix, h_matrix.conj().T))
h_hermitian_mul_signal = np.matmul(Inverse_of_H_matrix, h_hermitian_mul_signal)
for i in range(4):
Detected_Array[i][0],Detected_Array[i][1] = demod(h_hermitian_mul_signal[i])
Error_Count = 0
for row in range(0, Nt):
for col in range(0, Num_Bit):
if Bit[row][col] != Detected_Array[row][col]: # Comparing the original bits from the transmitter and the detected bits at the receiver
Error_Count += 1 # If those are different each other, It means error
Total_Error_Count += Error_Count
Bit_Count += Num_Bit*Nt
BER.append(Total_Error_Count/Bit_Count)
print("Bit_Count :", Bit_Count)
print("Eb/N0 :{} [dB]".format(BitPerNoiseDB))
print("The number of bits with errors :", Total_Error_Count)
print("BER :", BER[BitPerNoiseDB-BER_start] )
print("---------------------------------")
return BER