-adopting new PRACH equalization approach for successive cancellation

-adding frequency domain time offset detection
-adding to testing of PRACH
master
yagoda 4 years ago committed by Justin Tallon
parent ec7873e7cc
commit f6ea431555

@ -95,6 +95,7 @@ typedef struct SRSLTE_API {
uint32_t peak_offsets[65]; uint32_t peak_offsets[65];
uint32_t num_ra_preambles; uint32_t num_ra_preambles;
bool successive_cancellation; bool successive_cancellation;
bool freq_domain_offset_calc;
srslte_tdd_config_t tdd_config; srslte_tdd_config_t tdd_config;
uint32_t current_prach_idx; uint32_t current_prach_idx;
@ -119,13 +120,13 @@ typedef struct {
bool hs_flag; bool hs_flag;
srslte_tdd_config_t tdd_config; srslte_tdd_config_t tdd_config;
bool enable_successive_cancellation; bool enable_successive_cancellation;
bool enable_freq_domain_offset_calc;
} srslte_prach_cfg_t; } srslte_prach_cfg_t;
typedef struct { typedef struct {
int idx; int idx;
float offset;
float factor; float factor;
float phase; cf_t phase_array[2 * 839];
} srslte_prach_cancellation_t; } srslte_prach_cancellation_t;
typedef struct SRSLTE_API { typedef struct SRSLTE_API {

@ -34,7 +34,7 @@ float save_corr[4096];
// PRACH detection threshold is PRACH_DETECT_FACTOR*average // PRACH detection threshold is PRACH_DETECT_FACTOR*average
#define PRACH_DETECT_FACTOR 18 #define PRACH_DETECT_FACTOR 18
#define SUCCESSIVE_CANCELLATION_ITS 4
#define N_SEQS 64 // Number of prach sequences available #define N_SEQS 64 // Number of prach sequences available
#define N_RB_SC 12 // Number of subcarriers per resource block #define N_RB_SC 12 // Number of subcarriers per resource block
#define DELTA_F 15000 // Normal subcarrier spacing #define DELTA_F 15000 // Normal subcarrier spacing
@ -43,7 +43,7 @@ float save_corr[4096];
#define PHI 7 // PRACH phi parameter #define PHI 7 // PRACH phi parameter
#define PHI_4 2 // PRACH phi parameter for format 4 #define PHI_4 2 // PRACH phi parameter for format 4
#define MAX_ROOTS 838 // Max number of root sequences #define MAX_ROOTS 838 // Max number of root sequences
#define PRACH_CANCELLATION_HARD
#define PRACH_AMP 1.0 #define PRACH_AMP 1.0
int srslte_prach_set_cell_(srslte_prach_t* p, int srslte_prach_set_cell_(srslte_prach_t* p,
@ -408,6 +408,11 @@ int srslte_prach_set_cell_(srslte_prach_t* p,
p->detect_factor = PRACH_DETECT_FACTOR; p->detect_factor = PRACH_DETECT_FACTOR;
p->num_ra_preambles = cfg->num_ra_preambles; p->num_ra_preambles = cfg->num_ra_preambles;
p->successive_cancellation = cfg->enable_successive_cancellation; p->successive_cancellation = cfg->enable_successive_cancellation;
if (p->successive_cancellation && cfg->zero_corr_zone != 0) {
printf("successive cancellation only currently supported with zero_correlation_zone_config of 0 - disabling\n");
p->successive_cancellation = false;
}
p->freq_domain_offset_calc = cfg->enable_freq_domain_offset_calc;
if (&cfg->tdd_config) { if (&cfg->tdd_config) {
p->tdd_config = cfg->tdd_config; p->tdd_config = cfg->tdd_config;
} }
@ -571,14 +576,18 @@ int srslte_prach_detect(srslte_prach_t* p,
/// be detected more easily in the subsequent searches /// be detected more easily in the subsequent searches
void srslte_prach_cancellation (srslte_prach_t* p, cf_t *signal, uint32_t begin, int sig_len, srslte_prach_cancellation_t prach_cancel) void srslte_prach_cancellation (srslte_prach_t* p, cf_t *signal, uint32_t begin, int sig_len, srslte_prach_cancellation_t prach_cancel)
{ {
cf_t sub[sig_len]; cf_t sub[p->N_zc * 2];
memcpy(sub,&p->td_signals[p->root_seqs_idx[prach_cancel.idx]][p->N_cp], sig_len*sizeof(cf_t)); memcpy(sub, &p->dft_seqs[p->root_seqs_idx[prach_cancel.idx]][0], p->N_zc * sizeof(cf_t));
srslte_vec_sc_prod_cfc(sub, prach_cancel.factor, sub, p->N_seq); srslte_vec_prod_ccc(sub, prach_cancel.phase_array, sub, p->N_zc);
int offset = (int) (prach_cancel.offset*sig_len*DELTA_F_RA); #ifdef PRACH_CANCELLATION_HARD
srslte_vec_sc_prod_ccc(sub, cexpf(_Complex_I * prach_cancel.phase), sub, sig_len); cf_t res[p->N_zc * 2];
srslte_vec_sub_ccc(&signal[offset], sub, &signal[offset], sig_len); srslte_vec_prod_conj_ccc(p->prach_bins, sub, res, p->N_zc);
srslte_dft_run(&p->fft, signal, p->signal_fft); srslte_dft_run(&p->zc_ifft, p->corr_spec, p->corr_spec);
memcpy(p->prach_bins, &p->signal_fft[begin], p->N_zc * sizeof(cf_t)); srslte_vec_abs_square_cf(p->corr_spec, p->corr, p->N_zc);
prach_cancel.factor = sqrt(p->corr[0] / (p->N_zc * p->N_zc));
#endif
srslte_vec_sc_prod_cfc(sub, prach_cancel.factor, sub, p->N_zc);
srslte_vec_sub_ccc(p->prach_bins, sub, p->prach_bins, p->N_zc);
} }
// this function checks if we have already detected and stored this particular PRACH index and if so, doesnt store it // this function checks if we have already detected and stored this particular PRACH index and if so, doesnt store it
@ -591,7 +600,7 @@ bool srslte_prach_have_stored(srslte_prach_t* p,int current_idx, uint32_t* indic
} }
return false; return false;
} }
// set the offset based on the time domain time offset estimation
float srslte_prach_set_offset(srslte_prach_t* p, int n_win) { float srslte_prach_set_offset(srslte_prach_t* p, int n_win) {
float corr = 1.0; float corr = 1.0;
if (p->peak_offsets[n_win] > 30) { if (p->peak_offsets[n_win] > 30) {
@ -603,6 +612,28 @@ float srslte_prach_set_offset(srslte_prach_t* p, int n_win) {
return corr * p->peak_offsets[n_win] / (DELTA_F_RA * p->N_zc); return corr * p->peak_offsets[n_win] / (DELTA_F_RA * p->N_zc);
} }
// calculates the timing offset of the incoming PRACH by calculating the phase in frequency - alternative to time domain
// approach
int srslte_prach_calculate_time_offset(srslte_prach_t* p, cf_t* cross)
{
float freq_domain_phase = cargf(srslte_vec_acc_cc(cross, p->N_zc));
float ratio = (float)(p->N_ifft_ul * DELTA_F) / (float)(MAX_N_zc * DELTA_F_RA);
int time_offset = round((ratio * freq_domain_phase * p->N_zc) / (2 * M_PI));
return time_offset;
}
// calculates the aggregate phase offset of the incomming PRACH signal so it can be applied to the reference signal
// before it is subtracted from the input
void srslte_prach_calculate_correction_array(srslte_prach_t* p,
cf_t* corr_freq,
srslte_prach_cancellation_t* prach_cancel)
{
float phase[p->N_zc];
srslte_vec_arg_deg_cf(corr_freq, 0, phase, p->N_zc);
for (int i = 0; i < p->N_zc; i++) {
prach_cancel->phase_array[i] = cexpf(_Complex_I * (phase[i] / (180.0f / M_PI)));
}
}
// This function carries out the main processing on the incomming PRACH signal // This function carries out the main processing on the incomming PRACH signal
int srslte_prach_process(srslte_prach_t* p, int srslte_prach_process(srslte_prach_t* p,
cf_t* signal, cf_t* signal,
@ -618,11 +649,19 @@ int srslte_prach_process(srslte_prach_t* p,
float max_to_cancel = 0; float max_to_cancel = 0;
cancellation_idx = -1; cancellation_idx = -1;
int max_idx = 0; int max_idx = 0;
cf_t cross[p->N_zc];
cf_t corr_freq[p->N_zc];
bzero(cross, sizeof(cf_t) * p->N_zc);
bzero(corr_freq, sizeof(cf_t) * p->N_zc);
for (int i = 0; i < p->num_ra_preambles; i++) { for (int i = 0; i < p->num_ra_preambles; i++) {
cf_t* root_spec = p->dft_seqs[p->root_seqs_idx[i]]; cf_t* root_spec = p->dft_seqs[p->root_seqs_idx[i]];
srslte_vec_prod_conj_ccc(p->prach_bins, root_spec, p->corr_spec, p->N_zc); srslte_vec_prod_conj_ccc(p->prach_bins, root_spec, p->corr_spec, p->N_zc);
srslte_vec_prod_conj_ccc(p->corr_spec, &p->corr_spec[1], cross, p->N_zc - 1);
if (p->successive_cancellation) {
memcpy(corr_freq, p->corr_spec, p->N_zc * sizeof(cf_t));
}
srslte_dft_run(&p->zc_ifft, p->corr_spec, p->corr_spec); srslte_dft_run(&p->zc_ifft, p->corr_spec, p->corr_spec);
srslte_vec_abs_square_cf(p->corr_spec, p->corr, p->N_zc); srslte_vec_abs_square_cf(p->corr_spec, p->corr, p->N_zc);
@ -657,33 +696,35 @@ int srslte_prach_process(srslte_prach_t* p,
} }
} }
} }
if (max_peak > p->detect_factor * corr_ave) { if (max_peak > (p->detect_factor * corr_ave)) {
for (int j = 0; j < n_wins; j++) { for (int j = 0; j < n_wins; j++) {
if (p->peak_values[j] > p->detect_factor * corr_ave) { if (p->peak_values[j] > p->detect_factor * corr_ave) {
if (indices) { if (indices) {
if (p->successive_cancellation) { if (p->successive_cancellation) {
if (max_peak > max_to_cancel) { if (max_peak > max_to_cancel) {
cancellation_idx = (i * n_wins) + j; cancellation_idx = (i * n_wins) + j;
max_to_cancel = max_peak; max_to_cancel = max_peak;
prach_cancel.idx = cancellation_idx; prach_cancel.idx = cancellation_idx; // this stores the best candidate for the successive cancellation
prach_cancel.offset = srslte_prach_set_offset(p, j); prach_cancel.factor = (sqrt(
prach_cancel.factor = sqrt((max_peak / 2) / ((sig_len / 2) * p->N_zc * p->N_zc)); max_peak / (p->N_zc * p->N_zc))); // this is the scaling factor for the successive cancellation
prach_cancel.phase = cargf(p->corr_spec[max_idx]); srslte_prach_calculate_correction_array(
p, corr_freq, &prach_cancel); // this calculates the correction array for the PRACH sig before it is
// used to cancel
} }
if (srslte_prach_have_stored(p, ((i * n_wins) + j), indices, *n_indices)) { if (srslte_prach_have_stored(p, ((i * n_wins) + j), indices, *n_indices)) {
break; break;
} }
} }
srslte_vec_fprint_c(stdout, p->corr_spec, 10) printf("max_idx %d\n", max_idx);
printf("cargf(p->corr_spec[max_idx]) %f\n", cargf(p->corr_spec[max_idx]));
indices[*n_indices] = (i * n_wins) + j; indices[*n_indices] = (i * n_wins) + j;
} }
if (peak_to_avg) { if (peak_to_avg) {
peak_to_avg[*n_indices] = p->peak_values[j] / corr_ave; peak_to_avg[*n_indices] = p->peak_values[j] / corr_ave;
} }
if (t_offsets) { if (t_offsets) {
t_offsets[*n_indices] = srslte_prach_set_offset(p, j); t_offsets[*n_indices] =
printf("t_offsets[*n_indices] %f\n", t_offsets[*n_indices]); (p->freq_domain_offset_calc)
? (((float)srslte_prach_calculate_time_offset(p, cross)) / ((float)p->N_ifft_ul * DELTA_F))
: (srslte_prach_set_offset(p, j));
} }
(*n_indices)++; (*n_indices)++;
} }
@ -691,6 +732,7 @@ int srslte_prach_process(srslte_prach_t* p,
} }
} }
if (cancellation_idx != -1) { if (cancellation_idx != -1) {
// if a peak has been found, this applies cancellation, if many found, subtracts strongest
srslte_prach_cancellation(p, signal, begin, sig_len, prach_cancel); srslte_prach_cancellation(p, signal, begin, sig_len, prach_cancel);
} else { } else {
return 1; return 1;
@ -729,7 +771,7 @@ int srslte_prach_detect_offset(srslte_prach_t* p,
uint32_t begin = PHI + (K * k_0) + (K / 2); uint32_t begin = PHI + (K * k_0) + (K / 2);
memcpy(p->prach_bins, &p->signal_fft[begin], p->N_zc * sizeof(cf_t)); memcpy(p->prach_bins, &p->signal_fft[begin], p->N_zc * sizeof(cf_t));
int loops = (p->successive_cancellation) ? p->num_ra_preambles : 1; int loops = (p->successive_cancellation) ? SUCCESSIVE_CANCELLATION_ITS : 1;
// if successive cancellation is enabled, we perform the entire search process p->num_ra_preambles times, removing // if successive cancellation is enabled, we perform the entire search process p->num_ra_preambles times, removing
// the highest power PRACH preamble each time. // the highest power PRACH preamble each time.
for (int l = 0; l < loops; l++) { for (int l = 0; l < loops; l++) {

@ -44,8 +44,12 @@ uint32_t zero_corr_zone = 1;
uint32_t n_seqs = 64; uint32_t n_seqs = 64;
uint32_t num_ra_preambles = 0; // use default uint32_t num_ra_preambles = 0; // use default
bool test_successive_cancellation = false; bool test_successive_cancellation =
bool test_offset_calculation = true; false; // this enables the successive cancellation algorithm, computationally complex
bool test_offset_calculation = false; // this should not be enabled in make test, only for use in manual testing
bool stagger_prach_power_and_phase =
false; // this will make the prachs have different power and phases, more realistic scenario
bool freq_domain_offset_calc = false; // this will work best with one or two simultaenous prach
srslte_filesource_t fsrc; srslte_filesource_t fsrc;
void usage(char* prog) void usage(char* prog)
@ -90,36 +94,37 @@ void parse_args(int argc, char** argv)
} }
} }
} }
// this function staggers power and phase of the different PRACH signals for more realisitc testing
void stagger_prach_powers(srslte_prach_t prach, cf_t *preamble, cf_t* preamble_sum, int freq_offset, int n_seqs, int *offsets) { void stagger_prach_powers(srslte_prach_t prach, cf_t *preamble, cf_t* preamble_sum, int freq_offset, int n_seqs, int *offsets) {
for (int seq_index = 0; seq_index < n_seqs; seq_index++) { for (int seq_index = 0; seq_index < n_seqs; seq_index++) {
srslte_prach_gen(&prach, seq_index, freq_offset, preamble); srslte_prach_gen(&prach, seq_index, freq_offset, preamble);
if (seq_index == 0) { if (seq_index == 0) {
srslte_vec_sc_prod_ccc(preamble, cexpf(_Complex_I * 0.1), preamble, prach.N_cp + prach.N_seq); srslte_vec_sc_prod_ccc(preamble, cexpf(_Complex_I * 0.5), preamble, prach.N_cp + prach.N_seq);
srslte_vec_sc_prod_cfc(preamble, 0.1, preamble, prach.N_cp + prach.N_seq); srslte_vec_sc_prod_cfc(preamble, 1, preamble, prach.N_cp + prach.N_seq);
} }
if (seq_index == 1) { if (seq_index == 1) {
srslte_vec_sc_prod_ccc(preamble, cexpf(_Complex_I * 0.4), preamble, prach.N_cp + prach.N_seq); srslte_vec_sc_prod_ccc(preamble, cexpf(_Complex_I * 1), preamble, prach.N_cp + prach.N_seq);
srslte_vec_sc_prod_cfc(preamble, 0.4, preamble, prach.N_cp + prach.N_seq); srslte_vec_sc_prod_cfc(preamble, 1, preamble, prach.N_cp + prach.N_seq);
} }
if (seq_index == 2) { if (seq_index == 2) {
srslte_vec_sc_prod_ccc(preamble, cexpf(_Complex_I * 0.1), preamble, prach.N_cp + prach.N_seq); srslte_vec_sc_prod_ccc(preamble, cexpf(_Complex_I * 0.1), preamble, prach.N_cp + prach.N_seq);
srslte_vec_sc_prod_cfc(preamble, 0.07, preamble, prach.N_cp + prach.N_seq); srslte_vec_sc_prod_cfc(preamble, 0.2, preamble, prach.N_cp + prach.N_seq);
} }
if (seq_index == 3) { if (seq_index == 3) {
srslte_vec_sc_prod_ccc(preamble, cexpf(_Complex_I * 0.9), preamble, prach.N_cp + prach.N_seq); srslte_vec_sc_prod_ccc(preamble, cexpf(_Complex_I * 0.9), preamble, prach.N_cp + prach.N_seq);
srslte_vec_sc_prod_cfc(preamble,0.6, preamble, prach.N_cp + prach.N_seq); srslte_vec_sc_prod_cfc(preamble, 0.7, preamble, prach.N_cp + prach.N_seq);
} }
if (seq_index == 4) { if (seq_index == 4) {
srslte_vec_sc_prod_ccc(preamble, cexpf(_Complex_I * 0.3), preamble, prach.N_cp + prach.N_seq); srslte_vec_sc_prod_ccc(preamble, cexpf(_Complex_I * 0.3), preamble, prach.N_cp + prach.N_seq);
srslte_vec_sc_prod_cfc(preamble, 1, preamble, prach.N_cp + prach.N_seq); srslte_vec_sc_prod_cfc(preamble, 0.15, preamble, prach.N_cp + prach.N_seq);
} }
if (seq_index == 5) { if (seq_index == 5) {
srslte_vec_sc_prod_cfc(preamble, 0.05, preamble, prach.N_cp + prach.N_seq); srslte_vec_sc_prod_cfc(preamble, 0.15, preamble, prach.N_cp + prach.N_seq);
} }
int off = (offset == -1) ? offsets[seq_index] : offset;
for (int i = 0; i < prach.N_cp + prach.N_seq; i++) { for (int i = 0; i < prach.N_cp + prach.N_seq; i++) {
preamble_sum[i] += preamble[i]; preamble_sum[i + off] += preamble[i];
} }
} }
} }
@ -153,26 +158,20 @@ int main(int argc, char** argv)
prach_cfg.zero_corr_zone = zero_corr_zone; prach_cfg.zero_corr_zone = zero_corr_zone;
prach_cfg.num_ra_preambles = num_ra_preambles; prach_cfg.num_ra_preambles = num_ra_preambles;
prach_cfg.enable_successive_cancellation = test_successive_cancellation; prach_cfg.enable_successive_cancellation = test_successive_cancellation;
prach_cfg.enable_freq_domain_offset_calc = freq_domain_offset_calc;
int srate = srslte_sampling_freq_hz(nof_prb); int srate = srslte_sampling_freq_hz(nof_prb);
int divisor = srate / PRACH_SRATE; int divisor = srate / PRACH_SRATE;
if (test_offset_calculation) { if (test_offset_calculation || test_successive_cancellation || stagger_prach_power_and_phase) {
n_seqs = 1; n_seqs = 6;
prach_cfg.num_ra_preambles = 4;
prach_cfg.zero_corr_zone = 0; prach_cfg.zero_corr_zone = 0;
printf("limiting number of preambles to 15 for offset calculation test\n"); prach_cfg.num_ra_preambles = 8;
for (int i = 0; i < 15; i++) { printf("limiting number of preambles to 6\n");
offsets[i] = ((rand()%(25*divisor))); if (test_offset_calculation) {
for (int i = 0; i < 6; i++) {
offsets[i] = (rand() % 50);
}
} }
memset(offsets, 0, sizeof(int) * 64);
}
if (test_successive_cancellation) {
printf("limiting number of preambles to 6 for successive cancellation test\n");
prach_cfg.num_ra_preambles = 6;
n_seqs = 6;
prach_cfg.zero_corr_zone = 0;
memset(offsets, 0, sizeof(int) * 64);
} }
if (srslte_prach_init(&prach, srslte_symbol_sz(nof_prb))) { if (srslte_prach_init(&prach, srslte_symbol_sz(nof_prb))) {
@ -192,13 +191,13 @@ int main(int argc, char** argv)
indices[i] = 0; indices[i] = 0;
srslte_prach_set_detect_factor(&prach, 10); srslte_prach_set_detect_factor(&prach, 10);
if (test_successive_cancellation) { if (stagger_prach_power_and_phase) {
stagger_prach_powers(prach, preamble, preamble_sum, prach_cfg.freq_offset, n_seqs, offsets); stagger_prach_powers(prach, preamble, preamble_sum, prach_cfg.freq_offset, n_seqs, offsets);
} else { } else {
for (seq_index = 0; seq_index < n_seqs; seq_index++) { for (seq_index = 0; seq_index < n_seqs; seq_index++) {
srslte_prach_gen(&prach, seq_index, prach_cfg.freq_offset, preamble); srslte_prach_gen(&prach, seq_index, prach_cfg.freq_offset, preamble);
int off = (offset == -1) ? offsets[seq_index] : offset;
for (int i = prach.N_cp; i < prach.N_cp + prach.N_seq; i++) { for (int i = prach.N_cp; i < prach.N_cp + prach.N_seq; i++) {
int off = (offset == -1)?offsets[seq_index]:offset;
preamble_sum[i + off] += preamble[i]; preamble_sum[i + off] += preamble[i];
} }
} }
@ -227,7 +226,10 @@ int main(int argc, char** argv)
if (test_offset_calculation) { if (test_offset_calculation) {
int error = (int)(t_offsets[i] * srate) - offsets[i]; int error = (int)(t_offsets[i] * srate) - offsets[i];
if (abs(error) > divisor) { if (abs(error) > divisor) {
printf("preamble %d has incorrect offset calculated as %d, should be %d\n", i, (int)(t_offsets[i] * srate) , offsets[i]); printf("preamble %d has incorrect offset calculated as %d, should be %d\n",
indices[i],
(int)(t_offsets[i] * srate),
offsets[i]);
err++; err++;
} }
} }

Loading…
Cancel
Save