diff --git a/src/bts.h b/src/bts.h index af66dfc8..b31db5fc 100644 --- a/src/bts.h +++ b/src/bts.h @@ -142,6 +142,9 @@ struct gprs_rlcmac_bts { uint8_t alpha, gamma; uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */ uint32_t ms_idle_sec; + uint8_t cs_adj_enabled; + uint8_t cs_adj_upper_limit; + uint8_t cs_adj_lower_limit; /* TBF handling, make private or move into TBFController */ /* list of uplink TBFs */ diff --git a/src/gprs_ms.cpp b/src/gprs_ms.cpp index dc8783c4..f5924fd8 100644 --- a/src/gprs_ms.cpp +++ b/src/gprs_ms.cpp @@ -351,3 +351,39 @@ void GprsMs::set_ms_class(uint8_t ms_class_) m_ms_class = ms_class_; } +void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate) +{ + struct gprs_rlcmac_bts *bts_data; + + OSMO_ASSERT(m_bts != NULL); + bts_data = m_bts->bts_data(); + + if (error_rate < 0) + return; + + /* TODO: Support different CS values for UL and DL */ + + if (error_rate > bts_data->cs_adj_upper_limit) { + if (m_current_cs_dl > 1) { + m_current_cs_dl -= 1; + m_current_cs_ul = m_current_cs_dl; + LOGP(DRLCMACDL, LOGL_INFO, + "MS (IMSI %s): High error rate %d%%, " + "reducing CS level to %d\n", + imsi(), error_rate, m_current_cs_dl); + } + } else if (error_rate < bts_data->cs_adj_lower_limit) { + if (m_current_cs_dl < 4) { + m_current_cs_dl += 1; + m_current_cs_ul = m_current_cs_dl; + LOGP(DRLCMACDL, LOGL_INFO, + "MS (IMSI %s): Low error rate %d%%, " + "increasing CS level to %d\n", + imsi(), error_rate, m_current_cs_dl); + } + } else { + LOGP(DRLCMACDL, LOGL_DEBUG, + "MS (IMSI %s): Medium error rate %d%%, ignored\n", + imsi(), error_rate); + } +} diff --git a/src/gprs_ms.h b/src/gprs_ms.h index 01858366..27c4be40 100644 --- a/src/gprs_ms.h +++ b/src/gprs_ms.h @@ -86,6 +86,8 @@ public: void detach_tbf(gprs_rlcmac_tbf *tbf); + void update_error_rate(gprs_rlcmac_tbf *tbf, int percent); + bool is_idle() const {return !m_ul_tbf && !m_dl_tbf && !m_ref;} void* operator new(size_t num); diff --git a/src/pcu_main.cpp b/src/pcu_main.cpp index 9e96af08..2e4a970f 100644 --- a/src/pcu_main.cpp +++ b/src/pcu_main.cpp @@ -173,6 +173,9 @@ int main(int argc, char *argv[]) bts->n3105 = 8; bts->alpha = 0; /* a = 0.0 */ bts->ms_idle_sec = 60; /* slightly above T3314 (default 44s, 24.008, 11.2.2) */ + bts->cs_adj_enabled = 1; + bts->cs_adj_upper_limit = 33; /* Decrease CS if the error rate is above */ + bts->cs_adj_lower_limit = 10; /* Increase CS if the error rate is below */ msgb_set_talloc_ctx(tall_pcu_ctx); diff --git a/src/rlc.h b/src/rlc.h index 55cae3ff..313f3c72 100644 --- a/src/rlc.h +++ b/src/rlc.h @@ -59,6 +59,8 @@ struct gprs_rlc_data { uint8_t block[RLC_MAX_LEN]; /* block len of history */ uint8_t len; + + uint8_t cs; }; /* diff --git a/src/tbf.h b/src/tbf.h index f50c489a..8e4c3b98 100644 --- a/src/tbf.h +++ b/src/tbf.h @@ -363,6 +363,7 @@ protected: bool dl_window_stalled() const; void reuse_tbf(const uint8_t *data, const uint16_t len); void start_llc_timer(); + int analyse_errors(char *show_rbb, uint8_t ssn); struct osmo_timer_list m_llc_timer; }; diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp index 0aa41a26..0726ef79 100644 --- a/src/tbf_dl.cpp +++ b/src/tbf_dl.cpp @@ -412,6 +412,7 @@ struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t /* now we still have untransmitted LLC data, so we fill mac block */ rlc_data = m_rlc.block(bsn); data = rlc_data->prepare(block_data_len); + rlc_data->cs = cs; rh = (struct rlc_dl_header *)data; rh->pt = 0; /* Data Block */ @@ -669,6 +670,53 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block( return dl_msg; } +static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn, uint16_t mod_sns) +{ + return (ssn - 1 - bitnum) & mod_sns; +} + +int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn) +{ + gprs_rlc_data *rlc_data; + uint16_t lost = 0, received = 0, skipped = 0; + + /* SSN - 1 is in range V(A)..V(S)-1 */ + for (int bitpos = 0; bitpos < m_window.ws(); bitpos++) { + uint16_t bsn = bitnum_to_bsn(bitpos, ssn, m_window.mod_sns()); + + if (bsn == ((m_window.v_a() - 1) & m_window.mod_sns())) + break; + + rlc_data = m_rlc.block(bsn); + if (!rlc_data) + continue; + + if (rlc_data->cs != current_cs()) { + /* This block has already been encoded with a different + * CS, so it doesn't help us to decide, whether the + * current CS is ok. Ignore it. */ + skipped += 1; + continue; + } + + if (show_rbb[m_window.ws() - 1 - bitpos] == 'R') { + if (!m_window.m_v_b.is_acked(bsn)) + received += 1; + } else { + lost += 1; + } + } + + LOGP(DRLCMACDL, LOGL_DEBUG, "%s DL analysis, range=%d:%d, lost=%d, recv=%d, skipped=%d\n", + name(), m_window.v_a(), m_window.v_s(), lost, received, skipped); + + if (lost + received == 0) + return -1; + + return lost * 100 / (lost + received); +} + + int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb) { int16_t dist; /* must be signed */ @@ -676,6 +724,7 @@ int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb) char show_rbb[65]; char show_v_b[RLC_MAX_SNS + 1]; const uint16_t mod_sns = m_window.mod_sns(); + int error_rate; Decoding::extract_rbb(rbb, show_rbb); /* show received array in debug (bit 64..1) */ @@ -698,6 +747,11 @@ int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb) return 1; /* indicate to free TBF */ } + if (bts_data()->cs_adj_enabled && ms()) { + error_rate = analyse_errors(show_rbb, ssn); + ms()->update_error_rate(this, error_rate); + } + m_window.update(bts, show_rbb, ssn, &lost, &received);