C MIMO
pds_telecom.c File Reference
#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>
Include dependency graph for pds_telecom.c:

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...
 
complexotx_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...
 
complexorx_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 ()
 

Function Documentation

◆ calculate_capacity()

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.

Parameters
snr_dBThe signal-to-noise ratio (SNR) in decibels (dB).
Returns
The capacity of the channel in bits per symbol.

◆ calculate_EVM()

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.

Parameters
original_signalA 2D array of complex numbers representing the original transmitted signal.
received_signalA 2D array of complex numbers representing the signal received after transmission.
NstreamThe number of streams in the signal.
NsymbolThe total number of symbols in the signal.
Returns
The calculated EVM of the signal in dB. If the signal power is zero, returns infinity.

◆ calculate_SNR()

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).

Parameters
original_signalA 2D array of complex numbers representing the original transmitted signal.
received_signalA 2D array of complex numbers representing the signal received after transmission.
NstreamThe number of streams in the signal.
NsymbolThe total number of symbols in the signal.
Returns
The calculated SNR of the signal in dB.

◆ channel_gen()

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.

Parameters
NrThe number of receiving antennas.
NtThe number of transmitting antennas.
sigmaThe standard deviation value for the channel creation.
Returns
A complex matrix representing the generated transfer channel. The caller is responsible for freeing the allocated memory using the free() function.
Note
This function assumes that the GSL library is correctly installed and linked to the project.

◆ channel_rd_gen()

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.

Parameters
NrThe number of receiving antennas.
NtThe number of transmitting antennas.
sigmaThe standard deviation of the noise.
Returns
A complex matrix representing the noise in the communication channel. The caller is responsible for freeing the allocated memory using the free() function.
Note
This function assumes that the GSL library is correctly installed and linked to the project.

◆ channel_transmission()

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.

Parameters
HMatrix representing the communication channel.
xpInput vector to be transmitted through the channel.
HlinhasThe number of rows in the H matrix.
HcolunasThe number of columns in the H matrix.
xpLinhasThe number of rows in the xp vector.
xpColunasThe number of columns in the xp vector.
rThe 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].
Returns
The resulting matrix of the signal transmission through the channel, plus the noise.

◆ general_matrix_product()

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.

Parameters
mtx_aThe first complex matrix to be multiplied.
mtx_bThe second complex matrix to be multiplied.
linhas_aThe number of rows of the matrix mtx_a.
colunas_aThe number of columns of the matrix mtx_a.
linhas_bThe number of rows of the matrix mtx_b.
colunas_bThe number of columns of the matrix mtx_b.
Returns
A new complex matrix resulting from the multiplication of mtx_a and mtx_b. The caller is responsible for freeing the allocated memory using the free() function.
Note
This function assumes that the matrices mtx_a and mtx_b have been correctly allocated and have compatible dimensions for multiplication.

◆ generate_statistics()

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:

  1. test: An integer parameter used for testing.
  2. Nr: The number of receive antennas.
  3. Nt: The number of transmit antennas.
  4. r: Noise interval.
  5. error_percentage: The percentage of symbols received with errors in relation to the total symbols.
  6. ber: The Bit Error Rate (BER).
  7. snr_dB: The Signal-to-Noise Ratio (SNR) in decibels.
  8. evm_dB: The Error Vector Magnitude (EVM) in decibels.
  9. cap: The capacity of the communication channel in bits per symbol.
Parameters
sThe vector of transmitted QAM symbols.
finalsThe vector of received QAM symbols.
numBytesThe number of transmitted bytes (considering 4 QAM symbols per byte).
testeAn integer parameter used for testing.
NrThe number of receive antennas.
NtThe number of transmit antennas.
rThe radius of the QAM constellation.
original_signalA 2D array of complex numbers representing the original transmitted signal.
received_signalA 2D array of complex numbers representing the signal received after transmission.
NstreamThe number of streams in the signal.
NsymbolThe total number of symbols in the signal.
Note
This function displays the statistics on the standard output and also writes them to a CSV file. Visualizations of these statistics can be viewed in the following Jupyter notebook: https://colab.research.google.com/github/lasseufpa/C_MIMO/blob/1-implement-command-line-parsing-for-antenna-or-similar-configuration-in-mimo-system-simulation/analyzer.ipynb

◆ getUserInput()

void getUserInput ( int *  Nr,
int *  Nt,
int *  r 
)

Get user input for the values of Nr, Nt, and r to custom mode.

Parameters
NrPointer to an integer where the value for Nr will be stored.
NtPointer to an integer where the value for Nt will be stored.
rPointer to an integer where the value for r will be stored.

◆ rx_combiner()

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.

Parameters
UAllocated U matrix.
xtTransmitted xt vector.
UlinhasThe number of rows in the U matrix.
UcolunasThe number of columns in the U matrix.
xtlinhasThe number of rows in the xt vector.
xtcolunasThe number of columns in the xt vector.
Returns
Returns the xc vector.

◆ rx_data_depadding()

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.

