ms: Reduce DL CS level if only a few LLC bytes are left

If just a few bytes are left to send to the MS, it makes sense to
reduce the coding scheme level to increase the throughput. This
has been shown by Chen and Goodman in their paper "Theoretical
Analysis of GPRS Throughput and Delay". See their throughput over C/I
measurement graphs (figures 4 and 5 in the paper) for details.

This commit implements a simplified CS downgrade feature for the
downlink. The coding scheme will be downgraded if there are only a
few octets are left to be send over the TBF (see the
downgrade-threshold command below) and the NACK rate is not low (the
CS will not get degraded on a high quality RF link). As an exception,
CS-3 will be degraded to CS-1, since CS-2 does not improve the
throughput in general when a few small packets are sent and the
signal fades slowly (see Chen/Goodman).

The following VTY command is added to the config-pcu node:

- cs downgrade-threshold <1-10000>
- cs no downgrade-threshold

to set the threshold of the number of remaining bytes to be RLC/MAC
encoded. The CS will only be reduced, if the number is below the
threshold. The 'no' command disables this feature completely. The
default value is 200 octets.

Sponsored-by: On-Waves ehf
diff --git a/tests/ms/MsTest.cpp b/tests/ms/MsTest.cpp
index 403de43..f2faefc 100644
--- a/tests/ms/MsTest.cpp
+++ b/tests/ms/MsTest.cpp
@@ -24,6 +24,7 @@
 #include "gprs_debug.h"
 #include "gprs_ms.h"
 #include "gprs_ms_storage.h"
+#include "bts.h"
 
 extern "C" {
 #include "pcu_vty.h"
@@ -468,6 +469,42 @@
 	printf("=== end %s ===\n", __func__);
 }
 
+static void test_ms_cs_selection()
+{
+	BTS the_bts;
+	gprs_rlcmac_bts *bts = the_bts.bts_data();
+	uint32_t tlli = 0xffeeddbb;
+
+	gprs_rlcmac_dl_tbf *dl_tbf;
+	GprsMs *ms;
+
+	printf("=== start %s ===\n", __func__);
+
+	bts->initial_cs_dl = 4;
+	bts->initial_cs_ul = 1;
+	bts->cs_downgrade_threshold = 0;
+
+	ms = new GprsMs(&the_bts, tlli);
+
+	OSMO_ASSERT(ms->is_idle());
+
+	dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
+	dl_tbf->direction = GPRS_RLCMAC_DL_TBF;
+
+	dl_tbf->set_ms(ms);
+	OSMO_ASSERT(!ms->is_idle());
+
+	OSMO_ASSERT(ms->current_cs_dl() == 4);
+
+	bts->cs_downgrade_threshold = 200;
+
+	OSMO_ASSERT(ms->current_cs_dl() == 3);
+
+	talloc_free(dl_tbf);
+
+	printf("=== end %s ===\n", __func__);
+}
+
 static const struct log_info_cat default_categories[] = {
 	{"DPCU", "", "GPRS Packet Control Unit (PCU)", LOGL_INFO, 1},
 };
@@ -507,6 +544,7 @@
 	test_ms_change_tlli();
 	test_ms_storage();
 	test_ms_timeout();
+	test_ms_cs_selection();
 
 	if (getenv("TALLOC_REPORT_FULL"))
 		talloc_report_full(tall_pcu_ctx, stderr);
diff --git a/tests/ms/MsTest.err b/tests/ms/MsTest.err
index 9e9df55..ed53f48 100644
--- a/tests/ms/MsTest.err
+++ b/tests/ms/MsTest.err
@@ -54,3 +54,5 @@
 Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
 Timeout for MS object, TLLI = 0xffeeddbb
 Destroying MS object, TLLI = 0xffeeddbb
+Creating MS object, TLLI = 0xffeeddbb
+Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0xffeeddbb DIR=DL STATE=NULL)
diff --git a/tests/ms/MsTest.ok b/tests/ms/MsTest.ok
index c49e840..f14cceb 100644
--- a/tests/ms/MsTest.ok
+++ b/tests/ms/MsTest.ok
@@ -16,3 +16,5 @@
   ms_active() was called
   ms_idle() was called
 === end test_ms_timeout ===
+=== start test_ms_cs_selection ===
+=== end test_ms_cs_selection ===