aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/bts.cpp64
-rw-r--r--src/gprs_ms.cpp17
-rw-r--r--src/pcu_l1_if.h46
-rw-r--r--src/pcu_vty_functions.cpp16
4 files changed, 140 insertions, 3 deletions
diff --git a/src/bts.cpp b/src/bts.cpp
index f94799b6..5fafd84a 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -790,11 +790,62 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
"at no request\n");
}
+static void get_rx_qual_meas(struct pcu_l1_meas *meas, uint8_t rx_qual_enc)
+{
+ static const int16_t rx_qual_map[] = {
+ 0, /* 0,14 % */
+ 0, /* 0,28 % */
+ 1, /* 0,57 % */
+ 1, /* 1,13 % */
+ 2, /* 2,26 % */
+ 5, /* 4,53 % */
+ 9, /* 9,05 % */
+ 18, /* 18,10 % */
+ };
+
+ meas->set_ms_rx_qual(rx_qual_map[
+ OSMO_MIN(rx_qual_enc, ARRAY_SIZE(rx_qual_map)-1)
+ ]);
+}
+
+static void get_meas(struct pcu_l1_meas *meas,
+ const Packet_Resource_Request_t *qr)
+{
+ unsigned i;
+
+ meas->set_ms_c_value(qr->C_VALUE);
+ if (qr->Exist_SIGN_VAR)
+ meas->set_ms_sign_var((qr->SIGN_VAR + 2) / 4); /* SIGN_VAR * 0.25 dB */
+
+ for (i = 0; i < OSMO_MIN(ARRAY_SIZE(qr->Slot), ARRAY_SIZE(meas->ts)); i++)
+ {
+ if (qr->Slot[i].Exist)
+ meas->set_ms_i_level(i, -2 * qr->Slot[i].I_LEVEL);
+ }
+}
+
+static void get_meas(struct pcu_l1_meas *meas,
+ const Channel_Quality_Report_t *qr)
+{
+ unsigned i;
+
+ get_rx_qual_meas(meas, qr->RXQUAL);
+ meas->set_ms_c_value(qr->C_VALUE);
+ meas->set_ms_sign_var((qr->SIGN_VAR + 2) / 4); /* SIGN_VAR * 0.25 dB */
+
+ for (i = 0; i < OSMO_MIN(ARRAY_SIZE(qr->Slot), ARRAY_SIZE(meas->ts)); i++)
+ {
+ if (qr->Slot[i].Exist)
+ meas->set_ms_i_level(i, -2 * qr->Slot[i].I_LEVEL_TN);
+ }
+}
+
void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_nack, uint32_t fn)
{
int8_t tfi = 0; /* must be signed */
struct gprs_rlcmac_dl_tbf *tbf;
int rc;
+ struct pcu_l1_meas meas;
tfi = ack_nack->DOWNLINK_TFI;
tbf = bts()->dl_tbf_by_poll_fn(fn, trx_no(), ts_no);
@@ -841,6 +892,11 @@ void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_n
/* schedule uplink assignment */
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
}
+ /* get measurements */
+ if (tbf->ms()) {
+ get_meas(&meas, &ack_nack->Channel_Quality_Report);
+ tbf->ms()->update_l1_meas(&meas);
+ }
}
void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request, uint32_t fn)
@@ -853,6 +909,7 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
uint32_t tlli = request->ID.u.TLLI;
uint8_t ms_class = 0;
uint8_t ta = 0;
+ struct pcu_l1_meas meas;
GprsMs *ms = bts()->ms_by_tlli(tlli);
/* Keep the ms, even if it gets idle temporarily */
@@ -907,6 +964,12 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
ul_tbf->control_ts = ts_no;
/* schedule uplink assignment */
ul_tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
+
+ /* get measurements */
+ if (ul_tbf->ms()) {
+ get_meas(&meas, request);
+ ul_tbf->ms()->update_l1_meas(&meas);
+ }
return;
}
@@ -933,7 +996,6 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
"RX: [PCU <- BTS] %s FIXME: Packet resource request\n",
tbf_name(ul_tbf));
}
-
}
void gprs_rlcmac_pdch::rcv_measurement_report(Packet_Measurement_Report_t *report, uint32_t fn)
diff --git a/src/gprs_ms.cpp b/src/gprs_ms.cpp
index cc211713..fe560e86 100644
--- a/src/gprs_ms.cpp
+++ b/src/gprs_ms.cpp
@@ -421,6 +421,7 @@ void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
{
struct gprs_rlcmac_bts *bts_data;
uint8_t max_cs_ul = 4;
+ unsigned i;
OSMO_ASSERT(m_bts != NULL);
bts_data = m_bts->bts_data();
@@ -464,4 +465,20 @@ void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
m_l1_meas.set_ber(meas->ber);
if (meas->have_link_qual)
m_l1_meas.set_link_qual(meas->link_qual);
+
+ if (meas->have_ms_rx_qual)
+ m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
+ if (meas->have_ms_c_value)
+ m_l1_meas.set_ms_c_value(meas->ms_c_value);
+ if (meas->have_ms_sign_var)
+ m_l1_meas.set_ms_sign_var(meas->ms_sign_var);
+
+ if (meas->have_ms_i_level) {
+ for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
+ if (meas->ts[i].have_ms_i_level)
+ m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
+ else
+ m_l1_meas.ts[i].have_ms_i_level = 0;
+ }
+ }
}
diff --git a/src/pcu_l1_if.h b/src/pcu_l1_if.h
index 88c83991..59b9cba2 100644
--- a/src/pcu_l1_if.h
+++ b/src/pcu_l1_if.h
@@ -37,16 +37,42 @@ extern "C" {
* L1 Measurement values
*/
+struct pcu_l1_meas_ts {
+ unsigned have_ms_i_level:1;
+
+ int16_t ms_i_level; /* I_LEVEL in dB */
+
+#ifdef __cplusplus
+ pcu_l1_meas_ts& set_ms_i_level(int16_t v) {
+ ms_i_level = v; have_ms_i_level = 1; return *this;
+ }
+
+ pcu_l1_meas_ts() :
+ have_ms_i_level(0)
+ {}
+#endif
+};
+
struct pcu_l1_meas {
unsigned have_rssi:1;
unsigned have_ber:1;
unsigned have_bto:1;
unsigned have_link_qual:1;
+ unsigned have_ms_rx_qual:1;
+ unsigned have_ms_c_value:1;
+ unsigned have_ms_sign_var:1;
+ unsigned have_ms_i_level:1;
int8_t rssi; /* RSSI in dBm */
uint8_t ber; /* Bit error rate in % */
int16_t bto; /* Burst timing offset in quarter bits */
- int16_t link_qual; /* Link quality in db */
+ int16_t link_qual; /* Link quality in dB */
+ int16_t ms_rx_qual; /* MS RXQUAL value in % */
+ int16_t ms_c_value; /* C value in dB */
+ int16_t ms_sign_var; /* SIGN_VAR in dB */
+
+ struct pcu_l1_meas_ts ts[8];
+
#ifdef __cplusplus
pcu_l1_meas& set_rssi(int8_t v) { rssi = v; have_rssi = 1; return *this;}
pcu_l1_meas& set_ber(uint8_t v) { ber = v; have_ber = 1; return *this;}
@@ -54,11 +80,27 @@ struct pcu_l1_meas {
pcu_l1_meas& set_link_qual(int16_t v) {
link_qual = v; have_link_qual = 1; return *this;
}
+ pcu_l1_meas& set_ms_rx_qual(int16_t v) {
+ ms_rx_qual = v; have_ms_rx_qual = 1; return *this;
+ }
+ pcu_l1_meas& set_ms_c_value(int16_t v) {
+ ms_c_value = v; have_ms_c_value = 1; return *this;
+ }
+ pcu_l1_meas& set_ms_sign_var(int16_t v) {
+ ms_sign_var = v; have_ms_sign_var = 1; return *this;
+ }
+ pcu_l1_meas& set_ms_i_level(size_t idx, int16_t v) {
+ ts[idx].set_ms_i_level(v); have_ms_i_level = 1; return *this;
+ }
pcu_l1_meas() :
have_rssi(0),
have_ber(0),
have_bto(0),
- have_link_qual(0)
+ have_link_qual(0),
+ have_ms_rx_qual(0),
+ have_ms_c_value(0),
+ have_ms_sign_var(0),
+ have_ms_i_level(0)
{}
#endif
};
diff --git a/src/pcu_vty_functions.cpp b/src/pcu_vty_functions.cpp
index bf4843f1..4f54e8e2 100644
--- a/src/pcu_vty_functions.cpp
+++ b/src/pcu_vty_functions.cpp
@@ -58,6 +58,8 @@ int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
static int show_ms(struct vty *vty, GprsMs *ms)
{
+ unsigned i;
+
vty_out(vty, "MS TLLI=%08x, IMSI=%s%s", ms->tlli(), ms->imsi(), VTY_NEWLINE);
vty_out(vty, " Timing advance (TA): %d%s", ms->ta(), VTY_NEWLINE);
vty_out(vty, " Coding scheme uplink: CS-%d%s", ms->current_cs_ul(),
@@ -79,6 +81,20 @@ static int show_ms(struct vty *vty, GprsMs *ms)
if (ms->l1_meas()->have_bto)
vty_out(vty, " Burst timing offset: %d/4 bit%s",
ms->l1_meas()->bto, VTY_NEWLINE);
+ if (ms->l1_meas()->have_ms_rx_qual)
+ vty_out(vty, " MS RX quality: %d %%%s",
+ ms->l1_meas()->ms_rx_qual, VTY_NEWLINE);
+ if (ms->l1_meas()->have_ms_c_value)
+ vty_out(vty, " MS C value: %d dB%s",
+ ms->l1_meas()->ms_c_value, VTY_NEWLINE);
+ if (ms->l1_meas()->have_ms_sign_var)
+ vty_out(vty, " MS SIGN variance: %d dB%s",
+ ms->l1_meas()->ms_sign_var, VTY_NEWLINE);
+ for (i = 0; i < ARRAY_SIZE(ms->l1_meas()->ts); ++i) {
+ if (ms->l1_meas()->ts[i].have_ms_i_level)
+ vty_out(vty, " MS I level (slot %d): %d dB%s",
+ i, ms->l1_meas()->ts[i].ms_i_level, VTY_NEWLINE);
+ }
if (ms->ul_tbf())
vty_out(vty, " Uplink TBF: TFI=%d, state=%s%s",
ms->ul_tbf()->tfi(),