aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--doc/manuals/chapters/bts.adoc77
-rw-r--r--include/osmocom/bsc/acc.h72
-rw-r--r--include/osmocom/bsc/bts.h1
-rw-r--r--src/osmo-bsc/acc.c366
-rw-r--r--src/osmo-bsc/bsc_vty.c35
-rw-r--r--src/osmo-bsc/bts.c1
-rw-r--r--src/osmo-bsc/system_information.c15
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/acc/Makefile.am37
-rw-r--r--tests/acc/acc_test.c493
-rw-r--r--tests/acc/acc_test.ok620
-rw-r--r--tests/gsm0408/gsm0408_test.c2
-rw-r--r--tests/testsuite.at6
14 files changed, 1610 insertions, 117 deletions
diff --git a/configure.ac b/configure.ac
index d25925b41..4a5807985 100644
--- a/configure.ac
+++ b/configure.ac
@@ -241,6 +241,7 @@ AC_OUTPUT(
src/utils/Makefile
tests/Makefile
tests/atlocal
+ tests/acc/Makefile
tests/gsm0408/Makefile
tests/bsc/Makefile
tests/codec_pref/Makefile
diff --git a/doc/manuals/chapters/bts.adoc b/doc/manuals/chapters/bts.adoc
index 94e89a41c..6e7a308c7 100644
--- a/doc/manuals/chapters/bts.adoc
+++ b/doc/manuals/chapters/bts.adoc
@@ -416,17 +416,28 @@ have to deal with an increased load due to duplicate RACH requests. However,
in order to minimize the delay when a RACH request or response gets lost the
MS should not wait too long before retransmitting.
-==== Load Management
+==== Access Control Class Load Management
-Every SIM card is member of one of the ten regular ACCs (0-9). Access to the
-BTS can be restricted to SIMs that are members of certain ACCs.
+Every SIM card is member of one of the ten regular ACCs (0-9). Access to the BTS
+can be restricted to SIMs that are members of certain ACCs.
-Since the ACCs are distributed uniformly across all SIMs allowing only ACCs
-0-4 to connect to the BTS should reduce its load by 50%.
+Furthermore, high priority users (such as PLMN staff, public or emergency
+services, etc.) may be members of one or more ACCs from 11-15.
+
+Since the ACCs 0-9 are distributed uniformly across all SIMs, for instance
+allowing only ACCs 0-4 to connect to the BTS should reduce its load by 50% at
+the expense of not serving 50% of the subscribers.
The default is to allow all ACCs to connect.
-.Example: Restrict access to the BTS by ACC
+OsmoBSC supports several levels of ACC management to allow or restrict access
+either permanently or temporarily on each BTS.
+
+The first level of management consists of an access list to flag specific ACCs
+as permanently barred (the list can be updated at any time through VTY as seen
+below). As indicated above, the default is to allow all ACCs (0-15).
+
+.Example: Restrict permanent access to the BTS by ACC
----
network
bts 0
@@ -436,12 +447,33 @@ network
<1> Disallow SIMs with access-class 1 from connecting to the BTS
<2> Permit SIMs with access-class 9 to connect to the BTS.
+On really crowded areas, a BTS may struggle to service all mobile stations
+willing to use it, and which may end up in collapse. In this kind of scenarios
+it is a good idea to temporarily further restrict the amount of allowed ACCs
+(hence restrict the amount of subscribers allowed to reach the BTS).
+However, doing so on a permanent basis would be unfair to subscribers from
+barred ACCs. Hence, OsmoBSC can be configured to temporarily generate ACC
+subsets of the permanent set presented above, and rotate them over time to allow
+fair access to all subscribers. This feature is only aimed at ACCs 0-9,
+since ACCs 11-15 are considered high priority and hence are always configured
+based on permanent list policy.
+
+.Example: Configure rotative access to the BTS
+----
+network
+ bts 0
+ access-control-rotate 3 <1>
+ access-control-rotate-quantum 20 <2>
+----
+<1> Only allow up to 3 concurrent allowed ACCs from the permanent list
+<2> Rotate the generated permanent list subsets every 20 seconds in a fair fashion
-Smaller cells with lots of subscribers can be overwhelmed with traffic after
-the network is turned on. This is especially true in areas with little to no
-reception from other networks. To manage the load OsmoBSC has an option to
-enable one Access Class at a time so initial access to the network is
-distributed across a longer time.
+Furthermore, cells with large number of subscribers and limited overlapping
+coverage may become overwhelmed with traffic after the cell starts brodacasting.
+This is especially true in areas with little to no reception from other
+networks. To manage the load, OsmoBSC has an option to further restrict the
+rotating ACC subset during startup and slowly increment it over time and taking
+current load into account.
.Example: Ramp up access to the BTS after startup
----
@@ -456,6 +488,29 @@ network
<3> At each step enable one more ACC
+Here a full example of all the mechanisms combined can be found:
+
+.Example: Full ACC Load Management config setup
+----
+bts 0
+ rach access-control-class 5 barred <1>
+ rach access-control-class 6 barred
+ rach access-control-class 7 barred
+ rach access-control-class 8 barred
+ rach access-control-class 9 barred
+ access-control-class-rotate 3 <2>
+ access-control-class-rotate-quantum 20 <3>
+ access-control-class-ramping <4>
+ access-control-class-ramping-step-size 1 <5>
+ access-control-class-ramping-step-interval dynamic <6>
+----
+<1> ACCs 5-9 are administratively barred, ie they will never be used until somebody manually enables them in VTY config
+<2> Allow access through temporary subsets of len=3 from ACC set 0-4: (0,1,2) -> (1,2,3) -> (2,3,4) -> (3,4,0), etc.
+<3> Each subset iteration will happen every 20 seconds
+<4> During startup since ramping is enabled, it will further restrict the rotate subset size parameter (len=3)
+<5> The rotate subset size parameter will be increased one ACC slot at a time: len=0 -> len=1 -> len=2 -> len=3
+<6> The time until the subset size is further increased will be calculated based on current channel load
+
==== RACH Parameter Configuration
The following parameters allow control over how the MS can access the random
diff --git a/include/osmocom/bsc/acc.h b/include/osmocom/bsc/acc.h
index 31fc74fbd..bd75f9599 100644
--- a/include/osmocom/bsc/acc.h
+++ b/include/osmocom/bsc/acc.h
@@ -27,6 +27,40 @@
#include <osmocom/core/timer.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
+#define ACC_MGR_QUANTUM_DEFAULT 20 /* 20 seconds */
+
+/* Manage rotating subset of allowed Access Class as per configuration */
+struct acc_mgr {
+ struct gsm_bts *bts; /*!< backpointer to BTS using this ACC manager */
+ /* Administrative Maximum Number of ACC 0-9 to be allowed at the same time.
+ Configurable through VTY cmd "access-control-class-roundrobin",
+ defaults to all allowed (10) */
+ uint8_t len_allowed_adm;
+ /* Further limiting the number of ACC to use. It may be lower due
+ to ramping, based for instance on channel or system load. */
+ uint8_t len_allowed_ramp;
+
+ /* Time until next subset is generated */
+ uint32_t rotation_time_sec;
+ struct osmo_timer_list rotate_timer;
+
+ /* Bitmask containing subset of allowed ACC 0-9 on current rotation iteration */
+ uint16_t allowed_subset_mask;
+ /* Number of bits (ACC) set in allowed_subset_mask: 0->min(len_allowed_ramp, len_allowed_adm) */
+ uint8_t allowed_subset_mask_count;
+ /* Number of ACC 0-9 allowed as per adminsitrative (permanent) config. */
+ uint8_t allowed_permanent_count;
+};
+
+void acc_mgr_init(struct acc_mgr *acc_mgr, struct gsm_bts *bts);
+uint8_t acc_mgr_get_len_allowed_adm(struct acc_mgr *acc_mgr);
+uint8_t acc_mgr_get_len_allowed_ramp(struct acc_mgr *acc_mgr);
+void acc_mgr_set_len_allowed_adm(struct acc_mgr *acc_mgr, uint8_t len_allowed_adm);
+void acc_mgr_set_len_allowed_ramp(struct acc_mgr *acc_mgr, uint8_t len_allowed_ramp);
+void acc_mgr_set_rotation_time(struct acc_mgr *acc_mgr, uint32_t rotation_time_sec);
+void acc_mgr_perm_subset_changed(struct acc_mgr *acc_mgr, struct gsm48_rach_control *rach_control);
+void acc_mgr_apply_acc(struct acc_mgr *acc_mgr, struct gsm48_rach_control *rach_control);
+
/*!
* Access control class (ACC) ramping is used to slowly make the cell available to
* an increasing number of MS. This avoids overload at startup time in cases where
@@ -50,17 +84,6 @@ struct acc_ramp {
bool acc_ramping_enabled; /*!< whether ACC ramping is enabled */
/*!
- * Bitmask which keeps track of access control classes that are currently denied
- * access. The function acc_ramp_apply() uses this mask to modulate bits from
- * octets 2 and 3 in RACH Control Parameters (see 3GPP 44.018 10.5.2.29).
- * Ramping is only concerned with ACCs 0-9. While any of the bits 0-9 is set,
- * the corresponding ACC is barred.
- * ACCs 11-15 should always be allowed, and ACC 10 denies emergency calls for
- * all ACCs from 0-9 inclusive; these ACCs are ignored in this implementation.
- */
- uint16_t barred_accs;
-
- /*!
* This controls the maximum number of ACCs to allow per ramping step (1 - 10).
* The compile-time default value is ACC_RAMP_STEP_SIZE_DEFAULT.
* This value can be changed by VTY configuration.
@@ -126,33 +149,6 @@ static inline bool acc_ramp_step_interval_is_dynamic(struct acc_ramp *acc_ramp)
return !(acc_ramp->step_interval_is_fixed);
}
-/*!
- * Return bitmasks which correspond to access control classes that are currently
- * denied access. Ramping is only concerned with those bits which control access
- * for ACCs 0-9, and any of the other bits will always be set to zero in these masks, i.e.
- * it is safe to OR these bitmasks with the corresponding fields in struct gsm48_rach_control.
- * \param[in] acc_ramp Pointer to acc_ramp structure.
- */
-static inline uint8_t acc_ramp_get_barred_t2(struct acc_ramp *acc_ramp)
-{
- return ((acc_ramp->barred_accs >> 8) & 0x03);
-};
-static inline uint8_t acc_ramp_get_barred_t3(struct acc_ramp *acc_ramp)
-{
- return (acc_ramp->barred_accs & 0xff);
-}
-
-/*!
- * Potentially mark certain Access Control Classes (ACCs) as barred in accordance to ACC ramping.
- * \param[in] rach_control RACH control parameters in which barred ACCs will be configured.
- * \param[in] acc_ramp Pointer to acc_ramp structure.
- */
-static inline void acc_ramp_apply(struct gsm48_rach_control *rach_control, struct acc_ramp *acc_ramp)
-{
- rach_control->t2 |= acc_ramp_get_barred_t2(acc_ramp);
- rach_control->t3 |= acc_ramp_get_barred_t3(acc_ramp);
-}
-
void acc_ramp_init(struct acc_ramp *acc_ramp, struct gsm_bts *bts);
int acc_ramp_set_step_size(struct acc_ramp *acc_ramp, unsigned int step_size);
int acc_ramp_set_step_interval(struct acc_ramp *acc_ramp, unsigned int step_interval);
diff --git a/include/osmocom/bsc/bts.h b/include/osmocom/bsc/bts.h
index 730dee9e0..16053a3bb 100644
--- a/include/osmocom/bsc/bts.h
+++ b/include/osmocom/bsc/bts.h
@@ -449,6 +449,7 @@ struct gsm_bts {
uint32_t si_mode_static;
/* access control class ramping */
+ struct acc_mgr acc_mgr;
struct acc_ramp acc_ramp;
/* exclude the BTS from the global RF Lock handling */
diff --git a/src/osmo-bsc/acc.c b/src/osmo-bsc/acc.c
index a1941e42e..b25f2fdc3 100644
--- a/src/osmo-bsc/acc.c
+++ b/src/osmo-bsc/acc.c
@@ -1,4 +1,4 @@
-/* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+/* (C) 2018-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
*
* Author: Stefan Sperling <ssperling@sysmocom.de>
*
@@ -20,6 +20,8 @@
*/
#include <strings.h>
+#include <stdint.h>
+#include <inttypes.h>
#include <errno.h>
#include <stdbool.h>
@@ -43,42 +45,319 @@ static bool acc_is_permanently_barred(struct gsm_bts *bts, unsigned int acc)
return (bts->si_common.rach_control.t3 & (1 << (acc)));
}
-static void allow_one_acc(struct acc_ramp *acc_ramp, unsigned int acc)
+/*!
+ * Return bitmasks which correspond to access control classes that are currently
+ * denied access. Ramping is only concerned with those bits which control access
+ * for ACCs 0-9, and any of the other bits will always be set to zero in these masks, i.e.
+ * it is safe to OR these bitmasks with the corresponding fields in struct gsm48_rach_control.
+ * \param[in] acc_mgr Pointer to acc_mgr structure.
+ */
+static inline uint8_t acc_mgr_get_barred_t2(struct acc_mgr *acc_mgr)
{
- OSMO_ASSERT(acc <= 9);
- if (acc_ramp->barred_accs & (1 << acc))
- LOG_BTS(acc_ramp->bts, DRSL, LOGL_NOTICE,
- "ACC RAMP: allowing Access Control Class %u\n", acc);
- acc_ramp->barred_accs &= ~(1 << acc);
+ return ((~acc_mgr->allowed_subset_mask) >> 8) & 0x03;
+};
+static inline uint8_t acc_mgr_get_barred_t3(struct acc_mgr *acc_mgr)
+{
+ return (~acc_mgr->allowed_subset_mask) & 0xff;
}
-static void barr_one_acc(struct acc_ramp *acc_ramp, unsigned int acc)
+static uint8_t acc_mgr_subset_len(struct acc_mgr *acc_mgr)
{
- OSMO_ASSERT(acc <= 9);
- if ((acc_ramp->barred_accs & (1 << acc)) == 0)
- LOG_BTS(acc_ramp->bts, DRSL, LOGL_NOTICE,
- "ACC RAMP: barring Access Control Class %u\n", acc);
- acc_ramp->barred_accs |= (1 << acc);
+ return OSMO_MIN(acc_mgr->len_allowed_ramp, acc_mgr->len_allowed_adm);
}
-static void barr_all_accs(struct acc_ramp *acc_ramp)
+static void acc_mgr_enable_rotation_cond(struct acc_mgr *acc_mgr)
{
- unsigned int acc;
- for (acc = 0; acc < 10; acc++) {
- if (!acc_is_permanently_barred(acc_ramp->bts, acc))
- barr_one_acc(acc_ramp, acc);
+ if (acc_mgr->allowed_permanent_count && acc_mgr->allowed_subset_mask_count &&
+ acc_mgr->allowed_permanent_count != acc_mgr->allowed_subset_mask_count) {
+ if (!osmo_timer_pending(&acc_mgr->rotate_timer))
+ osmo_timer_schedule(&acc_mgr->rotate_timer, acc_mgr->rotation_time_sec, 0);
+ } else {
+ /* No rotation needed, disable rotation timer */
+ if (osmo_timer_pending(&acc_mgr->rotate_timer))
+ osmo_timer_del(&acc_mgr->rotate_timer);
}
}
-static void allow_all_accs(struct acc_ramp *acc_ramp)
+static void acc_mgr_gen_subset(struct acc_mgr *acc_mgr, bool update_si)
{
- unsigned int acc;
+ uint8_t acc;
+
+ acc_mgr->allowed_subset_mask = 0; /* clean mask */
+ acc_mgr->allowed_subset_mask_count = 0;
+ acc_mgr->allowed_permanent_count = 0;
+
for (acc = 0; acc < 10; acc++) {
- if (!acc_is_permanently_barred(acc_ramp->bts, acc))
- allow_one_acc(acc_ramp, acc);
+ if (acc_is_permanently_barred(acc_mgr->bts, acc))
+ continue;
+ acc_mgr->allowed_permanent_count++;
+ if (acc_mgr->allowed_subset_mask_count < acc_mgr_subset_len(acc_mgr)) {
+ acc_mgr->allowed_subset_mask |= (1 << acc);
+ acc_mgr->allowed_subset_mask_count++;
+ }
+ }
+
+ acc_mgr_enable_rotation_cond(acc_mgr);
+
+ LOG_BTS(acc_mgr->bts, DRSL, LOGL_INFO,
+ "ACC: New ACC allowed subset 0x%03" PRIx16 " (active_len=%" PRIu8
+ ", ramp_len=%" PRIu8 ", adm_len=%" PRIu8 ", perm_len=%" PRIu8 ", rotation=%s)\n",
+ acc_mgr->allowed_subset_mask, acc_mgr->allowed_subset_mask_count,
+ acc_mgr->len_allowed_ramp, acc_mgr->len_allowed_adm,
+ acc_mgr->allowed_permanent_count,
+ osmo_timer_pending(&(acc_mgr)->rotate_timer) ? "on" : "off");
+
+ /* Trigger SI data update, acc_mgr_apply_acc will bew called */
+ if (update_si)
+ gsm_bts_set_system_infos(acc_mgr->bts);
+}
+
+static uint8_t get_highest_allowed_acc(uint16_t mask)
+{
+ for (int i = 9; i >= 0; i--) {
+ if (mask & (1 << i))
+ return i;
+ }
+ OSMO_ASSERT(0);
+ return 0;
+}
+
+static uint8_t get_lowest_allowed_acc(uint16_t mask)
+{
+ for (int i = 0; i < 10; i++) {
+ if (mask & (1 << i))
+ return i;
+ }
+ OSMO_ASSERT(0);
+ return 0;
+}
+
+#define LOG_ACC_CHG(acc_mgr, level, old_mask, verb_str) \
+ LOG_BTS((acc_mgr)->bts, DRSL, level, \
+ "ACC: %s ACC allowed active subset 0x%03" PRIx16 " -> 0x%03" PRIx16 \
+ " (active_len=%" PRIu8 ", ramp_len=%" PRIu8 ", adm_len=%" PRIu8 \
+ ", perm_len=%" PRIu8 ", rotation=%s)\n", \
+ verb_str, old_mask, (acc_mgr)->allowed_subset_mask, \
+ (acc_mgr)->allowed_subset_mask_count, \
+ (acc_mgr)->len_allowed_ramp, (acc_mgr)->len_allowed_adm, \
+ (acc_mgr)->allowed_permanent_count, \
+ osmo_timer_pending(&(acc_mgr)->rotate_timer) ? "on" : "off")
+
+/* Call when either adm_len or ramp_len changed (and values have been updated) */
+static void acc_mgr_subset_length_changed(struct acc_mgr *acc_mgr)
+{
+ uint16_t old_mask = acc_mgr->allowed_subset_mask;
+ uint8_t curr_len = acc_mgr->allowed_subset_mask_count;
+ uint8_t new_len = acc_mgr_subset_len(acc_mgr);
+ int8_t diff = new_len - curr_len;
+ uint8_t i;
+
+ if (curr_len == new_len)
+ return;
+
+ if (new_len == 0) {
+ acc_mgr->allowed_subset_mask = 0;
+ acc_mgr->allowed_subset_mask_count = 0;
+ acc_mgr_enable_rotation_cond(acc_mgr);
+ LOG_ACC_CHG(acc_mgr, LOGL_INFO, old_mask, "update");
+ gsm_bts_set_system_infos(acc_mgr->bts);
+ return;
+ }
+
+ if (curr_len == 0) {
+ acc_mgr_gen_subset(acc_mgr, true);
+ return;
+ }
+
+ /* Try to add new ACCs to the set starting from highest one (since we rotate rolling up) */
+ if (diff > 0) { /* curr_len < new_len */
+ uint8_t highest = get_highest_allowed_acc(acc_mgr->allowed_subset_mask);
+ /* It's fine skipping highest in the loop since it's known to be already set: */
+ for (i = (highest + 1) % 10; i != highest; i = (i + 1) % 10) {
+ if (acc_is_permanently_barred(acc_mgr->bts, i))
+ continue;
+ if (acc_mgr->allowed_subset_mask & (1 << i))
+ continue; /* already in set */
+ acc_mgr->allowed_subset_mask |= (1 << i);
+ acc_mgr->allowed_subset_mask_count++;
+ diff--;
+ if (diff == 0)
+ break;
+ }
+ } else { /* curr_len > new_len, try removing from lowest one. */
+ uint8_t lowest = get_lowest_allowed_acc(acc_mgr->allowed_subset_mask);
+ i = lowest;
+ do {
+ if ((acc_mgr->allowed_subset_mask & (1 << i))) {
+ acc_mgr->allowed_subset_mask &= ~(1 << i);
+ acc_mgr->allowed_subset_mask_count--;
+ diff++;
+ if (diff == 0)
+ break;
+ }
+ i = (i + 1) % 10;
+ } while(i != lowest);
+ }
+
+ acc_mgr_enable_rotation_cond(acc_mgr);
+ LOG_ACC_CHG(acc_mgr, LOGL_INFO, old_mask, "update");
+
+ /* if we updated the set, notify about it */
+ if (curr_len != acc_mgr->allowed_subset_mask_count)
+ gsm_bts_set_system_infos(acc_mgr->bts);
+
+}
+
+/* Eg: (2,3,4) -> first=2; last=4. (3,7,8) -> first=3, last=8; (8,9,2) -> first=8, last=2 */
+void get_subset_limits(struct acc_mgr *acc_mgr, uint8_t *first, uint8_t *last)
+{
+ uint8_t lowest = get_lowest_allowed_acc(acc_mgr->allowed_subset_mask);
+ uint8_t highest = get_highest_allowed_acc(acc_mgr->allowed_subset_mask);
+ /* check if there's unselected ACCs between lowest and highest, that
+ * means subset is wrapping around, eg: (8,9,1)
+ * Assumption: The permanent set is bigger than the current selected subset */
+ bool is_wrapped = false;
+ uint8_t i = (lowest + 1) % 10;
+ do {
+ if (!acc_is_permanently_barred(acc_mgr->bts, i) &&
+ !(acc_mgr->allowed_subset_mask & (1 << i))) {
+ is_wrapped = true;
+ break;
+ }
+ i = (i + 1 ) % 10;
+ } while (i != (highest + 1) % 10);
+
+ if (is_wrapped) {
+ *first = highest;
+ *last = lowest;
+ } else {
+ *first = lowest;
+ *last = highest;
}
}
+static void do_acc_rotate_step(void *data)
+{
+ struct acc_mgr *acc_mgr = data;
+ uint8_t i;
+ uint8_t first, last;
+ uint16_t old_mask = acc_mgr->allowed_subset_mask;
+
+ /* Assumption: The size of the subset didn't change, that's handled by
+ * acc_mgr_subset_length_changed()
+ */
+
+ /* Assumption: Rotation timer has been disabled if no ACC is allowed */
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask_count != 0);
+
+ /* One ACC is rotated at a time: Drop first ACC and add next from last ACC */
+ get_subset_limits(acc_mgr, &first, &last);
+
+ acc_mgr->allowed_subset_mask &= ~(1 << first);
+ i = (last + 1) % 10;
+ do {
+ if (!acc_is_permanently_barred(acc_mgr->bts, i) &&
+ !(acc_mgr->allowed_subset_mask & (1 << i))) {
+ /* found first one which can be allowed, do it and be done */
+ acc_mgr->allowed_subset_mask |= (1 << i);
+ break;
+ }
+ i = (i + 1 ) % 10;
+ } while (i != (last + 1) % 10);
+ osmo_timer_schedule(&acc_mgr->rotate_timer, acc_mgr->rotation_time_sec, 0);
+
+ if (old_mask != acc_mgr->allowed_subset_mask) {
+ LOG_ACC_CHG(acc_mgr, LOGL_INFO, old_mask, "rotate");
+ gsm_bts_set_system_infos(acc_mgr->bts);
+ }
+}
+
+void acc_mgr_init(struct acc_mgr *acc_mgr, struct gsm_bts *bts)
+{
+ acc_mgr->bts = bts;
+ acc_mgr->len_allowed_adm = 10; /* Allow all by default */
+ acc_mgr->len_allowed_ramp = 10;
+ acc_mgr->rotation_time_sec = ACC_MGR_QUANTUM_DEFAULT;
+ osmo_timer_setup(&acc_mgr->rotate_timer, do_acc_rotate_step, acc_mgr);
+ /* FIXME: Don't update SI yet, avoid crash due to bts->model being NULL */
+ acc_mgr_gen_subset(acc_mgr, false);
+}
+
+uint8_t acc_mgr_get_len_allowed_adm(struct acc_mgr *acc_mgr)
+{
+ return acc_mgr->len_allowed_adm;
+}
+
+uint8_t acc_mgr_get_len_allowed_ramp(struct acc_mgr *acc_mgr)
+{
+ return acc_mgr->len_allowed_ramp;
+}
+
+void acc_mgr_set_len_allowed_adm(struct acc_mgr *acc_mgr, uint8_t len_allowed_adm)
+{
+ uint8_t old_len;
+
+ OSMO_ASSERT(len_allowed_adm <= 10);
+
+ if (acc_mgr->len_allowed_adm == len_allowed_adm)
+ return;
+
+ LOG_BTS(acc_mgr->bts, DRSL, LOGL_DEBUG,
+ "ACC: administrative rotate subset size set to %" PRIu8 "\n", len_allowed_adm);
+
+ old_len = acc_mgr_subset_len(acc_mgr);
+ acc_mgr->len_allowed_adm = len_allowed_adm;
+ if (old_len != acc_mgr_subset_len(acc_mgr))
+ acc_mgr_subset_length_changed(acc_mgr);
+}
+void acc_mgr_set_len_allowed_ramp(struct acc_mgr *acc_mgr, uint8_t len_allowed_ramp)
+{
+ uint8_t old_len;
+
+ OSMO_ASSERT(len_allowed_ramp <= 10);
+
+ if (acc_mgr->len_allowed_ramp == len_allowed_ramp)
+ return;
+
+ LOG_BTS(acc_mgr->bts, DRSL, LOGL_DEBUG,
+ "ACC: ramping rotate subset size set to %" PRIu8 "\n", len_allowed_ramp);
+
+ old_len = acc_mgr_subset_len(acc_mgr);
+ acc_mgr->len_allowed_ramp = len_allowed_ramp;
+ if (old_len != acc_mgr_subset_len(acc_mgr))
+ acc_mgr_subset_length_changed(acc_mgr);
+}
+
+void acc_mgr_set_rotation_time(struct acc_mgr *acc_mgr, uint32_t rotation_time_sec)
+{
+ LOG_BTS(acc_mgr->bts, DRSL, LOGL_DEBUG,
+ "ACC: rotate subset time set to %" PRIu32 " seconds\n", rotation_time_sec);
+ acc_mgr->rotation_time_sec = rotation_time_sec;
+}
+
+void acc_mgr_perm_subset_changed(struct acc_mgr *acc_mgr, struct gsm48_rach_control *rach_control)
+{
+ /* Even if amount is the same, the allowed/barred ones may have changed,
+ * so let's retrigger generation of an entire subset rather than
+ * rotating it */
+ acc_mgr_gen_subset(acc_mgr, true);
+}
+
+/*!
+ * Potentially mark certain Access Control Classes (ACCs) as barred in accordance to ACC policy.
+ * \param[in] acc_mgr Pointer to acc_mgr structure.
+ * \param[in] rach_control RACH control parameters in which barred ACCs will be configured.
+ */
+void acc_mgr_apply_acc(struct acc_mgr *acc_mgr, struct gsm48_rach_control *rach_control)
+{
+ rach_control->t2 |= acc_mgr_get_barred_t2(acc_mgr);
+ rach_control->t3 |= acc_mgr_get_barred_t3(acc_mgr);
+}
+
+
+//////////////////////////
+// acc_ramp
+//////////////////////////
static unsigned int get_next_step_interval(struct acc_ramp *acc_ramp)
{
struct gsm_bts *bts = acc_ramp->bts;
@@ -104,42 +383,14 @@ static unsigned int get_next_step_interval(struct acc_ramp *acc_ramp)
static void do_acc_ramping_step(void *data)
{
struct acc_ramp *acc_ramp = data;
- int i;
-
- /* Shortcut in case we only do one ramping step. */
- if (acc_ramp->step_size == ACC_RAMP_STEP_SIZE_MAX) {
- allow_all_accs(acc_ramp);
- gsm_bts_set_system_infos(acc_ramp->bts);
- return;
- }
-
- /* Allow 'step_size' ACCs, starting from ACC0. ACC9 will be allowed last. */
- for (i = 0; i < acc_ramp->step_size; i++) {
- int idx = ffs(acc_ramp_get_barred_t3(acc_ramp));
- if (idx > 0) {
- /* One of ACC0-ACC7 is still bared. */
- unsigned int acc = idx - 1;
- if (!acc_is_permanently_barred(acc_ramp->bts, acc))
- allow_one_acc(acc_ramp, acc);
- } else {
- idx = ffs(acc_ramp_get_barred_t2(acc_ramp));
- if (idx == 1 || idx == 2) {
- /* ACC8 or ACC9 is still barred. */
- unsigned int acc = idx - 1 + 8;
- if (!acc_is_permanently_barred(acc_ramp->bts, acc))
- allow_one_acc(acc_ramp, acc);
- } else {
- /* All ACCs are now allowed. */
- break;
- }
- }
- }
+ struct acc_mgr *acc_mgr = &acc_ramp->bts->acc_mgr;
+ uint8_t old_len = acc_mgr_get_len_allowed_ramp(acc_mgr);
+ uint8_t new_len = OSMO_MIN(10, old_len + acc_ramp->step_size);
- gsm_bts_set_system_infos(acc_ramp->bts);
+ acc_mgr_set_len_allowed_ramp(acc_mgr, new_len);
/* If we have not allowed all ACCs yet, schedule another ramping step. */
- if (acc_ramp_get_barred_t2(acc_ramp) != 0x00 ||
- acc_ramp_get_barred_t3(acc_ramp) != 0x00)
+ if (new_len != 10)
osmo_timer_schedule(&acc_ramp->step_timer, get_next_step_interval(acc_ramp), 0);
}
@@ -279,7 +530,6 @@ void acc_ramp_init(struct acc_ramp *acc_ramp, struct gsm_bts *bts)
acc_ramp->step_size = ACC_RAMP_STEP_SIZE_DEFAULT;
acc_ramp->step_interval_sec = ACC_RAMP_STEP_INTERVAL_MIN;
acc_ramp->step_interval_is_fixed = false;
- allow_all_accs(acc_ramp);
osmo_timer_setup(&acc_ramp->step_timer, do_acc_ramping_step, acc_ramp);
osmo_signal_register_handler(SS_NM, acc_ramp_nm_sig_cb, acc_ramp);
}
@@ -344,7 +594,7 @@ void acc_ramp_trigger(struct acc_ramp *acc_ramp)
if (acc_ramp_is_enabled(acc_ramp)) {
/* Set all available ACCs to barred and start ramping up. */
- barr_all_accs(acc_ramp);
+ acc_mgr_set_len_allowed_ramp(&acc_ramp->bts->acc_mgr, 0);
do_acc_ramping_step(acc_ramp);
}
}
@@ -358,5 +608,5 @@ void acc_ramp_abort(struct acc_ramp *acc_ramp)
if (osmo_timer_pending(&acc_ramp->step_timer))
osmo_timer_del(&acc_ramp->step_timer);
- allow_all_accs(acc_ramp);
+ acc_mgr_set_len_allowed_ramp(&acc_ramp->bts->acc_mgr, 10);
}
diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c
index 9cc76730e..0be4e6d45 100644
--- a/src/osmo-bsc/bsc_vty.c
+++ b/src/osmo-bsc/bsc_vty.c
@@ -404,6 +404,8 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
VTY_NEWLINE);
vty_out(vty, " Cell Reselection Hysteresis: %u dBm%s",
bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
+ vty_out(vty, " Access Control Class rotation allow mask: 0x%" PRIx16 "%s",
+ bts->acc_mgr.allowed_subset_mask, VTY_NEWLINE);
vty_out(vty, " Access Control Class ramping: %senabled%s",
acc_ramp_is_enabled(&bts->acc_ramp) ? "" : "not ", VTY_NEWLINE);
if (acc_ramp_is_enabled(&bts->acc_ramp)) {
@@ -948,6 +950,10 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
for (i = 0; i < 8; i++)
if ((i != 2) && (bts->si_common.rach_control.t2 & (0x1 << i)))
vty_out(vty, " rach access-control-class %d barred%s", i+8, VTY_NEWLINE);
+ if (bts->acc_mgr.len_allowed_adm < 10)
+ vty_out(vty, " access-control-class-rotate %" PRIu8 "%s", bts->acc_mgr.len_allowed_adm, VTY_NEWLINE);
+ if (bts->acc_mgr.rotation_time_sec != ACC_MGR_QUANTUM_DEFAULT)
+ vty_out(vty, " access-control-class-rotate-quantum %" PRIu32 "%s", bts->acc_mgr.rotation_time_sec, VTY_NEWLINE);
vty_out(vty, " %saccess-control-class-ramping%s", acc_ramp_is_enabled(&bts->acc_ramp) ? "" : "no ", VTY_NEWLINE);
if (!acc_ramp_step_interval_is_dynamic(&bts->acc_ramp)) {
vty_out(vty, " access-control-class-ramping-step-interval %u%s",
@@ -2745,6 +2751,9 @@ DEFUN(cfg_bts_rach_ac_class, cfg_bts_rach_ac_class_cmd,
else
bts->si_common.rach_control.t2 |= (0x1 << (control_class - 8));
+ if (control_class < 10)
+ acc_mgr_perm_subset_changed(&bts->acc_mgr, &bts->si_common.rach_control);
+
return CMD_SUCCESS;
}
@@ -3640,6 +3649,30 @@ DEFUN(cfg_bts_pcu_sock, cfg_bts_pcu_sock_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_bts_acc_rotate,
+ cfg_bts_acc_rotate_cmd,
+ "access-control-class-rotate <0-10>",
+ "Enable Access Control Class allowed subset rotation\n"
+ "Size of the rotating allowed ACC 0-9 subset (default=10, no subset)\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int len_allowed_adm = atoi(argv[0]);
+ acc_mgr_set_len_allowed_adm(&bts->acc_mgr, len_allowed_adm);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_acc_rotate_quantum,
+ cfg_bts_acc_rotate_quantum_cmd,
+ "access-control-class-rotate-quantum <1-65535>",
+ "Time between rotation of ACC 0-9 generated subsets\n"
+ "Time in seconds (default=" OSMO_STRINGIFY_VAL(ACC_MGR_QUANTUM_DEFAULT) ")\n")
+{
+ struct gsm_bts *bts = vty->index;
+ uint32_t rotation_time_sec = (uint32_t)atoi(argv[0]);
+ acc_mgr_set_rotation_time(&bts->acc_mgr, rotation_time_sec);
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_bts_acc_ramping,
cfg_bts_acc_ramping_cmd,
"access-control-class-ramping",
@@ -6493,6 +6526,8 @@ int bsc_vty_init(struct gsm_network *network)
install_element(BTS_NODE, &cfg_bts_amr_hr_hyst3_cmd);
install_element(BTS_NODE, &cfg_bts_amr_hr_start_mode_cmd);
install_element(BTS_NODE, &cfg_bts_pcu_sock_cmd);
+ install_element(BTS_NODE, &cfg_bts_acc_rotate_cmd);
+ install_element(BTS_NODE, &cfg_bts_acc_rotate_quantum_cmd);
install_element(BTS_NODE, &cfg_bts_acc_ramping_cmd);
install_element(BTS_NODE, &cfg_bts_no_acc_ramping_cmd);
install_element(BTS_NODE, &cfg_bts_acc_ramping_step_interval_cmd);
diff --git a/src/osmo-bsc/bts.c b/src/osmo-bsc/bts.c
index 779402416..4318b7ef9 100644
--- a/src/osmo-bsc/bts.c
+++ b/src/osmo-bsc/bts.c
@@ -387,6 +387,7 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num)
bts_init_cbch_state(&bts->cbch_basic, bts);
bts_init_cbch_state(&bts->cbch_extended, bts);
+ acc_mgr_init(&bts->acc_mgr, bts);
acc_ramp_init(&bts->acc_ramp, bts);
return bts;
diff --git a/src/osmo-bsc/system_information.c b/src/osmo-bsc/system_information.c
index fe78e8e8c..7011127ba 100644
--- a/src/osmo-bsc/system_information.c
+++ b/src/osmo-bsc/system_information.c
@@ -723,8 +723,7 @@ static int generate_si1(enum osmo_sysinfo_type t, struct gsm_bts *bts)
list_arfcn(si1->cell_channel_description, 0xce, "Serving cell:");
si1->rach_control = bts->si_common.rach_control;
- if (acc_ramp_is_enabled(&bts->acc_ramp))
- acc_ramp_apply(&si1->rach_control, &bts->acc_ramp);
+ acc_mgr_apply_acc(&bts->acc_mgr, &si1->rach_control);
/*
* SI1 Rest Octets (10.5.2.32), contains NCH position and band
@@ -755,8 +754,7 @@ static int generate_si2(enum osmo_sysinfo_type t, struct gsm_bts *bts)
si2->ncc_permitted = bts->si_common.ncc_permitted;
si2->rach_control = bts->si_common.rach_control;
- if (acc_ramp_is_enabled(&bts->acc_ramp))
- acc_ramp_apply(&si2->rach_control, &bts->acc_ramp);
+ acc_mgr_apply_acc(&bts->acc_mgr, &si2->rach_control);
return sizeof(*si2);
}
@@ -790,8 +788,7 @@ static int generate_si2bis(enum osmo_sysinfo_type t, struct gsm_bts *bts)
bts->si_valid &= ~(1 << SYSINFO_TYPE_2bis);
si2b->rach_control = bts->si_common.rach_control;
- if (acc_ramp_is_enabled(&bts->acc_ramp))
- acc_ramp_apply(&si2b->rach_control, &bts->acc_ramp);
+ acc_mgr_apply_acc(&bts->acc_mgr, &si2b->rach_control);
/* SI2bis Rest Octets as per 3GPP TS 44.018 ยง10.5.2.33 */
rc = rest_octets_si2bis(si2b->rest_octets);
@@ -912,8 +909,7 @@ static int generate_si3(enum osmo_sysinfo_type t, struct gsm_bts *bts)
si3->cell_options = bts->si_common.cell_options;
si3->cell_sel_par = bts->si_common.cell_sel_par;
si3->rach_control = bts->si_common.rach_control;
- if (acc_ramp_is_enabled(&bts->acc_ramp))
- acc_ramp_apply(&si3->rach_control, &bts->acc_ramp);
+ acc_mgr_apply_acc(&bts->acc_mgr, &si3->rach_control);
/* allow/disallow DTXu */
gsm48_set_dtx(&si3->cell_options, bts->dtxu, bts->dtxu, true);
@@ -962,8 +958,7 @@ static int generate_si4(enum osmo_sysinfo_type t, struct gsm_bts *bts)
gsm48_generate_lai2(&si4->lai, bts_lai(bts));
si4->cell_sel_par = bts->si_common.cell_sel_par;
si4->rach_control = bts->si_common.rach_control;
- if (acc_ramp_is_enabled(&bts->acc_ramp))
- acc_ramp_apply(&si4->rach_control, &bts->acc_ramp);
+ acc_mgr_apply_acc(&bts->acc_mgr, &si4->rach_control);
/* Optional: CBCH Channel Description + CBCH Mobile Allocation */
cbch_lchan = gsm_bts_get_cbch(bts);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9f2e0e0f0..07de847e3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,4 +1,5 @@
SUBDIRS = \
+ acc \
bsc \
codec_pref \
gsm0408 \
diff --git a/tests/acc/Makefile.am b/tests/acc/Makefile.am
new file mode 100644
index 000000000..4726ddc72
--- /dev/null
+++ b/tests/acc/Makefile.am
@@ -0,0 +1,37 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ -ggdb3 \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(COVERAGE_CFLAGS) \
+ $(NULL)
+
+EXTRA_DIST = \
+ acc_test.ok \
+ $(NULL)
+
+noinst_PROGRAMS = \
+ acc_test \
+ $(NULL)
+
+acc_test_SOURCES = \
+ acc_test.c \
+ $(NULL)
+
+acc_test_LDADD = \
+ $(top_builddir)/src/osmo-bsc/abis_nm.o \
+ $(top_builddir)/src/osmo-bsc/acc.o \
+ $(top_builddir)/src/osmo-bsc/bts.o \
+ $(top_builddir)/src/osmo-bsc/bts_trx.o \
+ $(top_builddir)/src/osmo-bsc/gsm_data.o \
+ $(top_builddir)/src/osmo-bsc/net_init.o \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOABIS_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(NULL)
diff --git a/tests/acc/acc_test.c b/tests/acc/acc_test.c
new file mode 100644
index 000000000..54f3de1fa
--- /dev/null
+++ b/tests/acc/acc_test.c
@@ -0,0 +1,493 @@
+/*
+ * (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+#include <osmocom/gsm/gsm23003.h>
+
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/debug.h>
+
+static void clock_debug(char* str)
+{
+ struct timespec ts;
+ struct timeval tv;
+ osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
+ osmo_gettimeofday(&tv, NULL);
+ fprintf(stderr, "sys={%lu.%06lu}: %s\n",
+ tv.tv_sec, tv.tv_usec, str);
+}
+
+#define bts_init(net) _bts_init(net, __func__)
+static inline struct gsm_bts *_bts_init(struct gsm_network *net, const char *msg)
+{
+ struct gsm_bts *bts = gsm_bts_alloc(net, 0);
+ if (!bts) {
+ fprintf(stderr, "BTS allocation failure in %s()\n", msg);
+ exit(1);
+ }
+ fprintf(stderr, "BTS allocation OK in %s()\n", msg);
+
+ bts->network = net;
+
+ return bts;
+}
+
+#define bts_del(bts) _bts_del(bts, __func__)
+static inline void _bts_del(struct gsm_bts *bts, const char *msg)
+{
+ osmo_stat_item_group_free(bts->bts_statg);
+ rate_ctr_group_free(bts->bts_ctrs);
+ if (osmo_timer_pending(&bts->acc_mgr.rotate_timer))
+ osmo_timer_del(&bts->acc_mgr.rotate_timer);
+ /* no need to llist_del(&bts->list), we never registered the bts there. */
+ talloc_free(bts);
+ fprintf(stderr, "BTS deallocated OK in %s()\n", msg);
+}
+
+static void do_allowed_len_adm_loop(struct acc_mgr *acc_mgr, uint8_t jump)
+{
+ int i;
+ fprintf(stderr, "%s(%" PRIu8 ")\n", __func__, jump);
+ /* Test decreasing the administrative (VTY) max subset size */
+ for (i = 10; i >= 0; i -= jump) {
+ acc_mgr_set_len_allowed_adm(acc_mgr, i);
+ }
+ if (i != 0)
+ acc_mgr_set_len_allowed_adm(acc_mgr, 0);
+ /* Test increasing the administrative (VTY) max subset size */
+ for (i = 0; i <= 10; i += jump) {
+ acc_mgr_set_len_allowed_adm(acc_mgr, i);
+ }
+ if (i != 10)
+ acc_mgr_set_len_allowed_adm(acc_mgr, 10);
+}
+
+static void do_allowed_len_ramp_loop(struct acc_mgr *acc_mgr, uint8_t jump)
+{
+ int i;
+ fprintf(stderr, "%s(%" PRIu8 ")\n", __func__, jump);
+ /* Test decreasing the administrative (VTY) max subset size */
+ for (i = 10; i >= 0; i -= jump) {
+ acc_mgr_set_len_allowed_ramp(acc_mgr, i);
+ }
+ if (i != 0)
+ acc_mgr_set_len_allowed_ramp(acc_mgr, 0);
+ /* Test increasing the administrative (VTY) max subset size */
+ for (i = 0; i <= 10; i += jump) {
+ acc_mgr_set_len_allowed_ramp(acc_mgr, i);
+ }
+ if (i != 10)
+ acc_mgr_set_len_allowed_ramp(acc_mgr, 10);
+}
+
+static void test_acc_mgr_no_ramp(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_mgr_get_len_allowed_adm(acc_mgr) == 10);
+ OSMO_ASSERT(acc_mgr_get_len_allowed_ramp(acc_mgr) == 10);
+ OSMO_ASSERT(acc_mgr->rotation_time_sec == ACC_MGR_QUANTUM_DEFAULT);
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask == 0x3ff);
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask_count == 10);
+ OSMO_ASSERT(acc_mgr->allowed_permanent_count == 10);
+
+
+ do_allowed_len_adm_loop(acc_mgr, 1);
+ do_allowed_len_adm_loop(acc_mgr, 4);
+
+ /* Now permantenly barr some ACC */
+ fprintf(stderr, "*** Barring some ACCs ***\n");
+ bts->si_common.rach_control.t2 |= 0x02;
+ bts->si_common.rach_control.t3 |= 0xa5;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ do_allowed_len_adm_loop(acc_mgr, 1);
+ do_allowed_len_adm_loop(acc_mgr, 4);
+
+ fprintf(stderr, "*** Barring ALL ACCs ***\n");
+ bts->si_common.rach_control.t2 |= 0x03;
+ bts->si_common.rach_control.t3 |= 0xff;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ fprintf(stderr, "*** Barring zero ACCs ***\n");
+ bts->si_common.rach_control.t2 = 0xfc;
+ bts->si_common.rach_control.t3 = 0x00;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ bts_del(bts);
+}
+
+static void test_acc_mgr_manual_ramp(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_mgr_get_len_allowed_adm(acc_mgr) == 10);
+ OSMO_ASSERT(acc_mgr_get_len_allowed_ramp(acc_mgr) == 10);
+ OSMO_ASSERT(acc_mgr->rotation_time_sec == ACC_MGR_QUANTUM_DEFAULT);
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask == 0x3ff);
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask_count == 10);
+ OSMO_ASSERT(acc_mgr->allowed_permanent_count == 10);
+
+ do_allowed_len_ramp_loop(acc_mgr, 1);
+ do_allowed_len_ramp_loop(acc_mgr, 4);
+
+ /* Now permantenly barr some ACC */
+ fprintf(stderr, "*** Barring some ACCs ***\n");
+ bts->si_common.rach_control.t2 |= 0x01;
+ bts->si_common.rach_control.t3 |= 0xb3;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ do_allowed_len_ramp_loop(acc_mgr, 1);
+ do_allowed_len_ramp_loop(acc_mgr, 4);
+
+ fprintf(stderr, "*** Barring ALL ACCs ***\n");
+ bts->si_common.rach_control.t2 |= 0x03;
+ bts->si_common.rach_control.t3 |= 0xff;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+ do_allowed_len_ramp_loop(acc_mgr, 1);
+ do_allowed_len_ramp_loop(acc_mgr, 4);
+
+ fprintf(stderr, "*** Barring zero ACCs ***\n");
+ bts->si_common.rach_control.t2 = 0xfc;
+ bts->si_common.rach_control.t3 = 0x00;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+ do_allowed_len_ramp_loop(acc_mgr, 1);
+ do_allowed_len_ramp_loop(acc_mgr, 4);
+
+ fprintf(stderr, "*** Barring some ACCs + adm len 4 ***\n");
+ acc_mgr_set_len_allowed_adm(acc_mgr, 4);
+ bts->si_common.rach_control.t2 = 0xfd;
+ bts->si_common.rach_control.t3 = 0xb3;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+ do_allowed_len_ramp_loop(acc_mgr, 1);
+ do_allowed_len_ramp_loop(acc_mgr, 4);
+
+ bts_del(bts);
+}
+
+static void test_acc_mgr_rotate(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ int i;
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_mgr_get_len_allowed_adm(acc_mgr) == 10);
+ OSMO_ASSERT(acc_mgr_get_len_allowed_ramp(acc_mgr) == 10);
+ OSMO_ASSERT(acc_mgr->rotation_time_sec == ACC_MGR_QUANTUM_DEFAULT);
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask == 0x3ff);
+ OSMO_ASSERT(acc_mgr->allowed_subset_mask_count == 10);
+ OSMO_ASSERT(acc_mgr->allowed_permanent_count == 10);
+
+ /* Test that rotation won't go over permanently barred ACC*/
+ fprintf(stderr, "*** Barring one ACC ***\n");
+ bts->si_common.rach_control.t2 |= 0x02;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+
+ acc_mgr_set_rotation_time(acc_mgr, 2);
+ acc_mgr_set_len_allowed_adm(acc_mgr, 4);
+
+ for (i = 0; i < 20; i++) {
+ osmo_gettimeofday_override_time.tv_sec += 2;
+ clock_debug("select()");
+ osmo_select_main(0);
+ }
+
+ bts_del(bts);
+}
+
+static void test_acc_ramp_fixed(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ int i;
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+ struct acc_ramp *acc_ramp = &bts->acc_ramp;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_ramp_is_enabled(acc_ramp) == false);
+ OSMO_ASSERT(acc_ramp_get_step_size(acc_ramp) == ACC_RAMP_STEP_SIZE_DEFAULT);
+ OSMO_ASSERT(acc_ramp_get_step_interval(acc_ramp) == ACC_RAMP_STEP_INTERVAL_MIN);
+ OSMO_ASSERT(acc_ramp_step_interval_is_dynamic(acc_ramp) == true);
+
+ /* Set super high rotation time so it doesn't interfer here: */
+ acc_mgr_set_rotation_time(acc_mgr, 5000);
+
+ //acc_ramp_set_step_interval_dynamic(acc_ramp);
+ OSMO_ASSERT(acc_ramp_set_step_interval(acc_ramp, 1) == -ERANGE);
+ OSMO_ASSERT(acc_ramp_set_step_interval(acc_ramp, 50) == 0);
+ acc_ramp_set_step_size(acc_ramp, 1);
+ acc_ramp_set_enabled(acc_ramp, true);
+
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+ acc_ramp_trigger(acc_ramp);
+
+ for (i = 0; i < 9; i++) {
+ osmo_gettimeofday_override_time.tv_sec += 50;
+ clock_debug("select()");
+ osmo_select_main(0);
+ }
+
+ bts_del(bts);
+}
+
+static void test_acc_ramp_fixed2(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ int i;
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+ struct acc_ramp *acc_ramp = &bts->acc_ramp;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_ramp_is_enabled(acc_ramp) == false);
+ OSMO_ASSERT(acc_ramp_get_step_size(acc_ramp) == ACC_RAMP_STEP_SIZE_DEFAULT);
+ OSMO_ASSERT(acc_ramp_get_step_interval(acc_ramp) == ACC_RAMP_STEP_INTERVAL_MIN);
+ OSMO_ASSERT(acc_ramp_step_interval_is_dynamic(acc_ramp) == true);
+
+ /* Set super high rotation time so it doesn't interfer here: */
+ acc_mgr_set_rotation_time(acc_mgr, 5000);
+ /* Set adm len to test that ramping won't go over it */
+ acc_mgr_set_len_allowed_adm(acc_mgr, 7);
+
+ acc_ramp_set_step_size(acc_ramp, 3);
+ acc_ramp_set_enabled(acc_ramp, true);
+
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+ acc_ramp_trigger(acc_ramp);
+
+ for (i = 0; i < 3; i++) {
+ osmo_gettimeofday_override_time.tv_sec += ACC_RAMP_STEP_INTERVAL_MIN;
+ clock_debug("select()");
+ osmo_select_main(0);
+ }
+
+ bts_del(bts);
+}
+
+static void test_acc_ramp_fixed3(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ int i;
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+ struct acc_ramp *acc_ramp = &bts->acc_ramp;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_ramp_is_enabled(acc_ramp) == false);
+ OSMO_ASSERT(acc_ramp_get_step_size(acc_ramp) == ACC_RAMP_STEP_SIZE_DEFAULT);
+ OSMO_ASSERT(acc_ramp_get_step_interval(acc_ramp) == ACC_RAMP_STEP_INTERVAL_MIN);
+ OSMO_ASSERT(acc_ramp_step_interval_is_dynamic(acc_ramp) == true);
+
+ /* Set super high rotation time so it doesn't interfer here: */
+ acc_mgr_set_rotation_time(acc_mgr, 5000);
+ /* Test that ramping won't go over permanently barred ACC*/
+ fprintf(stderr, "*** Barring some ACCs ***\n");
+ bts->si_common.rach_control.t2 |= 0x02;
+ bts->si_common.rach_control.t3 |= 0xa5;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ acc_ramp_set_step_size(acc_ramp, 1);
+ acc_ramp_set_enabled(acc_ramp, true);
+
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+ acc_ramp_trigger(acc_ramp);
+
+ for (i = 0; i < 9; i++) {
+ osmo_gettimeofday_override_time.tv_sec += ACC_RAMP_STEP_INTERVAL_MIN;
+ clock_debug("select()");
+ osmo_select_main(0);
+ }
+
+ bts_del(bts);
+}
+
+static void test_acc_ramp_dynamic(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ char buf[128];
+ unsigned int step_sec;
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+ struct acc_ramp *acc_ramp = &bts->acc_ramp;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_ramp_is_enabled(acc_ramp) == false);
+ OSMO_ASSERT(acc_ramp_get_step_size(acc_ramp) == ACC_RAMP_STEP_SIZE_DEFAULT);
+ OSMO_ASSERT(acc_ramp_get_step_interval(acc_ramp) == ACC_RAMP_STEP_INTERVAL_MIN);
+ OSMO_ASSERT(acc_ramp_step_interval_is_dynamic(acc_ramp) == true);
+
+ /* Set super high rotation time so it doesn't interfer here: */
+ acc_mgr_set_rotation_time(acc_mgr, 5000);
+
+ acc_ramp_set_step_interval_dynamic(acc_ramp);
+ acc_ramp_set_step_size(acc_ramp, 1);
+ acc_ramp_set_enabled(acc_ramp, true);
+
+ bts->chan_load_avg = 0; /*set 70% channel load */
+
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+ acc_ramp_trigger(acc_ramp);
+
+ while (osmo_timer_pending(&acc_ramp->step_timer)) {
+ bts->chan_load_avg += 10;
+ step_sec = ((bts->chan_load_avg * ACC_RAMP_STEP_INTERVAL_MAX) / 100);
+ osmo_gettimeofday_override_time.tv_sec += step_sec;
+ snprintf(buf, sizeof(buf), "select(): load=%" PRIu8 " -> step_sec=%u",
+ bts->chan_load_avg, step_sec);
+ clock_debug(buf);
+ osmo_select_main(0);
+ }
+
+ bts_del(bts);
+}
+
+static void test_acc_ramp_fixed_rotate(struct gsm_network *net)
+{
+ fprintf(stderr, "===%s===\n", __func__);
+ struct gsm_bts *bts = bts_init(net);
+ struct acc_mgr *acc_mgr = &bts->acc_mgr;
+ struct acc_ramp *acc_ramp = &bts->acc_ramp;
+
+ /* Validate are all allowed by default after allocation: */
+ OSMO_ASSERT(acc_ramp_is_enabled(acc_ramp) == false);
+ OSMO_ASSERT(acc_ramp_get_step_size(acc_ramp) == ACC_RAMP_STEP_SIZE_DEFAULT);
+ OSMO_ASSERT(acc_ramp_get_step_interval(acc_ramp) == ACC_RAMP_STEP_INTERVAL_MIN);
+ OSMO_ASSERT(acc_ramp_step_interval_is_dynamic(acc_ramp) == true);
+
+ OSMO_ASSERT(acc_ramp_set_step_interval(acc_ramp, 250) == 0);
+ acc_mgr_set_rotation_time(acc_mgr, 100);
+ /* Test that ramping + rotation won't go over permanently barred ACC*/
+ fprintf(stderr, "*** Barring one ACC ***\n");
+ bts->si_common.rach_control.t2 |= 0x02;
+ acc_mgr_perm_subset_changed(acc_mgr, &bts->si_common.rach_control);
+
+ acc_ramp_set_step_size(acc_ramp, 1);
+ acc_ramp_set_enabled(acc_ramp, true);
+
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+ acc_ramp_trigger(acc_ramp);
+
+ while (true) {
+ if (osmo_timer_pending(&acc_mgr->rotate_timer)) {
+ if ((osmo_gettimeofday_override_time.tv_sec + 50) % 250 == 0)
+ osmo_gettimeofday_override_time.tv_sec += 50;
+ else
+ osmo_gettimeofday_override_time.tv_sec += 100;
+ } else if (osmo_timer_pending(&acc_ramp->step_timer)) {
+ osmo_gettimeofday_override_time.tv_sec -= osmo_gettimeofday_override_time.tv_sec % 250;
+ osmo_gettimeofday_override_time.tv_sec += 250;
+ } else {
+ /* Once ramping is done, adm level is big enough and hence
+ * rotation is not needed and will be disabled. We are then done
+ */
+ break;
+ }
+ clock_debug("select()");
+ osmo_select_main(0);
+ }
+
+ bts_del(bts);
+}
+
+static const struct log_info_cat log_categories[] = {
+ [DRSL] = {
+ .name = "DRSL",
+ .description = "A-bis Radio Signalling Link (RSL)",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+};
+
+static const struct log_info log_info = {
+ .cat = log_categories,
+ .num_cat = ARRAY_SIZE(log_categories),
+};
+
+int main(int argc, char **argv)
+{
+ struct gsm_network *net;
+
+ osmo_gettimeofday_override = true;
+ osmo_gettimeofday_override_time = (struct timeval) {0, 0};
+
+ tall_bsc_ctx = talloc_named_const(NULL, 0, "gsm0408_test");
+ osmo_init_logging2(tall_bsc_ctx, &log_info);
+ log_set_log_level(osmo_stderr_target, LOGL_INFO);
+ log_set_print_category_hex(osmo_stderr_target, false);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_use_color(osmo_stderr_target, 0);
+
+ net = gsm_network_init(tall_bsc_ctx);
+ if (!net) {
+ fprintf(stderr, "Network init failure.\n");
+ return EXIT_FAILURE;
+ }
+
+ test_acc_mgr_no_ramp(net);
+ test_acc_mgr_manual_ramp(net);
+ test_acc_mgr_rotate(net);
+ test_acc_ramp_fixed(net);
+ test_acc_ramp_fixed2(net);
+ test_acc_ramp_fixed3(net);
+ test_acc_ramp_dynamic(net);
+ test_acc_ramp_fixed_rotate(net);
+
+ return EXIT_SUCCESS;
+}
+
+/* Whenever ACC code changes the set of barred ACCs, gsm_bts_set_system_infos()
+ * is called which ends up calling pcu_info_update */
+void pcu_info_update(struct gsm_bts *bts) {
+ struct gsm48_rach_control rach_control = {0};
+
+ acc_mgr_apply_acc(&bts->acc_mgr, &rach_control);
+ fprintf(stderr, "%s(): t2=0x%02" PRIx8 " t3=0x%02" PRIx8 "\n",
+ __func__, rach_control.t2, rach_control.t3);
+}
+
+
+struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
+ OSMO_ASSERT(0);
+}
+
+bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts) { return true; }
+void ts_fsm_alloc(struct gsm_bts_trx_ts *ts) {}
+int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan) { return 0; }
+int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { return 0; }
+int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
+{ return 0; }
+int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { return 0; }
diff --git a/tests/acc/acc_test.ok b/tests/acc/acc_test.ok
new file mode 100644
index 000000000..f377651ef
--- /dev/null
+++ b/tests/acc/acc_test.ok
@@ -0,0 +1,620 @@
+===test_acc_mgr_no_ramp===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_no_ramp()
+do_allowed_len_adm_loop(1)
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3fe (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x01
+(bts=0) ACC: update ACC allowed active subset 0x3fe -> 0x3fc (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x03
+(bts=0) ACC: update ACC allowed active subset 0x3fc -> 0x3f8 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x07
+(bts=0) ACC: update ACC allowed active subset 0x3f8 -> 0x3f0 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f
+(bts=0) ACC: update ACC allowed active subset 0x3f0 -> 0x3e0 (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1f
+(bts=0) ACC: update ACC allowed active subset 0x3e0 -> 0x3c0 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3f
+(bts=0) ACC: update ACC allowed active subset 0x3c0 -> 0x380 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7f
+(bts=0) ACC: update ACC allowed active subset 0x380 -> 0x300 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff
+(bts=0) ACC: update ACC allowed active subset 0x300 -> 0x200 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff
+(bts=0) ACC: update ACC allowed active subset 0x200 -> 0x000 (active_len=0, ramp_len=10, adm_len=0, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=10, adm_len=1, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe
+(bts=0) ACC: update ACC allowed active subset 0x001 -> 0x003 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc
+(bts=0) ACC: update ACC allowed active subset 0x003 -> 0x007 (active_len=3, ramp_len=10, adm_len=3, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8
+(bts=0) ACC: update ACC allowed active subset 0x007 -> 0x00f (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x01f (active_len=5, ramp_len=10, adm_len=5, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0
+(bts=0) ACC: update ACC allowed active subset 0x01f -> 0x03f (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0
+(bts=0) ACC: update ACC allowed active subset 0x03f -> 0x07f (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80
+(bts=0) ACC: update ACC allowed active subset 0x07f -> 0x0ff (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x1ff (active_len=9, ramp_len=10, adm_len=9, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x00
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00
+do_allowed_len_adm_loop(4)
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3f0 (active_len=6, ramp_len=10, adm_len=6, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f
+(bts=0) ACC: update ACC allowed active subset 0x3f0 -> 0x300 (active_len=2, ramp_len=10, adm_len=2, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff
+(bts=0) ACC: update ACC allowed active subset 0x300 -> 0x000 (active_len=0, ramp_len=10, adm_len=0, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x00f (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x0ff (active_len=8, ramp_len=10, adm_len=8, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00
+*** Barring some ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x15a (active_len=5, ramp_len=10, adm_len=10, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x02 t3=0xa5
+do_allowed_len_adm_loop(1)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=9, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=8, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=7, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=6, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x158 (active_len=4, ramp_len=10, adm_len=4, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xa7
+(bts=0) ACC: update ACC allowed active subset 0x158 -> 0x150 (active_len=3, ramp_len=10, adm_len=3, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xaf
+(bts=0) ACC: update ACC allowed active subset 0x150 -> 0x140 (active_len=2, ramp_len=10, adm_len=2, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xbf
+(bts=0) ACC: update ACC allowed active subset 0x140 -> 0x100 (active_len=1, ramp_len=10, adm_len=1, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xff
+(bts=0) ACC: update ACC allowed active subset 0x100 -> 0x000 (active_len=0, ramp_len=10, adm_len=0, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x002 (active_len=1, ramp_len=10, adm_len=1, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd
+(bts=0) ACC: update ACC allowed active subset 0x002 -> 0x00a (active_len=2, ramp_len=10, adm_len=2, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf5
+(bts=0) ACC: update ACC allowed active subset 0x00a -> 0x01a (active_len=3, ramp_len=10, adm_len=3, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe5
+(bts=0) ACC: update ACC allowed active subset 0x01a -> 0x05a (active_len=4, ramp_len=10, adm_len=4, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xa5
+(bts=0) ACC: update ACC allowed active subset 0x05a -> 0x15a (active_len=5, ramp_len=10, adm_len=5, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x02 t3=0xa5
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=6, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=7, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=8, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=9, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=10, perm_len=5, rotation=off)
+do_allowed_len_adm_loop(4)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=6, perm_len=5, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x140 (active_len=2, ramp_len=10, adm_len=2, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x02 t3=0xbf
+(bts=0) ACC: update ACC allowed active subset 0x140 -> 0x000 (active_len=0, ramp_len=10, adm_len=0, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x05a (active_len=4, ramp_len=10, adm_len=4, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xa5
+(bts=0) ACC: update ACC allowed active subset 0x05a -> 0x15a (active_len=5, ramp_len=10, adm_len=8, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x02 t3=0xa5
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=10, perm_len=5, rotation=off)
+*** Barring ALL ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=10, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+*** Barring zero ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00
+BTS deallocated OK in test_acc_mgr_no_ramp()
+===test_acc_mgr_manual_ramp===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_manual_ramp()
+do_allowed_len_ramp_loop(1)
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3fe (active_len=9, ramp_len=9, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x01
+(bts=0) ACC: update ACC allowed active subset 0x3fe -> 0x3fc (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x03
+(bts=0) ACC: update ACC allowed active subset 0x3fc -> 0x3f8 (active_len=7, ramp_len=7, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x07
+(bts=0) ACC: update ACC allowed active subset 0x3f8 -> 0x3f0 (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f
+(bts=0) ACC: update ACC allowed active subset 0x3f0 -> 0x3e0 (active_len=5, ramp_len=5, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1f
+(bts=0) ACC: update ACC allowed active subset 0x3e0 -> 0x3c0 (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3f
+(bts=0) ACC: update ACC allowed active subset 0x3c0 -> 0x380 (active_len=3, ramp_len=3, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7f
+(bts=0) ACC: update ACC allowed active subset 0x380 -> 0x300 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff
+(bts=0) ACC: update ACC allowed active subset 0x300 -> 0x200 (active_len=1, ramp_len=1, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff
+(bts=0) ACC: update ACC allowed active subset 0x200 -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe
+(bts=0) ACC: update ACC allowed active subset 0x001 -> 0x003 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc
+(bts=0) ACC: update ACC allowed active subset 0x003 -> 0x007 (active_len=3, ramp_len=3, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8
+(bts=0) ACC: update ACC allowed active subset 0x007 -> 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x01f (active_len=5, ramp_len=5, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0
+(bts=0) ACC: update ACC allowed active subset 0x01f -> 0x03f (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0
+(bts=0) ACC: update ACC allowed active subset 0x03f -> 0x07f (active_len=7, ramp_len=7, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80
+(bts=0) ACC: update ACC allowed active subset 0x07f -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x1ff (active_len=9, ramp_len=9, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x00
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00
+do_allowed_len_ramp_loop(4)
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3f0 (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f
+(bts=0) ACC: update ACC allowed active subset 0x3f0 -> 0x300 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff
+(bts=0) ACC: update ACC allowed active subset 0x300 -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00
+*** Barring some ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x24c (active_len=4, ramp_len=10, adm_len=10, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x01 t3=0xb3
+do_allowed_len_ramp_loop(1)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=9, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=8, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=7, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=6, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=5, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x248 (active_len=3, ramp_len=3, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xb7
+(bts=0) ACC: update ACC allowed active subset 0x248 -> 0x240 (active_len=2, ramp_len=2, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xbf
+(bts=0) ACC: update ACC allowed active subset 0x240 -> 0x200 (active_len=1, ramp_len=1, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff
+(bts=0) ACC: update ACC allowed active subset 0x200 -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x004 (active_len=1, ramp_len=1, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb
+(bts=0) ACC: update ACC allowed active subset 0x004 -> 0x00c (active_len=2, ramp_len=2, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3
+(bts=0) ACC: update ACC allowed active subset 0x00c -> 0x04c (active_len=3, ramp_len=3, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xb3
+(bts=0) ACC: update ACC allowed active subset 0x04c -> 0x24c (active_len=4, ramp_len=4, adm_len=10, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x01 t3=0xb3
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=5, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=6, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=7, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=8, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=9, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=10, adm_len=10, perm_len=4, rotation=off)
+do_allowed_len_ramp_loop(4)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=6, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x240 (active_len=2, ramp_len=2, adm_len=10, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xbf
+(bts=0) ACC: update ACC allowed active subset 0x240 -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x24c (active_len=4, ramp_len=4, adm_len=10, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x01 t3=0xb3
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=8, adm_len=10, perm_len=4, rotation=off)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x24c (active_len=4, ramp_len=10, adm_len=10, perm_len=4, rotation=off)
+*** Barring ALL ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=10, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+do_allowed_len_ramp_loop(1)
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=9, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=8, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=7, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=6, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=5, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=4, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=3, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=2, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=1, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=1, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=2, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=3, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=4, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=5, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=6, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=7, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=8, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=9, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=10, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+do_allowed_len_ramp_loop(4)
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=6, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=2, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=4, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=8, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x000 (active_len=0, ramp_len=10, adm_len=10, perm_len=0, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+*** Barring zero ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00
+do_allowed_len_ramp_loop(1)
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3fe (active_len=9, ramp_len=9, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x01
+(bts=0) ACC: update ACC allowed active subset 0x3fe -> 0x3fc (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x03
+(bts=0) ACC: update ACC allowed active subset 0x3fc -> 0x3f8 (active_len=7, ramp_len=7, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x07
+(bts=0) ACC: update ACC allowed active subset 0x3f8 -> 0x3f0 (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f
+(bts=0) ACC: update ACC allowed active subset 0x3f0 -> 0x3e0 (active_len=5, ramp_len=5, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x1f
+(bts=0) ACC: update ACC allowed active subset 0x3e0 -> 0x3c0 (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3f
+(bts=0) ACC: update ACC allowed active subset 0x3c0 -> 0x380 (active_len=3, ramp_len=3, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x7f
+(bts=0) ACC: update ACC allowed active subset 0x380 -> 0x300 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff
+(bts=0) ACC: update ACC allowed active subset 0x300 -> 0x200 (active_len=1, ramp_len=1, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff
+(bts=0) ACC: update ACC allowed active subset 0x200 -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe
+(bts=0) ACC: update ACC allowed active subset 0x001 -> 0x003 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc
+(bts=0) ACC: update ACC allowed active subset 0x003 -> 0x007 (active_len=3, ramp_len=3, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8
+(bts=0) ACC: update ACC allowed active subset 0x007 -> 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x01f (active_len=5, ramp_len=5, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0
+(bts=0) ACC: update ACC allowed active subset 0x01f -> 0x03f (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0
+(bts=0) ACC: update ACC allowed active subset 0x03f -> 0x07f (active_len=7, ramp_len=7, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80
+(bts=0) ACC: update ACC allowed active subset 0x07f -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x1ff (active_len=9, ramp_len=9, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x00
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00
+do_allowed_len_ramp_loop(4)
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3f0 (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x0f
+(bts=0) ACC: update ACC allowed active subset 0x3f0 -> 0x300 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0xff
+(bts=0) ACC: update ACC allowed active subset 0x300 -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00
+*** Barring some ACCs + adm len 4 ***
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3c0 (active_len=4, ramp_len=10, adm_len=4, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x3f
+(bts=0) ACC: New ACC allowed subset 0x24c (active_len=4, ramp_len=10, adm_len=4, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x01 t3=0xb3
+do_allowed_len_ramp_loop(1)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x248 (active_len=3, ramp_len=3, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xb7
+(bts=0) ACC: update ACC allowed active subset 0x248 -> 0x240 (active_len=2, ramp_len=2, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xbf
+(bts=0) ACC: update ACC allowed active subset 0x240 -> 0x200 (active_len=1, ramp_len=1, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xff
+(bts=0) ACC: update ACC allowed active subset 0x200 -> 0x000 (active_len=0, ramp_len=0, adm_len=4, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x004 (active_len=1, ramp_len=1, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb
+(bts=0) ACC: update ACC allowed active subset 0x004 -> 0x00c (active_len=2, ramp_len=2, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3
+(bts=0) ACC: update ACC allowed active subset 0x00c -> 0x04c (active_len=3, ramp_len=3, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xb3
+(bts=0) ACC: update ACC allowed active subset 0x04c -> 0x24c (active_len=4, ramp_len=4, adm_len=4, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x01 t3=0xb3
+do_allowed_len_ramp_loop(4)
+(bts=0) ACC: update ACC allowed active subset 0x24c -> 0x240 (active_len=2, ramp_len=2, adm_len=4, perm_len=4, rotation=on)
+pcu_info_update(): t2=0x01 t3=0xbf
+(bts=0) ACC: update ACC allowed active subset 0x240 -> 0x000 (active_len=0, ramp_len=0, adm_len=4, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x24c (active_len=4, ramp_len=4, adm_len=4, perm_len=4, rotation=off)
+pcu_info_update(): t2=0x01 t3=0xb3
+BTS deallocated OK in test_acc_mgr_manual_ramp()
+===test_acc_mgr_rotate===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_mgr_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1e0 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1f
+sys={2.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e0 -> 0x1c1 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3e
+sys={4.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c1 -> 0x0c3 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x3c
+sys={6.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0c3 -> 0x047 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xb8
+sys={8.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x047 -> 0x00f (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0
+sys={10.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00f -> 0x01e (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe1
+sys={12.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01e -> 0x03c (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc3
+sys={14.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03c -> 0x078 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x87
+sys={16.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x078 -> 0x0f0 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x0f
+sys={18.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0f0 -> 0x1e0 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1f
+sys={20.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e0 -> 0x1c1 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3e
+sys={22.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c1 -> 0x0c3 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x3c
+sys={24.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0c3 -> 0x047 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xb8
+sys={26.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x047 -> 0x00f (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0
+sys={28.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00f -> 0x01e (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe1
+sys={30.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x01e -> 0x03c (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc3
+sys={32.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03c -> 0x078 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x87
+sys={34.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x078 -> 0x0f0 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x0f
+sys={36.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0f0 -> 0x1e0 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x1f
+sys={38.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1e0 -> 0x1c1 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3e
+sys={40.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c1 -> 0x0c3 (active_len=4, ramp_len=10, adm_len=4, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x3c
+BTS deallocated OK in test_acc_mgr_rotate()
+===test_acc_ramp_fixed===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp_fixed()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe
+sys={50.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x001 -> 0x003 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc
+sys={100.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x003 -> 0x007 (active_len=3, ramp_len=3, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8
+sys={150.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x007 -> 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0
+sys={200.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x01f (active_len=5, ramp_len=5, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0
+sys={250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x01f -> 0x03f (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0
+sys={300.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x03f -> 0x07f (active_len=7, ramp_len=7, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80
+sys={350.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x07f -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00
+sys={400.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x1ff (active_len=9, ramp_len=9, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x00
+sys={450.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00
+BTS deallocated OK in test_acc_ramp_fixed()
+===test_acc_ramp_fixed2===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp_fixed2()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x3f8 (active_len=7, ramp_len=10, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x00 t3=0x07
+(bts=0) ACC: update ACC allowed active subset 0x3f8 -> 0x000 (active_len=0, ramp_len=0, adm_len=7, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x007 (active_len=3, ramp_len=3, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8
+sys={30.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x007 -> 0x03f (active_len=6, ramp_len=6, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0
+sys={60.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x03f -> 0x07f (active_len=7, ramp_len=9, adm_len=7, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80
+sys={90.000000}: select()
+BTS deallocated OK in test_acc_ramp_fixed2()
+===test_acc_ramp_fixed3===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp_fixed3()
+*** Barring some ACCs ***
+(bts=0) ACC: New ACC allowed subset 0x15a (active_len=5, ramp_len=10, adm_len=10, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x02 t3=0xa5
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x002 (active_len=1, ramp_len=1, adm_len=10, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd
+sys={30.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x002 -> 0x00a (active_len=2, ramp_len=2, adm_len=10, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf5
+sys={60.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x00a -> 0x01a (active_len=3, ramp_len=3, adm_len=10, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe5
+sys={90.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x01a -> 0x05a (active_len=4, ramp_len=4, adm_len=10, perm_len=5, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xa5
+sys={120.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x05a -> 0x15a (active_len=5, ramp_len=5, adm_len=10, perm_len=5, rotation=off)
+pcu_info_update(): t2=0x02 t3=0xa5
+sys={150.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=6, adm_len=10, perm_len=5, rotation=off)
+sys={180.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=7, adm_len=10, perm_len=5, rotation=off)
+sys={210.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=8, adm_len=10, perm_len=5, rotation=off)
+sys={240.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=9, adm_len=10, perm_len=5, rotation=off)
+sys={270.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x15a -> 0x15a (active_len=5, ramp_len=10, adm_len=10, perm_len=5, rotation=off)
+BTS deallocated OK in test_acc_ramp_fixed3()
+===test_acc_ramp_dynamic===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp_dynamic()
+(bts=0) ACC: update ACC allowed active subset 0x3ff -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe
+sys={60.000000}: select(): load=10 -> step_sec=60
+(bts=0) ACC: update ACC allowed active subset 0x001 -> 0x003 (active_len=2, ramp_len=2, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfc
+sys={180.000000}: select(): load=20 -> step_sec=120
+(bts=0) ACC: update ACC allowed active subset 0x003 -> 0x007 (active_len=3, ramp_len=3, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf8
+sys={360.000000}: select(): load=30 -> step_sec=180
+(bts=0) ACC: update ACC allowed active subset 0x007 -> 0x00f (active_len=4, ramp_len=4, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf0
+sys={600.000000}: select(): load=40 -> step_sec=240
+(bts=0) ACC: update ACC allowed active subset 0x00f -> 0x01f (active_len=5, ramp_len=5, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0
+sys={900.000000}: select(): load=50 -> step_sec=300
+(bts=0) ACC: update ACC allowed active subset 0x01f -> 0x03f (active_len=6, ramp_len=6, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0
+sys={1260.000000}: select(): load=60 -> step_sec=360
+(bts=0) ACC: update ACC allowed active subset 0x03f -> 0x07f (active_len=7, ramp_len=7, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x80
+sys={1680.000000}: select(): load=70 -> step_sec=420
+(bts=0) ACC: update ACC allowed active subset 0x07f -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00
+sys={2160.000000}: select(): load=80 -> step_sec=480
+(bts=0) ACC: update ACC allowed active subset 0x0ff -> 0x1ff (active_len=9, ramp_len=9, adm_len=10, perm_len=10, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x00
+sys={2700.000000}: select(): load=90 -> step_sec=540
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+pcu_info_update(): t2=0x00 t3=0x00
+BTS deallocated OK in test_acc_ramp_dynamic()
+===test_acc_ramp_fixed_rotate===
+(bts=0) ACC: New ACC allowed subset 0x3ff (active_len=10, ramp_len=10, adm_len=10, perm_len=10, rotation=off)
+BTS allocation OK in test_acc_ramp_fixed_rotate()
+*** Barring one ACC ***
+(bts=0) ACC: New ACC allowed subset 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x000 (active_len=0, ramp_len=0, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x03 t3=0xff
+(bts=0) ACC: New ACC allowed subset 0x001 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfe
+sys={100.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x001 -> 0x002 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfd
+sys={200.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x002 -> 0x004 (active_len=1, ramp_len=1, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xfb
+sys={250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x004 -> 0x00c (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xf3
+sys={350.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x00c -> 0x018 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe7
+sys={450.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x018 -> 0x030 (active_len=2, ramp_len=2, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xcf
+sys={500.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x030 -> 0x070 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x8f
+sys={600.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x070 -> 0x0e0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x1f
+sys={700.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0e0 -> 0x1c0 (active_len=3, ramp_len=3, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3f
+sys={750.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1c0 -> 0x1c1 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x3e
+sys={850.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1c1 -> 0x0c3 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x3c
+sys={950.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0c3 -> 0x047 (active_len=4, ramp_len=4, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xb8
+sys={1000.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x047 -> 0x0c7 (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x38
+sys={1100.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0c7 -> 0x04f (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xb0
+sys={1200.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x04f -> 0x01f (active_len=5, ramp_len=5, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xe0
+sys={1250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x01f -> 0x03f (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0xc0
+sys={1350.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x03f -> 0x07e (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x81
+sys={1450.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x07e -> 0x0fc (active_len=6, ramp_len=6, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x03
+sys={1500.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x0fc -> 0x1fc (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x03
+sys={1600.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fc -> 0x1f9 (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x06
+sys={1700.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1f9 -> 0x0fb (active_len=7, ramp_len=7, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x04
+sys={1750.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x0fb -> 0x1fb (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x04
+sys={1850.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x1fb -> 0x0ff (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x03 t3=0x00
+sys={1950.000000}: select()
+(bts=0) ACC: rotate ACC allowed active subset 0x0ff -> 0x1fe (active_len=8, ramp_len=8, adm_len=10, perm_len=9, rotation=on)
+pcu_info_update(): t2=0x02 t3=0x01
+sys={2000.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1fe -> 0x1ff (active_len=9, ramp_len=9, adm_len=10, perm_len=9, rotation=off)
+pcu_info_update(): t2=0x02 t3=0x00
+sys={2250.000000}: select()
+(bts=0) ACC: update ACC allowed active subset 0x1ff -> 0x1ff (active_len=9, ramp_len=10, adm_len=10, perm_len=9, rotation=off)
+BTS deallocated OK in test_acc_ramp_fixed_rotate()
diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c
index e53b83aba..35531f81a 100644
--- a/tests/gsm0408/gsm0408_test.c
+++ b/tests/gsm0408/gsm0408_test.c
@@ -140,6 +140,8 @@ static inline void _bts_del(struct gsm_bts *bts, const char *msg)
{
osmo_stat_item_group_free(bts->bts_statg);
rate_ctr_group_free(bts->bts_ctrs);
+ if (osmo_timer_pending(&bts->acc_mgr.rotate_timer))
+ osmo_timer_del(&bts->acc_mgr.rotate_timer);
/* no need to llist_del(&bts->list), we never registered the bts there. */
talloc_free(bts);
printf("BTS deallocated OK in %s()\n", msg);
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 1a190dda3..1c43249bd 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -20,6 +20,12 @@ cat $abs_srcdir/abis/abis_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/abis/abis_test], [], [expout], [ignore])
AT_CLEANUP
+AT_SETUP([acc])
+AT_KEYWORDS([acc])
+cat $abs_srcdir/acc/acc_test.ok > experr
+AT_CHECK([$abs_top_builddir/tests/acc/acc_test], [], [ignore], [experr])
+AT_CLEANUP
+
AT_SETUP([bsc])
AT_KEYWORDS([bsc])
cat $abs_srcdir/bsc/bsc_test.ok > expout