aboutsummaryrefslogtreecommitdiffstats
path: root/src/common/measurement.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/measurement.c')
-rw-r--r--src/common/measurement.c120
1 files changed, 104 insertions, 16 deletions
diff --git a/src/common/measurement.c b/src/common/measurement.c
index b5869872..87e853ef 100644
--- a/src/common/measurement.c
+++ b/src/common/measurement.c
@@ -10,6 +10,7 @@
#include <osmo-bts/measurement.h>
#include <osmo-bts/scheduler.h>
#include <osmo-bts/rsl.h>
+#include <osmo-bts/power_control.h>
#include <osmo-bts/ta_control.h>
/* Tables as per TS 45.008 Section 8.3 */
@@ -342,7 +343,7 @@ int lchan_new_ul_meas(struct gsm_lchan *lchan,
if (!ulm->is_sub)
dest->is_sub = ts45008_83_is_sub(lchan, fn);
- DEBUGPFN(DMEAS, fn, "%s adding measurement (ber10k=%u, ta_offs=%d, ci=%0.2f, is_sub=%u, rssi=-%u), num_ul_meas=%d, fn_mod=%u\n",
+ DEBUGPFN(DMEAS, fn, "%s adding measurement (ber10k=%u, ta_offs=%d, ci_cB=%d, is_sub=%u, rssi=-%u), num_ul_meas=%d, fn_mod=%u\n",
gsm_lchan_name(lchan), ulm->ber10k, ulm->ta_offs_256bits,
ulm->c_i, dest->is_sub, ulm->inv_rssi, lchan->meas.num_ul_meas,
fn_mod);
@@ -556,8 +557,10 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
struct gsm_meas_rep_unidir *mru;
uint32_t ber_full_sum = 0;
uint32_t irssi_full_sum = 0;
+ int32_t ci_full_sum = 0;
uint32_t ber_sub_sum = 0;
uint32_t irssi_sub_sum = 0;
+ int32_t ci_sub_sum = 0;
int32_t ta256b_sum = 0;
unsigned int num_meas_sub = 0;
unsigned int num_meas_sub_actual = 0;
@@ -589,7 +592,7 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
/* When AMR is used, we expect at least one SUB frame, since
* the SACCH will always be SUB frame. There may occur more
* SUB frames but since DTX periods in AMR are dynamic, we
- * can not know how much exactly. */
+ * can not know how many exactly. */
num_meas_sub_expect = 1;
}
@@ -625,11 +628,13 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
m = &lchan->meas.uplink[i + num_ul_meas_excess];
if (m->is_sub) {
irssi_sub_sum += m->inv_rssi;
+ ci_sub_sum += m->c_i;
num_meas_sub_actual++;
is_sub = true;
}
irssi_full_sum += m->inv_rssi;
ta256b_sum += m->ta_offs_256bits;
+ ci_full_sum += m->c_i;
num_ul_meas_actual++;
} else {
@@ -698,27 +703,32 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
else
irssi_full_sum = irssi_full_sum / num_ul_meas_actual;
- if (!num_ul_meas_actual)
+ if (!num_ul_meas_actual) {
ta256b_sum = lchan->meas.ms_toa256;
- else
+ ci_full_sum = lchan->meas.ul_ci_cb_full;
+ } else {
ta256b_sum = ta256b_sum / (signed)num_ul_meas_actual;
+ ci_full_sum = ci_full_sum / (signed)num_ul_meas_actual;
+ }
if (!num_meas_sub)
ber_sub_sum = MEASUREMENT_DUMMY_BER;
else
ber_sub_sum = ber_sub_sum / num_meas_sub;
- if (!num_meas_sub_actual)
+ if (!num_meas_sub_actual) {
irssi_sub_sum = MEASUREMENT_DUMMY_IRSSI;
- else
+ ci_sub_sum = lchan->meas.ul_ci_cb_sub;
+ } else {
irssi_sub_sum = irssi_sub_sum / num_meas_sub_actual;
+ ci_sub_sum = ci_sub_sum / (signed)num_meas_sub_actual;
+ }
LOGPLCHAN(lchan, DMEAS, LOGL_INFO,
- "Computed TA256(% 4d) BER-FULL(%2u.%02u%%), RSSI-FULL(-%3udBm), "
- "BER-SUB(%2u.%02u%%), RSSI-SUB(-%3udBm)\n",
- ta256b_sum, ber_full_sum / 100, ber_full_sum % 100,
- irssi_full_sum, ber_sub_sum / 100, ber_sub_sum % 100,
- irssi_sub_sum);
+ "Computed TA256(% 4d), BER-FULL(%2u.%02u%%), RSSI-FULL(-%3udBm), C/I-FULL(% 4d cB), "
+ "BER-SUB(%2u.%02u%%), RSSI-SUB(-%3udBm), C/I-SUB(% 4d cB)\n",
+ ta256b_sum, ber_full_sum / 100, ber_full_sum % 100, irssi_full_sum, ci_full_sum,
+ ber_sub_sum / 100, ber_sub_sum % 100, irssi_sub_sum, ci_sub_sum);
/* store results */
mru = &lchan->meas.ul_res;
@@ -727,6 +737,8 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
mru->full.rx_qual = ber10k_to_rxqual(ber_full_sum);
mru->sub.rx_qual = ber10k_to_rxqual(ber_sub_sum);
lchan->meas.ms_toa256 = ta256b_sum;
+ lchan->meas.ul_ci_cb_full = ci_full_sum;
+ lchan->meas.ul_ci_cb_sub = ci_sub_sum;
LOGPLCHAN(lchan, DMEAS, LOGL_INFO,
"UL MEAS RXLEV_FULL(%u), RXLEV_SUB(%u), RXQUAL_FULL(%u), RXQUAL_SUB(%u), "
@@ -739,11 +751,6 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
lchan_meas_compute_extended(lchan);
- /* Compute new ta_req value. This has to be done here since the value
- * in lchan->meas.num_ul_meas together with lchan->meas.ms_toa256
- * is needed for the computation. */
- lchan_ms_ta_ctrl(lchan);
-
lchan->meas.num_ul_meas = 0;
/* return 1 to indicate that the computation has been done and the next
@@ -771,3 +778,84 @@ void lchan_meas_reset(struct gsm_lchan *lchan)
memset(&lchan->meas, 0, sizeof(lchan->meas));
lchan->meas.last_fn = LCHAN_FN_DUMMY;
}
+
+static inline uint8_t ms_to2rsl(const struct gsm_lchan *lchan, const struct lapdm_entity *le)
+{
+ return (lchan->ms_t_offs >= 0) ? lchan->ms_t_offs : (lchan->p_offs - le->ta);
+}
+
+static inline bool ms_to_valid(const struct gsm_lchan *lchan)
+{
+ return (lchan->ms_t_offs >= 0) || (lchan->p_offs >= 0);
+}
+
+/* Called every time a Measurement Result (TS 08.58 8.4.8) is received from
+ * lower layers and has to be forwarded to BSC */
+int handle_ms_meas_report(struct gsm_lchan *lchan, struct gsm48_hdr *gh, unsigned int len)
+{
+ int timing_offset, rc;
+ struct lapdm_entity *le;
+ bool dtxu_used;
+ uint8_t ms_pwr;
+ uint8_t ms_ta;
+ int8_t ul_rssi;
+ int16_t ul_ci_cb;
+
+ le = &lchan->lapdm_ch.lapdm_acch;
+
+ timing_offset = ms_to_valid(lchan) ? ms_to2rsl(lchan, le) : -1;
+ rc = rsl_tx_meas_res(lchan, (uint8_t *)gh, len, timing_offset);
+ if (rc == 0) /* Count successful transmissions */
+ lchan->meas.res_nr++;
+
+ /* Run control loops now that we have all the information: */
+ /* 3GPP TS 45.008 sec 4.2: UL L1 SACCH Header contains TA and
+ * MS_PWR used "for the last burst of the previous SACCH
+ * period". Since MS must use the values provided in DL SACCH
+ * starting at next meas period, the value of the "last burst"
+ * is actually the value used in the entire meas period. Since
+ * it contains info about the previous meas period, we want to
+ * feed the Control Loop with the measurements for the same
+ * period (the previous one), which is stored in lchan->meas(.ul_res):
+ */
+ if (len == 0) {
+ dtxu_used = true;
+ ms_ta = lchan->ta_ctrl.current;
+ ms_pwr = lchan->ms_power_ctrl.current;
+ } else {
+ /* if len!=0, it means we were able to parse L1Header in UL SACCH: */
+ OSMO_ASSERT(lchan->meas.flags | LC_UL_M_F_L1_VALID);
+
+ ms_ta = lchan->meas.l1_info.ta;
+ ms_pwr = lchan->meas.l1_info.ms_pwr;
+ switch (gh->msg_type) {
+ case GSM48_MT_RR_MEAS_REP:
+ dtxu_used = (len > sizeof(*gh) + 1) && !!(gh->data[0] & 0x40);
+ break;
+ case GSM48_MT_RR_EXT_MEAS_REP:
+ default:
+ dtxu_used = true; /* FIXME: not implemented */
+ break;
+ }
+ }
+
+ if (dtxu_used) {
+ ul_rssi = rxlev2dbm(lchan->meas.ul_res.sub.rx_lev);
+ ul_ci_cb = lchan->meas.ul_ci_cb_sub;
+ } else {
+ ul_rssi = rxlev2dbm(lchan->meas.ul_res.full.rx_lev);
+ ul_ci_cb = lchan->meas.ul_ci_cb_full;
+ }
+ lchan_ms_ta_ctrl(lchan, ms_ta, lchan->meas.ms_toa256);
+ lchan_ms_pwr_ctrl(lchan, ms_pwr, ul_rssi, ul_ci_cb);
+ if (gh)
+ lchan_bs_pwr_ctrl(lchan, (const struct gsm48_hdr *) gh);
+
+ /* Reset state for next iteration */
+ lchan->tch.dtx.dl_active = false;
+ lchan->meas.flags &= ~LC_UL_M_F_OSMO_EXT_VALID;
+ lchan->meas.flags &= ~LC_UL_M_F_L1_VALID;
+ lchan->ms_t_offs = -1;
+ lchan->p_offs = -1;
+ return rc;
+}