edge: Replace integer cs by GprsCodingScheme
Currently the TBF and MS object use a plain integer value
(current_cs) to manage the coding scheme. This makes it difficult to
support the MCS schemes. GprsCodingScheme supports a partial ordering
of these values (CS and MCS) and provides safe increment and
decrement methods.
Use the GprsCodingScheme type instead of integer for cs fields and
variables. Add a 'mode' to GprsMs which can be set to either GPRS,
EGPRS, or EGPRS_GMSK which also set the initial values of
current_cs_ul/dl. Select the mode based on max_mcs_ul and max_mcs_dl.
Sponsored-by: On-Waves ehf
diff --git a/src/encoding.cpp b/src/encoding.cpp
index 158625e..acd8f43 100644
--- a/src/encoding.cpp
+++ b/src/encoding.cpp
@@ -193,7 +193,7 @@
if (!use_egprs) {
bitvec_write_field(dest, wp,0x0,1); // Message escape
- bitvec_write_field(dest, wp,tbf->current_cs()-1, 2); // CHANNEL_CODING_COMMAND
+ bitvec_write_field(dest, wp,tbf->current_cs().to_num()-1, 2); // CHANNEL_CODING_COMMAND
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
bitvec_write_field(dest, wp,tbf->ta(),6); // TIMING_ADVANCE_VALUE
@@ -209,7 +209,7 @@
bitvec_write_field(dest, wp,0x0,2); // EGPRS message contents
bitvec_write_field(dest, wp,0x0,1); // No CONTENTION_RESOLUTION_TLLI
bitvec_write_field(dest, wp,0x0,1); // No COMPACT reduced MA
- bitvec_write_field(dest, wp,tbf->current_cs()-1, 4); // EGPRS Modulation and Coding IE
+ bitvec_write_field(dest, wp,tbf->current_cs().to_num()-1, 4); // EGPRS Modulation and Coding IE
bitvec_write_field(dest, wp,0x0,1); // No RESEGMENT
bitvec_write_field(dest, wp,0x0,5); // EGPRS Window Size = 64
bitvec_write_field(dest, wp,0x0,1); // No Access Technologies Request
@@ -426,7 +426,7 @@
struct gprs_rlcmac_ul_tbf *tbf, bool is_final)
{
- bitvec_write_field(dest, wp, tbf->current_cs() - 1, 2); // CHANNEL_CODING_COMMAND
+ bitvec_write_field(dest, wp, tbf->current_cs().to_num() - 1, 2); // CHANNEL_CODING_COMMAND
write_packet_ack_nack_desc_gprs(bts, dest, wp, &tbf->m_window, is_final);
bitvec_write_field(dest, wp, 1, 1); // 1: have CONTENTION_RESOLUTION_TLLI
diff --git a/src/gprs_ms.cpp b/src/gprs_ms.cpp
index 2d48284..187132f 100644
--- a/src/gprs_ms.cpp
+++ b/src/gprs_ms.cpp
@@ -97,8 +97,6 @@
m_ta(0),
m_ms_class(0),
m_egprs_ms_class(0),
- m_current_cs_ul(1),
- m_current_cs_dl(1),
m_is_idle(true),
m_ref(0),
m_list(this),
@@ -107,7 +105,8 @@
m_reserved_dl_slots(0),
m_reserved_ul_slots(0),
m_current_trx(NULL),
- m_codel_state(NULL)
+ m_codel_state(NULL),
+ m_mode(GprsCodingScheme::GPRS)
{
int codel_interval = LLC_CODEL_USE_DEFAULT;
@@ -117,17 +116,11 @@
memset(&m_timer, 0, sizeof(m_timer));
m_timer.cb = GprsMs::timeout;
m_llc_queue.init();
- if (m_bts) {
- m_current_cs_ul = m_bts->bts_data()->initial_cs_ul;
- if (m_current_cs_ul < 1)
- m_current_cs_ul = 1;
- m_current_cs_dl = m_bts->bts_data()->initial_cs_dl;
- if (m_current_cs_dl < 1)
- m_current_cs_dl = 1;
+ set_mode(m_mode);
+ if (m_bts)
codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
- }
if (codel_interval) {
if (codel_interval == LLC_CODEL_USE_DEFAULT)
@@ -215,6 +208,42 @@
unref();
}
+void GprsMs::set_mode(GprsCodingScheme::Mode mode)
+{
+ m_mode = mode;
+
+ switch (m_mode) {
+ case GprsCodingScheme::GPRS:
+ if (m_bts) {
+ m_current_cs_ul = GprsCodingScheme::getGprsByNum(
+ m_bts->bts_data()->initial_cs_ul);
+ m_current_cs_dl = GprsCodingScheme::getGprsByNum(
+ m_bts->bts_data()->initial_cs_dl);
+ }
+ if (!m_current_cs_ul.isGprs())
+ m_current_cs_ul = GprsCodingScheme::CS1;
+ if (!m_current_cs_dl.isGprs())
+ m_current_cs_dl = GprsCodingScheme::CS1;
+
+ break;
+
+ case GprsCodingScheme::EGPRS_GMSK:
+ case GprsCodingScheme::EGPRS:
+ if (m_bts) {
+ m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
+ m_bts->bts_data()->initial_mcs_ul);
+ m_current_cs_dl = GprsCodingScheme::getEgprsByNum(
+ m_bts->bts_data()->initial_mcs_dl);
+ }
+ if (!m_current_cs_ul.isEgprs())
+ m_current_cs_ul = GprsCodingScheme::MCS1;
+ if (!m_current_cs_dl.isEgprs())
+ m_current_cs_dl = GprsCodingScheme::MCS1;
+
+ break;
+ }
+}
+
void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
{
if (tbf->direction == GPRS_RLCMAC_DL_TBF)
@@ -464,9 +493,9 @@
{
struct gprs_rlcmac_bts *bts_data;
int64_t now;
- uint8_t max_cs_dl = 4;
+ GprsCodingScheme max_cs_dl = this->max_cs_dl();
- OSMO_ASSERT(m_bts != NULL);
+ OSMO_ASSERT(max_cs_dl);
bts_data = m_bts->bts_data();
if (error_rate < 0)
@@ -474,32 +503,30 @@
now = now_msec();
- if (bts_data->max_cs_dl)
- max_cs_dl = bts_data->max_cs_dl;
-
/* TODO: Check for TBF direction */
/* TODO: Support different CS values for UL and DL */
m_nack_rate_dl = error_rate;
if (error_rate > bts_data->cs_adj_upper_limit) {
- if (m_current_cs_dl > 1) {
- m_current_cs_dl -= 1;
+ if (m_current_cs_dl.to_num() > 1) {
+ m_current_cs_dl.dec(mode());
LOGP(DRLCMACDL, LOGL_INFO,
"MS (IMSI %s): High error rate %d%%, "
- "reducing CS level to %d\n",
- imsi(), error_rate, m_current_cs_dl);
+ "reducing CS level to %s\n",
+ imsi(), error_rate, m_current_cs_dl.name());
m_last_cs_not_low = now;
}
} else if (error_rate < bts_data->cs_adj_lower_limit) {
if (m_current_cs_dl < max_cs_dl) {
if (now - m_last_cs_not_low > 1000) {
- m_current_cs_dl += 1;
+ m_current_cs_dl.inc(mode());
LOGP(DRLCMACDL, LOGL_INFO,
"MS (IMSI %s): Low error rate %d%%, "
- "increasing DL CS level to %d\n",
- imsi(), error_rate, m_current_cs_dl);
+ "increasing DL CS level to %s\n",
+ imsi(), error_rate,
+ m_current_cs_dl.name());
m_last_cs_not_low = now;
} else {
LOGP(DRLCMACDL, LOGL_DEBUG,
@@ -516,46 +543,125 @@
}
}
-void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
+GprsCodingScheme GprsMs::max_cs_ul() const
{
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();
- if (bts_data->max_cs_ul)
- max_cs_ul = bts_data->max_cs_ul;
+ if (m_current_cs_ul.isGprs()) {
+ if (!bts_data->max_cs_ul)
+ return GprsCodingScheme(GprsCodingScheme::CS4);
- if (meas->have_link_qual) {
- int old_link_qual = meas->link_qual;
- int low = bts_data->cs_lqual_ranges[current_cs_ul()-1].low;
- int high = bts_data->cs_lqual_ranges[current_cs_ul()-1].high;
- uint8_t new_cs_ul = m_current_cs_ul;
-
- if (m_l1_meas.have_link_qual)
- old_link_qual = m_l1_meas.link_qual;
-
- if (meas->link_qual < low && old_link_qual < low)
- new_cs_ul = m_current_cs_ul - 1;
- else if (meas->link_qual > high && old_link_qual > high &&
- m_current_cs_ul < max_cs_ul)
- new_cs_ul = m_current_cs_ul + 1;
-
- if (m_current_cs_ul != new_cs_ul) {
- LOGP(DRLCMACDL, LOGL_INFO,
- "MS (IMSI %s): "
- "Link quality %ddB (%ddB) left window [%d, %d], "
- "modifying uplink CS level: %d -> %d\n",
- imsi(), meas->link_qual, old_link_qual,
- low, high,
- m_current_cs_ul, new_cs_ul);
-
- m_current_cs_ul = new_cs_ul;
- }
+ return GprsCodingScheme::getGprsByNum(bts_data->max_cs_ul);
}
+ if (!m_current_cs_ul.isEgprs())
+ return GprsCodingScheme(); /* UNKNOWN */
+
+ if (bts_data->max_mcs_ul)
+ return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_ul);
+ else if (bts_data->max_cs_ul)
+ return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_ul);
+
+ return GprsCodingScheme(GprsCodingScheme::MCS4);
+}
+
+GprsCodingScheme GprsMs::max_cs_dl() const
+{
+ struct gprs_rlcmac_bts *bts_data;
+
+ OSMO_ASSERT(m_bts != NULL);
+ bts_data = m_bts->bts_data();
+
+ if (m_current_cs_dl.isGprs()) {
+ if (!bts_data->max_cs_dl)
+ return GprsCodingScheme(GprsCodingScheme::CS4);
+
+ return GprsCodingScheme::getGprsByNum(bts_data->max_cs_dl);
+ }
+
+ if (!m_current_cs_dl.isEgprs())
+ return GprsCodingScheme(); /* UNKNOWN */
+
+ if (bts_data->max_mcs_dl)
+ return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_dl);
+ else if (bts_data->max_cs_dl)
+ return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_dl);
+
+ return GprsCodingScheme(GprsCodingScheme::MCS4);
+}
+
+void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
+{
+ struct gprs_rlcmac_bts *bts_data;
+ GprsCodingScheme max_cs_ul = this->max_cs_ul();
+
+ int old_link_qual;
+ int low;
+ int high;
+ GprsCodingScheme new_cs_ul = m_current_cs_ul;
+ unsigned current_cs_num = m_current_cs_ul.to_num();
+
+ bts_data = m_bts->bts_data();
+
+ if (!max_cs_ul) {
+ LOGP(DRLCMACDL, LOGL_ERROR,
+ "max_cs_ul cannot be derived (current UL CS: %s)\n",
+ m_current_cs_ul.name());
+ return;
+ }
+
+ if (!m_current_cs_ul)
+ return;
+
+ if (!meas->have_link_qual)
+ return;
+
+ old_link_qual = meas->link_qual;
+
+ if (m_current_cs_ul.isGprs()) {
+ low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
+ high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
+ } else if (m_current_cs_ul.isEgprs()) {
+ /* TODO, use separate table */
+ if (current_cs_num > 4)
+ current_cs_num = 4;
+ low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
+ high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
+ } else {
+ return;
+ }
+
+ if (m_l1_meas.have_link_qual)
+ old_link_qual = m_l1_meas.link_qual;
+
+ if (meas->link_qual < low && old_link_qual < low)
+ new_cs_ul.dec(mode());
+ else if (meas->link_qual > high && old_link_qual > high &&
+ m_current_cs_ul < max_cs_ul)
+ new_cs_ul.inc(mode());
+
+ if (m_current_cs_ul != new_cs_ul) {
+ LOGP(DRLCMACDL, LOGL_INFO,
+ "MS (IMSI %s): "
+ "Link quality %ddB (%ddB) left window [%d, %d], "
+ "modifying uplink CS level: %s -> %s\n",
+ imsi(), meas->link_qual, old_link_qual,
+ low, high,
+ m_current_cs_ul.name(), new_cs_ul.name());
+
+ m_current_cs_ul = new_cs_ul;
+ }
+}
+
+void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
+{
+ unsigned i;
+
+ update_cs_ul(meas);
+
if (meas->have_rssi)
m_l1_meas.set_rssi(meas->rssi);
if (meas->have_bto)
@@ -582,9 +688,9 @@
}
}
-uint8_t GprsMs::current_cs_dl() const
+GprsCodingScheme GprsMs::current_cs_dl() const
{
- uint8_t cs = m_current_cs_dl;
+ GprsCodingScheme cs = m_current_cs_dl;
size_t unencoded_octets;
if (!m_bts)
@@ -605,11 +711,11 @@
return cs;
/* The throughput would probably be better if the CS level was reduced */
- cs -= 1;
+ cs.dec(mode());
/* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
- if (cs == 2)
- cs -= 1;
+ if (cs == GprsCodingScheme(GprsCodingScheme::CS2))
+ cs.dec(mode());
return cs;
}
diff --git a/src/gprs_ms.h b/src/gprs_ms.h
index f9b63f2..246f71e 100644
--- a/src/gprs_ms.h
+++ b/src/gprs_ms.h
@@ -74,6 +74,8 @@
bool check_tlli(uint32_t tlli);
void reset();
+ GprsCodingScheme::Mode mode() const;
+ void set_mode(GprsCodingScheme::Mode mode);
const char *imsi() const;
void set_imsi(const char *imsi);
@@ -85,8 +87,10 @@
void set_ms_class(uint8_t ms_class);
void set_egprs_ms_class(uint8_t ms_class);
- uint8_t current_cs_ul() const;
- uint8_t current_cs_dl() const;
+ GprsCodingScheme current_cs_ul() const;
+ GprsCodingScheme current_cs_dl() const;
+ GprsCodingScheme max_cs_ul() const;
+ GprsCodingScheme max_cs_dl() const;
int first_common_ts() const;
uint8_t dl_slots() const;
@@ -134,6 +138,7 @@
void unref();
void start_timer();
void stop_timer();
+ void update_cs_ul(const pcu_l1_meas*);
private:
BTS *m_bts;
@@ -152,8 +157,8 @@
uint8_t m_ms_class;
uint8_t m_egprs_ms_class;
/* current coding scheme */
- uint8_t m_current_cs_ul;
- uint8_t m_current_cs_dl;
+ GprsCodingScheme m_current_cs_ul;
+ GprsCodingScheme m_current_cs_dl;
gprs_llc_queue m_llc_queue;
@@ -172,6 +177,7 @@
gprs_rlcmac_trx *m_current_trx;
struct gprs_codel *m_codel_state;
+ GprsCodingScheme::Mode m_mode;
};
inline bool GprsMs::is_idle() const
@@ -220,11 +226,16 @@
return m_egprs_ms_class;
}
-inline uint8_t GprsMs::current_cs_ul() const
+inline GprsCodingScheme GprsMs::current_cs_ul() const
{
return m_current_cs_ul;
}
+inline GprsCodingScheme::Mode GprsMs::mode() const
+{
+ return m_mode;
+}
+
inline void GprsMs::set_timeout(unsigned secs)
{
m_delay = secs;
diff --git a/src/pcu_vty_functions.cpp b/src/pcu_vty_functions.cpp
index 7082d99..c059233 100644
--- a/src/pcu_vty_functions.cpp
+++ b/src/pcu_vty_functions.cpp
@@ -55,7 +55,8 @@
if (tbf->pdch[i])
vty_out(vty, "%d ", i);
}
- vty_out(vty, " CS=%d%s%s", tbf->ms() ? tbf->ms()->current_cs_dl() : 1,
+ vty_out(vty, " CS=%s%s%s",
+ tbf->ms() ? tbf->ms()->current_cs_dl().name() : "???",
VTY_NEWLINE, VTY_NEWLINE);
}
@@ -83,10 +84,11 @@
llist_for_each(ms_iter, &bts->ms_store().ms_list()) {
GprsMs *ms = ms_iter->entry();
- vty_out(vty, "MS TLLI=%08x, TA=%d, CS-UL=%d, CS-DL=%d, LLC=%d, "
+ vty_out(vty, "MS TLLI=%08x, TA=%d, CS-UL=%s, CS-DL=%s, LLC=%d, "
"IMSI=%s%s",
ms->tlli(),
- ms->ta(), ms->current_cs_ul(), ms->current_cs_dl(),
+ ms->ta(), ms->current_cs_ul().name(),
+ ms->current_cs_dl().name(),
ms->llc_queue()->size(),
ms->imsi(),
VTY_NEWLINE);
@@ -101,9 +103,9 @@
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(),
+ vty_out(vty, " Coding scheme uplink: %s%s", ms->current_cs_ul().name(),
VTY_NEWLINE);
- vty_out(vty, " Coding scheme downlink: CS-%d%s", ms->current_cs_dl(),
+ vty_out(vty, " Coding scheme downlink: %s%s", ms->current_cs_dl().name(),
VTY_NEWLINE);
vty_out(vty, " MS class: %d%s", ms->ms_class(), VTY_NEWLINE);
vty_out(vty, " EGPRS MS class: %d%s", ms->egprs_ms_class(), VTY_NEWLINE);
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 185f0bd..0da0557 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -165,15 +165,15 @@
m_ms_class = ms_class_;
}
-uint8_t gprs_rlcmac_tbf::current_cs() const
+GprsCodingScheme gprs_rlcmac_tbf::current_cs() const
{
- uint8_t cs;
+ GprsCodingScheme cs;
if (direction == GPRS_RLCMAC_UL_TBF)
- cs = m_ms ? m_ms->current_cs_ul() : bts->bts_data()->initial_cs_ul;
+ cs = m_ms ? m_ms->current_cs_ul() : GprsCodingScheme();
else
- cs = m_ms ? m_ms->current_cs_dl() : bts->bts_data()->initial_cs_dl;
+ cs = m_ms ? m_ms->current_cs_dl() : GprsCodingScheme();
- return cs < 1 ? 1 : cs;
+ return cs;
}
gprs_llc_queue *gprs_rlcmac_tbf::llc_queue()
@@ -543,10 +543,8 @@
bts = tbf->bts->bts_data();
- if (tbf->is_egprs_enabled()) {
- /* TODO: only for 8PSK, otherwise the GPRS MS class has to be used */
+ if (ms->mode() == GprsCodingScheme::EGPRS)
ms_class = egprs_ms_class;
- }
tbf->m_created_ts = time(NULL);
tbf->set_ms_class(ms_class);
@@ -592,6 +590,17 @@
return 0;
}
+static void setup_egprs_mode(gprs_rlcmac_bts *bts, GprsMs *ms)
+{
+ if (GprsCodingScheme::getEgprsByNum(bts->max_mcs_ul).isEgprsGmsk() &&
+ GprsCodingScheme::getEgprsByNum(bts->max_mcs_dl).isEgprsGmsk() &&
+ ms->mode() != GprsCodingScheme::EGPRS)
+ {
+ ms->set_mode(GprsCodingScheme::EGPRS_GMSK);
+ } else {
+ ms->set_mode(GprsCodingScheme::EGPRS);
+ }
+}
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
GprsMs *ms, int8_t use_trx,
@@ -616,11 +625,10 @@
ms = bts->bts->ms_alloc(ms_class, egprs_ms_class);
if (egprs_ms_class > 0 && bts->egprs_enabled) {
- /* TODO: only for 8PSK, otherwise the GPRS MS class has to be used */
- ms_class = egprs_ms_class;
tbf->enable_egprs();
tbf->m_window.set_sns(RLC_EGPRS_SNS);
tbf->m_window.set_ws(RLC_EGPRS_MIN_WS);
+ setup_egprs_mode(bts, ms);
}
rc = setup_tbf(tbf, ms, use_trx, ms_class, egprs_ms_class, single_slot);
diff --git a/src/tbf.h b/src/tbf.h
index 8ba6575..b00f4d5 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -134,7 +134,7 @@
void set_ta(uint8_t);
uint8_t ms_class() const;
void set_ms_class(uint8_t);
- uint8_t current_cs() const;
+ GprsCodingScheme current_cs() const;
gprs_llc_queue *llc_queue();
const gprs_llc_queue *llc_queue() const;
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index e805992..9e73a0e 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -425,21 +425,15 @@
uint16_t space, chunk;
gprs_rlc_data *rlc_data;
const uint16_t bsn = m_window.v_s();
- uint8_t cs_n = 1;
+ GprsCodingScheme cs = current_cs();
if (m_llc.frame_length() == 0)
schedule_next_frame();
- cs_n = current_cs();
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d, CS=%s\n",
+ m_window.v_s(), cs.name());
- LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d, CS=%d\n",
- m_window.v_s(), cs_n);
-
- OSMO_ASSERT(cs_n >= 1);
- OSMO_ASSERT(cs_n <= 4);
-
- /* TODO: Use GprsCodingScheme everywhere and remove cast */
- GprsCodingScheme cs((GprsCodingScheme::Scheme)cs_n);
+ OSMO_ASSERT(cs.isValid());
/* total length of block, including spare bits */
const uint8_t block_length = cs.sizeDL();
@@ -748,8 +742,7 @@
/* Get statistics for current CS */
- /* TODO: Use GprsCodingScheme everywhere and remove cast */
- if (rlc_data->cs != GprsCodingScheme((GprsCodingScheme::Scheme)current_cs())) {
+ 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. */