BTS: Convert relative frame numbers to absolute frame numbers

The implementation of the method rcv_rach() in class BTS, restores
the absolute frame number of the incoming RACH-Request by using
the relative frame number (RFn = Fn mod 42432) from the rach
request and the already known internal absolute frame number
m_cur_fn, which is continusly updated by the CCU interface.

In some rare cases, a RACH request might be received by the BTS,
a very short time before the frame number wraps in its 42432.
Depending on the PCU location, RACH request might be received
by the BSC, which forwards it to the PCU. It is then likely
that, while the RACH request is being forwarded to the PCU, the
PCU internal absolute frame number wraps before the RACH
can be processed. The relative frame number from the rach
request would then be interpreted as if it were received after
the wrapping of the internal frame number modulos.

This commit adds logic to detect and resolve this race condition.
Also a unit test is added to check some cornercases.

Change-Id: I74f00c11e5739d49f370ce6c357149e81d9aa759
diff --git a/src/bts.cpp b/src/bts.cpp
index 21e9d96..548d000 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -34,6 +34,7 @@
 	#include <osmocom/core/msgb.h>
 	#include <osmocom/core/stats.h>
 	#include <osmocom/gsm/protocol/gsm_04_08.h>
+	#include <osmocom/gsm/gsm_utils.h>
 }
 
 #include <arpa/inet.h>
@@ -41,6 +42,9 @@
 #include <errno.h>
 #include <string.h>
 
+#define RFN_MODULUS 42432
+#define RFN_THRESHOLD RFN_MODULUS / 2
+
 extern void *tall_pcu_ctx;
 
 static BTS s_bts;
@@ -516,6 +520,57 @@
 	return 0;
 }
 
+/* Determine the full frame number from a relative frame number */
+uint32_t BTS::rfn_to_fn(uint32_t rfn)
+{
+	uint32_t m_cur_rfn;
+	uint32_t fn;
+	uint32_t fn_rounded;
+
+	/* Note: If a BTS is sending in a rach request it will be fully aware
+	 * of the frame number. If the PCU is used in a BSC-co-located setup.
+	 * The BSC will forward the incoming RACH request. The RACH request
+	 * only contains the relative frame number (Fn % 42432) in its request
+	 * reference. This PCU implementation has to fit both scenarios, so
+	 * we need to assume that Fn is a relative frame number. */
+
+	/* Ensure that all following calculations are performed with the
+	 * relative frame number */
+	rfn = rfn % 42432;
+
+	/* Compute an internal relative frame number from the full internal
+	   frame number */
+	m_cur_rfn = m_cur_fn % RFN_MODULUS;
+
+	/* Compute a "rounded" version of the internal frame number, which
+	 * exactly fits in the RFN_MODULUS raster */
+	fn_rounded = m_cur_fn - m_cur_rfn;
+
+	/* If the delta between the internal and the external relative frame
+	 * number exceeds a certain limit, we need to assume that the incoming
+	 * rach request belongs to a the previous rfn period. To correct this,
+	 * we roll back the rounded frame number by one RFN_MODULUS */
+	if (abs(rfn - m_cur_rfn) > RFN_THRESHOLD) {
+		LOGP(DRLCMAC, LOGL_DEBUG,
+		     "Race condition between rfn (%u) and m_cur_fn (%u) detected: rfn belongs to the previos modulus %u cycle, wrappng...\n",
+		     rfn, m_cur_fn, RFN_MODULUS);
+		if (fn_rounded < RFN_MODULUS) {
+			LOGP(DRLCMAC, LOGL_DEBUG,
+			"Cornercase detected: wrapping crosses %u border\n",
+			GSM_MAX_FN);
+			fn_rounded = GSM_MAX_FN - (RFN_MODULUS - fn_rounded);
+		}
+		else
+			fn_rounded -= RFN_MODULUS;
+	}
+
+	/* The real frame number is the sum of the rounded frame number and the
+	 * relative framenumber computed via RACH */
+	fn = fn_rounded + rfn;
+
+	return fn;
+}
+
 int BTS::rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, uint8_t is_11bit,
 		enum ph_burst_type burst_type)
 {
@@ -536,20 +591,8 @@
 	if (is_11bit)
 		rach_frame_11bit();
 
-	/* Note: If a BTS is sending in a rach request it will be fully aware
-	 * of the frame number. If the PCU is used in a BSC-co-located setup.
-	 * The BSC will forward the incoming RACH request. The RACH request
-	 * only contains the relative frame number (Fn % 42432) in its request
-	 * reference. This PCU implementation has to fit both secenarious, so
-	 * we need to assume that Fn is a relative frame number. */
-
-	/* Ensure that all following calculations are performed with the
-	 * relative frame number */
-	Fn = Fn % 42432;
-
-	/* Restore the full frame number
-	 * (See also 3GPP TS 44.018, section 10.5.2.38) */
-	Fn = Fn + m_cur_fn - m_cur_fn % 42432;
+	/* Determine full frame number */
+	Fn = rfn_to_fn(Fn);
 
 	LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, "
 		"so we provide one: ra=0x%02x Fn=%u qta=%d is_11bit=%d:\n",