1515#include " srsran/ran/precoding/precoding_weight_matrix.h"
1616#include " srsran/ran/pusch/pusch_constants.h"
1717#include " srsran/ran/srs/srs_channel_matrix.h"
18+ #include " srsran/srsvec/mean.h"
1819#include " srsran/support/math_utils.h"
1920#include < algorithm>
2021#include < array>
2324
2425using namespace srsran ;
2526
26- static constexpr cf_t sqrt1_2 (M_SQRT1_2, 0 );
27- static constexpr cf_t sqrt1_2j (0 , M_SQRT1_2);
28- static constexpr cf_t dot5 (0.5 , 0 );
29- static constexpr cf_t dot5j (0 , 0.5 );
27+ static constexpr cf_t sqrt1_2 (M_SQRT1_2, 0 .0F );
28+ static constexpr cf_t sqrt1_2j (0 .0F , M_SQRT1_2);
29+ static constexpr cf_t sqrt1_8 (M_SQRT1_2 / 2 , 0 .0F );
30+ static constexpr cf_t sqrt1_8j (0 .0F , M_SQRT1_2 / 2 );
31+ static constexpr cf_t dot5 (0 .5F , 0 .0F );
32+ static constexpr cf_t dot5j (0 .0F , 0 .5F );
33+ static constexpr cf_t zero;
3034
3135// TS38.211 Table 6.3.1.5-1
3236static const std::array<precoding_weight_matrix, 6 > codebook_1layer_2port = {
33- {precoding_weight_matrix ({sqrt1_2, 0 }, 1 , 2 ),
34- precoding_weight_matrix ({0 , sqrt1_2}, 1 , 2 ),
37+ {precoding_weight_matrix ({sqrt1_2, zero }, 1 , 2 ),
38+ precoding_weight_matrix ({zero , sqrt1_2}, 1 , 2 ),
3539 precoding_weight_matrix ({sqrt1_2, sqrt1_2}, 1 , 2 ),
3640 precoding_weight_matrix ({sqrt1_2, -sqrt1_2}, 1 , 2 ),
3741 precoding_weight_matrix ({sqrt1_2, sqrt1_2j}, 1 , 2 ),
3842 precoding_weight_matrix ({sqrt1_2, -sqrt1_2j}, 1 , 2 )}};
3943
4044// TS38.211 Table 6.3.1.5-3
41- static const std::array<precoding_weight_matrix, 28 > codebook_1layer_4port = {{
42- // TPMI Index 0-7
43- precoding_weight_matrix ({dot5, 0 , 0 , 0 }, 1 , 4 ),
44- precoding_weight_matrix ({0 , dot5, 0 , 0 }, 1 , 4 ),
45- precoding_weight_matrix ({0 , 0 , dot5, 0 }, 1 , 4 ),
46- precoding_weight_matrix ({0 , 0 , 0 , dot5}, 1 , 4 ),
47- precoding_weight_matrix ({dot5, 0 , dot5, 0 }, 1 , 4 ),
48- precoding_weight_matrix ({dot5, 0 , -dot5, 0 }, 1 , 4 ),
49- precoding_weight_matrix ({dot5, 0 , dot5j, 0 }, 1 , 4 ),
50- precoding_weight_matrix ({dot5, 0 , -dot5j, 0 }, 1 , 4 ),
51- // TPMI Index 8-15
52- precoding_weight_matrix ({0 , dot5, 0 , dot5}, 1 , 4 ),
53- precoding_weight_matrix ({0 , dot5, 0 , -dot5}, 1 , 4 ),
54- precoding_weight_matrix ({0 , dot5, 0 , dot5j}, 1 , 4 ),
55- precoding_weight_matrix ({0 , dot5, 0 , -dot5j}, 1 , 4 ),
56- precoding_weight_matrix ({dot5, dot5, dot5, dot5}, 1 , 4 ),
57- precoding_weight_matrix ({dot5, dot5, dot5j, dot5j}, 1 , 4 ),
58- precoding_weight_matrix ({dot5, dot5, -dot5, -dot5}, 1 , 4 ),
59- precoding_weight_matrix ({dot5, dot5, -dot5j, -dot5j}, 1 , 4 ),
60- // TPMI Index 16-23
61- precoding_weight_matrix ({dot5, dot5j, dot5, dot5j}, 1 , 4 ),
62- precoding_weight_matrix ({dot5, dot5j, dot5j, -dot5}, 1 , 4 ),
63- precoding_weight_matrix ({dot5, dot5j, -dot5, -dot5j}, 1 , 4 ),
64- precoding_weight_matrix ({dot5, dot5j, -dot5j, dot5}, 1 , 4 ),
65- precoding_weight_matrix ({dot5, -dot5, dot5, -dot5}, 1 , 4 ),
66- precoding_weight_matrix ({dot5, -dot5, dot5j, -dot5j}, 1 , 4 ),
67- precoding_weight_matrix ({dot5, -dot5, -dot5, dot5}, 1 , 4 ),
68- precoding_weight_matrix ({dot5, -dot5, -dot5j, dot5j}, 1 , 4 ),
69- // TPMI Index 24-27
70- precoding_weight_matrix ({dot5, -dot5j, dot5, -dot5j}, 1 , 4 ),
71- precoding_weight_matrix ({dot5, -dot5j, dot5j, dot5}, 1 , 4 ),
72- precoding_weight_matrix ({dot5, -dot5j, -dot5, dot5j}, 1 , 4 ),
73- precoding_weight_matrix ({dot5, -dot5j, -dot5j, -dot5}, 1 , 4 ),
45+ static const std::array<precoding_weight_matrix, 28 > codebook_1layer_4port = {
46+ {// TPMI Index 0-7
47+ precoding_weight_matrix ({dot5, zero, zero, zero}, 1 , 4 ),
48+ precoding_weight_matrix ({zero, dot5, zero, zero}, 1 , 4 ),
49+ precoding_weight_matrix ({zero, zero, dot5, zero}, 1 , 4 ),
50+ precoding_weight_matrix ({zero, zero, zero, dot5}, 1 , 4 ),
51+ precoding_weight_matrix ({dot5, zero, dot5, zero}, 1 , 4 ),
52+ precoding_weight_matrix ({dot5, zero, -dot5, zero}, 1 , 4 ),
53+ precoding_weight_matrix ({dot5, zero, dot5j, zero}, 1 , 4 ),
54+ precoding_weight_matrix ({dot5, zero, -dot5j, zero}, 1 , 4 ),
55+ // TPMI Index 8-15
56+ precoding_weight_matrix ({zero, dot5, zero, dot5}, 1 , 4 ),
57+ precoding_weight_matrix ({zero, dot5, zero, -dot5}, 1 , 4 ),
58+ precoding_weight_matrix ({zero, dot5, zero, dot5j}, 1 , 4 ),
59+ precoding_weight_matrix ({zero, dot5, zero, -dot5j}, 1 , 4 ),
60+ precoding_weight_matrix ({dot5, dot5, dot5, dot5}, 1 , 4 ),
61+ precoding_weight_matrix ({dot5, dot5, dot5j, dot5j}, 1 , 4 ),
62+ precoding_weight_matrix ({dot5, dot5, -dot5, -dot5}, 1 , 4 ),
63+ precoding_weight_matrix ({dot5, dot5, -dot5j, -dot5j}, 1 , 4 ),
64+ // TPMI Index 16-23
65+ precoding_weight_matrix ({dot5, dot5j, dot5, dot5j}, 1 , 4 ),
66+ precoding_weight_matrix ({dot5, dot5j, dot5j, -dot5}, 1 , 4 ),
67+ precoding_weight_matrix ({dot5, dot5j, -dot5, -dot5j}, 1 , 4 ),
68+ precoding_weight_matrix ({dot5, dot5j, -dot5j, dot5}, 1 , 4 ),
69+ precoding_weight_matrix ({dot5, -dot5, dot5, -dot5}, 1 , 4 ),
70+ precoding_weight_matrix ({dot5, -dot5, dot5j, -dot5j}, 1 , 4 ),
71+ precoding_weight_matrix ({dot5, -dot5, -dot5, dot5}, 1 , 4 ),
72+ precoding_weight_matrix ({dot5, -dot5, -dot5j, dot5j}, 1 , 4 ),
73+ // TPMI Index 24-27
74+ precoding_weight_matrix ({dot5, -dot5j, dot5, -dot5j}, 1 , 4 ),
75+ precoding_weight_matrix ({dot5, -dot5j, dot5j, dot5}, 1 , 4 ),
76+ precoding_weight_matrix ({dot5, -dot5j, -dot5, dot5j}, 1 , 4 ),
77+ precoding_weight_matrix ({dot5, -dot5j, -dot5j, -dot5}, 1 , 4 )}};
78+
79+ // TS38.211 Table 6.3.1.5-4
80+ static const std::array<precoding_weight_matrix, 3 > codebook_2layer_2port = {
81+ {precoding_weight_matrix ({sqrt1_2, zero, zero, sqrt1_2}, 2 , 2 ),
82+ precoding_weight_matrix ({dot5, dot5, dot5, -dot5}, 2 , 2 ),
83+ precoding_weight_matrix ({dot5, dot5, dot5j, -dot5j}, 2 , 2 )}};
84+
85+ // TS38.211 Table 6.3.1.5-5
86+ static const std::array<precoding_weight_matrix, 22 > codebook_2layer_4port = {{
87+ // TPMI Index 0-3
88+ precoding_weight_matrix ({dot5, zero, zero, dot5, zero, zero, zero, zero}, 2 , 4 ),
89+ precoding_weight_matrix ({dot5, zero, zero, zero, zero, dot5, zero, zero}, 2 , 4 ),
90+ precoding_weight_matrix ({dot5, zero, zero, zero, zero, zero, zero, dot5}, 2 , 4 ),
91+ precoding_weight_matrix ({zero, zero, dot5, zero, zero, dot5, zero, zero}, 2 , 4 ),
92+ // TPMI Index 4-7
93+ precoding_weight_matrix ({zero, zero, dot5, zero, zero, zero, zero, dot5}, 2 , 4 ),
94+ precoding_weight_matrix ({zero, zero, zero, zero, dot5, zero, zero, dot5}, 2 , 4 ),
95+ precoding_weight_matrix ({dot5, zero, zero, dot5, dot5, zero, zero, -dot5j}, 2 , 4 ),
96+ precoding_weight_matrix ({dot5, zero, zero, dot5, dot5, zero, zero, dot5j}, 2 , 4 ),
97+ // TPMI Index 8-11
98+ precoding_weight_matrix ({dot5, zero, zero, dot5, -dot5j, zero, zero, dot5}, 2 , 4 ),
99+ precoding_weight_matrix ({dot5, zero, zero, dot5, -dot5j, zero, zero, -dot5}, 2 , 4 ),
100+ precoding_weight_matrix ({dot5, zero, zero, dot5, -dot5, zero, zero, -dot5j}, 2 , 4 ),
101+ precoding_weight_matrix ({dot5, zero, zero, dot5, -dot5, zero, zero, dot5j}, 2 , 4 ),
102+ // TPMI Index 12-15
103+ precoding_weight_matrix ({dot5, zero, zero, dot5, dot5j, zero, zero, dot5}, 2 , 4 ),
104+ precoding_weight_matrix ({dot5, zero, zero, dot5, dot5j, zero, zero, -dot5}, 2 , 4 ),
105+ precoding_weight_matrix ({sqrt1_8, sqrt1_8, sqrt1_8, sqrt1_8, sqrt1_8, -sqrt1_8, sqrt1_8, -sqrt1_8}, 2 , 4 ),
106+ precoding_weight_matrix ({sqrt1_8, sqrt1_8, sqrt1_8, sqrt1_8, sqrt1_8j, -sqrt1_8j, sqrt1_8j, -sqrt1_8j}, 2 , 4 ),
107+ // TPMI Index 16-19
108+ precoding_weight_matrix ({sqrt1_8, sqrt1_8, sqrt1_8j, sqrt1_8j, sqrt1_8, -sqrt1_8, sqrt1_8j, -sqrt1_8j}, 2 , 4 ),
109+ precoding_weight_matrix ({sqrt1_8, sqrt1_8, sqrt1_8j, sqrt1_8j, sqrt1_8j, -sqrt1_8j, -sqrt1_8, sqrt1_8}, 2 , 4 ),
110+ precoding_weight_matrix ({sqrt1_8, sqrt1_8, -sqrt1_8, -sqrt1_8, sqrt1_8, -sqrt1_8, -sqrt1_8, sqrt1_8}, 2 , 4 ),
111+ precoding_weight_matrix ({sqrt1_8, sqrt1_8, -sqrt1_8, -sqrt1_8, sqrt1_8j, -sqrt1_8j, -sqrt1_8j, sqrt1_8j}, 2 , 4 ),
112+ // TPMI Index 20-21
113+ precoding_weight_matrix ({sqrt1_8, sqrt1_8, -sqrt1_8j, -sqrt1_8j, sqrt1_8, -sqrt1_8, -sqrt1_8j, sqrt1_8j}, 2 , 4 ),
114+ precoding_weight_matrix ({sqrt1_8, sqrt1_8, -sqrt1_8j, -sqrt1_8j, sqrt1_8j, -sqrt1_8j, sqrt1_8, -sqrt1_8}, 2 , 4 ),
74115}};
75116
117+ // Calculates the product of a channel matrix and a precoding weight matrix. The number of transmit ports of each matrix
118+ // must be equal.
119+ static precoding_weight_matrix product_channel_weight (const srs_channel_matrix& H, const precoding_weight_matrix& W)
120+ {
121+ srsran_assert (H.get_nof_tx_ports () == W.get_nof_ports (), " Number of Ports is not equal." );
122+ unsigned nof_tx_ports = W.get_nof_ports ();
123+ unsigned nof_layers = W.get_nof_layers ();
124+ unsigned nof_rx_ports = H.get_nof_rx_ports ();
125+
126+ precoding_weight_matrix result (nof_layers, nof_rx_ports);
127+ for (unsigned i_layer = 0 ; i_layer != nof_layers; ++i_layer) {
128+ for (unsigned i_rx_port = 0 ; i_rx_port != nof_rx_ports; ++i_rx_port) {
129+ cf_t sum = 0 ;
130+ for (unsigned i_tx_port = 0 ; i_tx_port != nof_tx_ports; ++i_tx_port) {
131+ sum += H.get_coefficient (i_rx_port, i_tx_port) * W.get_coefficient (i_layer, i_tx_port);
132+ }
133+ result.set_coefficient (sum, i_layer, i_rx_port);
134+ }
135+ }
136+
137+ return result;
138+ }
139+
76140static pusch_tpmi_select_info::tpmi_info get_tpmi_select_info_1layer (const srs_channel_matrix& channel,
77141 float noise_variance)
78142{
79143 unsigned nof_rx_ports = channel.get_nof_rx_ports ();
80144 unsigned nof_tx_ports = channel.get_nof_tx_ports ();
81145
82- // Select codebook in function of the number of transmit ports.
146+ // Select codebook as a function of the number of transmit ports.
83147 span<const precoding_weight_matrix> codebook = codebook_1layer_2port;
84148 if (nof_tx_ports == 4 ) {
85149 codebook = codebook_1layer_4port;
@@ -121,6 +185,67 @@ static pusch_tpmi_select_info::tpmi_info get_tpmi_select_info_1layer(const srs_c
121185 return {best_tpmi, convert_power_to_dB (best_sinr)};
122186}
123187
188+ static pusch_tpmi_select_info::tpmi_info get_tpmi_select_info_2layer (const srs_channel_matrix& channel,
189+ float noise_variance)
190+ {
191+ static constexpr unsigned nof_layers = 2 ;
192+ unsigned nof_rx_ports = channel.get_nof_rx_ports ();
193+ unsigned nof_tx_ports = channel.get_nof_tx_ports ();
194+
195+ // Select codebook as a function of the number of transmit ports.
196+ span<const precoding_weight_matrix> codebook = codebook_2layer_2port;
197+ if (nof_tx_ports == 4 ) {
198+ codebook = codebook_2layer_4port;
199+ }
200+
201+ float best_sinr = -std::numeric_limits<float >::infinity ();
202+ unsigned best_tpmi = 0 ;
203+
204+ // Iterate possible TPMIs.
205+ for (unsigned tpmi = 0 , tpmi_end = codebook.size (); tpmi != tpmi_end; ++tpmi) {
206+ // Select precoding matrix.
207+ const precoding_weight_matrix& weights = codebook[tpmi];
208+
209+ // Calculate the product of the channel matrix and the precoding weights.
210+ precoding_weight_matrix channel_weights = product_channel_weight (channel, weights);
211+
212+ // Calculate the product of the channel matrix (recall, it's an Nx2 matrix) and its hermitian transpose. Also, add
213+ // the noise variance. The diagonal coefficients are the squared norms of the channel matrix column vectors.
214+ std::array<float , nof_layers> norm_sq_ch;
215+ for (unsigned i_layer = 0 ; i_layer != nof_layers; ++i_layer) {
216+ float sum = noise_variance;
217+ for (unsigned i_port = 0 ; i_port != nof_rx_ports; ++i_port) {
218+ sum += std::norm (channel_weights.get_coefficient (i_layer, i_port));
219+ }
220+ norm_sq_ch[i_layer] = sum;
221+ }
222+
223+ // Calculate the anti-diagonal coefficients, which are xi and xi_conj.
224+ cf_t xi = cf_t ();
225+ for (unsigned i_port = 0 ; i_port != nof_rx_ports; ++i_port) {
226+ xi += std::conj (channel_weights.get_coefficient (0 , i_port)) * channel_weights.get_coefficient (1 , i_port);
227+ }
228+ float xi_mod_sq = std::norm (xi);
229+
230+ // Calculate the denominators.
231+ float det = ((norm_sq_ch[0 ] * norm_sq_ch[1 ]) - xi_mod_sq);
232+
233+ // Estimate noise variances.
234+ std::array<float , nof_layers> layer_sinr;
235+ for (unsigned i_layer = 0 ; i_layer != nof_layers; ++i_layer) {
236+ layer_sinr[i_layer] = det / (norm_sq_ch[i_layer] * noise_variance) - 1 .0F ;
237+ }
238+
239+ float sinr = srsvec::mean (layer_sinr);
240+ if (sinr > best_sinr) {
241+ best_sinr = sinr;
242+ best_tpmi = tpmi;
243+ }
244+ }
245+
246+ return {best_tpmi, convert_power_to_dB (best_sinr)};
247+ }
248+
124249pusch_tpmi_select_info srsran::get_tpmi_select_info (const srs_channel_matrix& channel, float noise_variance)
125250{
126251 unsigned nof_tx_ports = channel.get_nof_tx_ports ();
@@ -134,6 +259,11 @@ pusch_tpmi_select_info srsran::get_tpmi_select_info(const srs_channel_matrix& ch
134259 info.emplace_back (get_tpmi_select_info_1layer (channel, noise_variance));
135260 }
136261
262+ // Calculate TPMI select information for 2 layer.
263+ if (max_nof_layers >= 2 ) {
264+ info.emplace_back (get_tpmi_select_info_2layer (channel, noise_variance));
265+ }
266+
137267 // Return invalid information.
138268 return {info};
139269}
0 commit comments