Refactor and extend CPU channel model with float support
This commit refactors the CPU-based channel model for improved reusability and extends it with single-precision floating-point support. This serves as a performance and correctness baseline for the new CUDA implementation. Key changes include: - The original double-based multipath_channel function is refactored to remove hardcoded antenna dimensions from its signature. It now accepts dynamically sized arrays, making it more flexible for use in simulators like dlsim and ulsim. - A new multipath_channel_float function is introduced to operate on float data types, matching the GPU's native format. - A corresponding add_noise_float function is also added to complete the floating-point CPU pipeline. - The new float-based functions are designed to work with an interleaved IQ sample format ([I, Q, I, Q]), which is more suitable for vectorized (SSE) and GPU processing.
This commit is contained in:
committed by
Jaroslava Fiedlerova
parent
70726903f3
commit
c54f0f7955
@@ -28,147 +28,144 @@
|
|||||||
|
|
||||||
uint8_t multipath_channel_nosigconv(channel_desc_t *desc)
|
uint8_t multipath_channel_nosigconv(channel_desc_t *desc)
|
||||||
{
|
{
|
||||||
random_channel(desc,0);
|
random_channel(desc, 0);
|
||||||
return(1);
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void interleave_channel_output(float **rx_sig_re, float **rx_sig_im, float **output_interleaved, int nb_rx, int num_samples)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < nb_rx; i++) {
|
||||||
|
for (int j = 0; j < num_samples; j++) {
|
||||||
|
output_interleaved[i][2 * j] = rx_sig_re[i][j];
|
||||||
|
output_interleaved[i][2 * j + 1] = rx_sig_im[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//#define CHANNEL_SSE
|
//#define CHANNEL_SSE
|
||||||
|
|
||||||
#ifdef CHANNEL_SSE
|
#ifdef CHANNEL_SSE
|
||||||
void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *desc,
|
void __attribute__((no_sanitize_address)) multipath_channel(channel_desc_t *desc,
|
||||||
double tx_sig_re[NB_ANTENNAS_TX][30720*2],
|
double **tx_sig_re,
|
||||||
double tx_sig_im[NB_ANTENANS_TX][30720*2],
|
double **tx_sig_im,
|
||||||
double rx_sig_re[NB_ANTENNAS_RX][30720*2],
|
double **rx_sig_re,
|
||||||
double rx_sig_im[NB_ANTENNAS_RX][30720*2],
|
double **rx_sig_im,
|
||||||
uint32_t length,
|
uint32_t length,
|
||||||
uint8_t keep_channel,
|
uint8_t keep_channel,
|
||||||
int log_channel)
|
int log_channel)
|
||||||
{
|
{
|
||||||
|
int i, ii, j, l;
|
||||||
|
// int simd_length;
|
||||||
|
// int simd_length, tail_start;
|
||||||
|
simde__m128d rx_tmp128_re, rx_tmp128_im, tx128_re, tx128_im, ch128_r, ch128_i, pathloss128;
|
||||||
|
|
||||||
int i,ii,j,l;
|
double path_loss = pow(10, desc->path_loss_dB / 20);
|
||||||
int length1, length2, tail;
|
|
||||||
simde__m128d rx_tmp128_re_f, rx_tmp128_im_f, rx_tmp128_re, rx_tmp128_im, rx_tmp128_1, rx_tmp128_2, rx_tmp128_3, rx_tmp128_4,
|
|
||||||
tx128_re, tx128_im, ch128_x, ch128_y, pathloss128;
|
|
||||||
|
|
||||||
double path_loss = pow(10,desc->path_loss_dB/20);
|
|
||||||
uint64_t dd = desc->channel_offset;
|
uint64_t dd = desc->channel_offset;
|
||||||
|
|
||||||
pathloss128 = simde_mm_set1_pd(path_loss);
|
pathloss128 = simde_mm_set1_pd(path_loss);
|
||||||
|
|
||||||
#ifdef DEBUG_CH
|
if (keep_channel == 0) {
|
||||||
printf("[CHANNEL] keep = %d : path_loss = %g (%f), nb_rx %d, nb_tx %d, dd %lu, len %d \n",keep_channel,path_loss,desc->path_loss_dB,desc->nb_rx,desc->nb_tx,dd,desc->channel_length);
|
random_channel(desc, 0);
|
||||||
#endif
|
|
||||||
|
|
||||||
if (keep_channel) {
|
|
||||||
// do nothing - keep channel
|
|
||||||
} else {
|
|
||||||
random_channel(desc,0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
start_meas(&desc->convolution);
|
// simd_length = (length - dd) / 2;
|
||||||
|
// tail_start = simd_length * 2;
|
||||||
|
|
||||||
#ifdef DEBUG_CH
|
for (i = 0; i < (int)(length - dd); i += 2) {
|
||||||
|
for (ii = 0; ii < desc->nb_rx; ii++) {
|
||||||
|
rx_tmp128_re = simde_mm_setzero_pd();
|
||||||
|
rx_tmp128_im = simde_mm_setzero_pd();
|
||||||
|
|
||||||
for (int l = 0; l<(int)desc->channel_length; l++) {
|
for (i = 0; i < (int)(length - dd); i += 2) {
|
||||||
printf("%p (%f,%f) ",desc->ch[0],desc->ch[0][l].x,desc->ch[0][l].y);
|
for (ii = 0; ii < desc->nb_rx; ii++) {
|
||||||
}
|
rx_tmp128_re = simde_mm_setzero_pd();
|
||||||
|
rx_tmp128_im = simde_mm_setzero_pd();
|
||||||
printf("\n");
|
for (j = 0; j < desc->nb_tx; j++) {
|
||||||
#endif
|
for (l = 0; l < (int)desc->channel_length; l++) {
|
||||||
|
double tx_re[2] = {0.0, 0.0}, tx_im[2] = {0.0, 0.0};
|
||||||
tail = ((int)length-dd)%2;
|
if ((i - l) >= 0)
|
||||||
|
tx_re[0] = tx_sig_re[j][i - l];
|
||||||
if(tail)
|
if ((i + 1 - l) >= 0)
|
||||||
length1 = ((int)length-dd)-1;
|
tx_re[1] = tx_sig_re[j][i + 1 - l];
|
||||||
else
|
if ((i - l) >= 0)
|
||||||
length1 = ((int)length-dd);
|
tx_im[0] = tx_sig_im[j][i - l];
|
||||||
|
if ((i + 1 - l) >= 0)
|
||||||
length2 = length1/2;
|
tx_im[1] = tx_sig_im[j][i + 1 - l];
|
||||||
|
tx128_re = simde_mm_loadu_pd(tx_re);
|
||||||
for (i=0; i<length2; i++) { //
|
tx128_im = simde_mm_loadu_pd(tx_im);
|
||||||
for (ii=0; ii<desc->nb_rx; ii++) {
|
ch128_r = simde_mm_set1_pd(desc->ch[ii + (j * desc->nb_rx)][l].r);
|
||||||
// rx_tmp.x = 0;
|
ch128_i = simde_mm_set1_pd(desc->ch[ii + (j * desc->nb_rx)][l].i);
|
||||||
// rx_tmp.y = 0;
|
rx_tmp128_re =
|
||||||
rx_tmp128_re_f = simde_mm_setzero_pd();
|
simde_mm_add_pd(rx_tmp128_re,
|
||||||
rx_tmp128_im_f = simde_mm_setzero_pd();
|
simde_mm_sub_pd(simde_mm_mul_pd(tx128_re, ch128_r), simde_mm_mul_pd(tx128_im, ch128_i)));
|
||||||
|
rx_tmp128_im =
|
||||||
for (j=0; j<desc->nb_tx; j++) {
|
simde_mm_add_pd(rx_tmp128_im,
|
||||||
for (l = 0; l<(int)desc->channel_length; l++) {
|
simde_mm_add_pd(simde_mm_mul_pd(tx128_re, ch128_i), simde_mm_mul_pd(tx128_im, ch128_r)));
|
||||||
if ((i>=0) && (i-l)>=0) { //SIMD correct only if length1 > 2*channel_length...which is almost always satisfied
|
}
|
||||||
// tx.x = tx_sig_re[j][i-l];
|
|
||||||
// tx.y = tx_sig_im[j][i-l];
|
|
||||||
tx128_re = simde_mm_loadu_pd(&tx_sig_re[j][2 * i - l]); // tx_sig_re[j][i-l+1], tx_sig_re[j][i-l]
|
|
||||||
tx128_im = simde_mm_loadu_pd(&tx_sig_im[j][2 * i - l]);
|
|
||||||
} else {
|
|
||||||
//tx.x =0;
|
|
||||||
//tx.y =0;
|
|
||||||
tx128_re = simde_mm_setzero_pd();
|
|
||||||
tx128_im = simde_mm_setzero_pd();
|
|
||||||
}
|
}
|
||||||
|
simde_mm_storeu_pd(&rx_sig_re[ii][i + dd], simde_mm_mul_pd(rx_tmp128_re, pathloss128));
|
||||||
ch128_x = simde_mm_set1_pd(desc->ch[ii + (j * desc->nb_rx)][l].x);
|
simde_mm_storeu_pd(&rx_sig_im[ii][i + dd], simde_mm_mul_pd(rx_tmp128_im, pathloss128));
|
||||||
ch128_y = simde_mm_set1_pd(desc->ch[ii + (j * desc->nb_rx)][l].y);
|
}
|
||||||
// rx_tmp.x += (tx.x * desc->ch[ii+(j*desc->nb_rx)][l].x) - (tx.y * desc->ch[ii+(j*desc->nb_rx)][l].y);
|
|
||||||
// rx_tmp.y += (tx.y * desc->ch[ii+(j*desc->nb_rx)][l].x) + (tx.x * desc->ch[ii+(j*desc->nb_rx)][l].y);
|
|
||||||
rx_tmp128_1 = simde_mm_mul_pd(tx128_re, ch128_x);
|
|
||||||
rx_tmp128_2 = simde_mm_mul_pd(tx128_re, ch128_y);
|
|
||||||
rx_tmp128_3 = simde_mm_mul_pd(tx128_im, ch128_x);
|
|
||||||
rx_tmp128_4 = simde_mm_mul_pd(tx128_im, ch128_y);
|
|
||||||
rx_tmp128_re = simde_mm_sub_pd(rx_tmp128_1, rx_tmp128_4);
|
|
||||||
rx_tmp128_im = simde_mm_add_pd(rx_tmp128_2, rx_tmp128_3);
|
|
||||||
rx_tmp128_re_f = simde_mm_add_pd(rx_tmp128_re_f, rx_tmp128_re);
|
|
||||||
rx_tmp128_im_f = simde_mm_add_pd(rx_tmp128_im_f, rx_tmp128_im);
|
|
||||||
} //l
|
|
||||||
} // j
|
|
||||||
|
|
||||||
//rx_sig_re[ii][i+dd] = rx_tmp.x*path_loss;
|
|
||||||
//rx_sig_im[ii][i+dd] = rx_tmp.y*path_loss;
|
|
||||||
rx_tmp128_re_f = simde_mm_mul_pd(rx_tmp128_re_f, pathloss128);
|
|
||||||
rx_tmp128_im_f = simde_mm_mul_pd(rx_tmp128_im_f, pathloss128);
|
|
||||||
simde_mm_storeu_pd(&rx_sig_re[ii][2 * i + dd], rx_tmp128_re_f); // max index: length-dd -1 + dd = length -1
|
|
||||||
simde_mm_storeu_pd(&rx_sig_im[ii][2 * i + dd], rx_tmp128_im_f);
|
|
||||||
/*
|
|
||||||
if ((ii==0)&&((i%32)==0)) {
|
|
||||||
printf("%p %p %f,%f => %e,%e\n",rx_sig_re[ii],rx_sig_im[ii],rx_tmp.x,rx_tmp.y,rx_sig_re[ii][i-dd],rx_sig_im[ii][i-dd]);
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
//rx_sig_re[ii][i] = sqrt(.5)*(tx_sig_re[0][i] + tx_sig_re[1][i]);
|
|
||||||
//rx_sig_im[ii][i] = sqrt(.5)*(tx_sig_im[0][i] + tx_sig_im[1][i]);
|
|
||||||
|
|
||||||
} // ii
|
} // ii
|
||||||
} // i
|
} // i
|
||||||
|
|
||||||
stop_meas(&desc->convolution);
|
// Handle the final sample if the length is odd
|
||||||
|
if ((length - dd) % 2) {
|
||||||
|
int i_tail = length - dd - 1;
|
||||||
|
for (ii = 0; ii < desc->nb_rx; ii++) {
|
||||||
|
struct complexd rx_tmp = {0};
|
||||||
|
for (j = 0; j < desc->nb_tx; j++) {
|
||||||
|
struct complexd *chan = desc->ch[ii + (j * desc->nb_rx)];
|
||||||
|
for (l = 0; l < (int)desc->channel_length; l++) {
|
||||||
|
if ((i_tail - l) >= 0) {
|
||||||
|
struct complexd tx;
|
||||||
|
tx.r = tx_sig_re[j][i_tail - l];
|
||||||
|
tx.i = tx_sig_im[j][i_tail - l];
|
||||||
|
rx_tmp.r += (tx.r * chan[l].r) - (tx.i * chan[l].i);
|
||||||
|
rx_tmp.i += (tx.i * chan[l].r) + (tx.r * chan[l].i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rx_sig_re[ii][i_tail + dd] = rx_tmp.r * path_loss;
|
||||||
|
rx_sig_im[ii][i_tail + dd] = rx_tmp.i * path_loss;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *desc,
|
void __attribute__((no_sanitize_address)) multipath_channel(channel_desc_t *desc,
|
||||||
double *tx_sig_re[NB_ANTENNAS_TX],
|
double **tx_sig_re,
|
||||||
double *tx_sig_im[NB_ANTENNAS_TX],
|
double **tx_sig_im,
|
||||||
double *rx_sig_re[NB_ANTENNAS_RX],
|
double **rx_sig_re,
|
||||||
double *rx_sig_im[NB_ANTENNAS_RX],
|
double **rx_sig_im,
|
||||||
uint32_t length,
|
uint32_t length,
|
||||||
uint8_t keep_channel,
|
uint8_t keep_channel,
|
||||||
int log_channel)
|
int log_channel)
|
||||||
{
|
{
|
||||||
|
double path_loss = pow(10, desc->path_loss_dB / 20);
|
||||||
double path_loss = pow(10,desc->path_loss_dB/20);
|
|
||||||
uint64_t dd = desc->channel_offset;
|
uint64_t dd = desc->channel_offset;
|
||||||
|
|
||||||
#ifdef DEBUG_CH
|
#ifdef DEBUG_CH
|
||||||
printf("[CHANNEL] keep = %d : path_loss = %g (%f), nb_rx %d, nb_tx %d, dd %lu, len %d \n",
|
printf("[CHANNEL] keep = %d : path_loss = %g (%f), nb_rx %d, nb_tx %d, dd %lu, len %d \n",
|
||||||
keep_channel, path_loss, desc->path_loss_dB, desc->nb_rx, desc->nb_tx, dd, desc->channel_length);
|
keep_channel,
|
||||||
|
path_loss,
|
||||||
|
desc->path_loss_dB,
|
||||||
|
desc->nb_rx,
|
||||||
|
desc->nb_tx,
|
||||||
|
dd,
|
||||||
|
desc->channel_length);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (keep_channel) {
|
if (keep_channel) {
|
||||||
// do nothing - keep channel
|
// do nothing - keep channel
|
||||||
} else {
|
} else {
|
||||||
random_channel(desc,0);
|
random_channel(desc, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_CH
|
#ifdef DEBUG_CH
|
||||||
for (int l = 0; l<(int)desc->channel_length; l++) {
|
for (int l = 0; l < (int)desc->channel_length; l++) {
|
||||||
printf("ch[%i] = (%f, %f)\n", l, desc->ch[0][l].r, desc->ch[0][l].i);
|
printf("ch[%i] = (%f, %f)\n", l, desc->ch[0][l].r, desc->ch[0][l].i);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -178,27 +175,27 @@ void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *des
|
|||||||
get_cexp_doppler(cexp_doppler, desc, length);
|
get_cexp_doppler(cexp_doppler, desc, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<((int)length-dd); i++) {
|
for (int i = 0; i < ((int)length - dd); i++) {
|
||||||
for (int ii=0; ii<desc->nb_rx; ii++) {
|
for (int ii = 0; ii < desc->nb_rx; ii++) {
|
||||||
struct complexd rx_tmp={0};
|
struct complexd rx_tmp = {0};
|
||||||
for (int j=0; j<desc->nb_tx; j++) {
|
for (int j = 0; j < desc->nb_tx; j++) {
|
||||||
struct complexd *chan=desc->ch[ii+(j*desc->nb_rx)];
|
struct complexd *chan = desc->ch[ii + (j * desc->nb_rx)];
|
||||||
for (int l = 0; l<(int)desc->channel_length; l++) {
|
for (int l = 0; l < (int)desc->channel_length; l++) {
|
||||||
if ((i>=0) && (i-l)>=0) {
|
if ((i >= 0) && (i - l) >= 0) {
|
||||||
struct complexd tx;
|
struct complexd tx;
|
||||||
tx.r = tx_sig_re[j][i-l];
|
tx.r = tx_sig_re[j][i - l];
|
||||||
tx.i = tx_sig_im[j][i-l];
|
tx.i = tx_sig_im[j][i - l];
|
||||||
rx_tmp.r += (tx.r * chan[l].r) - (tx.i * chan[l].i);
|
rx_tmp.r += (tx.r * chan[l].r) - (tx.i * chan[l].i);
|
||||||
rx_tmp.i += (tx.i * chan[l].r) + (tx.r * chan[l].i);
|
rx_tmp.i += (tx.i * chan[l].r) + (tx.r * chan[l].i);
|
||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
if (i==0 && log_channel == 1) {
|
if (i==0 && log_channel == 1) {
|
||||||
printf("channel[%d][%d][%d] = %f dB \t(%e, %e)\n",
|
printf("channel[%d][%d][%d] = %f dB \t(%e, %e)\n",
|
||||||
ii, j, l, 10 * log10(pow(chan[l].r, 2.0) + pow(chan[l].i, 2.0)), chan[l].r, chan[l].i);
|
ii, j, l, 10 * log10(pow(chan[l].r, 2.0) + pow(chan[l].i, 2.0)), chan[l].r, chan[l].i);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} //l
|
} // l
|
||||||
} // j
|
} // j
|
||||||
#if 0
|
#if 0
|
||||||
if (desc->max_Doppler != 0.0)
|
if (desc->max_Doppler != 0.0)
|
||||||
rx_tmp = cdMul(rx_tmp, cexp_doppler[i]);
|
rx_tmp = cdMul(rx_tmp, cexp_doppler[i]);
|
||||||
@@ -206,26 +203,190 @@ void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *des
|
|||||||
|
|
||||||
#ifdef DOPPLER_DEBUG
|
#ifdef DOPPLER_DEBUG
|
||||||
printf("[k %2i] cexp_doppler = (%7.4f, %7.4f), abs(cexp_doppler) = %.4f\n",
|
printf("[k %2i] cexp_doppler = (%7.4f, %7.4f), abs(cexp_doppler) = %.4f\n",
|
||||||
i,
|
i,
|
||||||
cexp_doppler[i].r,
|
cexp_doppler[i].r,
|
||||||
cexp_doppler[i].i,
|
cexp_doppler[i].i,
|
||||||
sqrt(cexp_doppler[i].r * cexp_doppler[i].r + cexp_doppler[i].i * cexp_doppler[i].i));
|
sqrt(cexp_doppler[i].r * cexp_doppler[i].r + cexp_doppler[i].i * cexp_doppler[i].i));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
rx_sig_re[ii][i+dd] = rx_tmp.r*path_loss;
|
rx_sig_re[ii][i + dd] = rx_tmp.r * path_loss;
|
||||||
rx_sig_im[ii][i+dd] = rx_tmp.i*path_loss;
|
rx_sig_im[ii][i + dd] = rx_tmp.i * path_loss;
|
||||||
#ifdef DEBUG_CH
|
#ifdef DEBUG_CH
|
||||||
if ((i%32)==0) {
|
if ((i % 32) == 0) {
|
||||||
printf("rx aa %d: %f, %f => %e, %e\n",
|
printf("rx aa %d: %f, %f => %e, %e\n", ii, rx_tmp.r, rx_tmp.i, rx_sig_re[ii][i - dd], rx_sig_im[ii][i - dd]);
|
||||||
ii, rx_tmp.r, rx_tmp.i, rx_sig_re[ii][i-dd], rx_sig_im[ii][i-dd]);
|
}
|
||||||
}
|
#endif
|
||||||
#endif
|
// rx_sig_re[ii][i] = sqrt(.5)*(tx_sig_re[0][i] + tx_sig_re[1][i]);
|
||||||
//rx_sig_re[ii][i] = sqrt(.5)*(tx_sig_re[0][i] + tx_sig_re[1][i]);
|
// rx_sig_im[ii][i] = sqrt(.5)*(tx_sig_im[0][i] + tx_sig_im[1][i]);
|
||||||
//rx_sig_im[ii][i] = sqrt(.5)*(tx_sig_im[0][i] + tx_sig_im[1][i]);
|
|
||||||
|
|
||||||
} // ii
|
} // ii
|
||||||
} // i
|
} // i
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CHANNEL_SSE
|
||||||
|
void __attribute__((no_sanitize_address)) multipath_channel_float(channel_desc_t *desc,
|
||||||
|
float **tx_sig_interleaved,
|
||||||
|
float **rx_sig_re,
|
||||||
|
float **rx_sig_im,
|
||||||
|
uint32_t length,
|
||||||
|
uint8_t keep_channel,
|
||||||
|
int log_channel)
|
||||||
|
{
|
||||||
|
int i, ii, j, l;
|
||||||
|
simde__m128 rx_tmp128_re, rx_tmp128_im, tx128_re, tx128_im, ch128_r, ch128_i, pathloss128;
|
||||||
|
uint64_t dd = desc->channel_offset;
|
||||||
|
|
||||||
|
if (keep_channel == 0) {
|
||||||
|
random_channel(desc, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct complexd cexp_doppler[length];
|
||||||
|
if (desc->max_Doppler != 0.0) {
|
||||||
|
get_cexp_doppler(cexp_doppler, desc, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dd >= length) {
|
||||||
|
return; // No samples to process
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i <= (int)(length - dd) - 4; i += 4) {
|
||||||
|
for (ii = 0; ii < desc->nb_rx; ii++) {
|
||||||
|
rx_tmp128_re = simde_mm_setzero_ps();
|
||||||
|
rx_tmp128_im = simde_mm_setzero_ps();
|
||||||
|
|
||||||
|
for (j = 0; j < desc->nb_tx; j++) {
|
||||||
|
for (l = 0; l < (int)desc->channel_length; l++) {
|
||||||
|
int idx = i - l;
|
||||||
|
if (idx >= 0) {
|
||||||
|
// Load 2 complex numbers (4 floats) => {I0, Q0, I1, Q1}
|
||||||
|
simde__m128 vec0 = simde_mm_loadu_ps(&tx_sig_interleaved[j][2 * idx]);
|
||||||
|
simde__m128 vec1 = simde_mm_loadu_ps(&tx_sig_interleaved[j][2 * (idx + 2)]);
|
||||||
|
tx128_re = simde_mm_shuffle_ps(vec0, vec1, SIMDE_MM_SHUFFLE(2, 0, 2, 0)); // {I0,I1,I2,I3}
|
||||||
|
tx128_im = simde_mm_shuffle_ps(vec0, vec1, SIMDE_MM_SHUFFLE(3, 1, 3, 1)); // {Q0,Q1,Q2,Q3}
|
||||||
|
} else {
|
||||||
|
tx128_re = simde_mm_setzero_ps();
|
||||||
|
tx128_im = simde_mm_setzero_ps();
|
||||||
|
}
|
||||||
|
|
||||||
|
ch128_r = simde_mm_set1_ps((float)desc->ch[ii + (j * desc->nb_rx)][l].r);
|
||||||
|
ch128_i = simde_mm_set1_ps((float)desc->ch[ii + (j * desc->nb_rx)][l].i);
|
||||||
|
|
||||||
|
// re = (tx_re * ch_r) - (tx_im * ch_i)
|
||||||
|
rx_tmp128_re = simde_mm_add_ps(rx_tmp128_re,
|
||||||
|
simde_mm_sub_ps(simde_mm_mul_ps(tx128_re, ch128_r), simde_mm_mul_ps(tx128_im, ch128_i)));
|
||||||
|
// im = (tx_re * ch_i) + (tx_im * ch_r)
|
||||||
|
rx_tmp128_im = simde_mm_add_ps(rx_tmp128_im,
|
||||||
|
simde_mm_add_ps(simde_mm_mul_ps(tx128_re, ch128_i), simde_mm_mul_ps(tx128_im, ch128_r)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (desc->max_Doppler != 0.0) {
|
||||||
|
float doppler_re[4] = {(float)cexp_doppler[i].r, (float)cexp_doppler[i+1].r, (float)cexp_doppler[i+2].r, (float)cexp_doppler[i+3].r};
|
||||||
|
float doppler_im[4] = {(float)cexp_doppler[i].i, (float)cexp_doppler[i+1].i, (float)cexp_doppler[i+2].i, (float)cexp_doppler[i+3].i};
|
||||||
|
|
||||||
|
simde__m128 doppler128_r = simde_mm_loadu_ps(doppler_re);
|
||||||
|
simde__m128 doppler128_i = simde_mm_loadu_ps(doppler_im);
|
||||||
|
|
||||||
|
simde__m128 temp_re = rx_tmp128_re;
|
||||||
|
simde__m128 temp_im = rx_tmp128_im;
|
||||||
|
|
||||||
|
rx_tmp128_re = simde_mm_sub_ps(simde_mm_mul_ps(temp_re, doppler128_r), simde_mm_mul_ps(temp_im, doppler128_i));
|
||||||
|
rx_tmp128_im = simde_mm_add_ps(simde_mm_mul_ps(temp_im, doppler128_r), simde_mm_mul_ps(temp_re, doppler128_i));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
simde_mm_storeu_ps(&rx_sig_re[ii][i + dd], rx_tmp128_re);
|
||||||
|
simde_mm_storeu_ps(&rx_sig_im[ii][i + dd], rx_tmp128_im);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle remaining samples (tail loop) that are not a multiple of 4
|
||||||
|
for (; i < (int)(length - dd); i++) {
|
||||||
|
for (ii = 0; ii < desc->nb_rx; ii++) {
|
||||||
|
struct complexf rx_tmp = {0.0f, 0.0f};
|
||||||
|
for (j = 0; j < desc->nb_tx; j++) {
|
||||||
|
struct complexd *chan = desc->ch[ii + (j * desc->nb_rx)];
|
||||||
|
for (l = 0; l < (int)desc->channel_length; l++) {
|
||||||
|
if ((i - l) >= 0) {
|
||||||
|
struct complexf tx;
|
||||||
|
tx.r = tx_sig_interleaved[j][2 * (i - l)];
|
||||||
|
tx.i = tx_sig_interleaved[j][2 * (i - l) + 1];
|
||||||
|
rx_tmp.r += (tx.r * (float)chan[l].r) - (tx.i * (float)chan[l].i);
|
||||||
|
rx_tmp.i += (tx.i * (float)chan[l].r) + (tx.r * (float)chan[l].i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
if (desc->max_Doppler != 0.0) {
|
||||||
|
struct complexf doppler_factor = {(float)cexp_doppler[i].r, (float)cexp_doppler[i].i};
|
||||||
|
struct complexf temp = rx_tmp;
|
||||||
|
rx_tmp.r = (temp.r * doppler_factor.r) - (temp.i * doppler_factor.i);
|
||||||
|
rx_tmp.i = (temp.i * doppler_factor.r) + (temp.r * doppler_factor.i);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
rx_sig_re[ii][i + dd] = rx_tmp.r;
|
||||||
|
rx_sig_im[ii][i + dd] = rx_tmp.i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
void multipath_channel_float(channel_desc_t *desc,
|
||||||
|
float **tx_sig_interleaved,
|
||||||
|
float **rx_sig_re,
|
||||||
|
float **rx_sig_im,
|
||||||
|
uint32_t length,
|
||||||
|
uint8_t keep_channel,
|
||||||
|
int log_channel)
|
||||||
|
{
|
||||||
|
uint64_t dd = desc->channel_offset;
|
||||||
|
|
||||||
|
if (keep_channel == 0) {
|
||||||
|
random_channel(desc, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct complexd cexp_doppler[length];
|
||||||
|
if (desc->max_Doppler != 0.0) {
|
||||||
|
get_cexp_doppler(cexp_doppler, desc, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dd >= length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ((int)length - dd); i++) {
|
||||||
|
for (int ii = 0; ii < desc->nb_rx; ii++) {
|
||||||
|
struct complexf rx_tmp = {0.0f, 0.0f};
|
||||||
|
|
||||||
|
for (int j = 0; j < desc->nb_tx; j++) {
|
||||||
|
struct complexd *chan = desc->ch[ii + (j * desc->nb_rx)];
|
||||||
|
|
||||||
|
for (int l = 0; l < (int)desc->channel_length; l++) {
|
||||||
|
if ((i - l) >= 0) {
|
||||||
|
struct complexf tx;
|
||||||
|
tx.r = tx_sig_interleaved[j][2 * (i - l)];
|
||||||
|
tx.i = tx_sig_interleaved[j][2 * (i - l) + 1];
|
||||||
|
rx_tmp.r += (tx.r * (float)chan[l].r) - (tx.i * (float)chan[l].i);
|
||||||
|
rx_tmp.i += (tx.i * (float)chan[l].r) + (tx.r * (float)chan[l].i);
|
||||||
|
}
|
||||||
|
} // l (channel_length)
|
||||||
|
} // j (nb_tx)
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (desc->max_Doppler != 0.0) {
|
||||||
|
// Perform complex multiplication: rx_tmp = rx_tmp * cexp_doppler[i]
|
||||||
|
struct complexf doppler_factor = {(float)cexp_doppler[i].r, (float)cexp_doppler[i].i};
|
||||||
|
struct complexf temp = rx_tmp;
|
||||||
|
rx_tmp.r = (temp.r * doppler_factor.r) - (temp.i * doppler_factor.i);
|
||||||
|
rx_tmp.i = (temp.i * doppler_factor.r) + (temp.r * doppler_factor.i);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
rx_sig_re[ii][i + dd] = rx_tmp.r;
|
||||||
|
rx_sig_im[ii][i + dd] = rx_tmp.i;
|
||||||
|
|
||||||
|
} // ii (nb_rx)
|
||||||
|
} // i (length)
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -22,12 +22,11 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "sim.h"
|
#include "sim.h"
|
||||||
|
|
||||||
|
|
||||||
/* linear phase noise model */
|
/* linear phase noise model */
|
||||||
void phase_noise(double ts, int16_t *InRe, int16_t *InIm)
|
void phase_noise(double ts, int16_t *InRe, int16_t *InIm)
|
||||||
{
|
{
|
||||||
static uint64_t i=0;
|
static uint64_t i = 0;
|
||||||
double fd = 300;//0.01*30000
|
double fd = 300; // 0.01*30000
|
||||||
double phase = (double)(i * fd * ts);
|
double phase = (double)(i * fd * ts);
|
||||||
c16_t rot = get_sin_cos(phase);
|
c16_t rot = get_sin_cos(phase);
|
||||||
c16_t val = {.r = *InRe, .i = *InIm};
|
c16_t val = {.r = *InRe, .i = *InIm};
|
||||||
@@ -61,3 +60,29 @@ void add_noise(c16_t **rxdata,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add_noise_float(c16_t **rxdata,
|
||||||
|
const float **r_re,
|
||||||
|
const float **r_im,
|
||||||
|
const float sigma,
|
||||||
|
const int length,
|
||||||
|
const int slot_offset,
|
||||||
|
const double ts,
|
||||||
|
const int delay,
|
||||||
|
bool apply_phase_noise,
|
||||||
|
const uint8_t nb_antennas_rx)
|
||||||
|
{
|
||||||
|
float sqrt_sigma_div2 = sqrtf(sigma / 2.0f);
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
for (int ap = 0; ap < nb_antennas_rx; ap++) {
|
||||||
|
c16_t *rxd = &rxdata[ap][slot_offset + i + delay];
|
||||||
|
rxd->r = (int16_t)(r_re[ap][i] + (gaussZiggurat(0.0, 1.0) * sqrt_sigma_div2));
|
||||||
|
rxd->i = (int16_t)(r_im[ap][i] + (gaussZiggurat(0.0, 1.0) * sqrt_sigma_div2));
|
||||||
|
|
||||||
|
if (apply_phase_noise) {
|
||||||
|
phase_noise(ts, &rxd->r, &rxd->i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -412,13 +412,39 @@ int random_channel(channel_desc_t *desc, uint8_t abstraction_flag);
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void multipath_channel(channel_desc_t *desc,
|
void multipath_channel(channel_desc_t *desc,
|
||||||
double *tx_sig_re[NB_ANTENNAS_TX],
|
double **tx_sig_re,
|
||||||
double *tx_sig_im[NB_ANTENNAS_TX],
|
double **tx_sig_im,
|
||||||
double *rx_sig_re[NB_ANTENNAS_RX],
|
double **rx_sig_re,
|
||||||
double *rx_sig_im[NB_ANTENNAS_RX],
|
double **rx_sig_im,
|
||||||
uint32_t length,
|
uint32_t length,
|
||||||
uint8_t keep_channel,
|
uint8_t keep_channel,
|
||||||
int log_channel);
|
int log_channel);
|
||||||
|
|
||||||
|
void multipath_channel_float(channel_desc_t *desc,
|
||||||
|
float **tx_sig_interleaved,
|
||||||
|
float **rx_sig_re,
|
||||||
|
float **rx_sig_im,
|
||||||
|
uint32_t length,
|
||||||
|
uint8_t keep_channel,
|
||||||
|
int log_channel);
|
||||||
|
|
||||||
|
void interleave_channel_output(float **rx_sig_re,
|
||||||
|
float **rx_sig_im,
|
||||||
|
float **output_interleaved,
|
||||||
|
int nb_rx,
|
||||||
|
int num_samples);
|
||||||
|
|
||||||
|
void add_noise_float(c16_t **rxdata,
|
||||||
|
const float **r_re,
|
||||||
|
const float **r_im,
|
||||||
|
const float sigma2,
|
||||||
|
const int length,
|
||||||
|
const int slot_offset,
|
||||||
|
const double ts,
|
||||||
|
const int delay,
|
||||||
|
bool apply_phase_noise,
|
||||||
|
const uint8_t nb_antennas_rx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
\fn double compute_pbch_sinr(channel_desc_t *desc,
|
\fn double compute_pbch_sinr(channel_desc_t *desc,
|
||||||
channel_desc_t *desc_i1,
|
channel_desc_t *desc_i1,
|
||||||
|
|||||||
Reference in New Issue
Block a user