diff options
Diffstat (limited to 'tests/handover')
65 files changed, 3184 insertions, 1851 deletions
diff --git a/tests/handover/Makefile.am b/tests/handover/Makefile.am index 0f7953cd4..123fd61d6 100644 --- a/tests/handover/Makefile.am +++ b/tests/handover/Makefile.am @@ -21,14 +21,13 @@ AM_LDFLAGS = \ $(NULL) EXTRA_DIST = \ - handover_test.ok \ - neighbor_ident_test.ok \ - neighbor_ident_test.err \ + handover_tests.sh \ + handover_tests.ok \ + $(srcdir)/test*.ho_vty \ $(NULL) -noinst_PROGRAMS = \ +check_PROGRAMS = \ handover_test \ - neighbor_ident_test \ $(NULL) handover_test_SOURCES = \ @@ -41,62 +40,7 @@ handover_test_LDFLAGS = \ $(NULL) handover_test_LDADD = \ - $(top_builddir)/src/osmo-bsc/a_reset.o \ - $(top_builddir)/src/osmo-bsc/abis_nm.o \ - $(top_builddir)/src/osmo-bsc/abis_nm_vty.o \ - $(top_builddir)/src/osmo-bsc/abis_om2000.o \ - $(top_builddir)/src/osmo-bsc/abis_om2000_vty.o \ - $(top_builddir)/src/osmo-bsc/abis_rsl.o \ - $(top_builddir)/src/osmo-bsc/acc.o \ - $(top_builddir)/src/osmo-bsc/arfcn_range_encode.o \ - $(top_builddir)/src/osmo-bsc/assignment_fsm.o \ - $(top_builddir)/src/osmo-bsc/bsc_ctrl_commands.o \ - $(top_builddir)/src/osmo-bsc/bsc_init.o \ - $(top_builddir)/src/osmo-bsc/bsc_rf_ctrl.o \ - $(top_builddir)/src/osmo-bsc/bsc_rll.o \ - $(top_builddir)/src/osmo-bsc/bsc_subscr_conn_fsm.o \ - $(top_builddir)/src/osmo-bsc/bsc_subscriber.o \ - $(top_builddir)/src/osmo-bsc/bsc_vty.o \ - $(top_builddir)/src/osmo-bsc/bts.o \ - $(top_builddir)/src/osmo-bsc/bts_trx.o \ - $(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts.o \ - $(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.o \ - $(top_builddir)/src/osmo-bsc/bts_unknown.o \ - $(top_builddir)/src/osmo-bsc/chan_alloc.o \ - $(top_builddir)/src/osmo-bsc/codec_pref.o \ - $(top_builddir)/src/osmo-bsc/gsm_04_08_rr.o \ - $(top_builddir)/src/osmo-bsc/gsm_data.o \ - $(top_builddir)/src/osmo-bsc/handover_cfg.o \ - $(top_builddir)/src/osmo-bsc/handover_decision.o \ - $(top_builddir)/src/osmo-bsc/handover_decision_2.o \ - $(top_builddir)/src/osmo-bsc/handover_fsm.o \ - $(top_builddir)/src/osmo-bsc/handover_logic.o \ - $(top_builddir)/src/osmo-bsc/handover_vty.o \ - $(top_builddir)/src/osmo-bsc/lchan_fsm.o \ - $(top_builddir)/src/osmo-bsc/lchan_rtp_fsm.o \ - $(top_builddir)/src/osmo-bsc/lchan_select.o \ - $(top_builddir)/src/osmo-bsc/meas_feed.o \ - $(top_builddir)/src/osmo-bsc/meas_rep.o \ - $(top_builddir)/src/osmo-bsc/neighbor_ident.o \ - $(top_builddir)/src/osmo-bsc/neighbor_ident_vty.o \ - $(top_builddir)/src/osmo-bsc/net_init.o \ - $(top_builddir)/src/osmo-bsc/osmo_bsc_ctrl.o \ - $(top_builddir)/src/osmo-bsc/osmo_bsc_lcls.o \ - $(top_builddir)/src/osmo-bsc/osmo_bsc_mgcp.o \ - $(top_builddir)/src/osmo-bsc/osmo_bsc_msc.o \ - $(top_builddir)/src/osmo-bsc/paging.o \ - $(top_builddir)/src/osmo-bsc/pcu_sock.o \ - $(top_builddir)/src/osmo-bsc/penalty_timers.o \ - $(top_builddir)/src/osmo-bsc/rest_octets.o \ - $(top_builddir)/src/osmo-bsc/system_information.o \ - $(top_builddir)/src/osmo-bsc/timeslot_fsm.o \ - $(top_builddir)/src/osmo-bsc/smscb.o \ - $(top_builddir)/src/osmo-bsc/cbch_scheduler.o \ - $(top_builddir)/src/osmo-bsc/cbsp_link.o \ - $(top_builddir)/src/osmo-bsc/lcs_loc_req.o \ - $(top_builddir)/src/osmo-bsc/lcs_ta_req.o \ - $(top_builddir)/src/osmo-bsc/lb.o \ - $(top_builddir)/src/osmo-bsc/bsc_sccp.o \ + $(top_builddir)/src/osmo-bsc/libbsc.la \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOCTRL_LIBS) \ @@ -107,17 +51,6 @@ handover_test_LDADD = \ $(LIBOSMOMGCPCLIENT_LIBS) \ $(NULL) -neighbor_ident_test_SOURCES = \ - neighbor_ident_test.c \ - $(NULL) - -neighbor_ident_test_LDADD = \ - $(top_builddir)/src/osmo-bsc/neighbor_ident.o \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOCTRL_LIBS) \ - $(NULL) - .PHONY: update_exp update_exp: - $(builddir)/neighbor_ident_test >$(srcdir)/neighbor_ident_test.ok 2>$(srcdir)/neighbor_ident_test.err + $(srcdir)/handover_tests.sh $(srcdir) $(builddir) -u diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c index 84a48af5d..2c54526eb 100644 --- a/tests/handover/handover_test.c +++ b/tests/handover/handover_test.c @@ -20,12 +20,14 @@ #include <stdio.h> #include <stdlib.h> #include <errno.h> +#include <inttypes.h> #include <assert.h> #include <osmocom/core/application.h> #include <osmocom/core/select.h> #include <osmocom/core/talloc.h> +#include <osmocom/vty/vty.h> #include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h> @@ -34,6 +36,7 @@ #include <osmocom/bsc/bsc_subscriber.h> #include <osmocom/bsc/lchan_select.h> #include <osmocom/bsc/lchan_fsm.h> +#include <osmocom/bsc/assignment_fsm.h> #include <osmocom/bsc/handover_decision.h> #include <osmocom/bsc/system_information.h> #include <osmocom/bsc/handover.h> @@ -49,10 +52,13 @@ #include <osmocom/bsc/bsc_msc_data.h> #include <osmocom/bsc/bts.h> #include <osmocom/bsc/paging.h> +#include <osmocom/bsc/vty.h> +#include <osmocom/bsc/lchan.h> +#include <osmocom/mgcp_client/mgcp_client_pool.h> -void *ctx; +#include "../../bscconfig.h" -struct gsm_network *bsc_gsmnet; +void *ctx; /* override, requires '-Wl,--wrap=osmo_mgcpc_ep_ci_request'. * Catch modification of an MGCP connection. */ @@ -77,20 +83,64 @@ void __wrap_osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci, /* measurement report */ uint8_t meas_rep_ba = 0, meas_rep_valid = 1, meas_valid = 1, meas_multi_rep = 0; -uint8_t meas_dl_rxlev = 0, meas_dl_rxqual = 0; uint8_t meas_ul_rxlev = 0, meas_ul_rxqual = 0; -uint8_t meas_tx_power_ms = 0, meas_tx_power_bs = 0, meas_ta_ms = 0; +uint8_t meas_tx_power_ms = 0; uint8_t meas_dtx_ms = 0, meas_dtx_bs = 0, meas_nr = 0; -uint8_t meas_num_nc = 0, meas_rxlev_nc[6], meas_bsic_nc[6], meas_bcch_f_nc[6]; +char *codec_tch_f = NULL; +char *codec_tch_h = NULL; + +struct neighbor_meas { + uint8_t rxlev; + uint8_t bsic; + uint8_t bcch_f; +}; + +const struct timeval fake_time_start_time = { 123, 456 }; + +void fake_time_passes(time_t secs, suseconds_t usecs) +{ + struct timeval diff; + /* Add time to osmo_fsm timers, using osmo_gettimeofday() */ + osmo_gettimeofday_override_add(secs, usecs); + /* Add time to penalty timers, using osmo_clock_gettime() */ + osmo_clock_override_add(CLOCK_MONOTONIC, secs, usecs * 1000); + + timersub(&osmo_gettimeofday_override_time, &fake_time_start_time, &diff); + fprintf(stderr, "Total time passed: %d.%06d s\n", (int)diff.tv_sec, (int)diff.tv_usec); + + osmo_timers_prepare(); + osmo_timers_update(); +} + +void fake_time_start() +{ + struct timespec *clock_override; + + /* osmo_fsm uses osmo_gettimeofday(). To affect FSM timeouts, we need osmo_gettimeofday_override. */ + osmo_gettimeofday_override_time = fake_time_start_time; + osmo_gettimeofday_override = true; + + /* Penalty timers use osmo_clock_gettime(CLOCK_MONOTONIC). To affect these timeouts, we need + * osmo_gettimeofday_override. */ + clock_override = osmo_clock_override_gettimespec(CLOCK_MONOTONIC); + OSMO_ASSERT(clock_override); + clock_override->tv_sec = fake_time_start_time.tv_sec; + clock_override->tv_nsec = fake_time_start_time.tv_usec * 1000; + osmo_clock_override_enable(CLOCK_MONOTONIC, true); + fake_time_passes(0, 0); +} -static void gen_meas_rep(struct gsm_lchan *lchan) +static void gen_meas_rep(struct gsm_lchan *lchan, + uint8_t bs_power_db, uint8_t rxlev, uint8_t rxqual, uint8_t ta, + int neighbors_count, struct neighbor_meas *neighbors) { struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL"); struct abis_rsl_dchan_hdr *dh; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t ulm[3], l1i[2], *buf; struct gsm48_hdr *gh; struct gsm48_meas_res *mr; + int chan_nr = gsm_lchan2chan_nr(lchan, true); + OSMO_ASSERT(chan_nr >= 0); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; @@ -105,10 +155,10 @@ static void gen_meas_rep(struct gsm_lchan *lchan) ulm[2] = (meas_ul_rxqual << 3) | meas_ul_rxqual; msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, sizeof(ulm), ulm); - msgb_tv_put(msg, RSL_IE_BS_POWER, meas_tx_power_bs); + msgb_tv_put(msg, RSL_IE_BS_POWER, (bs_power_db / 2) & 0xf); l1i[0] = 0; - l1i[1] = meas_ta_ms; + l1i[1] = ta; msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, sizeof(l1i), l1i); buf = msgb_put(msg, 3); @@ -123,71 +173,94 @@ static void gen_meas_rep(struct gsm_lchan *lchan) gh->msg_type = GSM48_MT_RR_MEAS_REP; /* measurement results */ - mr->rxlev_full = meas_dl_rxlev; - mr->rxlev_sub = meas_dl_rxlev; - mr->rxqual_full = meas_dl_rxqual; - mr->rxqual_sub = meas_dl_rxqual; + mr->rxlev_full = rxlev; + mr->rxlev_sub = rxlev; + mr->rxqual_full = rxqual; + mr->rxqual_sub = rxqual; mr->dtx_used = meas_dtx_ms; mr->ba_used = meas_rep_ba; - mr->meas_valid = !meas_valid; /* 0 = valid */ - if (meas_rep_valid) { - mr->no_nc_n_hi = meas_num_nc >> 2; - mr->no_nc_n_lo = meas_num_nc & 3; - } else { - /* no results for serving cells */ - mr->no_nc_n_hi = 1; - mr->no_nc_n_lo = 3; - } - mr->rxlev_nc1 = meas_rxlev_nc[0]; - mr->rxlev_nc2_hi = meas_rxlev_nc[1] >> 1; - mr->rxlev_nc2_lo = meas_rxlev_nc[1] & 1; - mr->rxlev_nc3_hi = meas_rxlev_nc[2] >> 2; - mr->rxlev_nc3_lo = meas_rxlev_nc[2] & 3; - mr->rxlev_nc4_hi = meas_rxlev_nc[3] >> 3; - mr->rxlev_nc4_lo = meas_rxlev_nc[3] & 7; - mr->rxlev_nc5_hi = meas_rxlev_nc[4] >> 4; - mr->rxlev_nc5_lo = meas_rxlev_nc[4] & 15; - mr->rxlev_nc6_hi = meas_rxlev_nc[5] >> 5; - mr->rxlev_nc6_lo = meas_rxlev_nc[5] & 31; - mr->bsic_nc1_hi = meas_bsic_nc[0] >> 3; - mr->bsic_nc1_lo = meas_bsic_nc[0] & 7; - mr->bsic_nc2_hi = meas_bsic_nc[1] >> 4; - mr->bsic_nc2_lo = meas_bsic_nc[1] & 15; - mr->bsic_nc3_hi = meas_bsic_nc[2] >> 5; - mr->bsic_nc3_lo = meas_bsic_nc[2] & 31; - mr->bsic_nc4 = meas_bsic_nc[3]; - mr->bsic_nc5 = meas_bsic_nc[4]; - mr->bsic_nc6 = meas_bsic_nc[5]; - mr->bcch_f_nc1 = meas_bcch_f_nc[0]; - mr->bcch_f_nc2 = meas_bcch_f_nc[1]; - mr->bcch_f_nc3 = meas_bcch_f_nc[2]; - mr->bcch_f_nc4 = meas_bcch_f_nc[3]; - mr->bcch_f_nc5_hi = meas_bcch_f_nc[4] >> 1; - mr->bcch_f_nc5_lo = meas_bcch_f_nc[4] & 1; - mr->bcch_f_nc6_hi = meas_bcch_f_nc[5] >> 2; - mr->bcch_f_nc6_lo = meas_bcch_f_nc[5] & 3; - - msg->dst = lchan->ts->trx->bts->c0->rsl_link; + mr->meas_valid = 0; /* 0 = valid */ + mr->no_nc_n_hi = neighbors_count >> 2; + mr->no_nc_n_lo = neighbors_count & 3; + + mr->rxlev_nc1 = neighbors[0].rxlev; + mr->rxlev_nc2_hi = neighbors[1].rxlev >> 1; + mr->rxlev_nc2_lo = neighbors[1].rxlev & 1; + mr->rxlev_nc3_hi = neighbors[2].rxlev >> 2; + mr->rxlev_nc3_lo = neighbors[2].rxlev & 3; + mr->rxlev_nc4_hi = neighbors[3].rxlev >> 3; + mr->rxlev_nc4_lo = neighbors[3].rxlev & 7; + mr->rxlev_nc5_hi = neighbors[4].rxlev >> 4; + mr->rxlev_nc5_lo = neighbors[4].rxlev & 15; + mr->rxlev_nc6_hi = neighbors[5].rxlev >> 5; + mr->rxlev_nc6_lo = neighbors[5].rxlev & 31; + mr->bsic_nc1_hi = neighbors[0].bsic >> 3; + mr->bsic_nc1_lo = neighbors[0].bsic & 7; + mr->bsic_nc2_hi = neighbors[1].bsic >> 4; + mr->bsic_nc2_lo = neighbors[1].bsic & 15; + mr->bsic_nc3_hi = neighbors[2].bsic >> 5; + mr->bsic_nc3_lo = neighbors[2].bsic & 31; + mr->bsic_nc4 = neighbors[3].bsic; + mr->bsic_nc5 = neighbors[4].bsic; + mr->bsic_nc6 = neighbors[5].bsic; + mr->bcch_f_nc1 = neighbors[0].bcch_f; + mr->bcch_f_nc2 = neighbors[1].bcch_f; + mr->bcch_f_nc3 = neighbors[2].bcch_f; + mr->bcch_f_nc4 = neighbors[3].bcch_f; + mr->bcch_f_nc5_hi = neighbors[4].bcch_f >> 1; + mr->bcch_f_nc5_lo = neighbors[4].bcch_f & 1; + mr->bcch_f_nc6_hi = neighbors[5].bcch_f >> 2; + mr->bcch_f_nc6_lo = neighbors[5].bcch_f & 3; + + msg->dst = rsl_chan_link(lchan); msg->l2h = (unsigned char *)dh; msg->l3h = (unsigned char *)gh; abis_rsl_rcvmsg(msg); } -static struct gsm_bts *create_bts(int arfcn) +enum gsm_phys_chan_config pchan_from_str(const char *str) { + enum gsm_phys_chan_config pchan; + if (!strcmp(str, "dyn")) + return GSM_PCHAN_OSMO_DYN; + if (!strcmp(str, "c+s4")) + return GSM_PCHAN_CCCH_SDCCH4; + if (!strcmp(str, "-")) + return GSM_PCHAN_NONE; + pchan = gsm_pchan_parse(str); + if (pchan < 0) { + fprintf(stderr, "Invalid timeslot pchan type: %s\n", str); + exit(1); + } + return pchan; +} + +const char * const bts_default_ts[] = { + "c+s4", "TCH/F", "TCH/F", "TCH/F", "TCH/F", "TCH/H", "TCH/H", "-", +}; + +static struct gsm_bts *_create_bts(int num_trx, const char * const *ts_args, int ts_args_count) +{ + static int arfcn = 870; + static int ci = 0; struct gsm_bts *bts; struct e1inp_sign_link *rsl_link; int i; + int trx_i; + struct gsm_bts_trx *trx; + + fprintf(stderr, "- Creating BTS %d, %d TRX\n", bsc_gsmnet->num_bts, num_trx); - bts = bsc_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_UNKNOWN, 0x3f); + bts = bsc_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_UNKNOWN, HARDCODED_BSIC); if (!bts) { - printf("No resource for bts1\n"); + fprintf(stderr, "No resource for bts1\n"); return NULL; } - bts->location_area_code = 23; - bts->c0->arfcn = arfcn; + bts->location_area_code = 0x0017; + bts->cell_identity = ci++; + bts->c0->arfcn = arfcn++; bts->codec.efr = 1; bts->codec.hr = 1; @@ -195,31 +268,140 @@ static struct gsm_bts *create_bts(int arfcn) rsl_link = talloc_zero(ctx, struct e1inp_sign_link); rsl_link->trx = bts->c0; - bts->c0->rsl_link = rsl_link; - - bts->c0->mo.nm_state.operational = NM_OPSTATE_ENABLED; - bts->c0->mo.nm_state.availability = NM_AVSTATE_OK; - bts->c0->mo.nm_state.administrative = NM_STATE_UNLOCKED; - bts->c0->bb_transc.mo.nm_state.operational = NM_OPSTATE_ENABLED; - bts->c0->bb_transc.mo.nm_state.availability = NM_AVSTATE_OK; - bts->c0->bb_transc.mo.nm_state.administrative = NM_STATE_UNLOCKED; - - /* 4 full rate and 4 half rate channels */ - for (i = 1; i <= 6; i++) { - bts->c0->ts[i].pchan_from_config = (i < 5) ? GSM_PCHAN_TCH_F : GSM_PCHAN_TCH_H; - bts->c0->ts[i].mo.nm_state.operational = NM_OPSTATE_ENABLED; - bts->c0->ts[i].mo.nm_state.availability = NM_AVSTATE_OK; - bts->c0->ts[i].mo.nm_state.administrative = NM_STATE_UNLOCKED; + bts->c0->rsl_link_primary = rsl_link; + + for (trx_i = 0; trx_i < num_trx; trx_i++) { + while (!(trx = gsm_bts_trx_num(bts, trx_i))) + gsm_bts_trx_alloc(bts); + + trx->mo.nm_state.operational = NM_OPSTATE_ENABLED; + trx->mo.nm_state.availability = NM_AVSTATE_OK; + trx->mo.nm_state.administrative = NM_STATE_UNLOCKED; + trx->bb_transc.mo.nm_state.operational = NM_OPSTATE_ENABLED; + trx->bb_transc.mo.nm_state.availability = NM_AVSTATE_OK; + trx->bb_transc.mo.nm_state.administrative = NM_STATE_UNLOCKED; + + /* 4 full rate and 4 half rate channels */ + for (i = 0; i < 8; i++) { + int arg_i = trx_i * 8 + i; + const char *ts_arg; + if (arg_i >= ts_args_count) + ts_arg = bts_default_ts[i]; + else + ts_arg = ts_args[arg_i]; + fprintf(stderr, "\t%s", ts_arg); + trx->ts[i].pchan_from_config = pchan_from_str(ts_arg); + if (trx->ts[i].pchan_from_config == GSM_PCHAN_NONE) + continue; + trx->ts[i].mo.nm_state.operational = NM_OPSTATE_ENABLED; + trx->ts[i].mo.nm_state.availability = NM_AVSTATE_OK; + trx->ts[i].mo.nm_state.administrative = NM_STATE_UNLOCKED; + } + fprintf(stderr, "\n"); + + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + /* make sure ts->lchans[] get initialized */ + osmo_fsm_inst_dispatch(trx->ts[i].fi, TS_EV_RSL_READY, 0); + osmo_fsm_inst_dispatch(trx->ts[i].fi, TS_EV_OML_READY, 0); + + /* Unused dyn TS start out as used for PDCH */ + switch (trx->ts[i].pchan_on_init) { + case GSM_PCHAN_OSMO_DYN: + case GSM_PCHAN_TCH_F_PDCH: + ts_set_pchan_is(&trx->ts[i], GSM_PCHAN_PDCH); + break; + default: + break; + } + } } - for (i = 0; i < ARRAY_SIZE(bts->c0->ts); i++) { - /* make sure ts->lchans[] get initialized */ - osmo_fsm_inst_dispatch(bts->c0->ts[i].fi, TS_EV_RSL_READY, 0); - osmo_fsm_inst_dispatch(bts->c0->ts[i].fi, TS_EV_OML_READY, 0); + for (i = 0; i < bsc_gsmnet->num_bts; i++) { + if (gsm_generate_si(gsm_bts_num(bsc_gsmnet, i), SYSINFO_TYPE_2) <= 0) + fprintf(stderr, "Error generating SI2\n"); } return bts; } +char *lchans_use_str(struct gsm_bts_trx_ts *ts, const char *established_prefix, char established_char) +{ + char state_chars[8] = { 0 }; + struct gsm_lchan *lchan; + bool any_lchans_established = false; + bool any_lchans_in_use = false; + ts_for_n_lchans(lchan, ts, ts->max_primary_lchans) { + char state_char; + if (lchan_state_is(lchan, LCHAN_ST_UNUSED)) { + state_char = '-'; + } else { + any_lchans_in_use = true; + if (lchan_state_is(lchan, LCHAN_ST_ESTABLISHED)) { + any_lchans_established = true; + state_char = established_char; + } else { + state_char = '!'; + } + } + state_chars[lchan->nr] = state_char; + } + if (!any_lchans_in_use) + return "-"; + if (!any_lchans_established) + established_prefix = ""; + return talloc_asprintf(OTC_SELECT, "%s%s", established_prefix, state_chars); +} + +const char *ts_use_str(struct gsm_bts_trx_ts *ts) +{ + switch (ts->pchan_is) { + case GSM_PCHAN_CCCH_SDCCH4: + return "c+s4"; + + case GSM_PCHAN_NONE: + return "-"; + + case GSM_PCHAN_TCH_F: + return lchans_use_str(ts, "TCH/", 'F'); + + case GSM_PCHAN_TCH_H: + return lchans_use_str(ts, "TCH/", 'H'); + + default: + return gsm_pchan_name(ts->pchan_is); + } +} + +bool _expect_ts_use(struct gsm_bts *bts, struct gsm_bts_trx *trx, const char * const *ts_use) +{ + int i; + int mismatching_ts = -1; + + fprintf(stderr, "bts %d trx %d: expect:", bts->nr, trx->nr); + for (i = 0; i < 8; i++) + fprintf(stderr, "\t%s", ts_use[i]); + fprintf(stderr, "\nbts %d trx %d: got:", bts->nr, trx->nr); + + for (i = 0; i < 8; i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + const char *use = ts_use_str(ts); + + fprintf(stderr, "\t%s", use); + + if (!strcmp(ts_use[i], "*")) + continue; + if (strcasecmp(ts_use[i], use) && mismatching_ts < 0) + mismatching_ts = i; + } + fprintf(stderr, "\n"); + + if (mismatching_ts >= 0) { + fprintf(stderr, "Test failed: mismatching TS use in bts %d trx %d ts %d\n", + bts->nr, trx->nr, mismatching_ts); + return false; + } + return true; +} + void create_conn(struct gsm_lchan *lchan) { static unsigned int next_imsi = 0; @@ -246,39 +428,45 @@ void create_conn(struct gsm_lchan *lchan) snprintf(imsi, sizeof(imsi), "%06u", next_imsi); lchan->conn->bsub = bsc_subscr_find_or_create_by_imsi(net->bsc_subscribers, imsi, BSUB_USE_CONN); + /* Set RTP data that the MSC normally would have sent */ + OSMO_STRLCPY_ARRAY(conn->user_plane.msc_assigned_rtp_addr, "1.2.3.4"); + conn->user_plane.msc_assigned_rtp_port = 1234; + /* kick the FSM from INIT through to the ACTIVE state */ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MO_COMPL_L3, NULL); osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_CFM, NULL); } -/* create lchan */ -struct gsm_lchan *create_lchan(struct gsm_bts *bts, int full_rate, char *codec) +struct gsm_lchan *lchan_act(struct gsm_lchan *lchan, int full_rate, const char *codec) { - struct gsm_lchan *lchan; - - lchan = lchan_select_by_type(bts, (full_rate) ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H); - if (!lchan) { - printf("No resource for lchan\n"); - exit(EXIT_FAILURE); - } - /* serious hack into osmo_fsm */ lchan->fi->state = LCHAN_ST_ESTABLISHED; lchan->ts->fi->state = TS_ST_IN_USE; + lchan->type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H; + /* Fake osmo_mgcpc_ep_ci to indicate that the lchan is used for voice */ + lchan->mgw_endpoint_ci_bts = (void*)1; + + if (lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN) + ts_set_pchan_is(lchan->ts, full_rate ? GSM_PCHAN_TCH_F : GSM_PCHAN_TCH_H); + if (lchan->ts->pchan_on_init == GSM_PCHAN_TCH_F_PDCH) { + OSMO_ASSERT(full_rate); + ts_set_pchan_is(lchan->ts, GSM_PCHAN_TCH_F); + } + LOG_LCHAN(lchan, LOGL_DEBUG, "activated by handover_test.c\n"); create_conn(lchan); if (!strcasecmp(codec, "FR") && full_rate) - lchan->tch_mode = GSM48_CMODE_SPEECH_V1; + lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_V1; else if (!strcasecmp(codec, "HR") && !full_rate) - lchan->tch_mode = GSM48_CMODE_SPEECH_V1; + lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_V1; else if (!strcasecmp(codec, "EFR") && full_rate) - lchan->tch_mode = GSM48_CMODE_SPEECH_EFR; + lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_EFR; else if (!strcasecmp(codec, "AMR")) { - lchan->tch_mode = GSM48_CMODE_SPEECH_AMR; - lchan->activate.info.s15_s0 = 0x0002; + lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_AMR; + lchan->current_ch_mode_rate.s15_s0 = 0x0002; } else { - printf("Given codec unknown\n"); + fprintf(stderr, "Given codec unknown\n"); exit(EXIT_FAILURE); } @@ -293,63 +481,128 @@ struct gsm_lchan *create_lchan(struct gsm_bts *bts, int full_rate, char *codec) .len = 5, }; + chan_counts_ts_update(lchan->ts); + return lchan; } -/* parse channel request */ +struct gsm_lchan *create_lchan(struct gsm_bts *bts, int full_rate, const char *codec) +{ + struct gsm_lchan *lchan; -static int got_chan_req = 0; -static struct gsm_lchan *chan_req_lchan = NULL; + lchan = lchan_select_by_type(bts, (full_rate) ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H, + SELECT_FOR_HANDOVER, NULL); + if (!lchan) { + fprintf(stderr, "No resource for lchan\n"); + exit(EXIT_FAILURE); + } + + return lchan_act(lchan, full_rate, codec); +} -static int parse_chan_act(struct gsm_lchan *lchan, uint8_t *data) +static void lchan_release_ack(struct gsm_lchan *lchan) { - chan_req_lchan = lchan; - return 0; + if (!lchan->fi || lchan->fi->state != LCHAN_ST_WAIT_BEFORE_RF_RELEASE) + return; + /* don't wait before release */ + osmo_fsm_inst_state_chg(lchan->fi, LCHAN_ST_WAIT_RF_RELEASE_ACK, 0, 0); + if (lchan->fi->state == LCHAN_ST_UNUSED) + return; + /* ack the release */ + osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RSL_RF_CHAN_REL_ACK, 0); } -static int parse_chan_rel(struct gsm_lchan *lchan, uint8_t *data) +static void lchan_clear(struct gsm_lchan *lchan) { - chan_req_lchan = lchan; - return 0; + lchan_release(lchan, true, false, 0, NULL); + lchan_release_ack(lchan); } -/* parse handover request */ +static void ts_clear(struct gsm_bts_trx_ts *ts) +{ + struct gsm_lchan *lchan; -static int got_ho_req = 0; -static struct gsm_lchan *ho_req_lchan = NULL; + ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) { + if (lchan_state_is(lchan, LCHAN_ST_UNUSED)) + continue; + lchan_clear(lchan); + } + chan_counts_ts_update(ts); +} -static int parse_ho_command(struct gsm_lchan *lchan, uint8_t *data, int len) +bool _set_ts_use(struct gsm_bts *bts, struct gsm_bts_trx *trx, const char * const *ts_use) { - struct gsm48_hdr *gh = (struct gsm48_hdr *) data; - struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *) gh->data; - int arfcn; - struct gsm_bts *neigh; - - switch (gh->msg_type) { - case GSM48_MT_RR_HANDO_CMD: - arfcn = (ho->cell_desc.arfcn_hi << 8) | ho->cell_desc.arfcn_lo; - - /* look up trx. since every dummy bts uses different arfcn and - * only one trx, it is simple */ - llist_for_each_entry(neigh, &bsc_gsmnet->bts_list, list) { - if (neigh->c0->arfcn != arfcn) - continue; - ho_req_lchan = lchan; - return 0; + int i; + + fprintf(stderr, "Setting TS use:"); + for (i = 0; i < 8; i++) + fprintf(stderr, "\t%s", ts_use[i]); + fprintf(stderr, "\n"); + + for (i = 0; i < 8; i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + const char *want_use = ts_use[i]; + const char *is_use = ts_use_str(ts); + + if (!strcmp(want_use, "*")) + continue; + + /* If it is already as desired, don't change anything */ + if (!strcasecmp(want_use, is_use)) + continue; + + if (!strcasecmp(want_use, "tch/f")) { + if (!ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_F)) { + fprintf(stderr, "Error: bts %d trx %d ts %d cannot be used as TCH/F\n", + bts->nr, trx->nr, i); + return false; + } + ts_clear(ts); + + lchan_act(&ts->lchan[0], true, codec_tch_f ? : "AMR"); + } else if (!strcasecmp(want_use, "tch/h-") + || !strcasecmp(want_use, "tch/hh") + || !strcasecmp(want_use, "tch/-h")) { + bool act[2]; + int j; + + if (!ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_H)) { + fprintf(stderr, "Error: bts %d trx %d ts %d cannot be used as TCH/H\n", + bts->nr, trx->nr, i); + return false; + } + + if (ts->pchan_is != GSM_PCHAN_TCH_H) + ts_clear(ts); + + act[0] = (want_use[4] == 'h' || want_use[4] == 'H'); + act[1] = (want_use[5] == 'h' || want_use[5] == 'H'); + + for (j = 0; j < 2; j++) { + if (lchan_state_is(&ts->lchan[j], LCHAN_ST_UNUSED)) { + if (act[j]) + lchan_act(&ts->lchan[j], false, codec_tch_h ? : "AMR"); + } else if (!act[j]) + lchan_clear(&ts->lchan[j]); + } + } else if (!strcmp(want_use, "-") || !strcasecmp(want_use, "PDCH")) { + ts_clear(ts); } - break; - case GSM48_MT_RR_ASS_CMD: - ho_req_lchan = lchan; - return 0; - break; - default: - fprintf(stderr, "Error, expecting HO or AS command\n"); - return -EINVAL; } - - return -1; + return true; } +/* parse channel request */ + +static struct gsm_lchan *new_chan_req = NULL; +static struct gsm_lchan *last_chan_req = NULL; + +static struct gsm_lchan *new_ho_cmd = NULL; +static struct gsm_lchan *last_ho_cmd = NULL; + +static struct gsm_lchan *new_as_cmd = NULL; +static struct gsm_lchan *last_as_cmd = NULL; + /* send channel activation ack */ static void send_chan_act_ack(struct gsm_lchan *lchan, int act) { @@ -360,20 +613,60 @@ static void send_chan_act_ack(struct gsm_lchan *lchan, int act) dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; dh->c.msg_type = (act) ? RSL_MT_CHAN_ACTIV_ACK : RSL_MT_RF_CHAN_REL_ACK; dh->ie_chan = RSL_IE_CHAN_NR; - dh->chan_nr = gsm_lchan2chan_nr(lchan); + dh->chan_nr = gsm_lchan2chan_nr(lchan, true); - msg->dst = lchan->ts->trx->bts->c0->rsl_link; + msg->dst = rsl_chan_link(lchan); msg->l2h = (unsigned char *)dh; abis_rsl_rcvmsg(msg); } +/* Send RR Assignment Complete for SAPI[0] */ +static void send_assignment_complete(struct gsm_lchan *lchan) +{ + struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL"); + struct abis_rsl_rll_hdr *rh; + uint8_t chan_nr = gsm_lchan2chan_nr(lchan, true); + uint8_t *buf; + struct gsm48_hdr *gh; + struct gsm48_ho_cpl *hc; + + fprintf(stderr, "- Send RR Assignment Complete for %s\n", gsm_lchan_name(lchan)); + + rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh)); + rh->c.msg_discr = ABIS_RSL_MDISC_RLL; + rh->c.msg_type = RSL_MT_DATA_IND; + rh->ie_chan = RSL_IE_CHAN_NR; + rh->chan_nr = chan_nr; + rh->ie_link_id = RSL_IE_LINK_IDENT; + rh->link_id = 0x00; + + buf = msgb_put(msg, 3); + buf[0] = RSL_IE_L3_INFO; + buf[1] = (sizeof(*gh) + sizeof(*hc)) >> 8; + buf[2] = (sizeof(*gh) + sizeof(*hc)) & 0xff; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + hc = (struct gsm48_ho_cpl *) msgb_put(msg, sizeof(*hc)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_ASS_COMPL; + + msg->dst = rsl_chan_link(lchan); + msg->l2h = (unsigned char *)rh; + msg->l3h = (unsigned char *)gh; + + abis_rsl_rcvmsg(msg); +} + /* Send RLL Est Ind for SAPI[0] */ static void send_est_ind(struct gsm_lchan *lchan) { struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL"); struct abis_rsl_rll_hdr *rh; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr(lchan, true); + + fprintf(stderr, "- Send EST IND for %s\n", gsm_lchan_name(lchan)); rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh)); rh->c.msg_discr = ABIS_RSL_MDISC_RLL; @@ -383,24 +676,51 @@ static void send_est_ind(struct gsm_lchan *lchan) rh->ie_link_id = RSL_IE_LINK_IDENT; rh->link_id = 0x00; - msg->dst = lchan->ts->trx->bts->c0->rsl_link; + msg->dst = rsl_chan_link(lchan); + msg->l2h = (unsigned char *)rh; + + abis_rsl_rcvmsg(msg); +} + +static void send_ho_detect(struct gsm_lchan *lchan) +{ + struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL"); + struct abis_rsl_rll_hdr *rh; + uint8_t chan_nr = gsm_lchan2chan_nr(lchan, true); + + fprintf(stderr, "- Send HO DETECT for %s\n", gsm_lchan_name(lchan)); + + rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh)); + rh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; + rh->c.msg_type = RSL_MT_HANDO_DET; + rh->ie_chan = RSL_IE_CHAN_NR; + rh->chan_nr = chan_nr; + rh->ie_link_id = RSL_IE_LINK_IDENT; + rh->link_id = 0x00; + + msg->dst = rsl_chan_link(lchan); msg->l2h = (unsigned char *)rh; abis_rsl_rcvmsg(msg); + + send_est_ind(lchan); + osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RTP_READY, 0); + } -/* send handover complete */ static void send_ho_complete(struct gsm_lchan *lchan, bool success) { struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL"); struct abis_rsl_rll_hdr *rh; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr(lchan, true); uint8_t *buf; struct gsm48_hdr *gh; struct gsm48_ho_cpl *hc; - send_est_ind(lchan); - osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RTP_READY, 0); + if (success) + fprintf(stderr, "- Send HO COMPLETE for %s\n", gsm_lchan_name(lchan)); + else + fprintf(stderr, "- Send HO FAIL to %s\n", gsm_lchan_name(lchan)); rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh)); rh->c.msg_discr = ABIS_RSL_MDISC_RLL; @@ -422,7 +742,7 @@ static void send_ho_complete(struct gsm_lchan *lchan, bool success) gh->msg_type = success ? GSM48_MT_RR_HANDO_COMPL : GSM48_MT_RR_HANDO_FAIL; - msg->dst = lchan->ts->trx->bts->c0->rsl_link; + msg->dst = rsl_chan_link(lchan); msg->l2h = (unsigned char *)rh; msg->l3h = (unsigned char *)gh; @@ -438,910 +758,797 @@ int __wrap_abis_rsl_sendmsg(struct msgb *msg) struct e1inp_sign_link *sign_link = msg->dst; int rc; struct gsm_lchan *lchan = rsl_lchan_lookup(sign_link->trx, dh->chan_nr, &rc); + struct gsm_lchan *other_lchan; + struct gsm48_hdr *gh; if (rc) { - printf("rsl_lchan_lookup() failed\n"); + fprintf(stderr, "rsl_lchan_lookup() failed\n"); exit(1); } switch (dh->c.msg_type) { case RSL_MT_CHAN_ACTIV: - rc = parse_chan_act(lchan, dh->data); - if (rc == 0) - got_chan_req = 1; + if (new_chan_req) { + fprintf(stderr, "Test script is erratic: a channel is requested" + " while a previous channel request is still unhandled\n"); + exit(1); + } + new_chan_req = lchan; break; case RSL_MT_RF_CHAN_REL: - rc = parse_chan_rel(lchan, dh->data); - if (rc == 0) - send_chan_act_ack(chan_req_lchan, 0); + send_chan_act_ack(lchan, 0); + + /* send dyn TS back to PDCH if unused */ + switch (lchan->ts->pchan_on_init) { + case GSM_PCHAN_OSMO_DYN: + case GSM_PCHAN_TCH_F_PDCH: + switch (lchan->ts->pchan_is) { + case GSM_PCHAN_TCH_H: + other_lchan = &lchan->ts->lchan[ + (lchan == &lchan->ts->lchan[0])? + 1 : 0]; + if (lchan_state_is(other_lchan, LCHAN_ST_ESTABLISHED)) + break; + /* else fall thru */ + case GSM_PCHAN_TCH_F: + ts_set_pchan_is(lchan->ts, GSM_PCHAN_PDCH); + break; + default: + break; + } + break; + default: + break; + } + break; case RSL_MT_DATA_REQ: - rc = parse_ho_command(lchan, msg->l3h, msgb_l3len(msg)); - if (rc == 0) - got_ho_req = 1; + gh = (struct gsm48_hdr*)msg->l3h; + switch (gh->msg_type) { + case GSM48_MT_RR_HANDO_CMD: + if (new_ho_cmd || new_as_cmd) { + fprintf(stderr, "Test script is erratic: seen a Handover Command" + " while a previous Assignment or Handover Command is still unhandled\n"); + exit(1); + } + new_ho_cmd = lchan; + break; + case GSM48_MT_RR_ASS_CMD: + if (new_ho_cmd || new_as_cmd) { + fprintf(stderr, "Test script is erratic: seen an Assignment Command" + " while a previous Assignment or Handover Command is still unhandled\n"); + exit(1); + } + new_as_cmd = lchan; + break; + } break; case RSL_MT_IPAC_CRCX: break; case RSL_MT_DEACTIVATE_SACCH: break; default: - printf("unknown rsl message=0x%x\n", dh->c.msg_type); + fprintf(stderr, "unknown rsl message=0x%x\n", dh->c.msg_type); } return 0; } -/* test cases */ +struct gsm_bts *bts_by_num_str(const char *num_str) +{ + struct gsm_bts *bts = gsm_bts_num(bsc_gsmnet, atoi(num_str)); + OSMO_ASSERT(bts); + return bts; +} -static char *test_case_0[] = { - "2", +struct gsm_bts_trx *trx_by_num_str(struct gsm_bts *bts, const char *num_str) +{ + struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, atoi(num_str)); + OSMO_ASSERT(trx); + return trx; +} - "Stay in better cell\n\n" - "There are many neighbor cells, but only the current cell is the best\n" - "cell, so no handover is performed\n", +#define LCHAN_ARGS "lchan <0-255> <0-255> <0-7> <0-7>" +#define LCHAN_ARGS_DOC "identify an lchan\nBTS nr\nTRX nr\nTimeslot nr\nSubslot nr\n" - "create-bts", "7", - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "0", "30","0", - "6","0","20","1","21","2","18","3","20","4","23","5","19", - "expect-no-chan", - NULL -}; +static struct gsm_lchan *parse_lchan_args(const char **argv) +{ + struct gsm_bts *bts = bts_by_num_str(argv[0]); + struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]); + struct gsm_bts_trx_ts *ts = &trx->ts[atoi(argv[2])]; + return &ts->lchan[atoi(argv[3])]; +} -static char *test_case_1[] = { - "2", - - "Handover to best better cell\n\n" - "The best neighbor cell is selected\n", - - "create-bts", "7", - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "0", "10","0", - "6","0","20","1","21","2","18","3","20","4","23","5","19", - "expect-chan", "5", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; +#define LCHAN_WILDCARD_ARGS "lchan (<0-255>|*) (<0-255>|*) (<0-7>|*) (<0-7>|*)" +#define LCHAN_WILDCARD_ARGS_DOC "identify an lchan\nBTS nr\nall BTS\nTRX nr\nall BTS\nTimeslot nr\nall TS\nSubslot nr\nall subslots\n" -static char *test_case_2[] = { - "2", - - "Handover and Assignment must be enabled\n\n" - "This test will start with disabled assignment and handover. A\n" - "better neighbor cell (assignment enabled) will not be selected and \n" - "also no assignment from TCH/H to TCH/F to improve quality. There\n" - "will be no handover nor assignment. After enabling assignment on the\n" - "current cell, the MS will assign to TCH/F. After enabling handover\n" - "in the current cell, but disabling in the neighbor cell, handover\n" - "will not be performed, until it is enabled in the neighbor cell too.\n", - - "create-bts", "2", - "afs-rxlev-improve", "0", "5", - "create-ms", "0", "TCH/H", "AMR", - "as-enable", "0", "0", - "ho-enable", "0", "0", - "meas-rep", "0", "0","0", "1","0","30", - "expect-no-chan", - "as-enable", "0", "1", - "meas-rep", "0", "0","0", "1","0","30", - "expect-chan", "0", "1", - "ack-chan", - "expect-ho", "0", "5", - "ho-complete", - "ho-enable", "0", "1", - "ho-enable", "1", "0", - "meas-rep", "0", "0","0", "1","0","30", - "expect-no-chan", - "ho-enable", "1", "1", - "meas-rep", "0", "0","0", "1","0","30", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; +static void parse_lchan_wildcard_args(const char **argv, void (*cb)(struct gsm_lchan*, void*), void *cb_data) +{ + const char *bts_str = argv[0]; + const char *trx_str = argv[1]; + const char *ts_str = argv[2]; + const char *ss_str = argv[3]; + int bts_num = (strcmp(bts_str, "*") == 0)? -1 : atoi(bts_str); + int trx_num = (strcmp(trx_str, "*") == 0)? -1 : atoi(trx_str); + int ts_num = (strcmp(ts_str, "*") == 0)? -1 : atoi(ts_str); + int ss_num = (strcmp(ss_str, "*") == 0)? -1 : atoi(ss_str); + + int bts_i; + int trx_i; + int ts_i; + int ss_i; + + for (bts_i = ((bts_num == -1) ? 0 : bts_num); + bts_i < ((bts_num == -1) ? bsc_gsmnet->num_bts : bts_num + 1); + bts_i++) { + struct gsm_bts *bts = gsm_bts_num(bsc_gsmnet, bts_i); + + for (trx_i = ((trx_num == -1) ? 0 : trx_num); + trx_i < ((trx_num == -1) ? bts->num_trx : trx_num + 1); + trx_i++) { + struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_i); + + for (ts_i = ((ts_num == -1) ? 0 : ts_num); + ts_i < ((ts_num == -1) ? 8 : ts_num + 1); + ts_i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[ts_i]; + + for (ss_i = ((ss_num == -1) ? 0 : ss_num); + ss_i < ((ss_num == -1) ? pchan_subslots(ts->pchan_is) : ss_num + 1); + ss_i++) { + cb(&ts->lchan[ss_i], cb_data); + } + } + } + } +} -static char *test_case_3[] = { - "2", - - "Penalty timer must not run\n\n" - "The MS will try to handover to a better cell, but this will fail.\n" - "Even though the cell is still better, handover will not be performed\n" - "due to penalty timer after handover failure\n", - - "create-bts", "2", - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "0", "20","0", "1","0","30", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-failed", - "meas-rep", "0", "20","0", "1","0","30", - "expect-no-chan", - NULL -}; +static int vty_step = 1; + +#define VTY_ECHO() \ + fprintf(stderr, "\n%d: %s\n", vty_step++, vty->buf) + +#define TS_USE " (TCH/F|TCH/H-|TCH/-H|TCH/HH|PDCH" \ + "|tch/f|tch/h-|tch/-h|tch/hh|pdch" \ + "|-|*)" +#define TS_USE_DOC "'TCH/F': one FR call\n" \ + "'TCH/H-': HR TS with first subslot used as TCH/H, other subslot unused\n" \ + "'TCH/HH': HR TS with both subslots used as TCH/H\n" \ + "'TCH/-H': HR TS with only second subslot used as TCH/H\n" \ + "'PDCH': TS used for PDCH (e.g. unused dynamic TS)\n" \ + "'tch/f': one FR call\n" \ + "'tch/h-': HR TS with first subslot used as TCH/H, other subslot unused\n" \ + "'tch/hh': HR TS with both subslots used as TCH/H\n" \ + "'tch/-h': HR TS with only second subslot used as TCH/H\n" \ + "'pdch': TS used for PDCH (e.g. unused dynamic TS)\n" \ + "'-': TS unused\n" \ + "'*': TS allowed to be in any state\n" + +DEFUN(create_n_bts, create_n_bts_cmd, + "create-n-bts <1-255>", + "Create a number of BTS with four TCH/F and four TCH/H timeslots\n" + "Number of BTS to create\n") +{ + int i; + int n = atoi(argv[0]); + VTY_ECHO(); + for (i = 0; i < n; i++) + _create_bts(1, NULL, 0); + return CMD_SUCCESS; +} -static char *test_case_4[] = { - "2", - - "TCH/H keeping with HR codec\n\n" - "The MS is using half rate V1 codec, but the better cell is congested\n" - "at TCH/H slots. As the congestion is removed, the handover takes\n" - "place.\n", - - "create-bts", "2", - "set-min-free", "1", "TCH/H", "4", - "create-ms", "0", "TCH/H", "HR", - "meas-rep", "0", "20","0", "1","0","30", - "expect-no-chan", - "set-min-free", "1", "TCH/H", "3", - "meas-rep", "0", "20","0", "1","0","30", - "expect-chan", "1", "5", - "ack-chan", - "expect-ho", "0", "5", - "ho-complete", - NULL -}; +DEFUN(create_bts, create_bts_cmd, + "create-bts trx-count <1-255> timeslots .TS_CFG", + "Create a new BTS with specific timeslot configuration\n" + "Create N TRX in the new BTS\n" + "TRX count\n" + "Timeslot config\n" + "Timeslot types for 8 * trx-count, each being one of CCCH+SDCCH4|SDCCH8|TCH/F|TCH/H|TCH/F_TCH/H_SDCCH8_PDCH|...;" + " shorthands: cs+4 = CCCH+SDCCH4; dyn = TCH/F_TCH/H_SDCCH8_PDCH\n") +{ + int num_trx = atoi(argv[0]); + VTY_ECHO(); + _create_bts(num_trx, argv + 1, argc - 1); + return CMD_SUCCESS; +} -static char *test_case_5[] = { - "2", - - "TCH/F keeping with FR codec\n\n" - "The MS is using full rate V1 codec, but the better cell is congested\n" - "at TCH/F slots. As the congestion is removed, the handover takes\n" - "place.\n", - - "create-bts", "2", - "set-min-free", "1", "TCH/F", "4", - "create-ms", "0", "TCH/F", "FR", - "meas-rep", "0", "20","0", "1","0","30", - "expect-no-chan", - "set-min-free", "1", "TCH/F", "3", - "meas-rep", "0", "20","0", "1","0","30", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; +DEFUN(create_ms, create_ms_cmd, + "create-ms bts <0-999> (TCH/F|TCH/H) (AMR|HR|EFR)", + "Create an MS using the next free matching lchan on a given BTS\n" + "BTS index to subscribe on\n" + "lchan type to select\n" + "codec\n") +{ + const char *bts_nr_str = argv[0]; + const char *tch_type = argv[1]; + const char *codec = argv[2]; + struct gsm_lchan *lchan; + VTY_ECHO(); + fprintf(stderr, "- Creating mobile at BTS %s on " + "%s with %s codec\n", bts_nr_str, tch_type, codec); + lchan = create_lchan(bts_by_num_str(bts_nr_str), + !strcmp(tch_type, "TCH/F"), codec); + if (!lchan) { + fprintf(stderr, "Failed to create lchan!\n"); + return CMD_WARNING; + } + fprintf(stderr, " * New MS is at %s\n", gsm_lchan_name(lchan)); + return CMD_SUCCESS; +} -static char *test_case_6[] = { - "2", - - "TCH/F keeping with EFR codec\n\n" - "The MS is using full rate V2 codec, but the better cell is congested\n" - "at TCH/F slots. As the congestion is removed, the handover takes\n" - "place.\n", - - "create-bts", "2", - "set-min-free", "1", "TCH/F", "4", - "create-ms", "0", "TCH/F", "EFR", - "meas-rep", "0", "20","0", "1","0","30", - "expect-no-chan", - "set-min-free", "1", "TCH/F", "3", - "meas-rep", "0", "20","0", "1","0","30", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL +struct meas_rep_data { + int argc; + const char **argv; + uint8_t bs_power_db; }; -static char *test_case_7[] = { - "2", - - "TCH/F to TCH/H changing with AMR codec\n\n" - "The MS is using AMR V3 codec, the better cell is congested at TCH/F\n" - "slots. The handover is performed to non-congested TCH/H slots.\n", - - "create-bts", "2", - "set-min-free", "1", "TCH/F", "4", - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "0", "20","0", "1","0","30", - "expect-chan", "1", "5", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; +static void _meas_rep_cb(struct gsm_lchan *lc, void *data) +{ + struct meas_rep_data *d = data; + int argc = d->argc; + const char **argv = d->argv; + uint8_t rxlev; + uint8_t rxqual; + uint8_t ta; + int i; + struct neighbor_meas nm[6] = {}; -static char *test_case_8[] = { - "2", - - "No handover to a cell with no slots available\n\n" - "If no slot is available, no handover is performed\n", - - "create-bts", "2", - "create-ms", "0", "TCH/F", "AMR", - "create-ms", "1", "TCH/F", "AMR", - "create-ms", "1", "TCH/F", "AMR", - "create-ms", "1", "TCH/F", "AMR", - "create-ms", "1", "TCH/F", "AMR", - "create-ms", "1", "TCH/H", "AMR", - "create-ms", "1", "TCH/H", "AMR", - "create-ms", "1", "TCH/H", "AMR", - "create-ms", "1", "TCH/H", "AMR", - "meas-rep", "0", "0","0", "1","0","30", - "expect-no-chan", - NULL -}; + if (!lchan_state_is(lc, LCHAN_ST_ESTABLISHED)) + return; -static char *test_case_9[] = { - "2", - - "No more parallel handovers, if max_unsync_ho is defined\n\n" - "There are tree mobiles that want to handover, but only two can do\n" - "it at a time, because the maximum number is limited to two.\n", - - "create-bts", "2", - "set-max-ho", "1", "2", - "create-ms", "0", "TCH/F", "AMR", - "create-ms", "0", "TCH/F", "AMR", - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "0", "0","0", "1","0","30", - "expect-chan", "1", "1", - "meas-rep", "1", "0","0", "1","0","30", - "expect-chan", "1", "2", - "meas-rep", "2", "0","0", "1","0","30", - "expect-no-chan", - NULL -}; + rxlev = atoi(argv[0]); + rxqual = atoi(argv[1]); + ta = atoi(argv[2]); + argv += 3; + argc -= 3; -static char *test_case_10[] = { - "2", - - "Hysteresis\n\n" - "If neighbor cell is better, handover is only performed if the\n" - "amount of improvement is greater or equal hyteresis\n", - - "create-bts", "2", - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "0", "27","0", "1","0","30", - "expect-no-chan", - "meas-rep", "0", "26","0", "1","0","30", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; + if (!lchan_state_is(lc, LCHAN_ST_ESTABLISHED)) { + fprintf(stderr, "Error: sending measurement report for %s which is in state %s\n", + gsm_lchan_name(lc), lchan_state_name(lc)); + exit(1); + } -static char *test_case_11[] = { - "2", - - "No Hysteresis and minimum RX level\n\n" - "If current cell's RX level is below mimium level, handover must be\n" - "performed, no matter of the hysteresis. First do not perform\n" - "handover to better neighbor cell, because the hysteresis is not\n" - "met. Second do not perform handover because better neighbor cell is\n" - "below minimum RX level. Third perform handover because current cell\n" - "is below minimum RX level, even if the better neighbor cell (minimum\n" - "RX level reached) does not meet the hysteresis.\n", - - "create-bts", "2", - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "0", "10","0", "1","0","11", - "expect-no-chan", - "meas-rep", "0", "8","0", "1","0","9", - "expect-no-chan", - "meas-rep", "0", "9","0", "1","0","10", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; + /* skip the optional [neighbors] keyword */ + if (argc) { + argv++; + argc--; + } -static char *test_case_12[] = { - "2", - - "No handover to congested cell\n\n" - "The better neighbor cell is congested, so no handover is performed.\n" - "After the congestion is over, handover will be performed.\n", - - "create-bts", "2", - "create-ms", "0", "TCH/F", "AMR", - "set-min-free", "1", "TCH/F", "4", - "set-min-free", "1", "TCH/H", "4", - "meas-rep", "0", "20","0", "1","0","30", - "expect-no-chan", - "set-min-free", "1", "TCH/F", "3", - "set-min-free", "1", "TCH/H", "3", - "meas-rep", "0", "20","0", "1","0","30", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; + fprintf(stderr, "- Sending measurement report from %s: rxlev=%u rxqual=%u ta=%u (%d neighbors)\n", + gsm_lchan_name(lc), rxlev, rxqual, ta, argc); + + for (i = 0; i < 6; i++) { + int neighbor_bts_nr = i; + /* since our bts is not in the list of neighbor cells, we need to shift */ + if (neighbor_bts_nr >= lc->ts->trx->bts->nr) + neighbor_bts_nr++; + nm[i] = (struct neighbor_meas){ + .rxlev = argc > i ? atoi(argv[i]) : 0, + .bsic = 0x3f, + .bcch_f = i, + }; + if (i < argc) + fprintf(stderr, " * Neighbor cell #%d, actual BTS %d: rxlev=%d\n", i, neighbor_bts_nr, + nm[i].rxlev); + } + gen_meas_rep(lc, d->bs_power_db, rxlev, rxqual, ta, argc, nm); +} -static char *test_case_13[] = { - "2", - - "Handover to balance congestion\n\n" - "The current and the better cell are congested, so no handover is\n" - "performed. This is because handover would congest the neighbor cell\n" - "more. After congestion raises in the current cell, the handover is\n" - "performed to balance congestion\n", - - "create-bts", "2", - "create-ms", "0", "TCH/F", "AMR", - "set-min-free", "0", "TCH/F", "4", - "set-min-free", "0", "TCH/H", "4", - "set-min-free", "1", "TCH/F", "4", - "set-min-free", "1", "TCH/H", "4", - "meas-rep", "0", "20","0", "1","0","30", - "expect-no-chan", - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "0", "20","0", "1","0","30", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; +static int _meas_rep(struct vty *vty, uint8_t bs_power_db, int argc, const char **argv) +{ + struct meas_rep_data d = { + .argc = argc - 4, + .argv = argv + 4, + .bs_power_db = bs_power_db, + }; + parse_lchan_wildcard_args(argv, _meas_rep_cb, &d); + return CMD_SUCCESS; +} -static char *test_case_14[] = { - "2", - - "Handover to congested cell, if RX level is below minimum\n\n" - "The better neighbor cell is congested, so no handover is performed.\n" - "If the RX level of the current cell drops below minimum acceptable\n" - "level, the handover is performed.\n", - - "create-bts", "2", - "create-ms", "0", "TCH/F", "AMR", - "set-min-free", "1", "TCH/F", "4", - "set-min-free", "1", "TCH/H", "4", - "meas-rep", "0", "10","0", "1","0","30", - "expect-no-chan", - "meas-rep", "0", "9","0", "1","0","30", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; -static char *test_case_15[] = { - "2", - - "Handover to cell with worse RXLEV, if RXQUAL is below minimum\n\n" - "The neighbor cell has worse RXLEV, so no handover is performed.\n" - "If the RXQUAL of the current cell drops below minimum acceptable\n" - "level, the handover is performed. It is also required that 10\n" - "reports are received, before RXQUAL is checked.\n", - /* (See also test 28, which tests for RXQUAL triggering HO to congested cell.) */ - /* TODO: bad RXQUAL may want to prefer assignment within the same cell to avoid interference. - * See Performance Enhancements in a Frequency Hopping GSM Network (Nielsen Wigard 2002), Chapter - * 2.1.1, "Interference" in the list of triggers on p.157. */ - - "create-bts", "2", - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "0", "40","6", "1","0","30", - "expect-no-chan", - "meas-rep", "0", "40","6", "1","0","30", - "expect-no-chan", - "meas-rep", "0", "40","6", "1","0","30", - "expect-no-chan", - "meas-rep", "0", "40","6", "1","0","30", - "expect-no-chan", - "meas-rep", "0", "40","6", "1","0","30", - "expect-no-chan", - "meas-rep", "0", "40","6", "1","0","30", - "expect-no-chan", - "meas-rep", "0", "40","6", "1","0","30", - "expect-no-chan", - "meas-rep", "0", "40","6", "1","0","30", - "expect-no-chan", - "meas-rep", "0", "40","6", "1","0","30", - "expect-no-chan", - "meas-rep", "0", "40","6", "1","0","30", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; +#define MEAS_REP_ARGS LCHAN_WILDCARD_ARGS " rxlev <0-255> rxqual <0-7> ta <0-255>" \ + " [neighbors] [<0-255>] [<0-255>] [<0-255>] [<0-255>] [<0-255>] [<0-255>]" +#define MEAS_REP_DOC "Send measurement report\n" +#define MEAS_REP_ARGS_DOC \ + LCHAN_WILDCARD_ARGS_DOC \ + "rxlev\nrxlev\n" \ + "rxqual\nrxqual\n" \ + "timing advance\ntiming advance\n" \ + "neighbors list of rxlev reported by each neighbor cell\n" \ + "neighbor 0 rxlev\n" \ + "neighbor 1 rxlev\n" \ + "neighbor 2 rxlev\n" \ + "neighbor 3 rxlev\n" \ + "neighbor 4 rxlev\n" \ + "neighbor 5 rxlev\n" + +DEFUN(meas_rep, meas_rep_cmd, + "meas-rep " MEAS_REP_ARGS, + MEAS_REP_DOC MEAS_REP_ARGS_DOC) +{ + VTY_ECHO(); + return _meas_rep(vty, 0, argc, argv); +} -static char *test_case_16[] = { - "2", - - "Handover due to maximum TA exceeded\n\n" - "The MS in the current (best) cell has reached maximum allowed timing\n" - "advance. No handover is performed until the timing advance exceeds\n" - "it. The originating cell is still the best, but no handover is\n" - "performed back to that cell, because the penalty timer (due to\n" - "maximum allowed timing advance) is running.\n", - - "create-bts", "2", - "create-ms", "0", "TCH/F", "AMR", - "set-max-ta", "0", "5", /* of cell */ - "set-ta", "0", "5", /* of ms */ - "meas-rep", "0", "30","0", "1","0","20", - "expect-no-chan", - "set-ta", "0", "6", /* of ms */ - "meas-rep", "0", "30","0", "1","0","20", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - "meas-rep", "0", "20","0", "1","0","30", - "expect-no-chan", - NULL -}; +DEFUN(meas_rep_repeat, meas_rep_repeat_cmd, + "meas-rep repeat <0-999> " MEAS_REP_ARGS, + MEAS_REP_DOC + "Resend the same measurement report N times\nN\n" + MEAS_REP_ARGS_DOC) +{ + int count = atoi(argv[0]); + VTY_ECHO(); + argv += 1; + argc -= 1; + + while (count--) + _meas_rep(vty, 0, argc, argv); + return CMD_SUCCESS; +} -static char *test_case_17[] = { - "2", - - "Congestion check: No congestion\n\n" - "Three cells have different number of used slots, but there is no\n" - "congestion in any of these cells. No handover is performed.\n", - - "create-bts", "3", - "set-min-free", "0", "TCH/F", "2", - "set-min-free", "0", "TCH/H", "2", - "set-min-free", "1", "TCH/F", "2", - "set-min-free", "1", "TCH/H", "2", - "set-min-free", "2", "TCH/F", "2", - "set-min-free", "2", "TCH/H", "2", - "create-ms", "0", "TCH/F", "AMR", - "create-ms", "0", "TCH/F", "AMR", - "create-ms", "0", "TCH/H", "AMR", - "create-ms", "0", "TCH/H", "AMR", - "create-ms", "1", "TCH/F", "AMR", - "create-ms", "1", "TCH/H", "AMR", - "meas-rep", "0", "30","0", "2","0","20","1","20", - "expect-no-chan", - "meas-rep", "1", "30","0", "2","0","20","1","20", - "expect-no-chan", - "meas-rep", "2", "30","0", "2","0","20","1","20", - "expect-no-chan", - "meas-rep", "3", "30","0", "2","0","20","1","20", - "expect-no-chan", - "meas-rep", "4", "30","0", "2","0","20","1","20", - "expect-no-chan", - "meas-rep", "5", "30","0", "2","0","20","1","20", - "expect-no-chan", - "congestion-check", - "expect-no-chan", - NULL -}; +DEFUN(meas_rep_repeat_bspower, meas_rep_repeat_bspower_cmd, + "meas-rep repeat <0-999> bspower <0-31> " MEAS_REP_ARGS, + MEAS_REP_DOC + "Resend the same measurement report N times\nN\n" + "Send a nonzero BS Power value in the measurement report (downlink power reduction)\nBS Power reduction in dB\n" + MEAS_REP_ARGS_DOC) +{ + int count = atoi(argv[0]); + uint8_t bs_power_db = atoi(argv[1]); + VTY_ECHO(); + argv += 2; + argc -= 2; + + while (count--) + _meas_rep(vty, bs_power_db, argc, argv); + return CMD_SUCCESS; +} -static char *test_case_18[] = { - "2", - - "Congestion check: One out of three cells is congested\n\n" - "Three cells have different number of used slots, but there is\n" - "congestion at TCH/F in the first cell. Handover is performed with\n" - "the best candidate.\n", - - "create-bts", "3", - "set-min-free", "0", "TCH/F", "2", - "set-min-free", "0", "TCH/H", "2", - "set-min-free", "1", "TCH/F", "2", - "set-min-free", "1", "TCH/H", "2", - "set-min-free", "2", "TCH/F", "2", - "set-min-free", "2", "TCH/H", "2", - "create-ms", "0", "TCH/F", "AMR", - "create-ms", "0", "TCH/F", "AMR", - "create-ms", "0", "TCH/F", "AMR", - "create-ms", "0", "TCH/H", "AMR", - "create-ms", "0", "TCH/H", "AMR", - "create-ms", "1", "TCH/F", "AMR", - "create-ms", "1", "TCH/H", "AMR", - "meas-rep", "0", "30","0", "2","0","20","1","20", - "expect-no-chan", - "meas-rep", "1", "30","0", "2","0","20","1","20", - "expect-no-chan", - "meas-rep", "2", "30","0", "2","0","21","1","20", - "expect-no-chan", - "meas-rep", "3", "30","0", "2","0","20","1","20", - "expect-no-chan", - "meas-rep", "4", "30","0", "2","0","20","1","20", - "expect-no-chan", - "meas-rep", "5", "30","0", "2","0","20","1","20", - "expect-no-chan", - "meas-rep", "6", "30","0", "2","0","20","1","20", - "expect-no-chan", - "congestion-check", - "expect-chan", "1", "2", - "ack-chan", - "expect-ho", "0", "3", /* best candidate is MS 2 at BTS 1, TS 3 */ - "ho-complete", - NULL -}; +DEFUN(res_ind, res_ind_cmd, + "res-ind trx <0-255> <0-255> levels .LEVELS", + "Send Resource Indication for a specific TRX, indicating interference levels per lchan\n" + "Indicate a BTS and TRX\n" "BTS nr\n" "TRX nr\n" + "Indicate interference levels: each level is an index to bts->interf_meas_params.bounds_dbm[]," + " i.e. <0-5> or '-' to omit a report for this timeslot/lchan." + " Separate timeslots by spaces, for individual subslots directly concatenate values." + " If a timeslot has more subslots than provided, the last given value is repeated." + " For example: 'res-ind trx 0 0 levels - 1 23 -': on BTS 0 TRX 0, omit ratings for the entire first timeslot," + " send level=1 for timeslot 1, and for timeslot 2 send level=2 for subslot 0 and level=3 for subslot 1.\n") +{ + int i; + uint8_t level; + struct gsm_bts *bts = bts_by_num_str(argv[0]); + struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]); + struct msgb *msg = msgb_alloc_headroom(256, 64, "RES-IND"); + struct abis_rsl_common_hdr *rslh; + uint8_t *res_info_len; + VTY_ECHO(); + + /* In this test suite, always act as if the interf_meas_params_cfg were already sent to the BTS via OML */ + bts->interf_meas_params_used = bts->interf_meas_params_cfg; + + argv += 2; + argc -= 2; + + rslh = (struct abis_rsl_common_hdr*)msgb_put(msg, sizeof(*rslh)); + rslh->msg_discr = ABIS_RSL_MDISC_TRX; + rslh->msg_type = RSL_MT_RF_RES_IND; + msgb_put_u8(msg, RSL_IE_RESOURCE_INFO); + res_info_len = msg->tail; + msgb_put_u8(msg, 0); + + level = 0xff; + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + const char *ts_str = NULL; + struct gsm_lchan *lchan; + size_t given_subslots = 0; + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + + if (i < argc) { + ts_str = argv[i]; + given_subslots = strlen(ts_str); + } -static char *test_case_19[] = { - "2", - - "Congestion check: Balancing over congested cells\n\n" - "Two cells are congested, but the second cell is less congested.\n" - "Handover is performed to solve the congestion.\n", - - "create-bts", "2", - "set-min-free", "0", "TCH/F", "4", - "set-min-free", "1", "TCH/F", "4", - "create-ms", "0", "TCH/F", "FR", - "create-ms", "0", "TCH/F", "FR", - "create-ms", "0", "TCH/F", "FR", - "create-ms", "1", "TCH/F", "FR", - "meas-rep", "0", "30","0", "1","0","20", - "expect-no-chan", - "meas-rep", "1", "30","0", "1","0","21", - "expect-no-chan", - "meas-rep", "2", "30","0", "1","0","20", - "expect-no-chan", - "meas-rep", "3", "30","0", "1","0","20", - "expect-no-chan", - "congestion-check", - "expect-chan", "1", "2", - "ack-chan", - "expect-ho", "0", "2", /* best candidate is MS 1 at BTS 0, TS 2 */ - "ho-complete", - NULL -}; + ts_for_n_lchans(lchan, ts, ts->max_lchans_possible) { + int chan_nr; + + if (lchan->nr < given_subslots && ts_str) { + char subslot_val = ts_str[lchan->nr]; + switch (subslot_val) { + case '-': + level = INTERF_BAND_UNKNOWN; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + level = subslot_val - '0'; + break; + default: + OSMO_ASSERT(false); + } + } -static char *test_case_20[] = { - "2", - - "Congestion check: Solving congestion by handover TCH/F -> TCH/H\n\n" - "Two BTS, one MS in the first congested BTS must handover to\n" - "non-congested TCH/H of second BTS, in order to solve congestion\n", - "create-bts", "2", - "set-min-free", "0", "TCH/F", "4", - "set-min-free", "0", "TCH/H", "4", - "set-min-free", "1", "TCH/F", "4", - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "0", "30","0", "1","0","30", - "expect-no-chan", - "congestion-check", - "expect-chan", "1", "5", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; + if (level == INTERF_BAND_UNKNOWN) + continue; -static char *test_case_21[] = { - "2", - - "Congestion check: Balancing congestion by handover TCH/F -> TCH/H\n\n" - "Two BTS, one MS in the first congested BTS must handover to\n" - "less-congested TCH/H of second BTS, in order to balance congestion\n", - "create-bts", "2", - "set-min-free", "0", "TCH/F", "4", - "set-min-free", "0", "TCH/H", "4", - "set-min-free", "1", "TCH/F", "4", - "set-min-free", "1", "TCH/H", "4", - "create-ms", "0", "TCH/F", "AMR", - "create-ms", "0", "TCH/F", "AMR", - "create-ms", "0", "TCH/H", "AMR", - "meas-rep", "0", "30","0", "1","0","30", - "expect-no-chan", - "congestion-check", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; + chan_nr = gsm_lchan2chan_nr(lchan, true); + if (chan_nr < 0) + continue; -static char *test_case_22[] = { - "2", - - "Congestion check: Upgrading worst candidate from TCH/H -> TCH/F\n\n" - "There is only one BTS. The TCH/H slots are congested. Since\n" - "assignment is performed to less-congested TCH/F, the candidate with\n" - "the worst RX level is chosen.\n", - - "create-bts", "1", - "set-min-free", "0", "TCH/F", "4", - "set-min-free", "0", "TCH/H", "4", - "create-ms", "0", "TCH/H", "AMR", - "create-ms", "0", "TCH/H", "AMR", - "create-ms", "0", "TCH/H", "AMR", - "meas-rep", "0", "30","0", "0", - "meas-rep", "1", "34","0", "0", - "meas-rep", "2", "20","0", "0", - "expect-no-chan", - "congestion-check", - "expect-chan", "0", "1", - "ack-chan", - "expect-ho", "0", "6", - "ho-complete", - NULL -}; + msgb_put_u8(msg, chan_nr); + msgb_put_u8(msg, level << 5); + } + } -static char *test_case_23[] = { - "2", - - "Story: 'A neighbor is your friend'\n", - - "create-bts", "3", - - "print", - "Andreas is driving along the coast, on a sunny june afternoon.\n" - "Suddenly he is getting a call from his friend and neighbor Axel.\n" - "\n" - "What happens: Two MS are created, #0 for Axel, #1 for Andreas.", - /* Axel */ - "create-ms", "2", "TCH/F", "AMR", - /* andreas */ - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "1", "40","0", "1","0","30", - "expect-no-chan", - - "print", - "Axel asks Andreas if he would like to join them for a barbecue.\n" - "Axel's house is right in the neighborhood and the weather is fine.\n" - "Andreas agrees, so he drives to a close store to buy some barbecue\n" - "skewers.\n" - "\n" - "What happens: While driving, a different cell (mounted atop the\n" - "store) becomes better.", - /* drive to bts 1 */ - "meas-rep", "1", "20","0", "1","0","35", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - - "print", - "While Andreas is walking into the store, Axel asks, if he could also\n" - "bring some beer. Andreas has problems understanding him: \"I have a\n" - "bad reception here. The cell tower is right atop the store, but poor\n" - "coverage inside. Can you repeat please?\"\n" - "\n" - "What happens: Inside the store the close cell is so bad, that\n" - "handover back to the previous cell is required.", - /* bts 1 becomes bad, so bts 0 helps out */ - "meas-rep", "1", "5","0", "1","0","20", - "expect-chan", "0", "1", - "ack-chan", - "expect-ho", "1", "1", - "ho-complete", - - "print", - "After Andreas bought skewers and beer, he leaves the store.\n" - "\n" - "What happens: Outside the store the close cell is better again, so\n" - "handover back to the that cell is performed.", - /* bts 1 becomes better again */ - "meas-rep", "1", "20","0", "1","0","35", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - - "print", - /* bts 2 becomes better */ - "Andreas drives down to the lake where Axel's house is.\n" - "\n" - "What happens: There is a small cell at Axel's house, which becomes\n" - "better, because the current cell has no good comverage at the lake.", - "meas-rep", "1", "14","0", "2","0","2","1","63", - "expect-chan", "2", "2", - "ack-chan", - "expect-ho", "1", "1", - "ho-complete", - - "print", - "Andreas wonders why he still has good radio coverage: \"Last time it\n" - "was so bad\". Axel says: \"I installed a pico cell in my house,\n" - "now we can use our mobile phones down here at the lake.\"", - - NULL -}; + *res_info_len = msg->tail - res_info_len - 1; -static char *test_case_24[] = { - "2", - "No (or not enough) measurements for handover\n\n" - "Do not solve congestion in cell, because there is no measurement.\n" - "As soon as enough measurements available (1 in our case), perform\n" - "handover. Afterwards the old cell becomes congested and the new\n" - "cell is not. Do not perform handover until new measurements are\n" - "received.\n", - - /* two cells, first in congested, but no handover */ - "create-bts", "2", - "set-min-free", "0", "TCH/F", "4", - "set-min-free", "0", "TCH/H", "4", - "create-ms", "0", "TCH/F", "AMR", - "congestion-check", - "expect-no-chan", - - /* send measurement and trigger congestion check */ - "meas-rep", "0", "20","0", "1","0","20", - "expect-no-chan", - "congestion-check", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - - /* congest the first cell and remove congestion from second cell */ - "set-min-free", "0", "TCH/F", "0", - "set-min-free", "0", "TCH/H", "0", - "set-min-free", "1", "TCH/F", "4", - "set-min-free", "1", "TCH/H", "4", - - /* no handover until measurements applied */ - "congestion-check", - "expect-no-chan", - "meas-rep", "0", "20","0", "1","0","20", - "expect-no-chan", - "congestion-check", - "expect-chan", "0", "1", - "ack-chan", - "expect-ho", "1", "1", - "ho-complete", - NULL -}; + msg->dst = trx->rsl_link_primary; + msg->l2h = msg->data; + abis_rsl_rcvmsg(msg); -static char *test_case_25[] = { - "1", + return CMD_SUCCESS; +} - "Stay in better cell\n\n" - "There are many neighbor cells, but only the current cell is the best\n" - "cell, so no handover is performed\n", +DEFUN(congestion_check, congestion_check_cmd, + "congestion-check", + "Trigger a congestion check\n") +{ + VTY_ECHO(); + fprintf(stderr, "- Triggering congestion check\n"); + hodec2_congestion_check(bsc_gsmnet); + return CMD_SUCCESS; +} - "create-bts", "7", - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "0", "30","0", - "6","0","20","1","21","2","18","3","20","4","23","5","19", - "expect-no-chan", - NULL -}; +DEFUN(expect_no_chan, expect_no_chan_cmd, + "expect-no-chan", + "Expect that no channel request was sent from BSC to any cell\n") +{ + VTY_ECHO(); + fprintf(stderr, "- Expecting no channel request\n"); + if (new_chan_req) { + fprintf(stderr, " * Got channel request at %s\n", gsm_lchan_name(new_chan_req)); + fprintf(stderr, "Test failed, because channel was requested\n"); + exit(1); + } + fprintf(stderr, " * Got no channel request\n"); + return CMD_SUCCESS; +} -static char *test_case_26[] = { - "1", - - "Handover to best better cell\n\n" - "The best neighbor cell is selected\n", - - "create-bts", "7", - "create-ms", "0", "TCH/F", "AMR", - "meas-rep", "0", "10","0", - "6","0","20","1","21","2","18","3","20","4","23","5","19", - "expect-chan", "5", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; +static void _expect_chan_activ(struct gsm_lchan *lchan) +{ + fprintf(stderr, "- Expecting channel request at %s\n", + gsm_lchan_name(lchan)); + if (!new_chan_req) { + fprintf(stderr, "Test failed, because no channel was requested\n"); + exit(1); + } + last_chan_req = new_chan_req; + new_chan_req = NULL; + fprintf(stderr, " * Got channel request at %s\n", gsm_lchan_name(last_chan_req)); + if (lchan != last_chan_req) { + fprintf(stderr, "Test failed, because channel was requested on a different lchan than expected\n" + "expected: %s got: %s\n", + gsm_lchan_name(lchan), gsm_lchan_name(last_chan_req)); + exit(1); + } + send_chan_act_ack(lchan, 1); +} -static char *test_case_27[] = { - "2", - - "Congestion check: Upgrading worst candidate from TCH/H -> TCH/F\n\n" - "There is only one BTS. The TCH/H slots are congested. Since\n" - "assignment is performed to less-congested TCH/F, the candidate with\n" - "the worst RX level is chosen. (So far like test 22.)\n" - "After that, trigger more congestion checks to ensure stability.\n", - - "create-bts", "1", - "set-min-free", "0", "TCH/F", "2", - "set-min-free", "0", "TCH/H", "4", - "create-ms", "0", "TCH/H", "AMR", - "create-ms", "0", "TCH/H", "AMR", - "create-ms", "0", "TCH/H", "AMR", - "meas-rep", "0", "30","0", "0", - "meas-rep", "1", "34","0", "0", - "meas-rep", "2", "20","0", "0", - "expect-no-chan", - "congestion-check", - "expect-chan", "0", "1", - "ack-chan", - "expect-ho", "0", "6", - "ho-complete", - "congestion-check", - "expect-chan", "0", "2", - "ack-chan", - "expect-ho", "0", "5", - "ho-complete", - "congestion-check", - "expect-no-chan", - "congestion-check", - "expect-no-chan", - NULL -}; +static void _expect_ho_cmd(struct gsm_lchan *lchan) +{ + fprintf(stderr, "- Expecting Handover Command at %s\n", + gsm_lchan_name(lchan)); -static char *test_case_28[] = { - "2", - - "Handover to congested cell, if RX quality is below minimum\n\n" - "The better neighbor cell is congested, so no handover is performed.\n" - "If the RX quality of the current cell drops below minimum acceptable\n" - "level, the handover is performed. It is also required that 10\n" - "resports are received, before RX quality is checked.\n", - - "create-bts", "2", - "create-ms", "0", "TCH/F", "AMR", - "set-min-free", "1", "TCH/F", "4", - "set-min-free", "1", "TCH/H", "4", - "meas-rep", "0", "30","6", "1","0","40", - "expect-no-chan", - "meas-rep", "0", "30","6", "1","0","40", - "expect-no-chan", - "meas-rep", "0", "30","6", "1","0","40", - "expect-no-chan", - "meas-rep", "0", "30","6", "1","0","40", - "expect-no-chan", - "meas-rep", "0", "30","6", "1","0","40", - "expect-no-chan", - "meas-rep", "0", "30","6", "1","0","40", - "expect-no-chan", - "meas-rep", "0", "30","6", "1","0","40", - "expect-no-chan", - "meas-rep", "0", "30","6", "1","0","40", - "expect-no-chan", - "meas-rep", "0", "30","6", "1","0","40", - "expect-no-chan", - "meas-rep", "0", "30","6", "1","0","40", - "expect-chan", "1", "1", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; + if (!new_ho_cmd) { + fprintf(stderr, "Test failed, no Handover Command\n"); + exit(1); + } + fprintf(stderr, " * Got Handover Command at %s\n", gsm_lchan_name(new_ho_cmd)); + if (new_ho_cmd != lchan) { + fprintf(stderr, "Test failed, Handover Command not on the expected lchan\n"); + exit(1); + } + last_ho_cmd = new_ho_cmd; + new_ho_cmd = NULL; +} -static char *test_case_29[] = { - "2", - - "Congestion check: Balancing congestion by handover TCH/F -> TCH/H\n\n" - "One BTS, and TCH/F are considered congested, TCH/H are not.\n" - , - "create-bts", "1", - "set-min-free", "0", "TCH/F", "3", - "set-min-free", "0", "TCH/H", "0", - "create-ms", "0", "TCH/F", "AMR", - "create-ms", "0", "TCH/F", "AMR", - "create-ms", "0", "TCH/H", "AMR", - "meas-rep", "0", "30","0", "1","0","30", - "expect-no-chan", - "congestion-check", - "expect-chan", "0", "5", - "ack-chan", - "expect-ho", "0", "1", - "ho-complete", - NULL -}; +static void _expect_as_cmd(struct gsm_lchan *lchan) +{ + fprintf(stderr, "- Expecting Assignment Command at %s\n", + gsm_lchan_name(lchan)); + + if (!new_as_cmd) { + fprintf(stderr, "Test failed, no Assignment Command\n"); + exit(1); + } + fprintf(stderr, " * Got Assignment Command at %s\n", gsm_lchan_name(new_as_cmd)); + if (new_as_cmd != lchan) { + fprintf(stderr, "Test failed, Assignment Command not on the expected lchan\n"); + exit(1); + } + last_as_cmd = new_as_cmd; + new_as_cmd = NULL; +} +DEFUN(expect_chan, expect_chan_cmd, + "expect-chan " LCHAN_ARGS, + "Expect RSL Channel Activation of a specific lchan\n" + LCHAN_ARGS_DOC) +{ + VTY_ECHO(); + _expect_chan_activ(parse_lchan_args(argv)); + return CMD_SUCCESS; +} -static char **test_cases[] = { - test_case_0, - test_case_1, - test_case_2, - test_case_3, - test_case_4, - test_case_5, - test_case_6, - test_case_7, - test_case_8, - test_case_9, - test_case_10, - test_case_11, - test_case_12, - test_case_13, - test_case_14, - test_case_15, - test_case_16, - test_case_17, - test_case_18, - test_case_19, - test_case_20, - test_case_21, - test_case_22, - test_case_23, - test_case_24, - test_case_25, - test_case_26, - test_case_27, - test_case_28, - test_case_29, -}; +DEFUN(expect_handover_command, expect_handover_command_cmd, + "expect-ho-cmd " LCHAN_ARGS, + "Expect an RR Handover Command sent to a specific lchan\n" + LCHAN_ARGS_DOC) +{ + VTY_ECHO(); + _expect_ho_cmd(parse_lchan_args(argv)); + return CMD_SUCCESS; +} + +DEFUN(expect_assignment_command, expect_assignment_command_cmd, + "expect-as-cmd " LCHAN_ARGS, + "Expect Assignment Command for a given lchan\n" + LCHAN_ARGS_DOC) +{ + VTY_ECHO(); + _expect_as_cmd(parse_lchan_args(argv)); + return CMD_SUCCESS; +} + +DEFUN(ho_detection, ho_detection_cmd, + "ho-detect", + "Send Handover Detection to the most recent HO target lchan\n") +{ + VTY_ECHO(); + if (!last_chan_req) { + fprintf(stderr, "Cannot ack handover/assignment, because no chan request\n"); + exit(1); + } + send_ho_detect(last_chan_req); + return CMD_SUCCESS; +} + +DEFUN(ho_complete, ho_complete_cmd, + "ho-complete", + "Send Handover Complete for the most recent HO target lchan\n") +{ + VTY_ECHO(); + if (!last_chan_req) { + fprintf(stderr, "Cannot ack handover/assignment, because no chan request\n"); + exit(1); + } + if (!last_ho_cmd) { + fprintf(stderr, "Cannot ack handover/assignment, because no ho request\n"); + exit(1); + } + send_ho_complete(last_chan_req, true); + lchan_release_ack(last_ho_cmd); + return CMD_SUCCESS; +} + +DEFUN(expect_ho, expect_ho_cmd, + "expect-ho from " LCHAN_ARGS " to " LCHAN_ARGS, + "Expect a handover of a specific lchan to a specific target lchan;" + " shorthand for expect-chan, ack-chan, expect-ho, ho-complete.\n" + "lchan to handover from\n" LCHAN_ARGS_DOC + "lchan to handover to\n" LCHAN_ARGS_DOC) +{ + struct gsm_lchan *from = parse_lchan_args(argv); + struct gsm_lchan *to = parse_lchan_args(argv+4); + VTY_ECHO(); + + _expect_chan_activ(to); + _expect_ho_cmd(from); + send_ho_detect(to); + send_ho_complete(to, true); + + lchan_release_ack(from); + return CMD_SUCCESS; +} + +DEFUN(expect_as, expect_as_cmd, + "expect-as from " LCHAN_ARGS " to " LCHAN_ARGS, + "Expect an intra-cell re-assignment of a specific lchan to a specific target lchan;" + " shorthand for expect-chan, ack-chan, expect-as, TODO.\n" + "lchan to be re-assigned elsewhere\n" LCHAN_ARGS_DOC + "new lchan to re-assign to\n" LCHAN_ARGS_DOC) +{ + struct gsm_lchan *from = parse_lchan_args(argv); + struct gsm_lchan *to = parse_lchan_args(argv+4); + VTY_ECHO(); + + _expect_chan_activ(to); + if (from->ts->trx->bts != to->ts->trx->bts) { + vty_out(vty, "%% Error: re-assignment only works within the same BTS%s", VTY_NEWLINE); + return CMD_WARNING; + } + _expect_as_cmd(from); + send_assignment_complete(to); + send_est_ind(to); + + lchan_release_ack(from); + return CMD_SUCCESS; +} + +DEFUN(ho_failed, ho_failed_cmd, + "ho-failed", + "Fail the most recent handover request\n") +{ + VTY_ECHO(); + if (!last_chan_req) { + fprintf(stderr, "Cannot fail handover, because no chan request\n"); + exit(1); + } + if (!last_ho_cmd) { + fprintf(stderr, "Cannot fail handover, because no handover request\n"); + exit(1); + } + send_ho_complete(last_ho_cmd, false); + lchan_release_ack(last_chan_req); + return CMD_SUCCESS; +} + +DEFUN(expect_ts_use, expect_ts_use_cmd, + "expect-ts-use trx <0-255> <0-255> states" TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE, + "Expect timeslots of a BTS' TRX to be in a specific state\n" + "Indicate a BTS and TRX\n" "BTS nr\n" "TRX nr\n" + "List of 8 expected TS states\n" + TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC) +{ + struct gsm_bts *bts = bts_by_num_str(argv[0]); + struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]); + VTY_ECHO(); + argv += 2; + argc -= 2; + if (!_expect_ts_use(bts, trx, argv)) + exit(1); + return CMD_SUCCESS; +} + +DEFUN(codec_f, codec_f_cmd, + "codec tch/f (AMR|EFR|FR)", + "Define which codec should be used for new TCH/F lchans (for set-ts-use)\n" + "Configure the TCH/F codec to use\nAMR\nEFR\nFR\n") +{ + VTY_ECHO(); + osmo_talloc_replace_string(ctx, &codec_tch_f, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(codec_h, codec_h_cmd, + "codec tch/h (AMR|HR)", + "Define which codec should be used for new TCH/H lchans (for set-ts-use)\n" + "Configure the TCH/H codec to use\nAMR\nHR\n") +{ + VTY_ECHO(); + osmo_talloc_replace_string(ctx, &codec_tch_h, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(set_arfcn, set_arfcn_cmd, + "set-arfcn trx <0-255> <0-255> <0-1023>", + "Set the ARFCN for a BTS' TRX\n" + "Indicate a BTS and TRX\n" "BTS nr\n" "TRX nr\n" + "Absolute Radio Frequency Channel Number\n") +{ + enum gsm_band unused; + struct gsm_bts *bts = bts_by_num_str(argv[0]); + struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]); + int arfcn = atoi(argv[2]); + VTY_ECHO(); + + if (gsm_arfcn2band_rc(arfcn, &unused) < 0) { + vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE); + return CMD_WARNING; + } + + trx->arfcn = arfcn; + + if (generate_cell_chan_alloc(trx->bts) != 0) { + vty_out(vty, "%% Failed to re-generate Cell Allocation%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (int i = 0; i < bsc_gsmnet->num_bts; i++) { + if (gsm_generate_si(gsm_bts_num(bsc_gsmnet, i), SYSINFO_TYPE_2) <= 0) + fprintf(stderr, "Error generating SI2\n"); + } + + return CMD_SUCCESS; +} + +DEFUN(set_band, set_band_cmd, + "set-band bts <0-255> BAND", + "Set the frequency band for a BTS\n" + "Indicate a BTS\n" "BTS nr\n" + "Frequency band\n") +{ + struct gsm_bts *bts = bts_by_num_str(argv[0]); + int band = gsm_band_parse(argv[1]); + VTY_ECHO(); + + if (band < 0) { + vty_out(vty, "%% BAND %d is not a valid GSM band%s", + band, VTY_NEWLINE); + return CMD_WARNING; + } + + bts->band = band; + + return CMD_SUCCESS; +} + +DEFUN(set_ts_use, set_ts_use_cmd, + "set-ts-use trx <0-255> <0-255> states" TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE, + "Put timeslots of a BTS' TRX into a specific state\n" + "Indicate a BTS and TRX\n" "BTS nr\n" "TRX nr\n" + "List of 8 TS states to apply\n" + TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC) +{ + struct gsm_bts *bts = bts_by_num_str(argv[0]); + struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]); + VTY_ECHO(); + argv += 2; + argc -= 2; + if (!_set_ts_use(bts, trx, argv)) + exit(1); + if (!_expect_ts_use(bts, trx, argv)) + exit(1); + return CMD_SUCCESS; +} + +DEFUN(wait, wait_cmd, + "wait <0-999999> [<0-999>]", + "Let some fake time pass. The test continues instantaneously, but this overrides osmo_gettimeofday() to let" + " given amount of time pass virtually.\n" + "Seconds to fake-wait\n" + "Microseconds to fake-wait, in addition to the seconds waited\n") +{ + time_t seconds = atoi(argv[0]); + suseconds_t useconds = 0; + VTY_ECHO(); + if (argc > 1) + useconds = atoi(argv[1]) * 1000; + fake_time_passes(seconds, useconds); + return CMD_SUCCESS; +} + +static void ho_test_vty_init() +{ + install_element(CONFIG_NODE, &create_n_bts_cmd); + install_element(CONFIG_NODE, &create_bts_cmd); + install_element(CONFIG_NODE, &create_ms_cmd); + install_element(CONFIG_NODE, &meas_rep_cmd); + install_element(CONFIG_NODE, &meas_rep_repeat_cmd); + install_element(CONFIG_NODE, &meas_rep_repeat_bspower_cmd); + install_element(CONFIG_NODE, &res_ind_cmd); + install_element(CONFIG_NODE, &congestion_check_cmd); + install_element(CONFIG_NODE, &expect_no_chan_cmd); + install_element(CONFIG_NODE, &expect_chan_cmd); + install_element(CONFIG_NODE, &expect_handover_command_cmd); + install_element(CONFIG_NODE, &expect_assignment_command_cmd); + install_element(CONFIG_NODE, &ho_detection_cmd); + install_element(CONFIG_NODE, &ho_complete_cmd); + install_element(CONFIG_NODE, &expect_ho_cmd); + install_element(CONFIG_NODE, &expect_as_cmd); + install_element(CONFIG_NODE, &ho_failed_cmd); + install_element(CONFIG_NODE, &expect_ts_use_cmd); + install_element(CONFIG_NODE, &codec_f_cmd); + install_element(CONFIG_NODE, &codec_h_cmd); + install_element(CONFIG_NODE, &set_arfcn_cmd); + install_element(CONFIG_NODE, &set_band_cmd); + install_element(CONFIG_NODE, &set_ts_use_cmd); + install_element(CONFIG_NODE, &wait_cmd); +} static const struct log_info_cat log_categories[] = { [DHO] = { @@ -1414,48 +1621,76 @@ const struct log_info log_info = { .num_cat = ARRAY_SIZE(log_categories), }; +static struct vty_app_info vty_info = { + .name = "ho_test", + .copyright = + "Copyright (C) 2020 sysmocom - s.f.m.c. GmbH\r\n" + "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n" + "This is free software: you are free to change and redistribute it.\r\n" + "There is NO WARRANTY, to the extent permitted by law.\r\n", + .version = PACKAGE_VERSION, + .usr_attr_desc = { + [BSC_VTY_ATTR_RESTART_ABIS_OML_LINK] = \ + "This command applies on A-bis OML link (re)establishment", + [BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK] = \ + "This command applies on A-bis RSL link (re)establishment", + [BSC_VTY_ATTR_NEW_LCHAN] = \ + "This command applies for newly created lchans", + }, + .usr_attr_letters = { + [BSC_VTY_ATTR_RESTART_ABIS_OML_LINK] = 'o', + [BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK] = 'r', + [BSC_VTY_ATTR_NEW_LCHAN] = 'l', + }, +}; + int main(int argc, char **argv) { - char **test_case; - struct gsm_bts *bts[256]; - int bts_num = 0; - struct gsm_lchan *lchan[256]; - int lchan_num = 0; - int i; - int algorithm; - int test_case_i; - int last_test_i; + char *test_file = NULL; + int rc; + + if (argc < 2) { + fprintf(stderr, "Pass a handover test script as argument\n"); + exit(1); + } + test_file = argv[1]; ctx = talloc_named_const(NULL, 0, "handover_test"); msgb_talloc_ctx_init(ctx, 0); - - test_case_i = argc > 1? atoi(argv[1]) : -1; - last_test_i = ARRAY_SIZE(test_cases) - 1; - - if (test_case_i < 0 || test_case_i > last_test_i) { - for (i = 0; i <= last_test_i; i++) { - printf("Test #%d (algorithm %s):\n%s\n", i, - test_cases[i][0], test_cases[i][1]); - } - printf("\nPlease specify test case number 0..%d\n", last_test_i); - return EXIT_FAILURE; - } + vty_info.tall_ctx = ctx; osmo_init_logging2(ctx, &log_info); + log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME); + log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END); log_set_print_category(osmo_stderr_target, 1); log_set_print_category_hex(osmo_stderr_target, 0); - log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME); + log_set_print_level(osmo_stderr_target, 1); + log_set_print_timestamp(osmo_stderr_target, 0); osmo_fsm_log_addr(false); + /* the 'wait' command above, intended to test penalty timers, adds seconds to the monotonic clock in "fake + * time". */ + fake_time_start(); + bsc_network_alloc(); if (!bsc_gsmnet) exit(1); - ts_fsm_init(); + /* The MGCP client which is handling the pool (mgcp_client_pool_vty_init) is used from the bsc_vty_init, so + * we must allocate an empty mgw pool even though we do not need it for this test. */ + bsc_gsmnet->mgw.mgw_pool = mgcp_client_pool_alloc(bsc_gsmnet); + if (!bsc_gsmnet->mgw.mgw_pool) + exit(1); + + vty_init(&vty_info); + bsc_vty_init(bsc_gsmnet); + ho_test_vty_init(); + lchan_fsm_init(); bsc_subscr_conn_fsm_init(); handover_fsm_init(); + assignment_fsm_init(); ho_set_algorithm(bsc_gsmnet->ho, 2); ho_set_ho_active(bsc_gsmnet->ho, true); @@ -1478,309 +1713,24 @@ int main(int argc, char **argv) /* We don't really need any specific model here */ bts_model_unknown_init(); - test_case = test_cases[test_case_i]; - - fprintf(stderr, "--------------------\n"); - fprintf(stderr, "Performing the following test %d (algorithm %s):\n%s", - test_case_i, test_case[0], test_case[1]); - algorithm = atoi(test_case[0]); - test_case += 2; - fprintf(stderr, "--------------------\n"); - /* Disable the congestion check timer, we will trigger manually. */ bsc_gsmnet->hodec2.congestion_check_interval_s = 0; handover_decision_1_init(); hodec2_init(bsc_gsmnet); - while (*test_case) { - if (!strcmp(*test_case, "create-bts")) { - static int arfcn = 870; - int n = atoi(test_case[1]); - fprintf(stderr, "- Creating %d BTS (one TRX each, " - "TS(1-4) are TCH/F, TS(5-6) are TCH/H)\n", n); - for (i = 0; i < n; i++) - bts[bts_num + i] = create_bts(arfcn++); - for (i = 0; i < n; i++) { - if (gsm_generate_si(bts[bts_num + i], SYSINFO_TYPE_2) <= 0) - fprintf(stderr, "Error generating SI2\n"); - } - bts_num += n; - test_case += 2; - } else - if (!strcmp(*test_case, "as-enable")) { - fprintf(stderr, "- Set assignment enable state at " - "BTS %s to %s\n", test_case[1], test_case[2]); - ho_set_hodec2_as_active(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); - test_case += 3; - } else - if (!strcmp(*test_case, "ho-enable")) { - fprintf(stderr, "- Set handover enable state at " - "BTS %s to %s\n", test_case[1], test_case[2]); - ho_set_ho_active(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); - test_case += 3; - } else - if (!strcmp(*test_case, "afs-rxlev-improve")) { - fprintf(stderr, "- Set afs RX level improvement at " - "BTS %s to %s\n", test_case[1], test_case[2]); - ho_set_hodec2_afs_bias_rxlev(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); - test_case += 3; - } else - if (!strcmp(*test_case, "afs-rxqual-improve")) { - fprintf(stderr, "- Set afs RX quality improvement at " - "BTS %s to %s\n", test_case[1], test_case[2]); - ho_set_hodec2_afs_bias_rxqual(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); - test_case += 3; - } else - if (!strcmp(*test_case, "set-min-free")) { - fprintf(stderr, "- Setting minimum required free %s " - "slots at BTS %s to %s\n", test_case[2], - test_case[1], test_case[3]); - if (!strcmp(test_case[2], "TCH/F")) - ho_set_hodec2_tchf_min_slots(bts[atoi(test_case[1])]->ho, atoi(test_case[3])); - else - ho_set_hodec2_tchh_min_slots(bts[atoi(test_case[1])]->ho, atoi(test_case[3])); - test_case += 4; - } else - if (!strcmp(*test_case, "set-max-ho")) { - fprintf(stderr, "- Setting maximum parallel handovers " - "at BTS %s to %s\n", test_case[1], - test_case[2]); - ho_set_hodec2_ho_max( bts[atoi(test_case[1])]->ho, atoi(test_case[2])); - test_case += 3; - } else - if (!strcmp(*test_case, "set-max-ta")) { - fprintf(stderr, "- Setting maximum timing advance " - "at BTS %s to %s\n", test_case[1], - test_case[2]); - ho_set_hodec2_max_distance(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); - test_case += 3; - } else - if (!strcmp(*test_case, "create-ms")) { - fprintf(stderr, "- Creating mobile #%d at BTS %s on " - "%s with %s codec\n", lchan_num, test_case[1], - test_case[2], test_case[3]); - lchan[lchan_num] = create_lchan(bts[atoi(test_case[1])], - !strcmp(test_case[2], "TCH/F"), test_case[3]); - if (!lchan[lchan_num]) { - printf("Failed to create lchan!\n"); - return EXIT_FAILURE; - } - fprintf(stderr, " * New MS is at BTS %d TS %d\n", - lchan[lchan_num]->ts->trx->bts->nr, - lchan[lchan_num]->ts->nr); - lchan_num++; - test_case += 4; - } else - if (!strcmp(*test_case, "set-ta")) { - fprintf(stderr, "- Setting maximum timing advance " - "at MS %s to %s\n", test_case[1], - test_case[2]); - meas_ta_ms = atoi(test_case[2]); - test_case += 3; - } else - if (!strcmp(*test_case, "meas-rep")) { - /* meas-rep <lchan-nr> <rxlev> <rxqual> <nr-of-neighbors> [<cell-idx> <rxlev> [...]] */ - int n = atoi(test_case[4]); - struct gsm_lchan *lc = lchan[atoi(test_case[1])]; - fprintf(stderr, "- Sending measurement report from " - "mobile #%s (rxlev=%s, rxqual=%s)\n", - test_case[1], test_case[2], test_case[3]); - meas_dl_rxlev = atoi(test_case[2]); - meas_dl_rxqual = atoi(test_case[3]); - meas_num_nc = n; - test_case += 5; - for (i = 0; i < n; i++) { - int nr = atoi(test_case[0]); - /* since our bts is not in the list of neighbor - * cells, we need to shift */ - if (nr >= lc->ts->trx->bts->nr) - nr++; - fprintf(stderr, " * Neighbor cell #%s, actual " - "BTS %d (rxlev=%s)\n", test_case[0], nr, - test_case[1]); - meas_bcch_f_nc[i] = atoi(test_case[0]); - /* bts number, not counting our own */ - meas_rxlev_nc[i] = atoi(test_case[1]); - meas_bsic_nc[i] = 0x3f; - test_case += 2; - } - got_chan_req = 0; - gen_meas_rep(lc); - } else - if (!strcmp(*test_case, "congestion-check")) { - fprintf(stderr, "- Triggering congestion check\n"); - got_chan_req = 0; - if (algorithm == 2) - hodec2_congestion_check(bsc_gsmnet); - test_case += 1; - } else - if (!strcmp(*test_case, "expect-chan")) { - fprintf(stderr, "- Expecting channel request at BTS %s " - "TS %s\n", test_case[1], test_case[2]); - if (!got_chan_req) { - printf("Test failed, because no channel was " - "requested\n"); - return EXIT_FAILURE; - } - fprintf(stderr, " * Got channel request at BTS %d " - "TS %d\n", chan_req_lchan->ts->trx->bts->nr, - chan_req_lchan->ts->nr); - if (chan_req_lchan->ts->trx->bts->nr - != atoi(test_case[1])) { - printf("Test failed, because channel was not " - "requested on expected BTS\n"); - return EXIT_FAILURE; - } - if (chan_req_lchan->ts->nr != atoi(test_case[2])) { - printf("Test failed, because channel was not " - "requested on expected TS\n"); - return EXIT_FAILURE; - } - test_case += 3; - } else - if (!strcmp(*test_case, "expect-no-chan")) { - fprintf(stderr, "- Expecting no channel request\n"); - if (got_chan_req) { - fprintf(stderr, " * Got channel request at " - "BTS %d TS %d\n", - chan_req_lchan->ts->trx->bts->nr, - chan_req_lchan->ts->nr); - printf("Test failed, because channel was " - "requested\n"); - return EXIT_FAILURE; - } - fprintf(stderr, " * Got no channel request\n"); - test_case += 1; - } else - if (!strcmp(*test_case, "expect-ho")) { - fprintf(stderr, "- Expecting handover/assignment " - "request at BTS %s TS %s\n", test_case[1], - test_case[2]); - if (!got_ho_req) { - printf("Test failed, because no handover was " - "requested\n"); - return EXIT_FAILURE; - } - fprintf(stderr, " * Got handover/assignment request at " - "BTS %d TS %d\n", - ho_req_lchan->ts->trx->bts->nr, - ho_req_lchan->ts->nr); - if (ho_req_lchan->ts->trx->bts->nr - != atoi(test_case[1])) { - printf("Test failed, because " - "handover/assignment was not commanded " - "at the expected BTS\n"); - return EXIT_FAILURE; - } - if (ho_req_lchan->ts->nr != atoi(test_case[2])) { - printf("Test failed, because " - "handover/assignment was not commanded " - "at the expected TS\n"); - return EXIT_FAILURE; - } - test_case += 3; - } else - if (!strcmp(*test_case, "ack-chan")) { - fprintf(stderr, "- Acknowledging channel request\n"); - if (!got_chan_req) { - printf("Cannot ack channel, because no " - "request\n"); - return EXIT_FAILURE; - } - test_case += 1; - got_ho_req = 0; - send_chan_act_ack(chan_req_lchan, 1); - } else - if (!strcmp(*test_case, "ho-complete")) { - fprintf(stderr, "- Acknowledging handover/assignment " - "request\n"); - if (!got_chan_req) { - printf("Cannot ack handover/assignment, " - "because no chan request\n"); - return EXIT_FAILURE; - } - if (!got_ho_req) { - printf("Cannot ack handover/assignment, " - "because no ho request\n"); - return EXIT_FAILURE; - } - test_case += 1; - got_chan_req = 0; - got_ho_req = 0; - /* switch lchan */ - for (i = 0; i < lchan_num; i++) { - if (lchan[i] == ho_req_lchan) { - fprintf(stderr, " * MS %d changes from " - "BTS=%d TS=%d to BTS=%d " - "TS=%d\n", i, - lchan[i]->ts->trx->bts->nr, - lchan[i]->ts->nr, - chan_req_lchan->ts->trx->bts->nr, - chan_req_lchan->ts->nr); - lchan[i] = chan_req_lchan; - } - } - send_ho_complete(chan_req_lchan, true); - } else - if (!strcmp(*test_case, "ho-failed")) { - fprintf(stderr, "- Making handover fail\n"); - if (!got_chan_req) { - printf("Cannot fail handover, because no chan " - "request\n"); - return EXIT_FAILURE; - } - test_case += 1; - got_chan_req = 0; - got_ho_req = 0; - send_ho_complete(ho_req_lchan, false); - } else - if (!strcmp(*test_case, "print")) { - fprintf(stderr, "\n%s\n\n", test_case[1]); - test_case += 2; - } else { - printf("Unknown test command '%s', please fix!\n", - *test_case); - return EXIT_FAILURE; - } - - { - /* Help the lchan out of releasing states */ - struct gsm_bts *bts; - llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { - struct gsm_bts_trx *trx; - llist_for_each_entry(trx, &bts->trx_list, list) { - int ts_nr; - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - struct gsm_lchan *lchan; - ts_for_each_lchan(lchan, &trx->ts[ts_nr]) { - - if (lchan->fi && lchan->fi->state == LCHAN_ST_WAIT_BEFORE_RF_RELEASE) { - osmo_fsm_inst_state_chg(lchan->fi, LCHAN_ST_WAIT_RF_RELEASE_ACK, 0, 0); - osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RSL_RF_CHAN_REL_ACK, 0); - } - } - } - } - } - } - } - - for (i = 0; i < lchan_num; i++) { - struct gsm_subscriber_connection *conn = lchan[i]->conn; - lchan[i]->conn = NULL; - conn->lchan = NULL; - osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_REGULAR, NULL); + rc = vty_read_config_file(test_file, NULL); + if (rc < 0) { + fprintf(stderr, "Failed to parse the test file: '%s'\n", test_file); } - fprintf(stderr, "--------------------\n"); - - printf("Test OK\n"); - - fprintf(stderr, "--------------------\n"); - talloc_free(ctx); - return EXIT_SUCCESS; + fprintf(stderr,"-------------------\n"); + if (!rc) + fprintf(stderr, "pass\n"); + else + fprintf(stderr, "FAIL\n"); + return rc; } void rtp_socket_free() {} @@ -1793,30 +1743,35 @@ void trau_mux_unmap() {} void trau_mux_map_lchan() {} void trau_recv_lchan() {} void trau_send_frame() {} -int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; } +/* Stub */ int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; } void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, uint8_t dlci, enum gsm0808_cause cause) {} -void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr) {} +void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_a5_n) {} int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_channel) { return 0; } -int bsc_paging_start(struct bsc_paging_params *params) -{ return 0; } void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) {} void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause) {} void bsc_cm_update(struct gsm_subscriber_connection *conn, const uint8_t *cm2, uint8_t cm2_len, const uint8_t *cm3, uint8_t cm3_len) {} -struct gsm0808_handover_required; -int bsc_tx_bssmap_ho_required(struct gsm_lchan *lchan, const struct gsm0808_cell_id_list2 *target_cells) -{ return 0; } -int bsc_tx_bssmap_ho_request_ack(struct gsm_subscriber_connection *conn, struct msgb *rr_ho_command) -{ return 0; } -int bsc_tx_bssmap_ho_detect(struct gsm_subscriber_connection *conn) { return 0; } -enum handover_result bsc_tx_bssmap_ho_complete(struct gsm_subscriber_connection *conn, - struct gsm_lchan *lchan) { return HO_RESULT_OK; } -void bsc_tx_bssmap_ho_failure(struct gsm_subscriber_connection *conn) {} -void osmo_bsc_sigtran_tx_reset(void) {} -void osmo_bsc_sigtran_tx_reset_ack(void) {} -void osmo_bsc_sigtran_reset(void) {} -void bssmap_reset_alloc(void) {} -void bssmap_reset_is_conn_ready(void) {} +const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep) +{ + return "fake-ep"; +} +const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci) +{ + return "fake-ci"; +} +const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci) +{ + static struct mgcp_conn_peer ret = { + .addr = "1.2.3.4", + .port = 1234, + .endpoint = "fake-endpoint", + }; + return &ret; +} +struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep) +{ + return NULL; +} diff --git a/tests/handover/handover_test.ok b/tests/handover/handover_test.ok deleted file mode 100644 index 678f9a34e..000000000 --- a/tests/handover/handover_test.ok +++ /dev/null @@ -1 +0,0 @@ -Test OK diff --git a/tests/handover/handover_tests.ok b/tests/handover/handover_tests.ok new file mode 100644 index 000000000..04241811c --- /dev/null +++ b/tests/handover/handover_tests.ok @@ -0,0 +1,57 @@ +pass test_amr_tch_f_to_h.ho_vty +pass test_amr_tch_f_to_h_balance_congestion.ho_vty +pass test_amr_tch_f_to_h_congestion.ho_vty +pass test_amr_tch_f_to_h_congestion_assignment.ho_vty +pass test_amr_tch_f_to_h_congestion_assignment_2.ho_vty +pass test_amr_tch_f_to_h_congestion_assignment_3.ho_vty +pass test_amr_tch_h_and_afs_bias.ho_vty +pass test_amr_tch_h_to_f_congestion.ho_vty +pass test_amr_tch_h_to_f_congestion_two_cells.ho_vty +pass test_amr_tch_h_to_f_rxlev.ho_vty +pass test_amr_tch_h_to_f_rxlev_congested.ho_vty +pass test_amr_tch_h_to_f_rxlev_oscillation.ho_vty +pass test_amr_tch_h_to_f_rxqual.ho_vty +pass test_amr_tch_h_to_f_rxqual_congested.ho_vty +pass test_amr_tch_h_to_f_rxqual_oscillation.ho_vty +pass test_balance_congestion.ho_vty +pass test_balance_congestion_2.ho_vty +pass test_balance_congestion_by_percentage.ho_vty +pass test_balance_congestion_tchf_tchh.ho_vty +pass test_bs_power.ho_vty +pass test_congestion.ho_vty +pass test_congestion_favor_best_target_rxlev.ho_vty +pass test_congestion_intra_vs_inter_cell.ho_vty +pass test_congestion_no_oscillation.ho_vty +pass test_congestion_no_oscillation2.ho_vty +pass test_disabled_ho_and_as.ho_vty +pass test_dyn_ts_amr_tch_f_to_h_congestion_assignment.ho_vty +pass test_dyn_ts_amr_tch_f_to_h_congestion_assignment_2.ho_vty +pass test_dyn_ts_amr_tch_h_to_f_congestion_assignment_2.ho_vty +pass test_dyn_ts_balance_congestion.ho_vty +pass test_dyn_ts_congestion_tch_f_vs_tch_h.ho_vty +pass test_dyn_ts_congestion_tch_f_vs_tch_h_2.ho_vty +pass test_dyn_ts_favor_half_used_tch_h_as_target.ho_vty +pass test_dyn_ts_favor_moving_half_used_tch_h.ho_vty +pass test_dyn_ts_favor_static_ts_as_target.ho_vty +pass test_ho_to_better_cell.ho_vty +pass test_ho_to_better_cell_2.ho_vty +pass test_hysteresis.ho_vty +pass test_insufficient_measurements.ho_vty +pass test_keep_efr_codec.ho_vty +pass test_keep_fr_codec.ho_vty +pass test_keep_hr_codec.ho_vty +pass test_max_handovers.ho_vty +pass test_max_ta.ho_vty +pass test_meas_rep_multi_band.ho_vty +pass test_min_rxlev_vs_congestion.ho_vty +pass test_min_rxlev_vs_hysteresis.ho_vty +pass test_neighbor_congested.ho_vty +pass test_neighbor_full.ho_vty +pass test_no_congestion.ho_vty +pass test_penalty_timer.ho_vty +pass test_resource_indication.ho_vty +pass test_rxqual.ho_vty +pass test_rxqual_vs_congestion.ho_vty +pass test_stay_in_better_cell.ho_vty +pass test_stay_in_better_cell_2.ho_vty +pass test_story.ho_vty diff --git a/tests/handover/handover_tests.sh b/tests/handover/handover_tests.sh new file mode 100755 index 000000000..4be0c1018 --- /dev/null +++ b/tests/handover/handover_tests.sh @@ -0,0 +1,61 @@ +#!/bin/sh +set -e +tests_dir="${1:-.}" +build_dir="${2:-.}" +update="$3" +test -d "$tests_dir" +test -d "$build_dir" + +if [ -n "$update" -a "x$update" != "x-u" -a "x$update" != "x-U" ]; then + echo "unknown argument: $update" + exit 1 +fi + +one_test() { + test_path="$1" + test_name="$(basename "$test_path")" + got_out="$(mktemp "tmp.$test_name.stdout.XXXXX")" + got_err="$(mktemp "tmp.$test_name.stderr.XXXXX")" + set +e + "$build_dir"/handover_test "$test_path" > "$got_out" 2> "$got_err" + rc=$? + expect_out="$test_path.ok" + expect_err="$test_path.err" + if [ "x$rc" = "x0" -a "x$update" = "x-U" ]; then + cp "$got_out" "$expect_out" + cp "$got_err" "$expect_err" + else + if [ -f "$expect_out" ]; then + diff -u "$expect_out" "$got_out" >&2 + fi + if [ -f "$expect_err" ]; then + diff -u "$expect_err" "$got_err" >&2 + fi + fi + rm "$got_out" + rm "$got_err" + set -e + return $rc +} + +results="$(mktemp "tmp.handover_test_results.XXXXX")" +for test_path in "$tests_dir"/test*.ho_vty ; do + test_name="$(basename "$test_path")" + if one_test "$test_path"; then + echo "pass $test_name" >> "$results" + else + echo "FAIL $test_name" >> "$results" + fi +done +set +e +cat "$results" +failed="$(grep FAIL "$results")" +if [ -z "$failed" -a "x$update" != "x" ]; then + cp "$results" "$tests_dir"/handover_tests.ok +fi +rm "$results" +if [ -n "$failed" ]; then + echo "tests failed" + exit 1 +fi +exit 0 diff --git a/tests/handover/neighbor_ident_test.c b/tests/handover/neighbor_ident_test.c deleted file mode 100644 index 9acbea035..000000000 --- a/tests/handover/neighbor_ident_test.c +++ /dev/null @@ -1,270 +0,0 @@ -/* Test the neighbor_ident.h API */ -/* - * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> - * All Rights Reserved - * - * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> - * - * 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 <talloc.h> -#include <stdio.h> -#include <errno.h> - -#include <osmocom/gsm/gsm0808.h> - -#include <osmocom/bsc/neighbor_ident.h> - -static struct neighbor_ident_list *nil; - -static const struct neighbor_ident_key *k(int from_bts, uint16_t arfcn, uint8_t bsic) -{ - static struct neighbor_ident_key key; - key = (struct neighbor_ident_key){ - .from_bts = from_bts, - .arfcn = arfcn, - .bsic = bsic, - }; - return &key; -} - -static const struct gsm0808_cell_id_list2 cgi1 = { - .id_discr = CELL_IDENT_WHOLE_GLOBAL, - .id_list_len = 1, - .id_list = { - { - .global = { - .lai = { - .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = false }, - .lac = 3, - }, - .cell_identity = 4, - } - }, - }, -}; - -static const struct gsm0808_cell_id_list2 cgi2 = { - .id_discr = CELL_IDENT_WHOLE_GLOBAL, - .id_list_len = 2, - .id_list = { - { - .global = { - .lai = { - .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = false }, - .lac = 3, - }, - .cell_identity = 4, - } - }, - { - .global = { - .lai = { - .plmn = { .mcc = 5, .mnc = 6, .mnc_3_digits = true }, - .lac = 7, - }, - .cell_identity = 8, - } - }, - }, -}; - -static const struct gsm0808_cell_id_list2 lac1 = { - .id_discr = CELL_IDENT_LAC, - .id_list_len = 1, - .id_list = { - { - .lac = 123 - }, - }, -}; - -static const struct gsm0808_cell_id_list2 lac2 = { - .id_discr = CELL_IDENT_LAC, - .id_list_len = 2, - .id_list = { - { - .lac = 456 - }, - { - .lac = 789 - }, - }, -}; - -static void print_cil(const struct gsm0808_cell_id_list2 *cil) -{ - unsigned int i; - if (!cil) { - printf(" cell_id_list == NULL\n"); - return; - } - switch (cil->id_discr) { - case CELL_IDENT_WHOLE_GLOBAL: - printf(" cell_id_list cgi[%u] = {\n", cil->id_list_len); - for (i = 0; i < cil->id_list_len; i++) - printf(" %2d: %s\n", i, osmo_cgi_name(&cil->id_list[i].global)); - printf(" }\n"); - break; - case CELL_IDENT_LAC: - printf(" cell_id_list lac[%u] = {\n", cil->id_list_len); - for (i = 0; i < cil->id_list_len; i++) - printf(" %2d: %u\n", i, cil->id_list[i].lac); - printf(" }\n"); - break; - default: - printf(" Unimplemented id_disc\n"); - } -} - -static int print_nil_i; - -static bool nil_cb(const struct neighbor_ident_key *key, const struct gsm0808_cell_id_list2 *val, - void *cb_data) -{ - printf(" %2d: %s\n", print_nil_i++, neighbor_ident_key_name(key)); - print_cil(val); - return true; -} - -static void print_nil() -{ - print_nil_i = 0; - neighbor_ident_iter(nil, nil_cb, NULL); - if (!print_nil_i) - printf(" (empty)\n"); -} - -#define check_add(key, val, expect_rc) \ - do { \ - int rc; \ - rc = neighbor_ident_add(nil, key, val); \ - printf("neighbor_ident_add(" #key ", " #val ") --> expect rc=" #expect_rc ", got %d\n", rc); \ - if (rc != expect_rc) \ - printf("ERROR\n"); \ - print_nil(); \ - } while(0) - -#define check_del(key, expect_rc) \ - do { \ - bool rc; \ - rc = neighbor_ident_del(nil, key); \ - printf("neighbor_ident_del(" #key ") --> %s\n", rc ? "entry deleted" : "nothing deleted"); \ - if (rc != expect_rc) \ - printf("ERROR: expected: %s\n", expect_rc ? "entry deleted" : "nothing deleted"); \ - print_nil(); \ - } while(0) - -#define check_get(key, expect_rc) \ - do { \ - const struct gsm0808_cell_id_list2 *rc; \ - rc = neighbor_ident_get(nil, key); \ - printf("neighbor_ident_get(" #key ") --> %s\n", \ - rc ? "entry returned" : "NULL"); \ - if (((bool)expect_rc) != ((bool) rc)) \ - printf("ERROR: expected %s\n", expect_rc ? "an entry" : "NULL"); \ - if (rc) \ - print_cil(rc); \ - } while(0) - -int main(void) -{ - void *ctx = talloc_named_const(NULL, 0, "neighbor_ident_test"); - - printf("\n--- testing NULL neighbor_ident_list\n"); - nil = NULL; - check_add(k(0, 1, 2), &cgi1, -ENOMEM); - check_get(k(0, 1, 2), false); - check_del(k(0, 1, 2), false); - - printf("\n--- adding entries, test that no two identical entries are added\n"); - nil = neighbor_ident_init(ctx); - check_add(k(0, 1, 2), &cgi1, 1); - check_get(k(0, 1, 2), true); - check_add(k(0, 1, 2), &cgi1, 1); - check_add(k(0, 1, 2), &cgi2, 2); - check_add(k(0, 1, 2), &cgi2, 2); - check_del(k(0, 1, 2), true); - - printf("\n--- Cannot mix cell identifier types for one entry\n"); - check_add(k(0, 1, 2), &cgi1, 1); - check_add(k(0, 1, 2), &lac1, -EINVAL); - check_del(k(0, 1, 2), true); - neighbor_ident_free(nil); - - printf("\n--- BTS matching: specific BTS is stronger\n"); - nil = neighbor_ident_init(ctx); - check_add(k(NEIGHBOR_IDENT_KEY_ANY_BTS, 1, 2), &lac1, 1); - check_add(k(3, 1, 2), &lac2, 2); - check_get(k(2, 1, 2), true); - check_get(k(3, 1, 2), true); - check_get(k(4, 1, 2), true); - check_get(k(NEIGHBOR_IDENT_KEY_ANY_BTS, 1, 2), true); - neighbor_ident_free(nil); - - printf("\n--- BSIC matching: 6bit and 9bit are different realms, and wildcard match is weaker\n"); - nil = neighbor_ident_init(ctx); - check_add(k(0, 1, BSIC_ANY), &cgi1, 1); - check_add(k(0, 1, 2), &lac1, 1); - check_add(k(0, 1, 2), &lac2, 2); - check_get(k(0, 1, 2), true); - check_get(k(0, 1, 2), true); - neighbor_ident_free(nil); - - printf("\n--- Value ranges\n"); - nil = neighbor_ident_init(ctx); - check_add(k(0, 6, 1 << 6), &lac1, -ERANGE); - check_add(k(0, 6, BSIC_ANY - 1), &lac1, -ERANGE); - check_add(k(NEIGHBOR_IDENT_KEY_ANY_BTS - 1, 1, BSIC_ANY), &cgi2, -ERANGE); - check_add(k(256, 1, BSIC_ANY), &cgi2, -ERANGE); - check_add(k(0, 0, BSIC_ANY), &cgi1, 1); - check_add(k(255, 65535, BSIC_ANY), &lac1, 1); - check_add(k(0, 0, 0), &cgi2, 2); - check_add(k(255, 65535, 0x3f), &lac2, 2); - - neighbor_ident_free(nil); - - printf("\n--- size limits\n"); - { - int i; - struct gsm0808_cell_id_list2 a = { .id_discr = CELL_IDENT_LAC }; - struct gsm0808_cell_id_list2 b = { - .id_discr = CELL_IDENT_LAC, - .id_list = { - { .lac = 423 } - }, - .id_list_len = 1, - }; - for (i = 0; i < ARRAY_SIZE(a.id_list); i++) { - a.id_list[a.id_list_len ++].lac = i; - } - - nil = neighbor_ident_init(ctx); - - i = neighbor_ident_add(nil, k(0, 1, 2), &a); - printf("Added first cell identifier list (added %u) --> rc = %d\n", a.id_list_len, i); - i = neighbor_ident_add(nil, k(0, 1, 2), &b); - printf("Added second cell identifier list (tried to add %u) --> rc = %d\n", b.id_list_len, i); - if (i != -ENOSPC) - printf("ERROR: expected rc=%d\n", -ENOSPC); - neighbor_ident_free(nil); - } - - OSMO_ASSERT(talloc_total_blocks(ctx) == 1); - talloc_free(ctx); - - return 0; -} diff --git a/tests/handover/neighbor_ident_test.err b/tests/handover/neighbor_ident_test.err deleted file mode 100644 index e69de29bb..000000000 --- a/tests/handover/neighbor_ident_test.err +++ /dev/null diff --git a/tests/handover/neighbor_ident_test.ok b/tests/handover/neighbor_ident_test.ok deleted file mode 100644 index 961a33cdc..000000000 --- a/tests/handover/neighbor_ident_test.ok +++ /dev/null @@ -1,186 +0,0 @@ - ---- testing NULL neighbor_ident_list -neighbor_ident_add(k(0, 1, 2), &cgi1) --> expect rc=-ENOMEM, got -12 - (empty) -neighbor_ident_get(k(0, 1, 2)) --> NULL -neighbor_ident_del(k(0, 1, 2)) --> nothing deleted - (empty) - ---- adding entries, test that no two identical entries are added -neighbor_ident_add(k(0, 1, 2), &cgi1) --> expect rc=1, got 1 - 0: BTS 0 to ARFCN 1 BSIC 2 - cell_id_list cgi[1] = { - 0: 001-02-3-4 - } -neighbor_ident_get(k(0, 1, 2)) --> entry returned - cell_id_list cgi[1] = { - 0: 001-02-3-4 - } -neighbor_ident_add(k(0, 1, 2), &cgi1) --> expect rc=1, got 1 - 0: BTS 0 to ARFCN 1 BSIC 2 - cell_id_list cgi[1] = { - 0: 001-02-3-4 - } -neighbor_ident_add(k(0, 1, 2), &cgi2) --> expect rc=2, got 2 - 0: BTS 0 to ARFCN 1 BSIC 2 - cell_id_list cgi[2] = { - 0: 001-02-3-4 - 1: 005-006-7-8 - } -neighbor_ident_add(k(0, 1, 2), &cgi2) --> expect rc=2, got 2 - 0: BTS 0 to ARFCN 1 BSIC 2 - cell_id_list cgi[2] = { - 0: 001-02-3-4 - 1: 005-006-7-8 - } -neighbor_ident_del(k(0, 1, 2)) --> entry deleted - (empty) - ---- Cannot mix cell identifier types for one entry -neighbor_ident_add(k(0, 1, 2), &cgi1) --> expect rc=1, got 1 - 0: BTS 0 to ARFCN 1 BSIC 2 - cell_id_list cgi[1] = { - 0: 001-02-3-4 - } -neighbor_ident_add(k(0, 1, 2), &lac1) --> expect rc=-EINVAL, got -22 - 0: BTS 0 to ARFCN 1 BSIC 2 - cell_id_list cgi[1] = { - 0: 001-02-3-4 - } -neighbor_ident_del(k(0, 1, 2)) --> entry deleted - (empty) - ---- BTS matching: specific BTS is stronger -neighbor_ident_add(k(NEIGHBOR_IDENT_KEY_ANY_BTS, 1, 2), &lac1) --> expect rc=1, got 1 - 0: BTS * to ARFCN 1 BSIC 2 - cell_id_list lac[1] = { - 0: 123 - } -neighbor_ident_add(k(3, 1, 2), &lac2) --> expect rc=2, got 2 - 0: BTS * to ARFCN 1 BSIC 2 - cell_id_list lac[1] = { - 0: 123 - } - 1: BTS 3 to ARFCN 1 BSIC 2 - cell_id_list lac[2] = { - 0: 456 - 1: 789 - } -neighbor_ident_get(k(2, 1, 2)) --> entry returned - cell_id_list lac[1] = { - 0: 123 - } -neighbor_ident_get(k(3, 1, 2)) --> entry returned - cell_id_list lac[2] = { - 0: 456 - 1: 789 - } -neighbor_ident_get(k(4, 1, 2)) --> entry returned - cell_id_list lac[1] = { - 0: 123 - } -neighbor_ident_get(k(NEIGHBOR_IDENT_KEY_ANY_BTS, 1, 2)) --> entry returned - cell_id_list lac[1] = { - 0: 123 - } - ---- BSIC matching: 6bit and 9bit are different realms, and wildcard match is weaker -neighbor_ident_add(k(0, 1, BSIC_ANY), &cgi1) --> expect rc=1, got 1 - 0: BTS 0 to ARFCN 1 (any BSIC) - cell_id_list cgi[1] = { - 0: 001-02-3-4 - } -neighbor_ident_add(k(0, 1, 2), &lac1) --> expect rc=1, got 1 - 0: BTS 0 to ARFCN 1 (any BSIC) - cell_id_list cgi[1] = { - 0: 001-02-3-4 - } - 1: BTS 0 to ARFCN 1 BSIC 2 - cell_id_list lac[1] = { - 0: 123 - } -neighbor_ident_add(k(0, 1, 2), &lac2) --> expect rc=2, got 3 -ERROR - 0: BTS 0 to ARFCN 1 (any BSIC) - cell_id_list cgi[1] = { - 0: 001-02-3-4 - } - 1: BTS 0 to ARFCN 1 BSIC 2 - cell_id_list lac[3] = { - 0: 123 - 1: 456 - 2: 789 - } -neighbor_ident_get(k(0, 1, 2)) --> entry returned - cell_id_list lac[3] = { - 0: 123 - 1: 456 - 2: 789 - } -neighbor_ident_get(k(0, 1, 2)) --> entry returned - cell_id_list lac[3] = { - 0: 123 - 1: 456 - 2: 789 - } - ---- Value ranges -neighbor_ident_add(k(0, 6, 1 << 6), &lac1) --> expect rc=-ERANGE, got -34 - (empty) -neighbor_ident_add(k(0, 6, BSIC_ANY - 1), &lac1) --> expect rc=-ERANGE, got -34 - (empty) -neighbor_ident_add(k(NEIGHBOR_IDENT_KEY_ANY_BTS - 1, 1, BSIC_ANY), &cgi2) --> expect rc=-ERANGE, got -34 - (empty) -neighbor_ident_add(k(256, 1, BSIC_ANY), &cgi2) --> expect rc=-ERANGE, got -34 - (empty) -neighbor_ident_add(k(0, 0, BSIC_ANY), &cgi1) --> expect rc=1, got 1 - 0: BTS 0 to ARFCN 0 (any BSIC) - cell_id_list cgi[1] = { - 0: 001-02-3-4 - } -neighbor_ident_add(k(255, 65535, BSIC_ANY), &lac1) --> expect rc=1, got 1 - 0: BTS 0 to ARFCN 0 (any BSIC) - cell_id_list cgi[1] = { - 0: 001-02-3-4 - } - 1: BTS 255 to ARFCN 65535 (any BSIC) - cell_id_list lac[1] = { - 0: 123 - } -neighbor_ident_add(k(0, 0, 0), &cgi2) --> expect rc=2, got 2 - 0: BTS 0 to ARFCN 0 (any BSIC) - cell_id_list cgi[1] = { - 0: 001-02-3-4 - } - 1: BTS 255 to ARFCN 65535 (any BSIC) - cell_id_list lac[1] = { - 0: 123 - } - 2: BTS 0 to ARFCN 0 BSIC 0 - cell_id_list cgi[2] = { - 0: 001-02-3-4 - 1: 005-006-7-8 - } -neighbor_ident_add(k(255, 65535, 0x3f), &lac2) --> expect rc=2, got 2 - 0: BTS 0 to ARFCN 0 (any BSIC) - cell_id_list cgi[1] = { - 0: 001-02-3-4 - } - 1: BTS 255 to ARFCN 65535 (any BSIC) - cell_id_list lac[1] = { - 0: 123 - } - 2: BTS 0 to ARFCN 0 BSIC 0 - cell_id_list cgi[2] = { - 0: 001-02-3-4 - 1: 005-006-7-8 - } - 3: BTS 255 to ARFCN 65535 BSIC 63 - cell_id_list lac[2] = { - 0: 456 - 1: 789 - } - ---- size limits -Added first cell identifier list (added 127) --> rc = 127 -Added second cell identifier list (tried to add 1) --> rc = -28 diff --git a/tests/handover/test_amr_tch_f_to_h.ho_vty b/tests/handover/test_amr_tch_f_to_h.ho_vty new file mode 100644 index 000000000..22c5e8752 --- /dev/null +++ b/tests/handover/test_amr_tch_f_to_h.ho_vty @@ -0,0 +1,14 @@ +# TCH/F to TCH/H changing with AMR codec +# The MS is using AMR V3 codec, the better cell is congested at TCH/F +# slots. The handover is performed to non-congested TCH/H slots. + +create-n-bts 2 +network + bts 1 + handover2 min-free-slots tch/f 4 +set-ts-use trx 0 0 states * TCH/F - - - - - - +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-ho from lchan 0 0 1 0 to lchan 1 0 5 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * - - - - TCH/H- - - + diff --git a/tests/handover/test_amr_tch_f_to_h_balance_congestion.ho_vty b/tests/handover/test_amr_tch_f_to_h_balance_congestion.ho_vty new file mode 100644 index 000000000..1b8969e1c --- /dev/null +++ b/tests/handover/test_amr_tch_f_to_h_balance_congestion.ho_vty @@ -0,0 +1,16 @@ +# Congestion check: Balancing congestion by handover TCH/F -> TCH/H +# Two BTS, one MS in the first congested BTS must handover to +# less-congested TCH/H of second BTS, in order to balance congestion + +create-n-bts 2 +network + handover2 min-free-slots tch/f 4 + handover2 min-free-slots tch/h 4 +set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/H- - - +meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 30 +expect-no-chan +congestion-check +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - TCH/F - - TCH/H- - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + diff --git a/tests/handover/test_amr_tch_f_to_h_congestion.ho_vty b/tests/handover/test_amr_tch_f_to_h_congestion.ho_vty new file mode 100644 index 000000000..5c934941e --- /dev/null +++ b/tests/handover/test_amr_tch_f_to_h_congestion.ho_vty @@ -0,0 +1,19 @@ +# Congestion check: Solving congestion by handover TCH/F -> TCH/H +# Two BTS, one MS in the first congested BTS must handover to +# non-congested TCH/H of second BTS, in order to solve congestion + +create-n-bts 2 +network + bts 0 + handover2 min-free-slots tch/f 4 + handover2 min-free-slots tch/h 4 + bts 1 + handover2 min-free-slots tch/f 4 +set-ts-use trx 0 0 states * TCH/F - - - - - - +meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 30 +expect-no-chan +congestion-check +expect-ho from lchan 0 0 1 0 to lchan 1 0 5 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * - - - - TCH/H- - - + diff --git a/tests/handover/test_amr_tch_f_to_h_congestion_assignment.ho_vty b/tests/handover/test_amr_tch_f_to_h_congestion_assignment.ho_vty new file mode 100644 index 000000000..84f34ff61 --- /dev/null +++ b/tests/handover/test_amr_tch_f_to_h_congestion_assignment.ho_vty @@ -0,0 +1,18 @@ +# Congestion check: Upgrading worst candidate from TCH/H -> TCH/F +# There is only one BTS. The TCH/H slots are congested. Since +# assignment is performed to less-congested TCH/F, the candidate with +# the worst RX level is chosen. + +create-n-bts 1 +network + bts 0 + handover2 min-free-slots tch/f 4 + handover2 min-free-slots tch/h 4 +set-ts-use trx 0 0 states * - - - - TCH/HH TCH/H- - +meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 +meas-rep lchan 0 0 5 1 rxlev 34 rxqual 0 ta 0 +meas-rep lchan 0 0 6 0 rxlev 20 rxqual 0 ta 0 +expect-no-chan +congestion-check +expect-as from lchan 0 0 6 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - TCH/HH - - diff --git a/tests/handover/test_amr_tch_f_to_h_congestion_assignment_2.ho_vty b/tests/handover/test_amr_tch_f_to_h_congestion_assignment_2.ho_vty new file mode 100644 index 000000000..2fa08da17 --- /dev/null +++ b/tests/handover/test_amr_tch_f_to_h_congestion_assignment_2.ho_vty @@ -0,0 +1,27 @@ +# Congestion check: Upgrading worst candidate from TCH/H -> TCH/F +# There is only one BTS. The TCH/H slots are congested. Since +# assignment is performed to less-congested TCH/F, the candidate with +# the worst RX level is chosen. (So far like test 22.) +# After that, trigger more congestion checks to ensure stability. + +create-n-bts 1 +network + bts 0 + handover2 min-free-slots tch/f 2 + handover2 min-free-slots tch/h 4 +set-ts-use trx 0 0 states * - - - - TCH/HH TCH/H- - +meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 +meas-rep lchan 0 0 5 1 rxlev 34 rxqual 0 ta 0 +meas-rep lchan 0 0 6 0 rxlev 20 rxqual 0 ta 0 +expect-no-chan +congestion-check +expect-as from lchan 0 0 6 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - TCH/HH - - +congestion-check +expect-as from lchan 0 0 5 0 to lchan 0 0 2 0 +expect-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/-H - - +congestion-check +expect-no-chan +congestion-check +expect-no-chan + diff --git a/tests/handover/test_amr_tch_f_to_h_congestion_assignment_3.ho_vty b/tests/handover/test_amr_tch_f_to_h_congestion_assignment_3.ho_vty new file mode 100644 index 000000000..0dca250d2 --- /dev/null +++ b/tests/handover/test_amr_tch_f_to_h_congestion_assignment_3.ho_vty @@ -0,0 +1,15 @@ +# Congestion check: Balancing congestion by handover TCH/F -> TCH/H +# One BTS, and TCH/F are considered congested, TCH/H are not. + +create-n-bts 1 +network + bts 0 + handover2 min-free-slots tch/f 3 + handover2 min-free-slots tch/h 0 +set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/H- - - +meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 30 +expect-no-chan +congestion-check +expect-as from lchan 0 0 1 0 to lchan 0 0 5 1 +expect-ts-use trx 0 0 states * - TCH/F - - TCH/HH - - + diff --git a/tests/handover/test_amr_tch_h_and_afs_bias.ho_vty b/tests/handover/test_amr_tch_h_and_afs_bias.ho_vty new file mode 100644 index 000000000..462cb0d59 --- /dev/null +++ b/tests/handover/test_amr_tch_h_and_afs_bias.ho_vty @@ -0,0 +1,13 @@ +# TCH/H has good RxLev and RxQual, AFS bias should not move it to TCH/F + +network + handover2 power budget hysteresis 3 + handover2 min rxlev -90 + handover2 min rxqual 5 + handover2 afs-bias rxlev 1 + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH +set-ts-use trx 0 0 states * - - - TCH/H- - - * +meas-rep lchan 0 0 4 0 rxlev 50 rxqual 1 ta 0 +# The TCH/H should stay where it is, because its levels are fine. +expect-no-chan diff --git a/tests/handover/test_amr_tch_h_to_f_congestion.ho_vty b/tests/handover/test_amr_tch_h_to_f_congestion.ho_vty new file mode 100644 index 000000000..0252d9f23 --- /dev/null +++ b/tests/handover/test_amr_tch_h_to_f_congestion.ho_vty @@ -0,0 +1,14 @@ +# Congestion check: Balancing congestion by handover TCH/H -> TCH/F +# One BTS, TCH/H are congested and should move to TCH/F. + +network + handover2 min-free-slots tch/f 0 + handover2 min-free-slots tch/h 6 + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH +set-ts-use trx 0 0 states * - - - TCH/H- - - * +meas-rep lchan 0 0 4 0 rxlev 30 rxqual 0 ta 0 +expect-no-chan +congestion-check +expect-as from lchan 0 0 4 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - - * diff --git a/tests/handover/test_amr_tch_h_to_f_congestion_two_cells.ho_vty b/tests/handover/test_amr_tch_h_to_f_congestion_two_cells.ho_vty new file mode 100644 index 000000000..fecd06853 --- /dev/null +++ b/tests/handover/test_amr_tch_h_to_f_congestion_two_cells.ho_vty @@ -0,0 +1,17 @@ +# Congestion check: Balancing congestion by handover TCH/H -> TCH/F +# TCH/H are congested and should move to TCH/F +# There are two cells, and the neighbor has weaker rxlev, so stay in the same cell. + +network + handover2 min-free-slots tch/f 0 + handover2 min-free-slots tch/h 6 + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH +set-ts-use trx 1 0 states * - - - TCH/H- - - * +meas-rep repeat 10 lchan 1 0 4 0 rxlev 30 rxqual 0 ta 0 neighbors 20 +expect-no-chan +congestion-check +expect-as from lchan 1 0 4 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - * +expect-ts-use trx 1 0 states * TCH/F - - - - - * diff --git a/tests/handover/test_amr_tch_h_to_f_rxlev.ho_vty b/tests/handover/test_amr_tch_h_to_f_rxlev.ho_vty new file mode 100644 index 000000000..a22ad6d85 --- /dev/null +++ b/tests/handover/test_amr_tch_h_to_f_rxlev.ho_vty @@ -0,0 +1,16 @@ +# Low RxLev causes upgrade of TCH/H to TCH/F + +network + handover2 afs-bias rxlev 0 + handover2 min rxlev -80 + handover2 window rxlev averaging 10 + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH +set-ts-use trx 0 0 states * - - - TCH/H- - - * +meas-rep repeat 9 lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# not enough values for rxlev averaging +expect-no-chan +meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev +expect-as from lchan 0 0 4 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - - * diff --git a/tests/handover/test_amr_tch_h_to_f_rxlev_congested.ho_vty b/tests/handover/test_amr_tch_h_to_f_rxlev_congested.ho_vty new file mode 100644 index 000000000..776b09311 --- /dev/null +++ b/tests/handover/test_amr_tch_h_to_f_rxlev_congested.ho_vty @@ -0,0 +1,63 @@ +# Low RxLev causes upgrade of TCH/H to TCH/F + +network + handover2 afs-bias rxlev 0 + handover2 min rxlev -80 + handover2 window rxlev averaging 10 + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H PDCH PDCH + + +set-ts-use trx 0 0 states * - - - TCH/HH TCH/HH * * +meas-rep repeat 9 lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# not enough values for rxlev averaging +expect-no-chan +meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev +expect-as from lchan 0 0 4 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - TCH/-H TCH/HH * * + + +# This situation actually balances congestion +set-ts-use trx 0 0 states * TCH/F - - TCH/HH TCH/HH * * +meas-rep repeat 9 lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# not enough values for rxlev averaging +expect-no-chan +meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev +expect-as from lchan 0 0 4 0 to lchan 0 0 2 0 +expect-ts-use trx 0 0 states * TCH/F TCH/F - TCH/-H TCH/HH * * + +# This situation moves congestion from TCH/H to TCH/F (TCH/H was 100% congested, then makes TCH/F 100% congested) +# The congestion requirements would normally forbid this, but since this is an "RxQual emergency", we should reassign. +set-ts-use trx 0 0 states * TCH/F TCH/F - TCH/HH TCH/HH * * +meas-rep repeat 9 lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# not enough values for rxlev averaging +expect-no-chan +meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev +expect-as from lchan 0 0 4 0 to lchan 0 0 3 0 +expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/-H TCH/HH * * + +# This situation worsens congestion (TCH/H was 50% congested, then makes TCH/F 100% congested) +# The congestion requirements would normally forbid this, but since this is an "RxQual emergency", we should reassign. +set-ts-use trx 0 0 states * TCH/F TCH/F - TCH/H- TCH/HH * * +meas-rep repeat 9 lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# not enough values for rxlev averaging +expect-no-chan +meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev +expect-as from lchan 0 0 4 0 to lchan 0 0 3 0 +expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - TCH/HH * * + + +# This situation creates congestion (TCH/H was not congested, then makes TCH/F 50% congested) +# The congestion requirements would normally forbid this, but since this is an "RxQual emergency", we should reassign. +set-ts-use trx 0 0 states * TCH/F - - TCH/H- - * * +meas-rep repeat 9 lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# not enough values for rxlev averaging +expect-no-chan +meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev +expect-as from lchan 0 0 4 0 to lchan 0 0 2 0 +expect-ts-use trx 0 0 states * TCH/F TCH/F - - - * * diff --git a/tests/handover/test_amr_tch_h_to_f_rxlev_oscillation.ho_vty b/tests/handover/test_amr_tch_h_to_f_rxlev_oscillation.ho_vty new file mode 100644 index 000000000..2ef927dde --- /dev/null +++ b/tests/handover/test_amr_tch_h_to_f_rxlev_oscillation.ho_vty @@ -0,0 +1,20 @@ +# Low RxLev causes upgrade of TCH/H to TCH/F. +# That leads to congestion of TCH/F, but do not handover back to non-congested TCH/H. + +network + handover2 afs-bias rxlev 0 + handover2 min rxlev -80 + handover2 window rxlev averaging 1 + handover2 min-free-slots tch/f 3 + handover2 min-free-slots tch/h 4 + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH +set-ts-use trx 0 0 states * - - - TCH/H- - - * +meas-rep lchan 0 0 4 0 rxlev 23 rxqual 1 ta 0 +# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev +expect-as from lchan 0 0 4 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - - * + +meas-rep lchan 0 0 1 0 rxlev 23 rxqual 1 ta 0 +congestion-check +expect-no-chan diff --git a/tests/handover/test_amr_tch_h_to_f_rxqual.ho_vty b/tests/handover/test_amr_tch_h_to_f_rxqual.ho_vty new file mode 100644 index 000000000..f3a2a9086 --- /dev/null +++ b/tests/handover/test_amr_tch_h_to_f_rxqual.ho_vty @@ -0,0 +1,39 @@ +# Low RxQual causes upgrade of TCH/H to TCH/F + +network + handover2 afs-bias rxlev 0 + handover2 min rxqual 5 + handover2 window rxqual averaging 2 + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH +set-ts-use trx 0 0 states * - - - TCH/H- - - * +meas-rep lchan 0 0 4 0 rxlev 30 rxqual 6 ta 0 +# not enough valus for rxqual averaging +expect-no-chan +meas-rep lchan 0 0 4 0 rxlev 30 rxqual 6 ta 0 +# average rxqual now at 6 which is worse than 5, reassign to TCH/F due to bad rxqual. +expect-as from lchan 0 0 4 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - - * +meas-rep repeat 2 lchan 0 0 1 0 rxlev 30 rxqual 5 ta 0 + +# After the upgrade to TCH/F, there should be a penalty timer against re-assgnment within this cell. +# Configure congestion resolution so that it would normally want to do a re-assignment: +network + handover2 min-free-slots tch/f 3 + handover2 window rxlev averaging 1 + handover2 min rxlev -90 + +# The penalty timer is still active, no re-assignment from congestion of TCH/H +congestion-check +expect-no-chan +expect-ts-use trx 0 0 states * TCH/F - - - - - * + +# But handover to another cell is not held off +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH +expect-ts-use trx 0 0 states * TCH/F - - - - - * +expect-ts-use trx 1 0 states * - - - - - - * + +meas-rep lchan 0 0 1 0 rxlev 30 rxqual 5 ta 0 neighbors 35 +expect-ho from lchan 0 0 1 0 to lchan 1 0 4 0 +expect-ts-use trx 0 0 states * - - - - - - * +expect-ts-use trx 1 0 states * - - - TCH/H- - - * diff --git a/tests/handover/test_amr_tch_h_to_f_rxqual_congested.ho_vty b/tests/handover/test_amr_tch_h_to_f_rxqual_congested.ho_vty new file mode 100644 index 000000000..79ff0a72f --- /dev/null +++ b/tests/handover/test_amr_tch_h_to_f_rxqual_congested.ho_vty @@ -0,0 +1,68 @@ +# Low RxQual causes upgrade of TCH/H to TCH/F, also when the cell is congested + +network + handover2 afs-bias rxlev 0 + handover2 min rxqual 5 + handover2 min rxlev -90 + handover2 window rxqual averaging 2 + handover2 min-free-slots tch/f 2 + handover2 min-free-slots tch/h 2 + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H PDCH PDCH + +# This situation actually reduces congestion +set-ts-use trx 0 0 states * - - - TCH/HH TCH/HH * * +meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0 +# not enough values for rxqual averaging +expect-no-chan +meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0 +# average rxqual now at 6 which is worse than 5, reassign to TCH/F due to bad rxqual. +expect-as from lchan 0 0 4 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - TCH/-H TCH/HH * * + + +# This situation actually balances congestion +set-ts-use trx 0 0 states * TCH/F - - TCH/HH TCH/HH * * +meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0 +# not enough values for rxqual averaging +expect-no-chan +meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0 +# average rxqual now at 6 which is worse than 5, reassign to TCH/F due to bad rxqual. +expect-as from lchan 0 0 4 0 to lchan 0 0 2 0 +expect-ts-use trx 0 0 states * TCH/F TCH/F - TCH/-H TCH/HH * * + + +# This situation moves congestion from TCH/H to TCH/F (TCH/H was 100% congested, then makes TCH/F 100% congested) +# The congestion requirements would normally forbid this, but since this is an "RxQual emergency", we should reassign. +set-ts-use trx 0 0 states * TCH/F TCH/F - TCH/HH TCH/HH * * +meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0 +# not enough values for rxqual averaging +expect-no-chan +meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0 +# average rxqual now at 6 which is worse than 5, reassign to TCH/F due to bad rxqual. +expect-as from lchan 0 0 4 0 to lchan 0 0 3 0 +expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/-H TCH/HH * * + + +# This situation worsens congestion (TCH/H was 50% congested, then makes TCH/F 100% congested) +# The congestion requirements would normally forbid this, but since this is an "RxQual emergency", we should reassign. +set-ts-use trx 0 0 states * TCH/F TCH/F - TCH/H- TCH/HH * * +meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0 +# not enough values for rxqual averaging +expect-no-chan +meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0 +# average rxqual now at 6 which is worse than 5, reassign to TCH/F due to bad rxqual. +expect-as from lchan 0 0 4 0 to lchan 0 0 3 0 +expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - TCH/HH * * + + +# This situation creates congestion (TCH/H was not congested, then makes TCH/F 50% congested) +# The congestion requirements would normally forbid this, but since this is an "RxQual emergency", we should reassign. +set-ts-use trx 0 0 states * TCH/F - - TCH/H- - * * +meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0 +# not enough values for rxqual averaging +expect-no-chan +meas-rep lchan 0 0 4 0 rxlev 50 rxqual 6 ta 0 +# average rxqual now at 6 which is worse than 5, reassign to TCH/F due to bad rxqual. +expect-as from lchan 0 0 4 0 to lchan 0 0 2 0 +expect-ts-use trx 0 0 states * TCH/F TCH/F - - - * * diff --git a/tests/handover/test_amr_tch_h_to_f_rxqual_oscillation.ho_vty b/tests/handover/test_amr_tch_h_to_f_rxqual_oscillation.ho_vty new file mode 100644 index 000000000..e628f032a --- /dev/null +++ b/tests/handover/test_amr_tch_h_to_f_rxqual_oscillation.ho_vty @@ -0,0 +1,20 @@ +# Low RxQual causes upgrade of TCH/H to TCH/F. +# That leads to congestion of TCH/F, but do not handover back to non-congested TCH/H. + +network + handover2 afs-bias rxlev 0 + handover2 min rxqual 5 + handover2 window rxqual averaging 1 + handover2 min-free-slots tch/f 3 + handover2 min-free-slots tch/h 4 + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH +set-ts-use trx 0 0 states * - - - TCH/H- - - * +meas-rep lchan 0 0 4 0 rxlev 30 rxqual 6 ta 0 +# average rxlev is now -110 + 23 = -87 < -80: reassign to TCH/F due to bad rxlev +expect-as from lchan 0 0 4 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - - * + +meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 +congestion-check +expect-no-chan diff --git a/tests/handover/test_balance_congestion.ho_vty b/tests/handover/test_balance_congestion.ho_vty new file mode 100644 index 000000000..0988f3bb4 --- /dev/null +++ b/tests/handover/test_balance_congestion.ho_vty @@ -0,0 +1,20 @@ +# Handover to balance congestion +# The current and the better cell are congested, so no handover is +# performed. This is because handover would congest the neighbor cell +# more. After congestion rises in the current cell, the handover is +# performed to balance congestion + +create-n-bts 2 +create-ms bts 0 TCH/F AMR +expect-ts-use trx 0 0 states * TCH/F - - - - - - +network + handover2 min-free-slots tch/f 4 + handover2 min-free-slots tch/h 4 +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-no-chan +set-ts-use trx 0 0 states * TCH/F TCH/F - - - - - +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - TCH/F - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + diff --git a/tests/handover/test_balance_congestion_2.ho_vty b/tests/handover/test_balance_congestion_2.ho_vty new file mode 100644 index 000000000..b157478f9 --- /dev/null +++ b/tests/handover/test_balance_congestion_2.ho_vty @@ -0,0 +1,18 @@ +# Congestion check: Balancing over congested cells +# Two cells are congested, but the second cell is less congested. +# Handover is performed to solve the congestion. + +create-n-bts 2 +network + handover2 min-free-slots tch/f 4 +codec tch/f FR +set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - - - - +set-ts-use trx 1 0 states * TCH/F - - - - - - +meas-rep lchan * * * * rxlev 30 rxqual 0 ta 0 neighbors 20 +meas-rep lchan 0 0 2 0 rxlev 30 rxqual 0 ta 0 neighbors 21 +expect-no-chan +congestion-check +expect-ho from lchan 0 0 2 0 to lchan 1 0 2 0 +expect-ts-use trx 0 0 states * TCH/F - TCH/F - - - - +expect-ts-use trx 1 0 states * TCH/F TCH/F - - - - - + diff --git a/tests/handover/test_balance_congestion_by_percentage.ho_vty b/tests/handover/test_balance_congestion_by_percentage.ho_vty new file mode 100644 index 000000000..aba7d3a5b --- /dev/null +++ b/tests/handover/test_balance_congestion_by_percentage.ho_vty @@ -0,0 +1,52 @@ +# To balance congestion, use the remaining free percentage instead of free lchan counts. +# +# Cell A has min-free-slots 2, and has all slots occupied. +# Cell B has min-free-slots 4, and has 2 slots remaining free. +# +# If we count congested lchans: cell A has a congestion count of 2: two more lchans in use than "allowed". +# If we move one lchan over to cell B, it ends up with a congestion count of 3, which is worse than 2. +# So when counting lchans, we decide that cell A should remain full. +# +# Instead, when comparing percentage of remaining lchans, we would see that cell A is loaded 100% above congestion (2 of +# 2 remaining lchans in use), but when moving one lchan to cell B, it would only be 75% loaded above its treshold (3 of +# 4 remaining lchans in use). So a percentage comparison would cause a handover to cell B. +# +# This test currently expects the behavior of counting lchans; a patch will change to use percentage, which should +# reflect in this test. + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH + +network + bts 0 + handover2 min-free-slots tch/f 2 + bts 1 + handover2 min-free-slots tch/f 4 + +set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F * +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F - - * + +meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30 +expect-no-chan + +# bts 0 is full, by target_overbooked_after_ho==75% < current_overbooked_before_ho==100%, a congestion balancing to bts +# 1 is performed. +congestion-check +expect-ho from lchan 0 0 1 0 to lchan 1 0 5 0 + + +# Make sure that no percentage based handover merely reverses the situation between two cells: + +network + bts 0 + handover2 min-free-slots tch/f 4 + bts 1 + handover2 min-free-slots tch/f 4 + +set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F - - * +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F - - - * + +# the condition is false: target_overbooked_after_ho==50% < current_overbooked_before_ho==50%, so no congestion +# balancing is performed. +congestion-check +expect-no-chan diff --git a/tests/handover/test_balance_congestion_tchf_tchh.ho_vty b/tests/handover/test_balance_congestion_tchf_tchh.ho_vty new file mode 100644 index 000000000..f151b2a41 --- /dev/null +++ b/tests/handover/test_balance_congestion_tchf_tchh.ho_vty @@ -0,0 +1,53 @@ +# Balance congestion across cells and across TCH/F and TCH/H. + +network + handover2 min-free-slots tch/f 3 + handover2 min-free-slots tch/h 3 + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H + +# both TCH/H and TCH/F have one lchan = 33% above congestion, nothing happens +set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH TCH/HH - +meas-rep lchan * * * * rxlev 10 rxqual 0 ta 0 +congestion-check +expect-no-chan + +# TCH/F = +1 = 33%, TCH/H = +2 = 66% above congestion. +# Moving a TCH/H to TCH/F would just reverse the situation to F=+2=66%. Nothing happens. +set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH TCH/HH TCH/H- +meas-rep lchan * * * * rxlev 10 rxqual 0 ta 0 +congestion-check +expect-no-chan + +# F=+1=33% H=+3=100%. Balance to F=+2=66% (which is < 100%) and H=+2=66% +set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH TCH/HH TCH/HH +meas-rep lchan * * * * rxlev 10 rxqual 0 ta 0 +congestion-check +expect-as from lchan 0 0 5 0 to lchan 0 0 3 0 + +# Now similar load percentages, just with different min-free-slots settings for tch/f vs tch/h. + +network + handover2 min-free-slots tch/f 3 + handover2 min-free-slots tch/h 5 + +# TCH/F has 1/3 = 33%, TCH/H has 1/5 = 20% overload. +# Moving one to TCH/H would mean 40% overload on TCH/H, which is above the current TCH/F of 33%. +# Nothing happens. +set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH - - +meas-rep lchan * * * * rxlev 20 rxqual 0 ta 0 +congestion-check +expect-no-chan + +# TCH/F = +1 = 33%, TCH/H = +2 = 40% above congestion. Moving a TCH/H to TCH/F would result +# in F=+2=66%>40%. Nothing happens. +set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH TCH/H- - +meas-rep lchan * * * * rxlev 20 rxqual 0 ta 0 +congestion-check +expect-no-chan + +# F=+1=33% H=+4=80%. Balance to F=+2=66%<80% and H=+3=60% +set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH TCH/HH TCH/H- +meas-rep lchan * * * * rxlev 20 rxqual 0 ta 0 +congestion-check +expect-as from lchan 0 0 5 0 to lchan 0 0 3 0 diff --git a/tests/handover/test_bs_power.ho_vty b/tests/handover/test_bs_power.ho_vty new file mode 100644 index 000000000..5d8c27846 --- /dev/null +++ b/tests/handover/test_bs_power.ho_vty @@ -0,0 +1,11 @@ +# Do not oscillate handover when the BTS applies BS power reduction + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH + +set-ts-use trx 0 0 states * TCH/F - - - - - * + +meas-rep repeat 10 bspower 20 lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +# there should be no handover, because the bspower reduction of 20 with an rxlev of 20 (= 40) is stronger than the +# neighbor at 30. +expect-no-chan diff --git a/tests/handover/test_congestion.ho_vty b/tests/handover/test_congestion.ho_vty new file mode 100644 index 000000000..529b8de61 --- /dev/null +++ b/tests/handover/test_congestion.ho_vty @@ -0,0 +1,21 @@ +# Congestion check: One out of three cells is congested +# Three cells have different number of used slots, but there is +# congestion at TCH/F in the first cell. Handover is performed with +# the best candidate. + +create-n-bts 3 +network + handover2 min-free-slots tch/f 2 + handover2 min-free-slots tch/h 2 +set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - TCH/HH - - +set-ts-use trx 1 0 states * TCH/F - - - TCH/H- - - +meas-rep lchan * * * * rxlev 30 rxqual 0 ta 0 neighbors 20 20 +meas-rep lchan 0 0 3 0 rxlev 30 rxqual 0 ta 0 neighbors 21 20 +expect-no-chan +expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - TCH/HH - - +expect-ts-use trx 1 0 states * TCH/F - - - TCH/H- - - +congestion-check +expect-ho from lchan 0 0 3 0 to lchan 1 0 2 0 +expect-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH - - +expect-ts-use trx 1 0 states * TCH/F TCH/F - - TCH/H- - - + diff --git a/tests/handover/test_congestion_favor_best_target_rxlev.ho_vty b/tests/handover/test_congestion_favor_best_target_rxlev.ho_vty new file mode 100644 index 000000000..e31d98d38 --- /dev/null +++ b/tests/handover/test_congestion_favor_best_target_rxlev.ho_vty @@ -0,0 +1,33 @@ +# A handover should mostly favor the best target rxlev: +# Two candidates for congestion resolution both reduce the RXLEV for the MS, +# candidate A results in 10 RXLEV loss, candidate B only in 5 RXLEV loss. +# But candidate A still results in a better RXLEV at the target than candidate B. +# So tolerate more RXLEV reduction if the resulting RXLEV still remains better. +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F + +network + bts 0 + handover2 min-free-slots tch/f 6 + +set-ts-use trx 0 0 states * TCH/F TCH/F - - - - - +meas-rep lchan 0 0 1 0 rxlev 40 rxqual 0 ta 0 neighbors 30 +meas-rep lchan 0 0 2 0 rxlev 30 rxqual 0 ta 0 neighbors 25 + +congestion-check +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - TCH/F - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + +# clear measurements for next run +set-ts-use trx 0 0 states * - - - - - - - +set-ts-use trx 1 0 states * - - - - - - - + +set-ts-use trx 0 0 states * TCH/F TCH/F - - - - - +meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 25 +meas-rep lchan 0 0 2 0 rxlev 40 rxqual 0 ta 0 neighbors 30 + +congestion-check +expect-ho from lchan 0 0 2 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - diff --git a/tests/handover/test_congestion_intra_vs_inter_cell.ho_vty b/tests/handover/test_congestion_intra_vs_inter_cell.ho_vty new file mode 100644 index 000000000..ddf6802f9 --- /dev/null +++ b/tests/handover/test_congestion_intra_vs_inter_cell.ho_vty @@ -0,0 +1,122 @@ +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/H TCH/H PDCH +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/H TCH/H PDCH + +network + handover2 min-free-slots tch/h 4 + +set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- * + +meas-rep lchan 0 0 5 0 rxlev 31 rxqual 0 ta 0 neighbors 20 +expect-no-chan +meas-rep lchan 0 0 6 0 rxlev 30 rxqual 0 ta 0 neighbors 20 +expect-no-chan + +congestion-check +expect-as from lchan 0 0 6 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - TCH/H- - * +expect-ts-use trx 1 0 states * - - - - - - * + +# clear measurements for next run +set-ts-use trx 0 0 states * - - - - - - * +set-ts-use trx 1 0 states * - - - - - - * + +set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- * +meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 20 +expect-no-chan +meas-rep lchan 0 0 6 0 rxlev 31 rxqual 0 ta 0 neighbors 20 +expect-no-chan + +congestion-check +expect-as from lchan 0 0 5 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - TCH/H- * +expect-ts-use trx 1 0 states * - - - - - - * + +# clear measurements for next run +set-ts-use trx 0 0 states * - - - - - - * +set-ts-use trx 1 0 states * - - - - - - * + +set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- * +meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 20 +expect-no-chan +meas-rep lchan 0 0 6 0 rxlev 31 rxqual 0 ta 0 neighbors 21 +expect-no-chan + +congestion-check +expect-as from lchan 0 0 5 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - TCH/H- * +expect-ts-use trx 1 0 states * - - - - - - * + +# clear measurements for next run +set-ts-use trx 0 0 states * - - - - - - * +set-ts-use trx 1 0 states * - - - - - - * + +set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- * +meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 21 +expect-no-chan +meas-rep lchan 0 0 6 0 rxlev 31 rxqual 0 ta 0 neighbors 20 +expect-no-chan + +congestion-check +expect-as from lchan 0 0 5 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - TCH/H- * +expect-ts-use trx 1 0 states * - - - - - - * + +# clear measurements for next run +set-ts-use trx 0 0 states * - - - - - - * +set-ts-use trx 1 0 states * - - - - - - * + +set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- * +meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 30 +expect-no-chan +meas-rep lchan 0 0 6 0 rxlev 31 rxqual 0 ta 0 neighbors 31 +expect-no-chan + +congestion-check +expect-as from lchan 0 0 5 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - TCH/H- * +expect-ts-use trx 1 0 states * - - - - - - * + +# clear measurements for next run +set-ts-use trx 0 0 states * - - - - - - * +set-ts-use trx 1 0 states * - - - - - - * + +set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- * +meas-rep lchan 0 0 5 0 rxlev 31 rxqual 0 ta 0 neighbors 31 +expect-no-chan +meas-rep lchan 0 0 6 0 rxlev 30 rxqual 0 ta 0 neighbors 30 +expect-no-chan + +congestion-check +expect-as from lchan 0 0 6 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - TCH/H- - * +expect-ts-use trx 1 0 states * - - - - - - * + +# clear measurements for next run +set-ts-use trx 0 0 states * - - - - - - * +set-ts-use trx 1 0 states * - - - - - - * + +set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- * +meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 31 +expect-no-chan +meas-rep lchan 0 0 6 0 rxlev 31 rxqual 0 ta 0 neighbors 30 +expect-no-chan + +congestion-check +expect-ho from lchan 0 0 5 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - TCH/H- * +expect-ts-use trx 1 0 states * TCH/F - - - - - * + +# clear measurements for next run +set-ts-use trx 0 0 states * - - - - - - * +set-ts-use trx 1 0 states * - - - - - - * + +set-ts-use trx 0 0 states * - - - - TCH/H- TCH/H- * +meas-rep lchan 0 0 5 0 rxlev 31 rxqual 0 ta 0 neighbors 30 +expect-no-chan +meas-rep lchan 0 0 6 0 rxlev 30 rxqual 0 ta 0 neighbors 31 +expect-no-chan + +congestion-check +expect-ho from lchan 0 0 6 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - TCH/H- - * +expect-ts-use trx 1 0 states * TCH/F - - - - - * diff --git a/tests/handover/test_congestion_no_oscillation.ho_vty b/tests/handover/test_congestion_no_oscillation.ho_vty new file mode 100644 index 000000000..a830cbe77 --- /dev/null +++ b/tests/handover/test_congestion_no_oscillation.ho_vty @@ -0,0 +1,28 @@ +# Do not oscillate handover from TCH/F to TCH/H on a neighbor due to congestion, +# and then back to the original cell due to RXLEV. + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH +network + bts 0 + handover2 min-free-slots tch/f 5 + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/H TCH/H PDCH + +set-ts-use trx 0 0 states * TCH/F TCH/F - - - - * +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F - - * + +meas-rep repeat 10 lchan 0 0 2 0 rxlev 40 rxqual 0 ta 0 neighbors 20 +expect-no-chan + +# bts 0 wants to lose one TCH/F. The neighbor's TCH/F are full, but TCH/H are available there. +congestion-check +expect-ho from lchan 0 0 2 0 to lchan 1 0 5 0 + +expect-ts-use trx 0 0 states * TCH/F - - - - - * +expect-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F TCH/H- - * + +# measurements continue to be the same +meas-rep lchan 1 0 5 0 rxlev 20 rxqual 0 ta 0 neighbors 40 + +# despite the better RXLEV, congestion prevents oscillation back to bts 0 +expect-no-chan diff --git a/tests/handover/test_congestion_no_oscillation2.ho_vty b/tests/handover/test_congestion_no_oscillation2.ho_vty new file mode 100644 index 000000000..44c4176c2 --- /dev/null +++ b/tests/handover/test_congestion_no_oscillation2.ho_vty @@ -0,0 +1,28 @@ +# Almost identical to test_amr_oscillation.ho_vty, this has just two more TCH/H slots in BTS 1, and did not trigger the +# oscillation bug (which has since been fixed, so that both tests behave identically now). + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH +network + bts 0 + handover2 min-free-slots tch/f 5 + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H + +set-ts-use trx 0 0 states * TCH/F TCH/F - - - - * +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F - - - + +meas-rep repeat 10 lchan 0 0 2 0 rxlev 40 rxqual 0 ta 0 neighbors 20 +expect-no-chan + +# bts 0 wants to lose one TCH/F. The neighbor's TCH/F are full, but TCH/H are available there. +congestion-check +expect-ho from lchan 0 0 2 0 to lchan 1 0 5 0 + +expect-ts-use trx 0 0 states * TCH/F - - - - - * +expect-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F TCH/H- - - + +# measurements continue to be the same +meas-rep lchan 1 0 5 0 rxlev 20 rxqual 0 ta 0 neighbors 40 + +# despite the better RXLEV, congestion prevents oscillation back to bts 0 +expect-no-chan diff --git a/tests/handover/test_disabled_ho_and_as.ho_vty b/tests/handover/test_disabled_ho_and_as.ho_vty new file mode 100644 index 000000000..586c3a70d --- /dev/null +++ b/tests/handover/test_disabled_ho_and_as.ho_vty @@ -0,0 +1,36 @@ +# Handover and Assignment must be enabled +# This test will start with disabled assignment and handover. A +# better neighbor cell (assignment enabled) will not be selected and +# also no assignment from TCH/H to TCH/F to improve quality. There +# will be no handover nor assignment. After enabling assignment on the +# current cell, the MS will assign to TCH/F. After enabling handover +# in the current cell, but disabling in the neighbor cell, handover +# will not be performed, until it is enabled in the neighbor cell too. +network + handover 0 + handover2 afs-bias rxlev 5 + handover2 assignment 0 + +create-n-bts 2 +set-ts-use trx 0 0 states * - - - - TCH/H- - - +meas-rep lchan 0 0 5 0 rxlev 0 rxqual 0 ta 0 neighbors 30 +expect-no-chan +network + bts 0 + handover2 assignment 1 +meas-rep lchan 0 0 5 0 rxlev 0 rxqual 0 ta 0 neighbors 30 +expect-as from lchan 0 0 5 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - - - +network + bts 0 + handover 1 +meas-rep lchan 0 0 1 0 rxlev 0 rxqual 0 ta 0 neighbors 30 +expect-no-chan +network + bts 1 + handover 1 +meas-rep lchan 0 0 1 0 rxlev 0 rxqual 0 ta 0 neighbors 30 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + diff --git a/tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment.ho_vty b/tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment.ho_vty new file mode 100644 index 000000000..6990132ee --- /dev/null +++ b/tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment.ho_vty @@ -0,0 +1,78 @@ +# Congestion check: Balancing congestion by handover TCH/F -> TCH/H +# With dynamic timeslots. +# As soon as only one TCH/F is left, there should be HO to a dyn TS. + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH + +network + bts 0 + handover2 min-free-slots tch/f 2 + handover2 min-free-slots tch/h 0 + handover2 assignment 1 +set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F pdch pdch pdch + +# (there must be at leas one measurement report on each lchan for congestion check to work) +meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30 + +congestion-check +expect-no-chan + +create-ms bts 0 TCH/F AMR +meas-rep lchan 0 0 5 0 rxlev 40 rxqual 0 ta 0 neighbors 30 +expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F TCH/F pdch * + +congestion-check +expect-as from lchan 0 0 5 0 to lchan 0 0 6 0 +expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F pdch TCH/H- * + +congestion-check +expect-as from lchan 0 0 4 0 to lchan 0 0 6 1 +expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F pdch pdch TCH/HH * + +congestion-check +expect-no-chan + +create-ms bts 0 TCH/F AMR +meas-rep lchan 0 0 4 0 rxlev 40 rxqual 0 ta 0 neighbors 30 +expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F pdch TCH/HH * + +congestion-check +expect-as from lchan 0 0 4 0 to lchan 0 0 5 0 +expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F pdch TCH/H- TCH/HH * + +congestion-check +expect-as from lchan 0 0 1 0 to lchan 0 0 5 1 +expect-ts-use trx 0 0 states * - TCH/F TCH/F pdch TCH/HH TCH/HH * + +congestion-check +expect-no-chan + +create-ms bts 0 TCH/F AMR +meas-rep lchan 0 0 1 0 rxlev 40 rxqual 0 ta 0 neighbors 30 +expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F pdch TCH/HH TCH/HH * + +congestion-check +expect-as from lchan 0 0 1 0 to lchan 0 0 4 0 +expect-ts-use trx 0 0 states * - TCH/F TCH/F TCH/H- TCH/HH TCH/HH * + +congestion-check +expect-as from lchan 0 0 2 0 to lchan 0 0 4 1 +expect-ts-use trx 0 0 states * - - TCH/F TCH/HH TCH/HH TCH/HH * + +congestion-check +expect-no-chan + +create-ms bts 0 TCH/F AMR +meas-rep lchan 0 0 1 0 rxlev 40 rxqual 0 ta 0 neighbors 30 +expect-ts-use trx 0 0 states * TCH/F - TCH/F TCH/HH TCH/HH TCH/HH * + +congestion-check +expect-no-chan + +create-ms bts 0 TCH/F AMR +meas-rep lchan 0 0 2 0 rxlev 40 rxqual 0 ta 0 neighbors 30 +expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/HH TCH/HH TCH/HH * + +congestion-check +expect-no-chan + diff --git a/tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment_2.ho_vty b/tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment_2.ho_vty new file mode 100644 index 000000000..08bc151ec --- /dev/null +++ b/tests/handover/test_dyn_ts_amr_tch_f_to_h_congestion_assignment_2.ho_vty @@ -0,0 +1,47 @@ +# If a handover from TCH/F to TCH/H frees a dynamic timeslot, +# take the freed TCH/H from the soure timeslot into account, +# both when the target is a dynamic timeslot and when the target is a static timeslot. + +create-bts trx-count 1 timeslots c+s4 dyn TCH/F TCH/F TCH/H PDCH PDCH PDCH + +network + bts 0 + handover2 min-free-slots tch/f 3 + handover2 min-free-slots tch/h 2 + handover2 assignment 1 + +set-ts-use trx 0 0 states * TCH/F - - - * * * +# (there must be at least one measurement report on each lchan for congestion check to work) +meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30 +congestion-check +expect-as from lchan 0 0 1 0 to lchan 0 0 4 0 +expect-ts-use trx 0 0 states * pdch - - TCH/H- * * * + +# Again with one more TCH/H occupied, there will still be two free TCH/H after HO on the dyn TS +set-ts-use trx 0 0 states * TCH/F - - TCH/H- * * * +meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30 +congestion-check +expect-as from lchan 0 0 1 0 to lchan 0 0 4 1 +expect-ts-use trx 0 0 states * pdch - - TCH/HH * * * + +# Again, with the target being a dyn TS +create-bts trx-count 1 timeslots c+s4 dyn TCH/F TCH/F dyn PDCH PDCH PDCH + +network + bts 1 + handover2 min-free-slots tch/f 3 + handover2 min-free-slots tch/h 2 + handover2 assignment 1 + +set-ts-use trx 1 0 states * TCH/F TCH/F - pdch * * * +meas-rep lchan 1 * * * rxlev 40 rxqual 0 ta 0 neighbors 30 +congestion-check +expect-as from lchan 1 0 1 0 to lchan 1 0 4 0 +expect-ts-use trx 1 0 states * pdch TCH/F - TCH/H- * * * + +# Again with one more TCH/H occupied, there will still be two free TCH/H after HO on the dyn TS +set-ts-use trx 1 0 states * TCH/F TCH/F - TCH/H- * * * +meas-rep lchan 1 * * * rxlev 40 rxqual 0 ta 0 neighbors 30 +congestion-check +expect-as from lchan 1 0 1 0 to lchan 1 0 4 1 +expect-ts-use trx 1 0 states * pdch TCH/F - TCH/HH * * * diff --git a/tests/handover/test_dyn_ts_amr_tch_h_to_f_congestion_assignment_2.ho_vty b/tests/handover/test_dyn_ts_amr_tch_h_to_f_congestion_assignment_2.ho_vty new file mode 100644 index 000000000..bf1edf22b --- /dev/null +++ b/tests/handover/test_dyn_ts_amr_tch_h_to_f_congestion_assignment_2.ho_vty @@ -0,0 +1,27 @@ +# If a handover from TCH/H to TCH/F frees a dynamic timeslot, +# take the freed TCH/F from the soure timeslot into account, +# when the target is a static timeslot. + +create-bts trx-count 1 timeslots c+s4 dyn TCH/F TCH/F TCH/F PDCH PDCH PDCH + +network + bts 0 + handover2 min-free-slots tch/f 2 + handover2 min-free-slots tch/h 2 + handover2 assignment 1 + +set-ts-use trx 0 0 states * TCH/H- - - - * * * +# (there must be at least one measurement report on each lchan for congestion check to work) +meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30 +congestion-check +expect-as from lchan 0 0 1 0 to lchan 0 0 2 0 +expect-ts-use trx 0 0 states * pdch TCH/F - - * * * + +# Again with one more TCH/F occupied, there will still be two free TCH/F after HO on the dyn TS +set-ts-use trx 0 0 states * TCH/H- - - TCH/F * * * +meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30 +congestion-check +expect-as from lchan 0 0 1 0 to lchan 0 0 2 0 +expect-ts-use trx 0 0 states * pdch TCH/F - TCH/F * * * + +# (TCH/H -> TCH/F onto a dyn TS will always make TCH/H congestion worse, so there is no useful test case left here) diff --git a/tests/handover/test_dyn_ts_balance_congestion.ho_vty b/tests/handover/test_dyn_ts_balance_congestion.ho_vty new file mode 100644 index 000000000..2fa11b6b0 --- /dev/null +++ b/tests/handover/test_dyn_ts_balance_congestion.ho_vty @@ -0,0 +1,37 @@ +# To balance congestion, consider cross effects between TCH/F and TCH/H when occupying a dynamic timeslot in the target: +# when balancing of TCH/F congestion would take up a dyn TS in the target, reducing TCH/H availability, the handover +# should not cause worse TCH/H congestion than in the source cell. + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F dyn dyn PDCH + +# for this test, avoid changing a TCH/F to a TCH/H by using a non-AMR codec +codec tch/f FR + +network + bts 0 + handover2 min-free-slots tch/f 2 + bts 1 + handover2 min-free-slots tch/f 4 + handover2 min-free-slots tch/h 4 + +set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F * +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F pdch pdch * + +meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30 +expect-no-chan + +# bts 0 is full for TCH/F. Looking at TCH/F, by target_overbooked_after_ho==75% < current_overbooked_before_ho==100%, a +# congestion balancing to bts 1 would be performed. But the TCH/F on the target cell would occupy a dynamic timeslot. +# That would reduce the TCH/H free slots by two and cause TCH/H being overbooked by 50%. On the source cell, TCH/H is +# not congested. No handover is performed because 50% in the target is more congestion for TCH/H than 0% in the source +# cell. +congestion-check +expect-no-chan + +# If there is no constraint on TCH/H in the target cell, the handover does take place. +network + bts 1 + handover2 min-free-slots tch/h 2 +congestion-check +expect-ho from lchan 0 0 1 0 to lchan 1 0 5 0 diff --git a/tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h.ho_vty b/tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h.ho_vty new file mode 100644 index 000000000..c5890a513 --- /dev/null +++ b/tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h.ho_vty @@ -0,0 +1,74 @@ +# If a handover from one TCH kind to the other occupies a dynamic timeslot, +# also adhere to congestion constraints of the other TCH kind, since taking up +# a dyn TS may reduce the available slot count for both kinds of TCH. + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH + +# A TCH/F has better rxlev at a neighbor, and the neighbor's TCH/F slots would +# not become congested. But taking up a neighbor's dynamic timeslot for TCH/F +# would reduce the TCH/H availability to cause congestion on TCH/H. No HO. + +network + handover2 min-free-slots tch/f 0 + handover2 min-free-slots tch/h 4 + +set-ts-use trx 0 0 states * TCH/F - - pdch pdch pdch * +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/HH pdch pdch * + +meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30 +expect-no-chan + +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 40 +# no handover because that results in congestion on TCH/H in bts 1 +expect-no-chan + +### + +set-ts-use trx 0 0 states * - - - pdch pdch pdch * +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/HH TCH/F pdch * +meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30 + +congestion-check +expect-ho from lchan 1 0 4 1 to lchan 0 0 4 0 +expect-ts-use trx 0 0 states * - - - TCH/H- pdch pdch * +expect-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/H- TCH/F pdch * + + +### + +set-ts-use trx 0 0 states * - - - pdch pdch pdch * +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/H- TCH/F TCH/F * + +congestion-check +# more FAIL: TCH/H moves to worse bts 0 due to congestion +expect-ho from lchan 1 0 4 0 to lchan 0 0 4 0 +expect-ts-use trx 0 0 states * - - - TCH/H- pdch pdch * +expect-ts-use trx 1 0 states * TCH/F TCH/F TCH/F pdch TCH/F TCH/F * + + +### + +set-ts-use trx 0 0 states * - - - TCH/H- pdch pdch * +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F pdch TCH/F TCH/F * + +congestion-check +expect-no-chan + +meas-rep lchan 1 * * * rxlev 40 rxqual 0 ta 0 neighbors 30 +expect-no-chan + +meas-rep lchan 0 * * * rxlev 30 rxqual 0 ta 0 neighbors 40 +# no HO because the target is congested on TCH/H. Moving to TCH/F would also +# reduce TCH/H lchans because it would convert another dyn TS. +expect-no-chan + +### + +set-ts-use trx 0 0 states * - - - pdch pdch pdch * +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F * + +congestion-check +# FAIL: TCH/F occupy dynamic timeslots -- should hand over to bts 0 to free a +# dyn TS and reduce TCH/H congestion. +expect-no-chan diff --git a/tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h_2.ho_vty b/tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h_2.ho_vty new file mode 100644 index 000000000..ef71d3e26 --- /dev/null +++ b/tests/handover/test_dyn_ts_congestion_tch_f_vs_tch_h_2.ho_vty @@ -0,0 +1,34 @@ +# If a handover from one TCH kind to the other occupies a dynamic timeslot, +# also adhere to congestion constraints of the other TCH kind, since taking up +# a dyn TS may reduce the available slot count for both kinds of TCH. + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH + +# A TCH/H has better rxlev at a neighbor, and the neighbor's TCH/H slots would +# not become congested. But taking up a neighbor's dynamic timeslot for TCH/H +# would reduce the TCH/F availability to cause congestion on TCH/F. No HO. + +network + handover2 min-free-slots tch/f 2 + handover2 min-free-slots tch/h 2 + +set-ts-use trx 0 0 states * - - - TCH/H- pdch pdch * +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/HH pdch pdch * + +meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30 +expect-no-chan + +meas-rep lchan 0 0 4 0 rxlev 20 rxqual 0 ta 0 neighbors 40 +# no handover because that results in congestion on TCH/F in bts 1 +expect-no-chan + + +# Now the same situation, except there already is a half occupied TCH/H, hence an added TCH/H would not change the TCH/F +# situation. The handover is performed. + +set-ts-use trx 0 0 states * - - - TCH/H- pdch pdch * +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/HH TCH/H- pdch * + +meas-rep lchan 0 0 4 0 rxlev 20 rxqual 0 ta 0 neighbors 40 +expect-ho from lchan 0 0 4 0 to lchan 1 0 5 1 diff --git a/tests/handover/test_dyn_ts_favor_half_used_tch_h_as_target.ho_vty b/tests/handover/test_dyn_ts_favor_half_used_tch_h_as_target.ho_vty new file mode 100644 index 000000000..73c236f20 --- /dev/null +++ b/tests/handover/test_dyn_ts_favor_half_used_tch_h_as_target.ho_vty @@ -0,0 +1,12 @@ +# assign new MS: re-use half used TCH/H to avoid switching more dyn TS to TCH/H + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH +set-ts-use trx 0 0 states * - - - pdch TCH/H- pdch pdch +create-ms bts 0 TCH/H AMR +expect-ts-use trx 0 0 states * - - - pdch TCH/HH pdch pdch + +# in static timeslots, there is NO preference to fill half-used TCH/H first +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/H TCH/H TCH/H PDCH +set-ts-use trx 1 0 states * - - - - TCH/H- - pdch +create-ms bts 1 TCH/H AMR +set-ts-use trx 1 0 states * - - - TCH/H- TCH/H- - pdch diff --git a/tests/handover/test_dyn_ts_favor_moving_half_used_tch_h.ho_vty b/tests/handover/test_dyn_ts_favor_moving_half_used_tch_h.ho_vty new file mode 100644 index 000000000..6548360e8 --- /dev/null +++ b/tests/handover/test_dyn_ts_favor_moving_half_used_tch_h.ho_vty @@ -0,0 +1,42 @@ +# Congestion check: favor moving a TCH/H that frees a half-used dyn TS completely. +# The algorithm should notice that this is about moving an lchan within the same cell, so all candidates will remain +# with unchanged rxlev after a re-assignment; hence the current rxlev for each candidate should not make a difference. + +create-bts trx-count 1 timeslots c+s4 TCH/F dyn dyn dyn dyn - - +network + handover2 min-free-slots tch/h 6 + +# Test with identical rxlev across lchans (trivial and unrealistic) +set-ts-use trx 0 0 states * - TCH/HH TCH/H- TCH/HH pdch - - +meas-rep lchan * * * * rxlev 30 rxqual 0 ta 0 +congestion-check +expect-as from lchan 0 0 3 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F TCH/HH pdch TCH/HH pdch - - + +# clear measurements for the next run +set-ts-use trx 0 0 states * - pdch pdch pdch pdch - - + +# Check that a weaker rxlev coming up earlier in the congestion checking loop does not override the favored half-used +# TCH/H +set-ts-use trx 0 0 states * - TCH/HH TCH/H- TCH/HH pdch - - +meas-rep lchan 0 0 2 1 rxlev 30 rxqual 0 ta 0 +meas-rep lchan 0 0 3 0 rxlev 31 rxqual 0 ta 0 +meas-rep lchan 0 0 4 0 rxlev 32 rxqual 0 ta 0 +meas-rep lchan 0 0 4 1 rxlev 33 rxqual 0 ta 0 +congestion-check +expect-as from lchan 0 0 3 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F TCH/HH pdch TCH/HH pdch - - + +# clear measurements for the next run +set-ts-use trx 0 0 states * - pdch pdch pdch pdch - - + +# Check that a weaker rxlev coming up later in the congestion checking loop does not override the favored half-used +# TCH/H +set-ts-use trx 0 0 states * - TCH/HH TCH/H- TCH/HH pdch - - +meas-rep lchan 0 0 2 1 rxlev 34 rxqual 0 ta 0 +meas-rep lchan 0 0 3 0 rxlev 33 rxqual 0 ta 0 +meas-rep lchan 0 0 4 0 rxlev 32 rxqual 0 ta 0 +meas-rep lchan 0 0 4 1 rxlev 31 rxqual 0 ta 0 +congestion-check +expect-as from lchan 0 0 3 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F TCH/HH pdch TCH/HH pdch - - diff --git a/tests/handover/test_dyn_ts_favor_static_ts_as_target.ho_vty b/tests/handover/test_dyn_ts_favor_static_ts_as_target.ho_vty new file mode 100644 index 000000000..0c55a2c97 --- /dev/null +++ b/tests/handover/test_dyn_ts_favor_static_ts_as_target.ho_vty @@ -0,0 +1,38 @@ +# If both a static and a dynamic TCH/H (even without pchan switch!) are available, we always prefer static TS. +create-bts trx-count 1 timeslots c+s4 dyn TCH/H dyn TCH/H dyn TCH/H PDCH + +network + bts 0 + channel allocator mode set-all ascending + +set-ts-use trx 0 0 states * TCH/-H TCH/-H TCH/-H TCH/-H TCH/-H TCH/-H * + +# the dynamic timeslot is already in TCH/H mode, and needs no pchan switch. It appears first in the list, hence it would +# be used first -- but we prefer using static TS when still available: +create-ms bts 0 TCH/H AMR +expect-ts-use trx 0 0 states * TCH/-H TCH/HH TCH/-H TCH/-H TCH/-H TCH/-H * +# ^ + +# Interference ratings do NOT influence whether a static or dynamic lchan (even without pchan switch) is going to be +# assigned. +network + bts 0 + channel allocator avoid-interference 1 + interference-meas level-bounds -115 -109 -103 -97 -91 -85 +# 0 1 2 3 4 5 + +# Here the dyn TS lchan happens to have less interference. But still the choice to prefer static over dynamic weighs +# stronger. The static TS with least interference is picked. +# dyn TCH/H dyn TCH/H dyn TCH/H +expect-ts-use trx 0 0 states * TCH/-H TCH/HH TCH/-H TCH/-H TCH/-H TCH/-H * +res-ind trx 0 0 levels - 4- -- 1- 4- 3- 2- - +create-ms bts 0 TCH/H AMR +expect-ts-use trx 0 0 states * TCH/-H TCH/HH TCH/-H TCH/-H TCH/-H TCH/HH * +# ^ +create-ms bts 0 TCH/H AMR +expect-ts-use trx 0 0 states * TCH/-H TCH/HH TCH/-H TCH/HH TCH/-H TCH/HH * +# ^ +# now only dynamic TS are left. The one dyn lchan with least interference is picked +create-ms bts 0 TCH/H AMR +expect-ts-use trx 0 0 states * TCH/-H TCH/HH TCH/HH TCH/HH TCH/-H TCH/HH * +# ^ diff --git a/tests/handover/test_ho_to_better_cell.ho_vty b/tests/handover/test_ho_to_better_cell.ho_vty new file mode 100644 index 000000000..afa0a8871 --- /dev/null +++ b/tests/handover/test_ho_to_better_cell.ho_vty @@ -0,0 +1,8 @@ +# Handover to best better cell +# The best neighbor cell is selected +create-n-bts 7 +set-ts-use trx 0 0 states * TCH/F - - - - - - +meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 20 21 18 20 23 19 +expect-ho from lchan 0 0 1 0 to lchan 5 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 5 0 states * TCH/F - - - - - - diff --git a/tests/handover/test_ho_to_better_cell_2.ho_vty b/tests/handover/test_ho_to_better_cell_2.ho_vty new file mode 100644 index 000000000..c3f554438 --- /dev/null +++ b/tests/handover/test_ho_to_better_cell_2.ho_vty @@ -0,0 +1,10 @@ +# Handover to best better cell +# The best neighbor cell is selected + +create-n-bts 7 +set-ts-use trx 0 0 states * TCH/F - - - - - - +meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 20 21 18 20 23 19 +expect-ho from lchan 0 0 1 0 to lchan 5 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 5 0 states * TCH/F - - - - - - + diff --git a/tests/handover/test_hysteresis.ho_vty b/tests/handover/test_hysteresis.ho_vty new file mode 100644 index 000000000..5c06a360a --- /dev/null +++ b/tests/handover/test_hysteresis.ho_vty @@ -0,0 +1,13 @@ +# Hysteresis +# If neighbor cell is better, handover is only performed if the +# amount of improvement is greater or equal hyteresis + +create-n-bts 2 +set-ts-use trx 0 0 states * TCH/F - - - - - - +meas-rep lchan 0 0 1 0 rxlev 27 rxqual 0 ta 0 neighbors 30 +expect-no-chan +meas-rep lchan 0 0 1 0 rxlev 26 rxqual 0 ta 0 neighbors 30 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + diff --git a/tests/handover/test_insufficient_measurements.ho_vty b/tests/handover/test_insufficient_measurements.ho_vty new file mode 100644 index 000000000..b67a2488e --- /dev/null +++ b/tests/handover/test_insufficient_measurements.ho_vty @@ -0,0 +1,46 @@ +# No (or not enough) measurements for handover +# Do not solve congestion in cell, because there is no measurement. +# As soon as enough measurements available (1 in our case), perform +# handover. Afterwards the old cell becomes congested and the new +# cell is not. Do not perform handover until new measurements are +# received. +# +# two cells, first in congested, but no handover: + +create-n-bts 2 +network + bts 0 + handover2 min-free-slots tch/f 4 + handover2 min-free-slots tch/h 4 +set-ts-use trx 0 0 states * TCH/F - - - - - - +congestion-check +expect-no-chan +expect-ts-use trx 0 0 states * TCH/F - - - - - - + +# send measurement and trigger congestion check: +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 20 +expect-no-chan +congestion-check +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + +# congest the first cell and remove congestion from second cell: +network + bts 0 + handover2 min-free-slots tch/f 0 + handover2 min-free-slots tch/h 0 + bts 1 + handover2 min-free-slots tch/f 4 + handover2 min-free-slots tch/h 4 + +# no handover until measurements applied: +congestion-check +expect-no-chan +meas-rep lchan 1 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 20 +expect-no-chan +congestion-check +expect-ho from lchan 1 0 1 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - - - +expect-ts-use trx 1 0 states * - - - - - - - + diff --git a/tests/handover/test_keep_efr_codec.ho_vty b/tests/handover/test_keep_efr_codec.ho_vty new file mode 100644 index 000000000..f1a9b40e6 --- /dev/null +++ b/tests/handover/test_keep_efr_codec.ho_vty @@ -0,0 +1,21 @@ +# TCH/F keeping with EFR codec +# The MS is using full rate V2 codec, but the better cell is congested +# at TCH/F slots. As the congestion is removed, the handover takes +# place. + +create-n-bts 2 +network + bts 1 + handover2 min-free-slots tch/f 4 +codec tch/f EFR +set-ts-use trx 0 0 states * TCH/F - - - - - - +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-no-chan +network + bts 1 + handover2 min-free-slots tch/f 3 +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + diff --git a/tests/handover/test_keep_fr_codec.ho_vty b/tests/handover/test_keep_fr_codec.ho_vty new file mode 100644 index 000000000..4729d2514 --- /dev/null +++ b/tests/handover/test_keep_fr_codec.ho_vty @@ -0,0 +1,21 @@ +# TCH/F keeping with FR codec +# The MS is using full rate V1 codec, but the better cell is congested +# at TCH/F slots. As the congestion is removed, the handover takes +# place. + +create-n-bts 2 +network + bts 1 + handover2 min-free-slots tch/f 4 +codec tch/f FR +set-ts-use trx 0 0 states * TCH/F - - - - - - +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-no-chan +network + bts 1 + handover2 min-free-slots tch/f 3 +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + diff --git a/tests/handover/test_keep_hr_codec.ho_vty b/tests/handover/test_keep_hr_codec.ho_vty new file mode 100644 index 000000000..2f514bf44 --- /dev/null +++ b/tests/handover/test_keep_hr_codec.ho_vty @@ -0,0 +1,20 @@ +# TCH/H keeping with HR codec +# The MS is using half rate V1 codec, but the better cell is congested +# at TCH/H slots. As the congestion is removed, the handover takes +# place. + +create-n-bts 2 +network + bts 1 + handover2 min-free-slots tch/h 4 +codec tch/h HR +set-ts-use trx 0 0 states * - - - - TCH/H- - - +meas-rep lchan 0 0 5 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-no-chan +network + bts 1 + handover2 min-free-slots tch/h 3 +meas-rep lchan 0 0 5 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-ho from lchan 0 0 5 0 to lchan 1 0 5 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * - - - - TCH/H- - - diff --git a/tests/handover/test_max_handovers.ho_vty b/tests/handover/test_max_handovers.ho_vty new file mode 100644 index 000000000..76a9ea9e7 --- /dev/null +++ b/tests/handover/test_max_handovers.ho_vty @@ -0,0 +1,18 @@ +# No more parallel handovers, if max_unsync_ho is defined +# There are three mobiles that want to handover, but only two can do +# it at a time, because the maximum number is limited to two. + +create-n-bts 2 +network + bts 1 + handover2 max-handovers 2 +set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - - - - +meas-rep lchan 0 0 1 0 rxlev 0 rxqual 0 ta 0 neighbors 30 +expect-chan lchan 1 0 1 0 +expect-ho-cmd lchan 0 0 1 0 +meas-rep lchan 0 0 2 0 rxlev 0 rxqual 0 ta 0 neighbors 30 +expect-chan lchan 1 0 2 0 +expect-ho-cmd lchan 0 0 2 0 +meas-rep lchan 0 0 3 0 rxlev 0 rxqual 0 ta 0 neighbors 30 +expect-no-chan + diff --git a/tests/handover/test_max_ta.ho_vty b/tests/handover/test_max_ta.ho_vty new file mode 100644 index 000000000..56dbb19d1 --- /dev/null +++ b/tests/handover/test_max_ta.ho_vty @@ -0,0 +1,37 @@ +# Handover due to maximum TA exceeded +# The MS in the current (best) cell has reached maximum allowed timing +# advance. No handover is performed until the timing advance exceeds +# it. The originating cell is still the best, but no handover is +# performed back to that cell, because the penalty timer (due to +# maximum allowed timing advance) is running. + +create-n-bts 2 +set-ts-use trx 0 0 states * TCH/F - - - - - - + +network + bts 0 + handover2 maximum distance 5 + handover2 penalty-time max-distance 17 + +meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 5 neighbors 20 +expect-no-chan +meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 6 neighbors 20 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + +# Penalty timer after TA was exceeded is running, so no handover back to the better cell: +meas-rep lchan 1 0 1 0 rxlev 20 rxqual 0 ta 6 neighbors 30 +expect-no-chan + +wait 16 +# Penalty timer still running +meas-rep lchan 1 0 1 0 rxlev 20 rxqual 0 ta 6 neighbors 30 +expect-no-chan + +wait 1 +# Now 17 seconds have passed, timeout is done, and a handover is performed again. +meas-rep lchan 1 0 1 0 rxlev 20 rxqual 0 ta 6 neighbors 30 +expect-ho from lchan 1 0 1 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - - - +expect-ts-use trx 1 0 states * - - - - - - - diff --git a/tests/handover/test_meas_rep_multi_band.ho_vty b/tests/handover/test_meas_rep_multi_band.ho_vty new file mode 100644 index 000000000..d5fa62448 --- /dev/null +++ b/tests/handover/test_meas_rep_multi_band.ho_vty @@ -0,0 +1,47 @@ +# Test ARFCN parsing from measurement report in multi-band BSS (OS#5717) + +create-n-bts 5 + +set-band bts 0 1800 +set-arfcn trx 0 0 600 + +set-band bts 1 900 +set-arfcn trx 1 0 1000 + +set-band bts 2 850 +set-arfcn trx 2 0 200 + +set-band bts 3 900 +set-arfcn trx 3 0 0 + +set-band bts 4 1800 +set-arfcn trx 4 0 800 + +# Attach MS to BTS 0, BTS 1-4 are neighbors +create-ms bts 0 TCH/F AMR + +expect-ts-use trx 0 0 states * TCH/F - - - - - - +expect-ts-use trx 1 0 states * - - - - - - - +expect-ts-use trx 2 0 states * - - - - - - - +expect-ts-use trx 3 0 states * - - - - - - - +expect-ts-use trx 4 0 states * - - - - - - - + +# Send a measurement report where TRX with ARFCN=800 has the best rxqual. If +# the BSC resolved the indexes in the measurement report correctly according to +# 3GPP TS 04.08 ยง 10.5.2.20, then the neighbors are the following: +# Sub list 1 (band == 1800, same band as the TRX where MS is attached): +# IDX=0 ARFCN=800 BSIC=63 RXLEV=-75dBm (BTS 4) +# Sub list 2 (other bands): +# IDX=1 ARFCN=200 BSIC=63 RXLEV=-110dBm (BTS 2) +# IDX=2 ARFCN=1000 BSIC=63 RXLEV=-110dBm (BTS 1) +# IDX=3 ARFCN=0 BSIC=63 RXLEV=-110dBm (BTS 3; at the end because ARFCN=0) +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 35 0 0 0 + +# If the BSC parsed the list correctly, it will request a handover to BTS 4. +expect-ho from lchan 0 0 1 0 to lchan 4 0 1 0 + +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * - - - - - - - +expect-ts-use trx 2 0 states * - - - - - - - +expect-ts-use trx 3 0 states * - - - - - - - +expect-ts-use trx 4 0 states * TCH/F - - - - - - diff --git a/tests/handover/test_min_rxlev_vs_congestion.ho_vty b/tests/handover/test_min_rxlev_vs_congestion.ho_vty new file mode 100644 index 000000000..d43b89cfa --- /dev/null +++ b/tests/handover/test_min_rxlev_vs_congestion.ho_vty @@ -0,0 +1,18 @@ +# Handover to congested cell, if RX level is below minimum +# The better neighbor cell is congested, so no handover is performed. +# If the RX level of the current cell drops below minimum acceptable +# level, the handover is performed. + +create-n-bts 2 +set-ts-use trx 0 0 states * TCH/F - - - - - - +network + bts 1 + handover2 min-free-slots tch/f 4 + handover2 min-free-slots tch/h 4 +meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 30 +expect-no-chan +meas-rep lchan 0 0 1 0 rxlev 9 rxqual 0 ta 0 neighbors 30 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + diff --git a/tests/handover/test_min_rxlev_vs_hysteresis.ho_vty b/tests/handover/test_min_rxlev_vs_hysteresis.ho_vty new file mode 100644 index 000000000..e034fadaa --- /dev/null +++ b/tests/handover/test_min_rxlev_vs_hysteresis.ho_vty @@ -0,0 +1,20 @@ +# No Hysteresis and minimum RX level +# If current cell's RX level is below mimium level, handover must be +# performed, no matter of the hysteresis. First do not perform +# handover to better neighbor cell, because the hysteresis is not +# met. Second do not perform handover because better neighbor cell is +# below minimum RX level. Third perform handover because current cell +# is below minimum RX level, even if the better neighbor cell (minimum +# RX level reached) does not meet the hysteresis. + +create-n-bts 2 +set-ts-use trx 0 0 states * TCH/F - - - - - - +meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 11 +expect-no-chan +meas-rep lchan 0 0 1 0 rxlev 8 rxqual 0 ta 0 neighbors 9 +expect-no-chan +meas-rep lchan 0 0 1 0 rxlev 9 rxqual 0 ta 0 neighbors 10 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + diff --git a/tests/handover/test_neighbor_congested.ho_vty b/tests/handover/test_neighbor_congested.ho_vty new file mode 100644 index 000000000..b2319d40f --- /dev/null +++ b/tests/handover/test_neighbor_congested.ho_vty @@ -0,0 +1,21 @@ +# No handover to congested cell +# The better neighbor cell is congested, so no handover is performed. +# After the congestion is over, handover will be performed. + +create-n-bts 2 +set-ts-use trx 0 0 states * TCH/F - - - - - - +network + bts 1 + handover2 min-free-slots tch/f 4 + handover2 min-free-slots tch/h 4 +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-no-chan +network + bts 1 + handover2 min-free-slots tch/f 3 + handover2 min-free-slots tch/h 3 +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + diff --git a/tests/handover/test_neighbor_full.ho_vty b/tests/handover/test_neighbor_full.ho_vty new file mode 100644 index 000000000..3b06d3dba --- /dev/null +++ b/tests/handover/test_neighbor_full.ho_vty @@ -0,0 +1,9 @@ +# No handover to a cell with no slots available +# If no slot is available, no handover is performed + +create-n-bts 2 +set-ts-use trx 0 0 states * TCH/F - - - - - - +set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F TCH/HH TCH/HH - +meas-rep lchan 0 0 1 0 rxlev 0 rxqual 0 ta 0 neighbors 30 +expect-no-chan + diff --git a/tests/handover/test_no_congestion.ho_vty b/tests/handover/test_no_congestion.ho_vty new file mode 100644 index 000000000..c8328c6f2 --- /dev/null +++ b/tests/handover/test_no_congestion.ho_vty @@ -0,0 +1,17 @@ +# Congestion check: No congestion +# Three cells have different number of used slots, but there is no +# congestion in any of these cells. No handover is performed. + +create-n-bts 3 +network + handover2 min-free-slots tch/f 2 + handover2 min-free-slots tch/h 2 +set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH - - +set-ts-use trx 1 0 states * TCH/F - - - TCH/H- - - +meas-rep lchan * * * * rxlev 30 rxqual 0 ta 0 neighbors 20 1 20 +expect-no-chan +congestion-check +expect-no-chan +expect-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH - - +expect-ts-use trx 1 0 states * TCH/F - - - TCH/H- - - + diff --git a/tests/handover/test_penalty_timer.ho_vty b/tests/handover/test_penalty_timer.ho_vty new file mode 100644 index 000000000..8d864a2d4 --- /dev/null +++ b/tests/handover/test_penalty_timer.ho_vty @@ -0,0 +1,45 @@ +# Penalty timer must not run +# The MS will try to handover to a better cell, but this will fail. +# Even though the cell is still better, handover will not be performed +# due to penalty timer after handover failure + +network + # set the timeout for LCHAN_ST_WAIT_AFTER_ERROR + timer X3111 5 + # set penalty timeout + handover2 penalty-time failed-ho 23 + +create-n-bts 2 +set-ts-use trx 0 0 states * TCH/F - - - - - - +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-chan lchan 1 0 1 0 +expect-ho-cmd lchan 0 0 1 0 +ho-failed +# first BTS still services the call: +expect-ts-use trx 0 0 states * TCH/F - - - - - - + +# lchan 1 0 1 0 is in LCHAN_ST_WAIT_AFTER_ERROR because the handover failed: +expect-ts-use trx 1 0 states * ! - - - - - - +wait 4 +expect-ts-use trx 1 0 states * ! - - - - - - +wait 1 +expect-ts-use trx 1 0 states * - - - - - - - +# back to UNUSED + +# No handover because the penalty timer is still running +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-no-chan + +wait 17 +# at this point, the penalty timer has not yet expired. (4+1+17 = 22 < 23) +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-no-chan +expect-ts-use trx 0 0 states * TCH/F - - - - - - +expect-ts-use trx 1 0 states * - - - - - - - + +wait 1 +# now the penalty timer of 23 seconds has passed and the handover is attempted again. +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - diff --git a/tests/handover/test_resource_indication.ho_vty b/tests/handover/test_resource_indication.ho_vty new file mode 100644 index 000000000..724372ee9 --- /dev/null +++ b/tests/handover/test_resource_indication.ho_vty @@ -0,0 +1,67 @@ +# Test effects of interference levels reported in Resource Indication. +# Note, this is not actually a handover test. + +create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F TCH/F TCH/F TCH/F PDCH + +# By default, the ordering is most-interference-first +network + bts 0 + channel allocator avoid-interference 1 + interference-meas level-bounds -85 -91 -97 -103 -109 -115 +# 0 1 2 3 4 5 + +res-ind trx 0 0 levels - 1 2 3 4 3 2 - +create-ms bts 0 TCH/F AMR +expect-ts-use trx 0 0 states * - - - TCH/F - - * + +# The ordering may also be configured reversed, still the lowest dBm value should win +network + bts 0 + interference-meas level-bounds -115 -109 -103 -97 -91 -85 +# 0 1 2 3 4 5 + +res-ind trx 0 0 levels - 5 4 2 - 3 4 - +create-ms bts 0 TCH/F AMR +expect-ts-use trx 0 0 states * - - TCH/F TCH/F - - * + +# Favor lchans that have an indicated interference level +res-ind trx 0 0 levels - - - - - 4 3 - +create-ms bts 0 TCH/F AMR +expect-ts-use trx 0 0 states * - - TCH/F TCH/F - TCH/F * + +# For equal levels, pick the first +res-ind trx 0 0 levels - 2 2 - - 2 - - +create-ms bts 0 TCH/F AMR +expect-ts-use trx 0 0 states * TCH/F - TCH/F TCH/F - TCH/F * + +# Test clamping of indexes > 5 +res-ind trx 0 0 levels - - 6 - - 4 - - +create-ms bts 0 TCH/F AMR +expect-ts-use trx 0 0 states * TCH/F - TCH/F TCH/F TCH/F TCH/F * + +# Also test for TCH/H +create-bts trx-count 1 timeslots c+s4 TCH/H TCH/H TCH/H TCH/H TCH/H TCH/H PDCH +network + bts 1 + channel allocator avoid-interference 1 + interference-meas level-bounds -115 -109 -103 -97 -91 -85 +# 0 1 2 3 4 5 + +res-ind trx 1 0 levels - 54 32 21 23 45 54 - +create-ms bts 1 TCH/H AMR +expect-ts-use trx 1 0 states * - - TCH/-H - - - * + +# Favor lchans that have an indicated interference level +res-ind trx 1 0 levels - - - 4- 3- - - - +create-ms bts 1 TCH/H AMR +expect-ts-use trx 1 0 states * - - TCH/-H TCH/H- - - * + +# For equal levels, pick the first +res-ind trx 1 0 levels - -2 22 2- -2 22 2- - +create-ms bts 1 TCH/H AMR +expect-ts-use trx 1 0 states * TCH/-H - TCH/-H TCH/H- - - * + +# Test clamping of indexes > 5 +res-ind trx 1 0 levels - 7- 67 6- -7 54 6- - +create-ms bts 1 TCH/H AMR +expect-ts-use trx 1 0 states * TCH/-H - TCH/-H TCH/H- TCH/-H - * diff --git a/tests/handover/test_rxqual.ho_vty b/tests/handover/test_rxqual.ho_vty new file mode 100644 index 000000000..6f86cf47f --- /dev/null +++ b/tests/handover/test_rxqual.ho_vty @@ -0,0 +1,49 @@ +# Handover to cell with worse RXLEV, if RXQUAL is below minimum +# The neighbor cell has worse RXLEV, so no handover is performed. +# If the RXQUAL of the current cell drops below minimum acceptable +# level, the handover is performed. It is also required that 10 +# reports are received, before RXQUAL is checked. +# +# (See also test 28, which tests for RXQUAL triggering HO to congested cell.) +# +# TODO: bad RXQUAL may want to prefer assignment within the same cell to avoid interference. +# See Performance Enhancements in a Frequency Hopping GSM Network (Nielsen Wigard 2002), Chapter +# 2.1.1, "Interference" in the list of triggers on p.157. + +# first show undesired oscillation when penalty-time low-rxqual-ho is disabled +network + handover2 penalty-time low-rxqual-ho 0 + +create-n-bts 2 +set-ts-use trx 0 0 states * TCH/F - - - - - - +meas-rep repeat 9 lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30 +expect-no-chan +meas-rep lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + +# Now the channel is on bts 1, which has lower rxlev than bts 0. +# The result is an undesired ho oscillation, because the penalty timer is zero +meas-rep lchan 1 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 40 +expect-ho from lchan 1 0 1 0 to lchan 0 0 1 0 + +# Set a proper penalty timeout and report bad-rxqual again +network + handover2 penalty-time low-rxqual-ho 10 +meas-rep repeat 10 lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 + +# This time the penalty timer prevents oscillation +meas-rep repeat 10 lchan 1 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 40 +expect-no-chan + +# After the penalty timeout passes, we do go back to the cell with stronger rxlev +wait 10 +meas-rep lchan 1 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 40 +expect-ho from lchan 1 0 1 0 to lchan 0 0 1 0 +# If the rxqual is still bad here after the penalty timeout, well, then we quickly snap back to the weaker cell, once +meas-rep repeat 10 lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +meas-rep repeat 10 lchan 1 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 40 +expect-no-chan diff --git a/tests/handover/test_rxqual_vs_congestion.ho_vty b/tests/handover/test_rxqual_vs_congestion.ho_vty new file mode 100644 index 000000000..21c4e7e18 --- /dev/null +++ b/tests/handover/test_rxqual_vs_congestion.ho_vty @@ -0,0 +1,19 @@ +# Handover to congested cell, if RX quality is below minimum +# The better neighbor cell is congested, so no handover is performed. +# If the RX quality of the current cell drops below minimum acceptable +# level, the handover is performed. It is also required that 10 +# resports are received, before RX quality is checked. + +create-n-bts 2 +set-ts-use trx 0 0 states * TCH/F - - - - - - +network + bts 1 + handover2 min-free-slots tch/f 4 + handover2 min-free-slots tch/h 4 +meas-rep repeat 9 lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40 +expect-no-chan +meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - + diff --git a/tests/handover/test_stay_in_better_cell.ho_vty b/tests/handover/test_stay_in_better_cell.ho_vty new file mode 100644 index 000000000..00e0e1a73 --- /dev/null +++ b/tests/handover/test_stay_in_better_cell.ho_vty @@ -0,0 +1,6 @@ +# Stay in better cell +# There are many neighbor cells, but only the current cell is the best cell, so no handover is performed +create-n-bts 7 +set-ts-use trx 0 0 states * TCH/F - - - - - - +meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 20 21 18 20 23 19 +expect-no-chan diff --git a/tests/handover/test_stay_in_better_cell_2.ho_vty b/tests/handover/test_stay_in_better_cell_2.ho_vty new file mode 100644 index 000000000..b3e76f8fc --- /dev/null +++ b/tests/handover/test_stay_in_better_cell_2.ho_vty @@ -0,0 +1,10 @@ +# Stay in better cell +# There are many neighbor cells, but only the current cell is the best +# cell, so no handover is performed + +create-n-bts 7 +create-ms bts 0 TCH/F AMR +expect-ts-use trx 0 0 states * TCH/F - - - - - - +meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 20 21 18 20 23 19 +expect-no-chan + diff --git a/tests/handover/test_story.ho_vty b/tests/handover/test_story.ho_vty new file mode 100644 index 000000000..4e827610e --- /dev/null +++ b/tests/handover/test_story.ho_vty @@ -0,0 +1,72 @@ +# Story: 'A neighbor is your friend' + +create-n-bts 3 + +# Andreas is driving along the coast, on a sunny june afternoon. +# Suddenly he is getting a call from his friend and neighbor Axel. +# +# What happens: Two MS are created, #0 for Axel, #1 for Andreas. +# Axel: +create-ms bts 2 TCH/F AMR +# andreas: +create-ms bts 0 TCH/F AMR +expect-ts-use trx 0 0 states * TCH/F - - - - - - +expect-ts-use trx 1 0 states * - - - - - - - +expect-ts-use trx 2 0 states * TCH/F - - - - - - +meas-rep lchan 0 0 1 0 rxlev 40 rxqual 0 ta 0 neighbors 30 +expect-no-chan + +# Axel asks Andreas if he would like to join them for a barbecue. +# Axel's house is right in the neighborhood and the weather is fine. +# Andreas agrees, so he drives to a close store to buy some barbecue +# skewers. +# +# What happens: While driving, a different cell (mounted atop the +# store) becomes better. +# drive to bts 1: +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 35 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - +expect-ts-use trx 2 0 states * TCH/F - - - - - - + +# While Andreas is walking into the store, Axel asks, if he could also +# bring some beer. Andreas has problems understanding him: "I have a +# bad reception here. The cell tower is right atop the store, but poor +# coverage inside. Can you repeat please?" +# +# What happens: Inside the store the close cell is so bad, that +# handover back to the previous cell is required. +# bts 1 becomes bad, so bts 0 helps out: +meas-rep lchan 1 0 1 0 rxlev 5 rxqual 0 ta 0 neighbors 20 +expect-ho from lchan 1 0 1 0 to lchan 0 0 1 0 +expect-ts-use trx 0 0 states * TCH/F - - - - - - +expect-ts-use trx 1 0 states * - - - - - - - +expect-ts-use trx 2 0 states * TCH/F - - - - - - + +# After Andreas bought skewers and beer, he leaves the store. +# +# What happens: Outside the store the close cell is better again, so +# handover back to the that cell is performed. +# bts 1 becomes better again: +meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 35 +expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * TCH/F - - - - - - +expect-ts-use trx 2 0 states * TCH/F - - - - - - + +# bts 2 becomes better: +# Andreas drives down to the lake where Axel's house is. +# +# What happens: There is a small cell at Axel's house, which becomes +# better, because the current cell has no good comverage at the lake. +meas-rep lchan 1 0 1 0 rxlev 14 rxqual 0 ta 0 neighbors 2 63 +expect-ho from lchan 1 0 1 0 to lchan 2 0 2 0 +expect-ts-use trx 0 0 states * - - - - - - - +expect-ts-use trx 1 0 states * - - - - - - - +expect-ts-use trx 2 0 states * TCH/F TCH/F - - - - - + +# Andreas wonders why he still has good radio coverage: "Last time it +# was so bad". Axel says: "I installed a pico cell in my house, +# now we can use our mobile phones down here at the lake." + |