Parameters
sPointer to the integer array containing the data.
numBytesThe original number of bytes before padding.
NstreamThe number of streams for which the array size should be a multiple.
Returns
A pointer to the integer array with the original values.
Note
The function returns a new array if the original number of bytes is not a multiple of the number of streams. The caller is responsible for freeing the allocated memory using the free() function.

◆ rx_data_write()

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'.

Parameters
sPointer to the integer array containing the data.
numBytesThe original number of bytes before padding.
fileNameThe name of the file to be written.
Note
The function does not return a value. It writes the decoded bytes directly to the file named 'fileName'. If the file cannot be opened for writing, an error message is printed to the console.

◆ rx_feq()

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.

Parameters
SAllocated S matrix.
xcTransmitted xc vector.
SlinhasThe number of rows in the S matrix.
ScolunasThe number of columns in the S matrix.
xcLinhasThe number of rows in the xc vector.
xcColunasThe number of columns in the xc vector.
Returns
Returns the xf vector.

◆ rx_layer_demapper()

complexo* rx_layer_demapper ( complexo **  mtx_stream,
int  Nstream,
long int  numBytes 
)

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.

Parameters
mtx_streamPointer to the complex matrix containing the data to be mapped.
NstreamThe number of transmission streams.
numBytesThe number of bytes contained in the complex matrix.
Returns
A pointer to the complex vector that contains the mapped data, or NULL in case of memory allocation error.
Note
The caller is responsible for freeing the memory allocated for the complex vector when it is no longer needed, using the free() function.

◆ rx_qam_demapper()

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:

  • (-1, 1) -> 0
  • (-1, -1) -> 1
  • (1, 1) -> 2
  • (1, -1) -> 3
  • Others -> 4
Parameters
vmapVector of complex numbers representing the QAM symbols.
numQAMThe number of QAM symbols in the vector.
Returns
A vector of integers containing the binary data demapped from the QAM symbols. The caller is responsible for freeing the allocated memory using the free() function.
Note
The function returns a pointer to integer 1 if memory allocation fails. This is a non-standard practice and it's recommended to return NULL in such cases. The caller should check for NULL to ensure that memory allocation was successful.

◆ square_channel_svd()

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.

Parameters
HThe square complex matrix to be decomposed.
UhThe resulting U matrix from the decomposition, containing the left eigenvectors.
ShThe resulting S matrix from the decomposition, containing the singular values on the diagonal.
VhThe resulting V matrix from the decomposition, containing the right eigenvectors.
linhasThe number of rows in the H matrix.
colunasThe number of columns in the H matrix.
Note
This function only considers the real part of the H matrix elements for the SVD calculation. The function returns immediately if complex elements are detected in the H matrix.

◆ transposed_channel_svd()

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.

Parameters
HTransposed complex matrix to be decomposed.
UhResulting U matrix from the decomposition, containing the left eigenvectors.
ShResulting S matrix from the decomposition, containing the singular values on the diagonal.
VhResulting V matrix from the decomposition, containing the right eigenvectors.
TlinhasThe number of rows of the transposed matrix H.
TcolunasThe number of columns of the transposed matrix H.
Note
This function only considers the real part of the matrix H elements for the SVD calculation. The function prints a warning if complex elements are detected in the matrix H, but ignores the imaginary part for the calculation.
Remarks
The 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.

◆ tx_data_padding()

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.

Parameters
sPointer to the integer array containing the data. This array should contain the binary representation of the data to be transmitted.
numBytesThe original number of bytes in the data before padding. This is the size of the array pointed to by s.
NstreamThe 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.
Returns
A pointer to the integer array with the padding performed, or NULL in case of memory allocation error. The caller is responsible for freeing this memory when it is no longer needed.

◆ tx_data_read()

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.

Parameters
fpPointer to the file to be read. The file should be opened in binary read mode before calling this function.
numBytesThe number of bytes to be read from the file. This should be the size of the data that you want to convert.
Returns
A pointer to the integer array that contains the converted data, or NULL in case of memory allocation error or if the file cannot be read.
Note
The caller is responsible for freeing the memory allocated for the integer array when it is no longer needed, using the free() function. The caller is also responsible for closing the file when it's no longer needed.

◆ tx_layer_mapper()

complexo** tx_layer_mapper ( complexo v,
int  Nstream,
long int  Nsymbol 
)

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.

Parameters
vPointer to the complex vector containing the data to be mapped.
NstreamThe number of transmission streams.
NsymbolThe number of symbols contained in the complex vector.
Returns
A pointer to the complex matrix that contains the mapped data, or NULL in case of memory allocation error.

◆ tx_precoder()

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.

Parameters
VThe V matrix that was allocated in main.
xThe x vector that is being transmitted
VlinhasThe number of rows in the V matrix.
VcolunasThe number of columns in the V matrix.
xlinhasThe number of rows in the x vector.
xcolunasThe number of columns in the x vector.
Returns
Returns the xp vector

◆ tx_qam_mapper()

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.

Parameters
sPointer to the integer array containing the binary data.
numBytesThe number of bytes contained in the integer array.
Returns
A pointer to the complex vector that contains the mapped QAM symbols, or NULL in case of memory allocation error.