Skip to content

Commit 729283e

Browse files
xavierarteagaasaezper
authored andcommitted
ran: implement TPMI selection for 2 layers
phy: review comments in TPMI selector ran: fix TPMI
1 parent c7d0585 commit 729283e

File tree

3 files changed

+191
-61
lines changed

3 files changed

+191
-61
lines changed

lib/ran/pusch/pusch_tpmi_select.cpp

Lines changed: 170 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
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>
@@ -23,63 +24,126 @@
2324

2425
using 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
3236
static 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+
76140
static 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+
124249
pusch_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
}

tests/unittests/ran/pusch/pusch_tpmi_select_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ TEST_P(PuschTpmiSelectFixture, VectorTest)
114114
unsigned max_nof_layers = std::min(nof_tx_ports, nof_rx_ports);
115115

116116
// Only one layer is currently supported.
117-
if (max_nof_layers > 1) {
117+
if (max_nof_layers > 2) {
118118
GTEST_SKIP();
119119
}
120120

0 commit comments

Comments
 (0)