C MIMO
|
#include <stdio.h>
#include <stdlib.h>
#include "../matrix/matrix.h"
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <time.h>
#include <math.h>
#include <string.h>
#include <libgen.h>
#include <stdbool.h>
#include <unistd.h>
Functions | |
double | calculate_capacity (double snr_dB) |
This function calculates the capacity of a communication channel. More... | |
double | calculate_EVM (complexo **original_signal, complexo **received_signal, int Nstream, long int Nsymbol) |
This function calculates the Error Vector Magnitude (EVM) of a signal. More... | |
double | calculate_SNR (complexo **original_signal, complexo **received_signal, int Nstream, long int Nsymbol) |
This function calculates the Signal-to-Noise Ratio (SNR) of a signal. More... | |
void | getUserInput (int *Nr, int *Nt, int *r) |
Get user input for the values of Nr, Nt, and r to custom mode. More... | |
int * | tx_data_read (FILE *fp, long int numBytes) |
Reads data from a file and converts it into an array of integers. More... | |
int * | tx_data_padding (int *s, long int numBytes, int Npadding) |
Performs padding of the data with zeros. More... | |
complexo * | tx_qam_mapper (int *s, long int numQAM) |
Maps binary data into a sequence of QAM symbols. More... | |
complexo ** | tx_layer_mapper (complexo *v, int Nstream, long int Nsymbol) |
Maps data from a complex vector to a complex matrix. More... | |
complexo * | rx_layer_demapper (complexo **mtx_stream, int Nstream, long int numBytes) |
Maps data from a complex matrix to a complex vector. More... | |
int * | rx_qam_demapper (complexo *vmap, long int numQAM) |
Demaps QAM symbols to binary data. More... | |
int * | rx_data_depadding (int *s, long int numBytes, int Nstream) |
Removes the "null" symbols that were filled (padding). More... | |
void | rx_data_write (int *s, long int numBytes, char *fileName) |
Recovers the original bytes. More... | |
complexo ** | general_matrix_product (complexo **mtx_a, complexo **mtx_b, int linhas_a, int colunas_a, int linhas_b, int colunas_b) |
Performs the multiplication of two complex matrices. More... | |
complexo ** | channel_gen (int Nr, int Nt, double sigma) |
Generates a complex matrix representing a transfer channel. More... | |
complexo ** | channel_rd_gen (int Nr, int Nt, double sigma) |
Generates a complex matrix representing the noise in the communication channel. More... | |
void | transposed_channel_svd (complexo **H, complexo **Uh, complexo **Sh, complexo **Vh, int Tlinhas, int Tcolunas) |
Performs Singular Value Decomposition (SVD) on a transposed matrix. More... | |
void | square_channel_svd (complexo **H, complexo **Uh, complexo **Sh, complexo **Vh, int linhas, int colunas) |
Performs Singular Value Decomposition (SVD) on a square matrix. More... | |
complexo ** | tx_precoder (complexo **V, complexo **x, int Vlinhas, int Vcolunas, int xlinhas, int xcolunas) |
Performs the multiplication of the stream symbols by the V matrix resulting from the SVD decomposition. More... | |
complexo ** | channel_transmission (complexo **H, complexo **xp, int Hlinhas, int Hcolunas, int xpLinhas, int xpColunas, int r) |
Performs the transmission of the signal through the communication channel. More... | |
complexo ** | rx_combiner (complexo **U, complexo **xt, int Ulinhas, int Ucolunas, int xtLinhas, int xtColunas) |
Performs the multiplication of signals received by Nr antennas by the U matrix. More... | |
complexo ** | rx_feq (complexo **S, complexo **xc, int Slinhas, int Scolunas, int xcLinhas, int xcColunas) |
Removes the interference from the H channel (S matrix from the SVD decomposition) More... | |
void | generate_statistics (int *s, int *finals, long int numBytes, int teste, int Nr, int Nt, double r, complexo **original_signal, complexo **received_signal, int Nstream, long int Nsymbol) |
Generates and outputs statistics about the transmitted and received QAM symbols. More... | |
complexo ** | expandMatrix (complexo **matriz, int linhas, int colunas, int linhasExtras, int padding) |
bool | is_running_in_wsl () |
bool | is_wsl_there () |
int | main () |
double calculate_capacity | ( | double | snr_dB | ) |
This function calculates the capacity of a communication channel.
The capacity is calculated using the Shannon Capacity formula, which is based on the signal-to-noise ratio (SNR). The SNR is provided in decibels (dB) and is converted to linear scale inside the function. The capacity is returned in bits per symbol.
snr_dB | The signal-to-noise ratio (SNR) in decibels (dB). |
double calculate_EVM | ( | complexo ** | original_signal, |
complexo ** | received_signal, | ||
int | Nstream, | ||
long int | Nsymbol | ||
) |
This function calculates the Error Vector Magnitude (EVM) of a signal.
EVM is a measure of the performance of a digital radio transmitter or receiver. A lower EVM means a better performance. In this function, the EVM is calculated by comparing the original transmitted signal with the received signal.
The function first calculates the power of the error signal and the power of the received signal. Then, it calculates the EVM as the square root of the ratio of the error power to the signal power. If the signal power is zero, the function returns infinity. Finally, the EVM is converted to decibels (dB) before being returned.
original_signal | A 2D array of complex numbers representing the original transmitted signal. |
received_signal | A 2D array of complex numbers representing the signal received after transmission. |
Nstream | The number of streams in the signal. |
Nsymbol | The total number of symbols in the signal. |
double calculate_SNR | ( | complexo ** | original_signal, |
complexo ** | received_signal, | ||
int | Nstream, | ||
long int | Nsymbol | ||
) |
This function calculates the Signal-to-Noise Ratio (SNR) of a signal.
SNR is a measure that compares the level of a desired signal to the level of background noise. It is defined as the ratio of signal power to the noise power. A higher SNR indicates a signal less affected by noise. In this function, the SNR is calculated by comparing the original transmitted signal with the received signal.
The function takes as input two 2D arrays of complex numbers representing the original and received signals, the number of streams in the signal, and the total number of symbols in the signal.
The calculated SNR is returned in decibels (dB).
original_signal | A 2D array of complex numbers representing the original transmitted signal. |
received_signal | A 2D array of complex numbers representing the signal received after transmission. |
Nstream | The number of streams in the signal. |
Nsymbol | The total number of symbols in the signal. |
complexo** channel_gen | ( | int | Nr, |
int | Nt, | ||
double | sigma | ||
) |
Generates a complex matrix representing a transfer channel.
This function generates a complex matrix that represents a transfer channel between transmitting antennas and receiving antennas. The elements of the matrix are random complex numbers with the imaginary part set to zero.
Nr | The number of receiving antennas. |
Nt | The number of transmitting antennas. |
sigma | The standard deviation value for the channel creation. |
complexo** channel_rd_gen | ( | int | Nr, |
int | Nt, | ||
double | sigma | ||
) |
Generates a complex matrix representing the noise in the communication channel.
This function generates a complex matrix representing the noise in the communication channel. The resulting matrix has dimensions Nr x Nt, where Nr is the number of receiving antennas and Nt is the number of transmitting antennas. The values of the matrix elements are generated randomly with a Gaussian distribution with mean 0 and standard deviation sigma.
Nr | The number of receiving antennas. |
Nt | The number of transmitting antennas. |
sigma | The standard deviation of the noise. |
complexo** channel_transmission | ( | complexo ** | H, |
complexo ** | xp, | ||
int | Hlinhas, | ||
int | Hcolunas, | ||
int | xpLinhas, | ||
int | xpColunas, | ||
int | r | ||
) |
Performs the transmission of the signal through the communication channel.
This function performs the transmission of the input signal xp through the communication channel represented by the H matrix. The result of the transmission is calculated by multiplying the H matrix by the xp vector. In addition, a Rd noise is added to the transmitted signal to simulate the characteristics of the communication channel.
H | Matrix representing the communication channel. |
xp | Input vector to be transmitted through the channel. |
Hlinhas | The number of rows in the H matrix. |
Hcolunas | The number of columns in the H matrix. |
xpLinhas | The number of rows in the xp vector. |
xpColunas | The number of columns in the xp vector. |
r | The value that defines the range of the noise to be added: 0 for [-0.001, 0.001], 1 for [-0.01, 0.01], 2 for [-0.5, 0.5], 3 for [-1, 1]. |
complexo** general_matrix_product | ( | complexo ** | mtx_a, |
complexo ** | mtx_b, | ||
int | linhas_a, | ||
int | colunas_a, | ||
int | linhas_b, | ||
int | colunas_b | ||
) |
Performs the multiplication of two complex matrices.
This function performs the multiplication of two complex matrices mtx_a
and mtx_b
, resulting in a new matrix matriz
. The validation of the multiplication operation is done by checking if the number of columns of the matrix mtx_a
is equal to the number of rows of the matrix mtx_b
. If they are not compatible, the function displays an error message and terminates the program.
mtx_a | The first complex matrix to be multiplied. |
mtx_b | The second complex matrix to be multiplied. |
linhas_a | The number of rows of the matrix mtx_a . |
colunas_a | The number of columns of the matrix mtx_a . |
linhas_b | The number of rows of the matrix mtx_b . |
colunas_b | The number of columns of the matrix mtx_b . |
mtx_a
and mtx_b
. The caller is responsible for freeing the allocated memory using the free() function.mtx_a
and mtx_b
have been correctly allocated and have compatible dimensions for multiplication. void generate_statistics | ( | int * | s, |
int * | finals, | ||
long int | numBytes, | ||
int | teste, | ||
int | Nr, | ||
int | Nt, | ||
double | r, | ||
complexo ** | original_signal, | ||
complexo ** | received_signal, | ||
int | Nstream, | ||
long int | Nsymbol | ||
) |
Generates and outputs statistics about the transmitted and received QAM symbols.
This function calculates and outputs statistics about the transmitted and received QAM symbols, comparing the transmitted symbols vector s
with the received symbols vector finals
. It counts the number of correct and incorrect transmissions and calculates the percentage of symbols received with errors in relation to the total symbols. It also calculates the Bit Error Rate (BER), Signal-to-Noise Ratio (SNR), Error Vector Magnitude (EVM), and the capacity of the communication channel. The results are displayed on the standard output and also saved to a CSV file named "output.csv".
The CSV file contains the following columns:
test
: An integer parameter used for testing.Nr
: The number of receive antennas.Nt
: The number of transmit antennas.r
: Noise interval.error_percentage
: The percentage of symbols received with errors in relation to the total symbols.ber
: The Bit Error Rate (BER).snr_dB
: The Signal-to-Noise Ratio (SNR) in decibels.evm_dB
: The Error Vector Magnitude (EVM) in decibels.cap
: The capacity of the communication channel in bits per symbol.s | The vector of transmitted QAM symbols. |
finals | The vector of received QAM symbols. |
numBytes | The number of transmitted bytes (considering 4 QAM symbols per byte). |
teste | An integer parameter used for testing. |
Nr | The number of receive antennas. |
Nt | The number of transmit antennas. |
r | The radius of the QAM constellation. |
original_signal | A 2D array of complex numbers representing the original transmitted signal. |
received_signal | A 2D array of complex numbers representing the signal received after transmission. |
Nstream | The number of streams in the signal. |
Nsymbol | The total number of symbols in the signal. |
void getUserInput | ( | int * | Nr, |
int * | Nt, | ||
int * | r | ||
) |
Get user input for the values of Nr, Nt, and r to custom mode.
Nr | Pointer to an integer where the value for Nr will be stored. |
Nt | Pointer to an integer where the value for Nt will be stored. |
r | Pointer to an integer where the value for r will be stored. |
complexo** rx_combiner | ( | complexo ** | U, |
complexo ** | xt, | ||
int | Ulinhas, | ||
int | Ucolunas, | ||
int | xtLinhas, | ||
int | xtColunas | ||
) |
Performs the multiplication of signals received by Nr antennas by the U matrix.
This function uses the transposed U matrix and multiplies it by the xt vector that we are transmitting, generating the combined vector xc.
U | Allocated U matrix. |
xt | Transmitted xt vector. |
Ulinhas | The number of rows in the U matrix. |
Ucolunas | The number of columns in the U matrix. |
xtlinhas | The number of rows in the xt vector. |
xtcolunas | The number of columns in the xt vector. |
int* rx_data_depadding | ( | int * | s, |
long int | numBytes, | ||
int | Nstream | ||
) |
Removes the "null" symbols that were filled (padding).
This function undoes the processing done by the data_padding function, returning our vector to its original size.
s | Pointer to the integer array containing the data. |
numBytes | The original number of bytes before padding. |
Nstream | The number of streams for which the array size should be a multiple. |
void rx_data_write | ( | int * | s, |
long int | numBytes, | ||
char * | fileName | ||
) |
Recovers the original bytes.
This function recovers the original bytes every 4 digits by performing the inverse process of the data_read function. It takes the integer array 's' and decodes it into character bytes to generate the file named 'fileName'.
s | Pointer to the integer array containing the data. |
numBytes | The original number of bytes before padding. |
fileName | The name of the file to be written. |
complexo** rx_feq | ( | complexo ** | S, |
complexo ** | xc, | ||
int | Slinhas, | ||
int | Scolunas, | ||
int | xcLinhas, | ||
int | xcColunas | ||
) |
Removes the interference from the H channel (S matrix from the SVD decomposition)
This function uses each non-zero element of the S matrix and divides it by each element of the xc vector in the same row.
S | Allocated S matrix. |
xc | Transmitted xc vector. |
Slinhas | The number of rows in the S matrix. |
Scolunas | The number of columns in the S matrix. |
xcLinhas | The number of rows in the xc vector. |
xcColunas | The number of columns in the xc vector. |
Maps data from a complex matrix to a complex vector.
This function maps data from a complex matrix to a complex vector, where each position in the matrix represents a transmission stream. The function dynamically allocates memory for the complex vector and returns a pointer to the vector.
mtx_stream | Pointer to the complex matrix containing the data to be mapped. |
Nstream | The number of transmission streams. |
numBytes | The number of bytes contained in the complex matrix. |
int* rx_qam_demapper | ( | complexo * | vmap, |
long int | numQAM | ||
) |
Demaps QAM symbols to binary data.
This function takes a vector of complex numbers representing QAM symbols and performs the demapping of these symbols to binary data. Each QAM symbol is associated with a binary value, according to the following table:
vmap | Vector of complex numbers representing the QAM symbols. |
numQAM | The number of QAM symbols in the vector. |
void square_channel_svd | ( | complexo ** | H, |
complexo ** | Uh, | ||
complexo ** | Sh, | ||
complexo ** | Vh, | ||
int | linhas, | ||
int | colunas | ||
) |
Performs Singular Value Decomposition (SVD) on a square matrix.
This function performs SVD on a square matrix, represented by a matrix of complex numbers. The function only uses the real part of the matrix elements for the SVD calculation. It dynamically allocates memory for the U, V matrices and the S vector, and stores the decomposition results in the Uh, Sh, and Vh matrices.
H | The square complex matrix to be decomposed. |
Uh | The resulting U matrix from the decomposition, containing the left eigenvectors. |
Sh | The resulting S matrix from the decomposition, containing the singular values on the diagonal. |
Vh | The resulting V matrix from the decomposition, containing the right eigenvectors. |
linhas | The number of rows in the H matrix. |
colunas | The number of columns in the H matrix. |
void transposed_channel_svd | ( | complexo ** | H, |
complexo ** | Uh, | ||
complexo ** | Sh, | ||
complexo ** | Vh, | ||
int | Tlinhas, | ||
int | Tcolunas | ||
) |
Performs Singular Value Decomposition (SVD) on a transposed matrix.
This function performs SVD on a transposed matrix, represented by a matrix of complex numbers. The function only uses the real part of the matrix elements for the SVD calculation. It dynamically allocates memory for the matrices U, V, and the vector S, and stores the decomposition results in the matrices Uh, Sh, and Vh.
H | Transposed complex matrix to be decomposed. |
Uh | Resulting U matrix from the decomposition, containing the left eigenvectors. |
Sh | Resulting S matrix from the decomposition, containing the singular values on the diagonal. |
Vh | Resulting V matrix from the decomposition, containing the right eigenvectors. |
Tlinhas | The number of rows of the transposed matrix H. |
Tcolunas | The number of columns of the transposed matrix H. |
transposed_channel_svd
function is similar to the square_channel_svd
function, but there's a crucial difference between them. The square_channel_svd
function takes a square matrix as a parameter, while the transposed_channel_svd
function takes the transposed matrix as a parameter. The transposed matrix is obtained by swapping the rows and columns of the original matrix. Therefore, while the square_channel_svd
function performs the SVD of a square matrix, the transposed_channel_svd
function performs the SVD of the transposed matrix. int* tx_data_padding | ( | int * | s, |
long int | numBytes, | ||
int | Npadding | ||
) |
Performs padding of the data with zeros.
This function performs padding of the data with zeros to ensure that the size of the integer array is an integer multiple of the number of streams (Nstream). Padding is necessary when the number of bytes in the data is not a multiple of the number of streams. In such cases, padding with zeros ensures that the data can be evenly divided among the streams.
s | Pointer to the integer array containing the data. This array should contain the binary representation of the data to be transmitted. |
numBytes | The original number of bytes in the data before padding. This is the size of the array pointed to by s . |
Nstream | The number of streams for which the array size should be a multiple. This represents the number of separate data streams that the data will be divided among. |
int* tx_data_read | ( | FILE * | fp, |
long int | numBytes | ||
) |
Reads data from a file and converts it into an array of integers.
This function reads binary data from a file and converts it into an array of integers. Each integer in the array represents 2 bits of the original data. The conversion is done by reading two bits at a time from the binary data and storing their combined value as an integer in the array.
fp | Pointer to the file to be read. The file should be opened in binary read mode before calling this function. |
numBytes | The number of bytes to be read from the file. This should be the size of the data that you want to convert. |
Maps data from a complex vector to a complex matrix.
This function maps data from a complex vector to a complex matrix, where each position in the matrix represents a transmission stream. The function dynamically allocates memory for the complex matrix and returns a pointer to the matrix.
v | Pointer to the complex vector containing the data to be mapped. |
Nstream | The number of transmission streams. |
Nsymbol | The number of symbols contained in the complex vector. |
complexo** tx_precoder | ( | complexo ** | V, |
complexo ** | x, | ||
int | Vlinhas, | ||
int | Vcolunas, | ||
int | xlinhas, | ||
int | xcolunas | ||
) |
Performs the multiplication of the stream symbols by the V matrix resulting from the SVD decomposition.
This function uses the V matrix and multiplies it by the x vector we are transmitting, generating the precoded vector xp.
V | The V matrix that was allocated in main. |
x | The x vector that is being transmitted |
Vlinhas | The number of rows in the V matrix. |
Vcolunas | The number of columns in the V matrix. |
xlinhas | The number of rows in the x vector. |
xcolunas | The number of columns in the x vector. |
complexo* tx_qam_mapper | ( | int * | s, |
long int | numQAM | ||
) |
Maps binary data into a sequence of QAM symbols.
This function maps binary data into a sequence of QAM (Quadrature Amplitude Modulation) symbols represented by complex numbers. The function dynamically allocates memory for the complex vector and returns a pointer to this vector.
The mapping is as follows: 0 -> (-1, 1) 1 -> (-1, -1) 2 -> (1, 1) 3 -> (1, -1) Any other value -> (0, 0)
The complexo
type is a struct with two members: real
and img
, representing the real and imaginary parts of a complex number.
If memory allocation fails, the function returns NULL and prints an error message to stdout. It's the caller's responsibility to check the return value and handle the error appropriately.
The caller is also responsible for freeing the memory allocated by this function when it's no longer needed.
s | Pointer to the integer array containing the binary data. |
numBytes | The number of bytes contained in the integer array. |