free5GRAN  V1.0
libphy.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2020 Telecom Paris
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15  */
16 
17 #include "libphy.h"
18 #include "../../variables/common_variables/common_variables.h"
19 #include <complex>
20 #include <vector>
21 #include <algorithm>
22 #include <fftw3.h>
23 #include <iostream>
24 
25 using namespace std;
26 
27 void free5GRAN::phy::signal_processing::channelEstimation(complex<float> * pilots, complex<float> * reference_pilot, vector<vector<int>> &pilot_indexes, vector<vector<complex<float>>> &coefficients, float &snr, int num_sc, int num_symbols, int pilot_size){
46  // Initializing variables
47  complex<float> noise_coef;
48  vector<int> found_indexes, symbols_with_pilot,symbols_with_no_pilot;
49  vector<complex<float>> coef_same_sc;
50  int upper_index, lower_index, step, step_width;
51  float re_int, im_int;
52  float mean_power = 0;
53  float mean_noise = 0;
54 
55  // Computing pilots transport_channel coefficients
56  for (int i = 0; i < pilot_size; i ++){
57  coefficients[pilot_indexes[0][i]][pilot_indexes[1][i]] = pilots[i] * conj(reference_pilot[i]) / (float) pow(abs(reference_pilot[i]),2);
58  }
59 
60  // Interpolating in frequency domain to complete transport_channel coefficients grid
61  for (int symbol = 0; symbol <num_symbols; symbol ++ ){
62  found_indexes.clear();
63  /*
64  * Adding in found_indexes all the pilot index that are in the current symbol
65  */
66  for (int k = 0; k < pilot_size; k ++){
67  if (pilot_indexes[0][k] == symbol){
68  found_indexes.push_back(pilot_indexes[1][k]);
69  }
70  }
71 
72 
73  if (found_indexes.size() != 0 ){
74  symbols_with_pilot.push_back(symbol);
75  /*
76  * Linear interpolation. Looping over each subcarrier of the current symbol
77  */
78  for (int sc = 0; sc < num_sc; sc ++){
79  // If the current resource element is not a pilot
80  if(find(found_indexes.begin(), found_indexes.end(), sc) == found_indexes.end()) {
81  // Get lower and upper pilot indexes for interpolating. If no lower, take the two closest upper pilots and respectively if no upper pilot
82  if (sc < found_indexes[0]){
83  lower_index = found_indexes[0];
84  upper_index = found_indexes[1];
85  }else if(sc > found_indexes[found_indexes.size() - 1]){
86  lower_index = found_indexes[found_indexes.size() - 2];
87  upper_index = found_indexes[found_indexes.size() - 1];
88  }else{
89  for (int k = 0; k < found_indexes.size() - 1; k ++){
90  if (found_indexes[k] < sc && found_indexes[k + 1] > sc){
91  lower_index = found_indexes[k];
92  upper_index = found_indexes[k + 1];
93  break;
94  }
95  }
96  }
97  step = sc - lower_index;
98  step_width = upper_index - lower_index;
99  // Interpolate real and imaginary part of the resource element transport_channel coefficient
100  re_int = step * (real(coefficients[symbol][upper_index]) - real(coefficients[symbol][lower_index])) / step_width + real(coefficients[symbol][lower_index]);
101  im_int = step * (imag(coefficients[symbol][upper_index]) - imag(coefficients[symbol][lower_index])) / step_width + imag(coefficients[symbol][lower_index]);
102  coefficients[symbol][sc] = complex<float>(re_int,im_int);
103  // Compute mean power over all the resource elements
104  mean_power += pow(abs(coefficients[symbol][sc]),2);
105  }
106  }
107  }else {
108  symbols_with_no_pilot.push_back(symbol);
109  }
110  }
111 
112  /*
113  * Time domain linear interpolation
114  */
115 
116  for (int s = 0; s < symbols_with_no_pilot.size(); s ++){
117  if (symbols_with_no_pilot[s] < symbols_with_pilot[0]){
118  lower_index = symbols_with_pilot[0];
119  upper_index = symbols_with_pilot[1];
120  }else if(symbols_with_no_pilot[s] > symbols_with_pilot[symbols_with_pilot.size() - 1]){
121  lower_index = symbols_with_pilot[symbols_with_pilot.size() - 2];
122  upper_index = symbols_with_pilot[symbols_with_pilot.size() - 1];
123  }else{
124  for (int k = 0; k < symbols_with_pilot.size() - 1; k ++){
125  if (symbols_with_pilot[k] < symbols_with_no_pilot[s] && symbols_with_pilot[k + 1] > symbols_with_no_pilot[s]){
126  lower_index = symbols_with_pilot[k];
127  upper_index = symbols_with_pilot[k + 1];
128  break;
129  }
130  }
131  }
132  step = symbols_with_no_pilot[s] - lower_index;
133  step_width = upper_index - lower_index;
134 
135 
136  for (int sc = 0; sc < num_sc; sc ++){
137  re_int = step * (real(coefficients[upper_index][sc]) - real(coefficients[lower_index][sc])) / step_width + real(coefficients[lower_index][sc]);
138  im_int = step * (imag(coefficients[upper_index][sc]) - imag(coefficients[lower_index][sc])) / step_width + imag(coefficients[lower_index][sc]);
139  coefficients[symbols_with_no_pilot[s]][sc] = complex<float>(re_int,im_int);
140  // Compute mean power over all the resource elements
141  mean_power += pow(abs(coefficients[symbols_with_no_pilot[s]][sc]),2);
142  }
143 
144  }
145  // Computing signal power.
146  mean_power /= num_sc * num_symbols;
147  mean_power = 10 * log10(mean_power);
148 
149 
150  // Computing signal noise by time averaging the mean standard deviation of the coefficients on all the subcarriers
151  for (int symbol = 0; symbol <num_symbols; symbol ++ ){
152  noise_coef = 0;
153  for (int sc = 0; sc < num_sc; sc ++){
154  noise_coef += coefficients[symbol][sc];
155  }
156  noise_coef /= num_sc;
157  for (int sc = 0; sc < num_sc; sc ++){
158  mean_noise += pow(abs(coefficients[symbol][sc] - noise_coef),2) / num_sc * num_symbols ;
159  }
160  }
161  mean_noise = 10 * log10(mean_noise);
162 
163  snr = mean_power - mean_noise;
164 }
165 
166 void free5GRAN::phy::signal_processing::hard_demodulation(vector<complex<float>> signal, int *bits, int signal_length, int modulation_scheme) {
174  for (int i = 0; i < signal_length; i ++){
175  /*
176  * BPSK demodulation
177  */
178  if (modulation_scheme == 0){
179  if (real(signal[i]) > 0){
180  bits[i] = 0;
181  }else {
182  bits[i] = 1;
183  }
184  }
185  /*
186  * QPSK demodulation
187  */
188  else if(modulation_scheme == 1){
189  if (real(signal[i]) > 0){
190  bits[2 * i] = 0;
191  }else {
192  bits[2 * i] = 1;
193  }
194  if (imag(signal[i]) > 0){
195  bits[2 * i + 1] = 0;
196  }else {
197  bits[2 * i + 1] = 1;
198  }
199  }
200  }
201 }
202 
203 void free5GRAN::phy::signal_processing::soft_demodulation(vector<complex<float>> signal, double *soft_bits, int signal_length, int modulation_scheme) {
216  /*
217  * QPSK soft-demodulation
218  */
219  if(modulation_scheme == 1){
220  complex<float> s_00, s_01, s_10, s_11;
221  double const_power = 1 / sqrt(2);
222  s_00 = complex<double>(const_power,const_power);
223  s_01 = complex<double>(const_power,-const_power);
224  s_10 = complex<double>(-const_power,const_power);
225  s_11 = complex<double>(-const_power,-const_power);
226  for (int i = 0; i < signal_length; i ++){
227  double l_01,l_02, l_11, l_12;
228  // Computing distance from current signal point to each of the four theoretical QPSK points
229  l_01 = abs(signal[i] - s_00);
230  l_02 = abs(signal[i] - s_01);
231  l_11 = abs(signal[i] - s_10);
232  l_12 = abs(signal[i] - s_11);
233  // Determine LLR soft bit of the first QPSK bit
234  soft_bits[2 * i] = pow(min(l_11,l_12),2) - pow(min(l_01,l_02),2);
235  // Computing distance from current signal point to each of the four theoretical QPSK points
236  l_01 = abs(signal[i] - s_00);
237  l_02 = abs(signal[i] - s_10);
238  l_11 = abs(signal[i] - s_01);
239  l_12 = abs(signal[i] - s_11);
240  // Determine LLR soft bit of the second QPSK bit
241  soft_bits[2 * i + 1] = pow(min(l_11,l_12),2) - pow(min(l_01,l_02),2);
242  }
243  }
244 
245 }
246 
247 void free5GRAN::phy::signal_processing::compute_fine_frequency_offset(vector<complex<float>> input_signal, int symbol_duration, int fft_size, int cp_length, int scs, float &output, int num_symbols) {
260  complex<float> out;
261  float phase_offset = 0;
262 
263  /*
264  * Averaging phase offset between cyclic prefix and corresponding symbol data
265  */
266  for (int symbol = 0; symbol < num_symbols; symbol ++){
267  out = 0;
268  for (int i = 0; i < cp_length; i ++){
269  out += conj(input_signal[i + symbol * symbol_duration]) * input_signal[i + symbol * symbol_duration + fft_size];
270  }
271  phase_offset += arg(out);
272  }
273 
274  phase_offset/= num_symbols;
275  /*
276  * Computing frequency offset corresponding to the computed phase offset
277  */
278  output = scs * phase_offset/(2 * M_PI);
279 
280 }
281 
282 void free5GRAN::phy::signal_processing::transpose_signal(vector<complex<float>> *input_signal, float freq_offset, int sample_rate, int input_length) {
291  complex<float> j(0, 1);
292  for (int i = 0; i < input_length; i ++){
293  (*input_signal)[i] = (*input_signal)[i] * exp(complex<float>(-2,0) * j * complex<float>(M_PI,0) * freq_offset * complex<float>((float) i,0) / complex<float>(sample_rate,0));
294  }
295 }
296 
297 
298 void free5GRAN::phy::signal_processing::channel_demapper(vector<vector<complex<float>>> &input_signal, vector<vector<vector<int>>> &ref, complex<float> **output_channels, vector<vector<vector<int>>> &output_indexes, int num_channels, int num_symbols, int num_sc){
311  int channel_counter[num_channels];
312  for (int i = 0; i < num_channels; i ++){
313  channel_counter[i] = 0;
314  }
315  /*
316  * Loop over each resource element in the grid
317  */
318  for (int symbol = 0; symbol < num_symbols; symbol ++){
319  for (int sc = 0; sc < num_sc; sc ++){
320  /*
321  * Loop over each transport_channel
322  */
323  for (int channel = 0; channel < num_channels; channel++){
324  /*
325  * If the current resource element belongs to the transport_channel, store the element and corresponding grid index
326  */
327  if (ref[channel][symbol][sc] == 1){
328  output_channels[channel][channel_counter[channel]] = input_signal[symbol][sc];
329  output_indexes[channel][0][channel_counter[channel]] = symbol;
330  output_indexes[channel][1][channel_counter[channel]] = sc;
331  channel_counter[channel] ++;
332  }
333  }
334  }
335  }
336 }
337 
346  int N;
347  double freq;
348  if (gscn < 7499){
349  int M;
350  for (int i = 0; i < 3; i ++){
351  M = 2 * i + 1;
352  if ((gscn - (M - 3)/2) % 3 == 0){
353  break;
354  }
355  }
356  N = (gscn - (M - 3)/2) / 3;
357  freq = N * 1.2e6 + M * 50e3;
358  }else if (gscn < 22256){
359  N = gscn - 7499;
360  freq = 3e9 + N * 1.44e6;
361  }else {
362  N = gscn - 22256;
363  freq = 24250.08e6 + N * 17.28e6;
364  }
365  return freq;
366 }
367 
368 
381  int index1,index2;
382  index1 = pdcch_config / 16;
383  index2 = pdcch_config % 16;
384  int *table1 = new int[4];
385  float *table2;
386  if (pbch_scs==15e3 && common_scs == 15e3){
387  table1 = free5GRAN::TS_38_213_TABLE_13_1[index1];
388  }else if (pbch_scs==15e3 && common_scs == 30e3){
389  table1 = free5GRAN::TS_38_213_TABLE_13_2[index1];
390  }else if (pbch_scs==30e3 && common_scs == 15e3){
391  table1 = free5GRAN::TS_38_213_TABLE_13_3[index1];
392  }else if (pbch_scs==30e3 && common_scs == 30e3){
393  table1 = free5GRAN::TS_38_213_TABLE_13_4[index1];
394  }
395  table2 = free5GRAN::TS_38_213_TABLE_13_11[index2];
396 
397  obj.multiplexing_pattern = table1[0];
398  obj.n_rb_coreset = table1[1];
399  obj.n_symb_coreset = table1[2];
400  obj.offset = table1[3];
401  obj.O = table2[0];
402  obj.num_ss_slots = table2[1];
403  obj.M = table2[2];
404 
405  if (table2[3] == -1){
406  if (i % 2 == 0){
407  obj.first_symb_index = 0;
408  }else {
410  }
411  }else {
412  obj.first_symb_index = table2[3];
413  }
414 
415  return obj;
416 }
417 
418 
419 void free5GRAN::phy::signal_processing::compute_rb_start_lrb_dci(int RIV, int n_size_bwp, int &lrb, int &rb_start){
430  lrb = floor(RIV/n_size_bwp) + 1;
431  rb_start = RIV - ((lrb - 1) * n_size_bwp);
432  if (lrb > n_size_bwp - rb_start){
433  lrb = n_size_bwp - lrb + 2;
434  rb_start = n_size_bwp - 1 - rb_start;
435  }
436 }
437 
438 void free5GRAN::phy::signal_processing::get_pdsch_dmrs_symbols(const string &type, int duration, int additionnal_position, int l0, int **output, int &size){
449  if (duration < 8 || additionnal_position==0){
450  size = 1;
451  (*output) = new int[size]{l0};
452  }else if (duration < 10){
453  size = 2;
454  (*output) = new int[size]{l0,7};
455  }else if (additionnal_position==1){
456  size = 2;
457  if (duration < 13){
458  (*output) = new int[size]{l0,9};
459  }else {
460  (*output) = new int[size]{l0,11};
461  }
462  }
463  else if (additionnal_position==2){
464  size = 3;
465  if (duration < 13){
466  (*output) = new int[size]{l0,6,9};
467  }else {
468  (*output) = new int[size]{l0,7,11};
469  }
470  }else if (additionnal_position==3){
471  if (duration < 12){
472  size = 3;
473  (*output) = new int[size]{l0,6,9};
474  }else {
475  size = 4;
476  (*output) = new int[size]{l0,5,8,11};
477  }
478  }
479 }
480 
481 
482 
483 void free5GRAN::phy::signal_processing::compute_cp_lengths(int scs, int nfft, int is_extended_cp, int num_symb_per_subframes, int *cp_lengths, int *cum_sum_cp_lengths){
496  int nom_cp = ((is_extended_cp) ? 512 : 144);
497  int base_cp = nom_cp * nfft / 2048;
498  cum_sum_cp_lengths[0] = 0;
499  for (int i = 0; i < num_symb_per_subframes; i ++){
500  if (i % (num_symb_per_subframes / 2) == 0){
501  cp_lengths[i] = (scs * nfft - num_symb_per_subframes * nfft - (num_symb_per_subframes - 2) * base_cp) / 2;
502  }else {
503  cp_lengths[i] = base_cp;
504  }
505  if (i < num_symb_per_subframes - 1){
506  cum_sum_cp_lengths[i + 1] = cum_sum_cp_lengths[i] + cp_lengths[i] + nfft;
507  }
508  }
509 }
510 
511 void free5GRAN::phy::signal_processing::compute_phase_decomp(int* cp_lengths, int* cum_sum_symb, float sampling_rate, float f0, int num_symb_per_subframes, complex<float>* phase_decomp_factor){
524  for (int i = 0; i < num_symb_per_subframes; i ++){
525  float t_start = cum_sum_symb[i]/ sampling_rate;
526  float t_cp = cp_lengths[i] / sampling_rate;
527  phase_decomp_factor[i] = exp(complex<float>(0,- 2 * f0 * M_PI) * (- t_start - t_cp));
528  }
529 
530 }
531 
532 int free5GRAN::phy::signal_processing::compute_nre(int num_symb_pdsch, int num_dmrs_symb){
540  return 12 * (num_symb_pdsch - num_dmrs_symb);
541 }
542 
543 void free5GRAN::phy::signal_processing::fft(vector<complex<float>> time_domain_signal, vector<vector<complex<float>>> &output_signal, int fft_size, int *cp_lengths, int *cum_sum_symb, int num_symbols, int num_sc_output, int first_symb_index, int offset){
557  // Initializing fft parameters
558  fftw_complex *fft_in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size);
559  fftw_complex *fft_out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size);
560 
561  fftw_plan fft_plan = fftw_plan_dft_1d(fft_size, fft_in, fft_out, FFTW_FORWARD, FFTW_MEASURE);
562 
563  for (int symbol = 0; symbol < num_symbols; symbol ++){
564  int symb_index = (first_symb_index + symbol) % free5GRAN::NUMBER_SYMBOLS_PER_SLOT_NORMAL_CP;
565  // Filling fft input signal with current symbol IQ
566  for (int i = 0; i < fft_size; i++){
567  fft_in[i][0] = real(time_domain_signal[i + offset + cum_sum_symb[symb_index] + cp_lengths[symb_index]]);
568  fft_in[i][1] = imag(time_domain_signal[i + offset + cum_sum_symb[symb_index] + cp_lengths[symb_index]]);
569  }
570  // Execute the fft
571  fftw_execute(fft_plan);
572  // Building the RE grid
573  for (int i = 0; i < num_sc_output / 2; i++){
574  output_signal[symbol][num_sc_output / 2 + i ] = complex<float>(fft_out[i][0],fft_out[i][1]);
575  output_signal[symbol][num_sc_output / 2 - i - 1] = complex<float>(fft_out[fft_size - i - 1][0],fft_out[fft_size - i - 1][1]);
576  }
577  }
578 }
579 
580 void free5GRAN::phy::signal_processing::get_candidates_frames_indexes(vector<vector<int>> &frame_indexes, int *frame_numbers, int sfn, int index_first_pss, int num_samples_before_pss, int frame_size){
581  if (num_samples_before_pss < index_first_pss) {
582  if (num_samples_before_pss + 2 * frame_size < index_first_pss){
583  frame_indexes[0][0] = index_first_pss - num_samples_before_pss - 2 * frame_size;
584  frame_indexes[0][1] = index_first_pss - num_samples_before_pss - frame_size - 1;
585  frame_indexes[1][0] = index_first_pss - num_samples_before_pss - frame_size;
586  frame_indexes[1][1] = index_first_pss - num_samples_before_pss - 1;
587  frame_numbers[0] = sfn - 2;
588  frame_numbers[1] = sfn - 1;
589  }
590  else if (num_samples_before_pss + frame_size < index_first_pss){
591  frame_indexes[0][0] = index_first_pss - num_samples_before_pss - frame_size;
592  frame_indexes[0][1] = index_first_pss - num_samples_before_pss - 1;
593  frame_indexes[1][0] = index_first_pss - num_samples_before_pss;
594  frame_indexes[1][1] = index_first_pss - num_samples_before_pss + frame_size - 1;
595  frame_numbers[0] = sfn - 1;
596  frame_numbers[1] = sfn;
597  }
598  else {
599  frame_indexes[0][0] = index_first_pss - num_samples_before_pss;
600  frame_indexes[0][1] = index_first_pss - num_samples_before_pss + frame_size - 1;
601  frame_indexes[1][0] = index_first_pss - num_samples_before_pss + frame_size;
602  frame_indexes[1][1] = index_first_pss - num_samples_before_pss + 2 * frame_size - 1;
603  frame_numbers[0] = sfn;
604  frame_numbers[1] = sfn + 1;
605  }
606  }else {
607  frame_indexes[0][0] = index_first_pss - num_samples_before_pss + frame_size;
608  frame_indexes[0][1] = index_first_pss - num_samples_before_pss + 2 * frame_size - 1;
609  frame_indexes[1][0] = index_first_pss - num_samples_before_pss + 2 * frame_size;
610  frame_indexes[1][1] = index_first_pss - num_samples_before_pss + 3 * frame_size - 1;
611  frame_numbers[0] = sfn + 1;
612  frame_numbers[1] = sfn + 2;
613  }
614 }
int TS_38_213_TABLE_13_4[16][4]
void compute_phase_decomp(int *cp_lengths, int *cum_sum_symb, float sampling_rate, float f0, int num_symb_per_subframes, complex< float > *phase_decomp_factor)
Definition: libphy.cpp:511
int NUMBER_SYMBOLS_PER_SLOT_NORMAL_CP
void get_candidates_frames_indexes(vector< vector< int >> &frame_indexes, int *frame_numbers, int sfn, int index_first_pss, int num_samples_before_pss, int frame_size)
Definition: libphy.cpp:580
void fft(vector< complex< float >> time_domain_signal, vector< vector< complex< float >>> &output_signal, int fft_size, int *cp_lengths, int *cum_sum_symb, int num_symbols, int num_sc_output, int first_symb_index, int offset)
Definition: libphy.cpp:543
void soft_demodulation(vector< complex< float >> signal, double *soft_bits, int signal_length, int modulation_scheme)
Definition: libphy.cpp:203
void get_pdsch_dmrs_symbols(const string &type, int duration, int additionnal_position, int l0, int **output, int &size)
Definition: libphy.cpp:438
int TS_38_213_TABLE_13_1[16][4]
free5GRAN::pdcch_t0ss_monitoring_occasions compute_pdcch_t0_ss_monitoring_occasions(int pdcch_config, int pbch_scs, int common_scs, int i)
Definition: libphy.cpp:369
void transpose_signal(vector< complex< float >> *input_signal, float freq_offset, int sample_rate, int input_length)
Definition: libphy.cpp:282
void compute_rb_start_lrb_dci(int RIV, int n_size_bwp, int &lrb, int &rb_start)
Definition: libphy.cpp:419
void channelEstimation(complex< float > *pilots, complex< float > *reference_pilot, vector< vector< int >> &pilot_indexes, vector< vector< complex< float >>> &coefficients, float &snr, int num_sc, int num_symbols, int pilot_size)
Definition: libphy.cpp:27
int compute_nre(int num_symb_pdsch, int num_dmrs_symb)
Definition: libphy.cpp:532
void channel_demapper(vector< vector< complex< float >>> &input_signal, vector< vector< vector< int >>> &ref, complex< float > **output_channels, vector< vector< vector< int >>> &output_indexes, int num_channels, int num_symbols, int num_sc)
Definition: libphy.cpp:298
int TS_38_213_TABLE_13_3[16][4]
float TS_38_213_TABLE_13_11[16][4]
double compute_freq_from_gscn(int gscn)
Definition: libphy.cpp:338
int TS_38_213_TABLE_13_2[16][4]
void compute_cp_lengths(int scs, int nfft, int is_extended_cp, int num_symb_per_subframes, int *cp_lengths, int *cum_sum_cp_lengths)
Definition: libphy.cpp:483
void compute_fine_frequency_offset(vector< complex< float >> input_signal, int symbol_duration, int fft_size, int cp_length, int scs, float &output, int num_symbols)
Definition: libphy.cpp:247
void hard_demodulation(vector< complex< float >> signal, int *bits, int signal_length, int modulation_scheme)
Definition: libphy.cpp:166