From 24a75bd5d7f61ddb936cade562007f2922c02357 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 7 Jul 2013 08:13:22 +0200 Subject: HO: Move handover VTY commands, prepare for other HO algorithms A special VTY command "handover algorithm x" is used to set the algorithm. A union of all handover option is provided, but depending on the selected algorithm, only the specific options are accepted, others are rejected. At VTY write, only the specific options are written to the config file. --- include/osmocom/bsc/gsm_data.h | 3 +- include/osmocom/bsc/handover_decision.h | 7 +- include/osmocom/bsc/vty.h | 2 + openbsc/src/libbsc/handover_vty.c | 511 ++++++++++++++++++++++++++++++++ src/libbsc/Makefile.am | 3 +- src/libbsc/bsc_vty.c | 329 +------------------- src/libbsc/handover_decision.c | 305 ------------------- src/libbsc/handover_decision_1.c | 305 +++++++++++++++++++ src/osmo-nitb/bsc_hack.c | 1 - 9 files changed, 835 insertions(+), 631 deletions(-) create mode 100644 openbsc/src/libbsc/handover_vty.c delete mode 100644 src/libbsc/handover_decision.c create mode 100644 src/libbsc/handover_decision_1.c diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h index 17ca5df57..94bdfa1be 100644 --- a/include/osmocom/bsc/gsm_data.h +++ b/include/osmocom/bsc/gsm_data.h @@ -281,7 +281,8 @@ struct gsm_network { /* control interface */ struct ctrl_handle *ctrl; - /* congestion check timer */ + /* global handover settings */ + int ho_algorithm; int ho_congest_timeout; struct osmo_timer_list ho_congest_timer; }; diff --git a/include/osmocom/bsc/handover_decision.h b/include/osmocom/bsc/handover_decision.h index 81078b05d..2ea17f1be 100644 --- a/include/osmocom/bsc/handover_decision.h +++ b/include/osmocom/bsc/handover_decision.h @@ -1,7 +1,12 @@ #ifndef _HANDOVER_DECISION_H #define _HANDOVER_DECISION_H -void on_dso_load_ho_dec(void); +void bts_dump_vty_handover(struct vty *vty, struct gsm_bts *bts); +void config_write_bts_handover(struct vty *vty, struct gsm_bts *bts); +void config_write_net_handover(struct vty *vty); +void bsc_vty_init_handover(void); + +void init_ho_1(void); #endif /* _HANDOVER_DECISION_H */ diff --git a/include/osmocom/bsc/vty.h b/include/osmocom/bsc/vty.h index 183fc2510..aa7060714 100644 --- a/include/osmocom/bsc/vty.h +++ b/include/osmocom/bsc/vty.h @@ -8,6 +8,8 @@ struct gsm_network; struct vty; +struct gsm_network *gsmnet_from_vty(struct vty *v); + void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *); struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base); diff --git a/openbsc/src/libbsc/handover_vty.c b/openbsc/src/libbsc/handover_vty.c new file mode 100644 index 000000000..e20773353 --- /dev/null +++ b/openbsc/src/libbsc/handover_vty.c @@ -0,0 +1,511 @@ +/* OpenBSC interface to quagga VTY */ +/* (C) 2009-2010 by Harald Welte + * 2013 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero 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 . + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +void bts_dump_vty_handover(struct vty *vty, struct gsm_bts *bts) +{ + int algorithm = bts->network->ho_algorithm; + + if (algorithm == 1 || algorithm == 2) + vty_out(vty, " Handover: %s%s", + bts->handover.ho_active ? "On" : "Off", VTY_NEWLINE); + if (algorithm == 2) + vty_out(vty, " Assignment: %s%s", + bts->handover.as_active ? "On" : "Off", VTY_NEWLINE); +} + +void config_write_bts_handover(struct vty *vty, struct gsm_bts *bts) +{ + int algorithm = bts->network->ho_algorithm; + + if (algorithm == 1 || algorithm == 2) + vty_out(vty, " handover enable handover %u%s", + bts->handover.ho_active, VTY_NEWLINE); + if (algorithm == 2) { + vty_out(vty, " handover enable assignment %u%s", + bts->handover.as_active, VTY_NEWLINE); + vty_out(vty, " handover set %s%s", + (bts->handover.full) ? "full" : "sub", VTY_NEWLINE); + vty_out(vty, " handover min rxlev %d%s", + bts->handover.min_rxlev, VTY_NEWLINE); + } + if (algorithm == 1 || algorithm == 2) { + vty_out(vty, " handover window rxlev averaging %u%s", + bts->handover.win_rxlev_avg, VTY_NEWLINE); + vty_out(vty, " handover window rxlev neighbor averaging %u%s", + bts->handover.win_rxlev_avg_neigh, VTY_NEWLINE); + vty_out(vty, " handover power budget hysteresis %u%s", + bts->handover.pwr_hysteresis, VTY_NEWLINE); + vty_out(vty, " handover power budget interval %u%s", + bts->handover.pwr_interval, VTY_NEWLINE); + } + if (algorithm == 2) { + vty_out(vty, " handover afs rxlev improvement %u%s", + bts->handover.afs_rxlev_improve, VTY_NEWLINE); + vty_out(vty, " handover min rxqual %u%s", + bts->handover.min_rxqual, VTY_NEWLINE); + vty_out(vty, " handover window rxqual averaging %u%s", + bts->handover.win_rxqual_avg, VTY_NEWLINE); + vty_out(vty, " handover afs rxqual improvement %u%s", + bts->handover.afs_rxqual_improve, VTY_NEWLINE); + } + if (algorithm == 1 || algorithm == 2) + vty_out(vty, " handover maximum distance %u%s", + bts->handover.max_distance, VTY_NEWLINE); + if (algorithm == 2) { + vty_out(vty, " handover min free tch-f %u%s", + bts->handover.min_free_tchf, VTY_NEWLINE); + vty_out(vty, " handover min free tch-h %u%s", + bts->handover.min_free_tchh, VTY_NEWLINE); + vty_out(vty, " handover max unsync handovers %u%s", + bts->handover.max_unsync_ho, VTY_NEWLINE); + vty_out(vty, " handover penalty max distance %u%s", + bts->handover.penalty_max_dist, VTY_NEWLINE); + vty_out(vty, " handover penalty handover failure %u%s", + bts->handover.penalty_ho_fail, VTY_NEWLINE); + vty_out(vty, " handover penalty assignment failure %u%s", + bts->handover.penalty_as_fail, VTY_NEWLINE); + } +} + +void config_write_net_handover(struct vty *vty) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + int algorithm = gsmnet->ho_algorithm; + + if (algorithm == 1 || algorithm == 2) + vty_out(vty, " handover algorithm %d%s", gsmnet->ho_algorithm, + VTY_NEWLINE); + if (algorithm == 2) + vty_out(vty, " handover congestion-check-timer %u%s", + gsmnet->ho_congest_timeout, VTY_NEWLINE); +} + +/* check if given algorithm is set, otherwise reject command */ +int is_cmd_for_algorithm(struct vty *vty, int for_1, int for_2) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + int algorithm = gsmnet->ho_algorithm; + + switch (algorithm) { + case 1: + if (for_1) + break; + vty_out(vty, "%% Given command is not allowed for selected " + "handover algorithm 1%s\n", VTY_NEWLINE); + return -EINVAL; + case 2: + if (for_2) + break; + vty_out(vty, "%% Given command is not allowed for selected " + "handover algorithm 2%s\n", VTY_NEWLINE); + return -EINVAL; + default: + vty_out(vty, "%% Please define handover algorithm before " + "giving handover config command%s\n", VTY_NEWLINE); + return -EINVAL; + } + + return 0; +} + +/* Per network configuration */ +#define NET_HO_STR "Global handover settings.\n" +DEFUN(cfg_net_ho_algorithm, + cfg_net_ho_algorithm_cmd, + "handover algorithm (1|2)", + NET_HO_STR + "Handover algorithm selection\n" + "Algorithm 1\nAlgorithm 2") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->ho_algorithm = atoi(argv[0]); + switch (gsmnet->ho_algorithm) { + case 1: + init_ho_1(); + break; + case 2: +// init_ho_2(); + break; + } + return CMD_SUCCESS; +} + +DEFUN(cfg_net_ho_congest_timer, + cfg_net_ho_congest_timer_cmd, + "handover congestion-check-timer <0-60>", + NET_HO_STR + "Handover congestion check timer\n" + "Time in seconds for congestion check interval (0 to disable)\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + gsmnet->ho_congest_timeout = atoi(argv[0]); + switch (gsmnet->ho_algorithm) { + case 2: +// init_ho_timer_2(); + break; + } + return CMD_SUCCESS; +} + +/* Per BTS configuration */ +#define HANDOVER_STR "Handover Options\n" +#define HO_ENABLE_STR HANDOVER_STR "Enable or disable handover/assignment\n" + +DEFUN(cfg_bts_ho_handover, cfg_bts_ho_handover_cmd, + "handover enable handover (0|1)", + HO_ENABLE_STR + "Enable or disable handover\n" + "Don't perform in-call handover\n" + "Perform in-call handover\n") +{ + int enable = atoi(argv[0]); + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 1, 1)) + return CMD_WARNING; + if (enable && ipacc_rtp_direct) { + vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode " + "is enabled by using the -P command line option%s", + VTY_NEWLINE); + return CMD_WARNING; + } + bts->handover.ho_active = enable; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_assignment, cfg_bts_ho_assignment_cmd, + "handover enable assignment (0|1)", + HO_ENABLE_STR + "Enable or disable assignment\n" + "Don't perform in-call assignment\n" + "Perform in-call assignment\n") +{ + int enable = atoi(argv[0]); + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + if (enable && ipacc_rtp_direct) { + vty_out(vty, "%% Cannot enable assignment unless RTP Proxy mode " + "is enabled by using the -P command line option%s", + VTY_NEWLINE); + return CMD_WARNING; + } + bts->handover.as_active = enable; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_set, cfg_bts_ho_set_cmd, + "handover set (full|sub)", + HANDOVER_STR + "Define measuement set of TDMA frames\n" + "Full set of 102/104 TDMA frames\n" + "Sub set of 4 TDMA frames (SACCH)") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + bts->handover.full = (argv[0][0]=='f'); + return CMD_SUCCESS; +} + +#define HO_MIN_STR HANDOVER_STR "Minimum Values\n" +#define HO_MAX_STR HANDOVER_STR "Maximum Values\n" +#define HO_MIN_FREE_STR HO_MIN_STR "Minimum free Slots\n" +#define HO_WIN_STR HANDOVER_STR "Measurement Window\n" +#define HO_WIN_RXLEV_STR HO_WIN_STR "Received Level Averaging\n" +#define HO_WIN_RXQUAL_STR HO_WIN_STR "Received Quality Averaging\n" +#define HO_PBUDGET_STR HANDOVER_STR "Power Budget\n" +#define HO_AFS_STR HANDOVER_STR "AMR on TCH/F\n" +#define HO_PENALTY_STR HANDOVER_STR "Penalty Time\n" +#define HO_AVG_COUNT_STR "Amount to use for Averaging\n" + +DEFUN(cfg_bts_ho_min_rxlev, cfg_bts_ho_min_rxlev_cmd, + "handover min rxlev <-110-50>", + HO_MIN_STR + "The minimum RxLev allowed in this cell\n" + "Minimum RxLev in dBm") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + bts->handover.min_rxlev = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_win_rxlev_avg, cfg_bts_ho_win_rxlev_avg_cmd, + "handover window rxlev averaging <1-10>", + HO_WIN_RXLEV_STR + "How many RxLev measurements are used for averaging\n" + HO_AVG_COUNT_STR) +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 1, 1)) + return CMD_WARNING; + bts->handover.win_rxlev_avg = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_win_rxlev_neigh_avg, cfg_bts_ho_win_rxlev_avg_neigh_cmd, + "handover window rxlev neighbor averaging <1-10>", + HO_WIN_RXLEV_STR "Neighbor\n" + "How many RxQual measurements are used for averaging\n" + HO_AVG_COUNT_STR) +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 1, 1)) + return CMD_WARNING; + bts->handover.win_rxlev_avg_neigh = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_pwr_hysteresis, cfg_bts_ho_pwr_hysteresis_cmd, + "handover power budget hysteresis <0-999>", + HO_PBUDGET_STR + "How many dB does a neighbor to be stronger to become a HO candidate\n" + "Hysteresis\n" "Number\n") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 1, 1)) + return CMD_WARNING; + bts->handover.pwr_hysteresis = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_pwr_interval, cfg_bts_ho_pwr_interval_cmd, + "handover power budget interval <1-99>", + HO_PBUDGET_STR + "How often to check if we have a better cell (SACCH frames)\n" + "Interval\n" "Number\n") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 1, 1)) + return CMD_WARNING; + bts->handover.pwr_interval = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_afs_rxlev_improve, cfg_bts_ho_afs_rxlev_improve_cmd, + "handover afs rxlev improvement <0-20>", + HO_AFS_STR + "RxLev\n" + "Improvement of RxLev over other codecs\n" + "RxLev in dB") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + bts->handover.afs_rxlev_improve = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_min_rxqual, cfg_bts_ho_min_rxqual_cmd, + "handover min rxqual <0-7>", + HO_MIN_STR + "The minimum RxQual allowed in this cell\n" + "Minimum RxQual in dBm") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + bts->handover.min_rxqual = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_win_rxqual_avg, cfg_bts_ho_win_rxqual_avg_cmd, + "handover window rxqual averaging <1-10>", + HO_WIN_RXQUAL_STR + "How many RxQual measurements are used for averaging\n" + HO_AVG_COUNT_STR) +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + bts->handover.win_rxqual_avg = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_afs_rxqual_improve, cfg_bts_ho_afs_rxqual_improve_cmd, + "handover afs rxqual improvement <0-7>", + HO_AFS_STR + "RxQual\n" + "Improvement of RxQual over other codecs\n" + "RxQual in dB") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + bts->handover.afs_rxqual_improve = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_max_distance, cfg_bts_ho_max_distance_cmd, + "handover maximum distance <0-9999>", + HANDOVER_STR + "How big is the maximum timing advance before HO is forced\n" + "Distance\n" "Number\n") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 1, 1)) + return CMD_WARNING; + bts->handover.max_distance = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_min_free_tchf, cfg_bts_ho_min_free_tchf_cmd, + "handover min free tch-f <0-9999>", + HO_MIN_FREE_STR + "Minimum free TCH/F timeslots before cell is congeted\n" + "Slots\n") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + bts->handover.min_free_tchf = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_min_free_tchh, cfg_bts_ho_min_free_tchh_cmd, + "handover min free tch-h <0-9999>", + HO_MIN_FREE_STR + "Minimum free TCH/H timeslots before cell is congeted\n" + "Slots\n") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + bts->handover.min_free_tchh = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_max_unsync_ho, cfg_bts_ho_max_unsync_ho_cmd, + "handover max unsync handovers <0-9999>", + HO_MAX_STR + "Maximum unsynchronous handovers\n" + "Handovers\nNumber\n") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + bts->handover.max_unsync_ho = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_penalty_max_dist, cfg_bts_ho_penalty_max_dist_cmd, + "handover penalty max distance <0-99999>", + HO_PENALTY_STR + "After leaving this cell due to exceeding the maximum allowed distance\n" + "Distance\nTime in Seconds\n") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + bts->handover.penalty_max_dist = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_penalty_ho_fail, cfg_bts_ho_penalty_ho_fail_cmd, + "handover penalty handover failure <0-99999>", + HO_PENALTY_STR + "After handover failure to this cell\n" + "Failure\nTime in Seconds\n") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + bts->handover.penalty_ho_fail = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ho_penalty_as_fail, cfg_bts_ho_penalty_as_fail_cmd, + "handover penalty assignment failure <0-99999>", + HO_PENALTY_STR + "After assignment failure in this cell\n" + "Failure\nTime in Seconds\n") +{ + struct gsm_bts *bts = vty->index; + + if (is_cmd_for_algorithm(vty, 0, 1)) + return CMD_WARNING; + bts->handover.penalty_as_fail = atoi(argv[0]); + return CMD_SUCCESS; +} + +void bsc_vty_init_handover(void) +{ + install_element(GSMNET_NODE, &cfg_net_ho_algorithm_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_congest_timer_cmd); + install_element(BTS_NODE, &cfg_bts_ho_handover_cmd); + install_element(BTS_NODE, &cfg_bts_ho_assignment_cmd); + install_element(BTS_NODE, &cfg_bts_ho_set_cmd); + install_element(BTS_NODE, &cfg_bts_ho_min_rxlev_cmd); + install_element(BTS_NODE, &cfg_bts_ho_win_rxlev_avg_cmd); + install_element(BTS_NODE, &cfg_bts_ho_win_rxlev_avg_neigh_cmd); + install_element(BTS_NODE, &cfg_bts_ho_pwr_hysteresis_cmd); + install_element(BTS_NODE, &cfg_bts_ho_pwr_interval_cmd); + install_element(BTS_NODE, &cfg_bts_ho_afs_rxlev_improve_cmd); + install_element(BTS_NODE, &cfg_bts_ho_min_rxqual_cmd); + install_element(BTS_NODE, &cfg_bts_ho_win_rxqual_avg_cmd); + install_element(BTS_NODE, &cfg_bts_ho_afs_rxqual_improve_cmd); + install_element(BTS_NODE, &cfg_bts_ho_max_distance_cmd); + install_element(BTS_NODE, &cfg_bts_ho_min_free_tchf_cmd); + install_element(BTS_NODE, &cfg_bts_ho_min_free_tchh_cmd); + install_element(BTS_NODE, &cfg_bts_ho_max_unsync_ho_cmd); + install_element(BTS_NODE, &cfg_bts_ho_penalty_max_dist_cmd); + install_element(BTS_NODE, &cfg_bts_ho_penalty_ho_fail_cmd); + install_element(BTS_NODE, &cfg_bts_ho_penalty_as_fail_cmd); +} + diff --git a/src/libbsc/Makefile.am b/src/libbsc/Makefile.am index 42fabab6d..fa5d6bdfe 100644 --- a/src/libbsc/Makefile.am +++ b/src/libbsc/Makefile.am @@ -16,7 +16,8 @@ libbsc_a_SOURCES = abis_nm.c abis_nm_vty.c \ bts_sysmobts.c \ chan_alloc.c \ gsm_subscriber_base.c \ - handover_decision.c handover_logic.c meas_rep.c \ + handover_decision_1.c \ + handover_vty.c handover_logic.c meas_rep.c \ rest_octets.c system_information.c \ e1_config.c \ bsc_api.c bsc_msc.c bsc_vty.c \ diff --git a/src/libbsc/bsc_vty.c b/src/libbsc/bsc_vty.c index d9b8cebef..bc0f67953 100644 --- a/src/libbsc/bsc_vty.c +++ b/src/libbsc/bsc_vty.c @@ -50,6 +50,7 @@ #include #include #include +#include #include "../../bscconfig.h" @@ -299,10 +300,8 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); e1isl_dump_vty(vty, bts->oml_link); } - vty_out(vty, " Handover: %s%s", bts->handover.ho_active ? "On" : "Off", - VTY_NEWLINE); - vty_out(vty, " Assignment: %s%s", bts->handover.as_active ? "On" : "Off", - VTY_NEWLINE); + + bts_dump_vty_handover(vty, bts); /* FIXME: chan_desc */ memset(&pl, 0, sizeof(pl)); @@ -713,44 +712,7 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) vty_out(vty, "auto%s", VTY_NEWLINE); } - vty_out(vty, " handover enable handover %u%s", - bts->handover.ho_active, VTY_NEWLINE); - vty_out(vty, " handover enable assignment %u%s", - bts->handover.as_active, VTY_NEWLINE); - vty_out(vty, " handover set %s%s", - (bts->handover.full) ? "full" : "sub", VTY_NEWLINE); - vty_out(vty, " handover min rxlev %d%s", - bts->handover.min_rxlev, VTY_NEWLINE); - vty_out(vty, " handover window rxlev averaging %u%s", - bts->handover.win_rxlev_avg, VTY_NEWLINE); - vty_out(vty, " handover window rxlev neighbor averaging %u%s", - bts->handover.win_rxlev_avg_neigh, VTY_NEWLINE); - vty_out(vty, " handover power budget hysteresis %u%s", - bts->handover.pwr_hysteresis, VTY_NEWLINE); - vty_out(vty, " handover power budget interval %u%s", - bts->handover.pwr_interval, VTY_NEWLINE); - vty_out(vty, " handover afs rxlev improvement %u%s", - bts->handover.afs_rxlev_improve, VTY_NEWLINE); - vty_out(vty, " handover min rxqual %u%s", - bts->handover.min_rxqual, VTY_NEWLINE); - vty_out(vty, " handover window rxqual averaging %u%s", - bts->handover.win_rxqual_avg, VTY_NEWLINE); - vty_out(vty, " handover afs rxqual improvement %u%s", - bts->handover.afs_rxqual_improve, VTY_NEWLINE); - vty_out(vty, " handover maximum distance %u%s", - bts->handover.max_distance, VTY_NEWLINE); - vty_out(vty, " handover min free tch-f %u%s", - bts->handover.min_free_tchf, VTY_NEWLINE); - vty_out(vty, " handover min free tch-h %u%s", - bts->handover.min_free_tchh, VTY_NEWLINE); - vty_out(vty, " handover max unsync handovers %u%s", - bts->handover.max_unsync_ho, VTY_NEWLINE); - vty_out(vty, " handover penalty max distance %u%s", - bts->handover.penalty_max_dist, VTY_NEWLINE); - vty_out(vty, " handover penalty handover failure %u%s", - bts->handover.penalty_ho_fail, VTY_NEWLINE); - vty_out(vty, " handover penalty assignment failure %u%s", - bts->handover.penalty_as_fail, VTY_NEWLINE); + config_write_bts_handover(vty, bts); config_write_bts_gprs(vty, bts); @@ -808,8 +770,7 @@ static int config_write_net(struct vty *vty) vty_out(vty, " dtx-used %u%s", gsmnet->dtx_enabled, VTY_NEWLINE); vty_out(vty, " subscriber-keep-in-ram %d%s", gsmnet->keep_subscr, VTY_NEWLINE); - vty_out(vty, " handover congestion-check-timer %u%s", - gsmnet->ho_congest_timeout, VTY_NEWLINE); + config_write_net_handover(vty); return CMD_SUCCESS; } @@ -1491,18 +1452,6 @@ DEFUN(cfg_net_subscr_keep, return CMD_SUCCESS; } -DEFUN(cfg_net_ho_congest_timer, - cfg_net_ho_congest_timer_cmd, - "handover congestion-check-timer <0-60>", - "Global handover settings.\n" - "Handover congestion check timer\n" - "Time in seconds for congestion check interval (0 to disable)\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->ho_congest_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - /* per-BTS configuration */ DEFUN(cfg_bts, cfg_bts_cmd, @@ -3221,252 +3170,6 @@ DEFUN(cfg_bts_amr_hr_hyst3, cfg_bts_amr_hr_hyst3_cmd, return CMD_SUCCESS; } -#define HANDOVER_STR "Handover Options\n" -#define HO_ENABLE_STR HANDOVER_STR "Enable or disable handover/assignment\n" - -DEFUN(cfg_bts_ho_handover, cfg_bts_ho_handover_cmd, - "handover enable handover (0|1)", - HO_ENABLE_STR - "Enable or disable handover\n" - "Don't perform in-call handover\n" - "Perform in-call handover\n") -{ - int enable = atoi(argv[0]); - struct gsm_bts *bts = vty->index; - - if (enable && ipacc_rtp_direct) { - vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode " - "is enabled by using the -P command line option%s", - VTY_NEWLINE); - return CMD_WARNING; - } - bts->handover.ho_active = enable; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_assignment, cfg_bts_ho_assignment_cmd, - "handover enable assignment (0|1)", - HO_ENABLE_STR - "Enable or disable assignment\n" - "Don't perform in-call assignment\n" - "Perform in-call assignment\n") -{ - int enable = atoi(argv[0]); - struct gsm_bts *bts = vty->index; - - if (enable && ipacc_rtp_direct) { - vty_out(vty, "%% Cannot enable assignment unless RTP Proxy mode " - "is enabled by using the -P command line option%s", - VTY_NEWLINE); - return CMD_WARNING; - } - bts->handover.as_active = enable; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_set, cfg_bts_ho_set_cmd, - "handover set (full|sub)", - HANDOVER_STR - "Define measuement set of TDMA frames\n" - "Full set of 102/104 TDMA frames\n" - "Sub set of 4 TDMA frames (SACCH)") -{ - struct gsm_bts *bts = vty->index; - bts->handover.full = (argv[0][0]=='f'); - return CMD_SUCCESS; -} - -#define HO_MIN_STR HANDOVER_STR "Minimum Values\n" -#define HO_MAX_STR HANDOVER_STR "Maximum Values\n" -#define HO_MIN_FREE_STR HO_MIN_STR "Minimum free Slots\n" -#define HO_WIN_STR HANDOVER_STR "Measurement Window\n" -#define HO_WIN_RXLEV_STR HO_WIN_STR "Received Level Averaging\n" -#define HO_WIN_RXQUAL_STR HO_WIN_STR "Received Quality Averaging\n" -#define HO_PBUDGET_STR HANDOVER_STR "Power Budget\n" -#define HO_AFS_STR HANDOVER_STR "AMR on TCH/F\n" -#define HO_PENALTY_STR HANDOVER_STR "Penalty Time\n" -#define HO_AVG_COUNT_STR "Amount to use for Averaging\n" - -DEFUN(cfg_bts_ho_min_rxlev, cfg_bts_ho_min_rxlev_cmd, - "handover min rxlev <-110-50>", - HO_MIN_STR - "The minimum RxLev allowed in this cell\n" - "Minimum RxLev in dBm") -{ - struct gsm_bts *bts = vty->index; - bts->handover.min_rxlev = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_win_rxlev_avg, cfg_bts_ho_win_rxlev_avg_cmd, - "handover window rxlev averaging <1-10>", - HO_WIN_RXLEV_STR - "How many RxLev measurements are used for averaging\n" - HO_AVG_COUNT_STR) -{ - struct gsm_bts *bts = vty->index; - bts->handover.win_rxlev_avg = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_win_rxlev_neigh_avg, cfg_bts_ho_win_rxlev_avg_neigh_cmd, - "handover window rxlev neighbor averaging <1-10>", - HO_WIN_RXLEV_STR "Neighbor\n" - "How many RxQual measurements are used for averaging\n" - HO_AVG_COUNT_STR) -{ - struct gsm_bts *bts = vty->index; - bts->handover.win_rxlev_avg_neigh = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_pwr_hysteresis, cfg_bts_ho_pwr_hysteresis_cmd, - "handover power budget hysteresis <0-999>", - HO_PBUDGET_STR - "How many dB does a neighbor to be stronger to become a HO candidate\n" - "Hysteresis\n" "Number\n") -{ - struct gsm_bts *bts = vty->index; - bts->handover.pwr_hysteresis = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_pwr_interval, cfg_bts_ho_pwr_interval_cmd, - "handover power budget interval <1-99>", - HO_PBUDGET_STR - "How often to check if we have a better cell (SACCH frames)\n" - "Interval\n" "Number\n") -{ - struct gsm_bts *bts = vty->index; - bts->handover.pwr_interval = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_afs_rxlev_improve, cfg_bts_ho_afs_rxlev_improve_cmd, - "handover afs rxlev improvement <0-20>", - HO_AFS_STR - "RxLev\n" - "Improvement of RxLev over other codecs\n" - "RxLev in dB") -{ - struct gsm_bts *bts = vty->index; - bts->handover.afs_rxlev_improve = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_min_rxqual, cfg_bts_ho_min_rxqual_cmd, - "handover min rxqual <0-7>", - HO_MIN_STR - "The minimum RxQual allowed in this cell\n" - "Minimum RxQual in dBm") -{ - struct gsm_bts *bts = vty->index; - bts->handover.min_rxqual = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_win_rxqual_avg, cfg_bts_ho_win_rxqual_avg_cmd, - "handover window rxqual averaging <1-10>", - HO_WIN_RXQUAL_STR - "How many RxQual measurements are used for averaging\n" - HO_AVG_COUNT_STR) -{ - struct gsm_bts *bts = vty->index; - bts->handover.win_rxqual_avg = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_afs_rxqual_improve, cfg_bts_ho_afs_rxqual_improve_cmd, - "handover afs rxqual improvement <0-7>", - HO_AFS_STR - "RxQual\n" - "Improvement of RxQual over other codecs\n" - "RxQual in dB") -{ - struct gsm_bts *bts = vty->index; - bts->handover.afs_rxqual_improve = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_max_distance, cfg_bts_ho_max_distance_cmd, - "handover maximum distance <0-9999>", - HANDOVER_STR - "How big is the maximum timing advance before HO is forced\n" - "Distance\n" "Number\n") -{ - struct gsm_bts *bts = vty->index; - bts->handover.max_distance = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_min_free_tchf, cfg_bts_ho_min_free_tchf_cmd, - "handover min free tch-f <0-9999>", - HO_MIN_FREE_STR - "Minimum free TCH/F timeslots before cell is congeted\n" - "Slots\n") -{ - struct gsm_bts *bts = vty->index; - bts->handover.min_free_tchf = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_min_free_tchh, cfg_bts_ho_min_free_tchh_cmd, - "handover min free tch-h <0-9999>", - HO_MIN_FREE_STR - "Minimum free TCH/H timeslots before cell is congeted\n" - "Slots\n") -{ - struct gsm_bts *bts = vty->index; - bts->handover.min_free_tchh = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_max_unsync_ho, cfg_bts_ho_max_unsync_ho_cmd, - "handover max unsync handovers <0-9999>", - HO_MAX_STR - "Maximum unsynchronous handovers\n" - "Handovers\nNumber\n") -{ - struct gsm_bts *bts = vty->index; - bts->handover.max_unsync_ho = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_penalty_max_dist, cfg_bts_ho_penalty_max_dist_cmd, - "handover penalty max distance <0-99999>", - HO_PENALTY_STR - "After leaving this cell due to exceeding the maximum allowed distance\n" - "Distance\nTime in Seconds\n") -{ - struct gsm_bts *bts = vty->index; - bts->handover.penalty_max_dist = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_penalty_ho_fail, cfg_bts_ho_penalty_ho_fail_cmd, - "handover penalty handover failure <0-99999>", - HO_PENALTY_STR - "After handover failure to this cell\n" - "Failure\nTime in Seconds\n") -{ - struct gsm_bts *bts = vty->index; - bts->handover.penalty_ho_fail = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ho_penalty_as_fail, cfg_bts_ho_penalty_as_fail_cmd, - "handover penalty assignment failure <0-99999>", - HO_PENALTY_STR - "After assignment failure in this cell\n" - "Failure\nTime in Seconds\n") -{ - struct gsm_bts *bts = vty->index; - bts->handover.penalty_as_fail = atoi(argv[0]); - return CMD_SUCCESS; -} - #define TRX_TEXT "Radio Transceiver\n" /* per TRX configuration */ @@ -3994,7 +3697,6 @@ int bsc_vty_init(const struct log_info *cat) install_element(GSMNET_NODE, &cfg_net_T3141_cmd); install_element(GSMNET_NODE, &cfg_net_dtx_cmd); install_element(GSMNET_NODE, &cfg_net_subscr_keep_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_congest_timer_cmd); install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd); install_element(GSMNET_NODE, &cfg_bts_cmd); @@ -4086,25 +3788,6 @@ int bsc_vty_init(const struct log_info *cat) install_element(BTS_NODE, &cfg_bts_amr_hr_hyst2_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_hyst3_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_start_mode_cmd); - install_element(BTS_NODE, &cfg_bts_ho_handover_cmd); - install_element(BTS_NODE, &cfg_bts_ho_assignment_cmd); - install_element(BTS_NODE, &cfg_bts_ho_set_cmd); - install_element(BTS_NODE, &cfg_bts_ho_min_rxlev_cmd); - install_element(BTS_NODE, &cfg_bts_ho_win_rxlev_avg_cmd); - install_element(BTS_NODE, &cfg_bts_ho_win_rxlev_avg_neigh_cmd); - install_element(BTS_NODE, &cfg_bts_ho_pwr_hysteresis_cmd); - install_element(BTS_NODE, &cfg_bts_ho_pwr_interval_cmd); - install_element(BTS_NODE, &cfg_bts_ho_afs_rxlev_improve_cmd); - install_element(BTS_NODE, &cfg_bts_ho_min_rxqual_cmd); - install_element(BTS_NODE, &cfg_bts_ho_win_rxqual_avg_cmd); - install_element(BTS_NODE, &cfg_bts_ho_afs_rxqual_improve_cmd); - install_element(BTS_NODE, &cfg_bts_ho_max_distance_cmd); - install_element(BTS_NODE, &cfg_bts_ho_min_free_tchf_cmd); - install_element(BTS_NODE, &cfg_bts_ho_min_free_tchh_cmd); - install_element(BTS_NODE, &cfg_bts_ho_max_unsync_ho_cmd); - install_element(BTS_NODE, &cfg_bts_ho_penalty_max_dist_cmd); - install_element(BTS_NODE, &cfg_bts_ho_penalty_ho_fail_cmd); - install_element(BTS_NODE, &cfg_bts_ho_penalty_as_fail_cmd); install_element(BTS_NODE, &cfg_trx_cmd); install_node(&trx_node, dummy_config_write); @@ -4144,5 +3827,7 @@ int bsc_vty_init(const struct log_info *cat) bsc_vty_init_extra(); + bsc_vty_init_handover(); + return 0; } diff --git a/src/libbsc/handover_decision.c b/src/libbsc/handover_decision.c deleted file mode 100644 index 5f9a2c3af..000000000 --- a/src/libbsc/handover_decision.c +++ /dev/null @@ -1,305 +0,0 @@ -/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This - * only implements the handover algorithm/decision, but not execution - * of it */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero 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 . - * - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/* issue handover to a cell identified by ARFCN and BSIC */ -static int handover_to_arfcn_bsic(struct gsm_lchan *lchan, - uint16_t arfcn, uint8_t bsic) -{ - struct gsm_bts *new_bts; - - /* resolve the gsm_bts structure for the best neighbor */ - new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic); - if (!new_bts) { - LOGP(DHO, LOGL_NOTICE, "unable to determine neighbor BTS " - "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic); - return -EINVAL; - } - - /* and actually try to handover to that cell */ - return bsc_handover_start(lchan, new_bts); -} - -/* did we get a RXLEV for a given cell in the given report? */ -static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr, - uint16_t arfcn, uint8_t bsic) -{ - int i; - - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - - /* search for matching report */ - if (!(mrc->arfcn == arfcn && mrc->bsic == bsic)) - continue; - - mrc->flags |= MRC_F_PROCESSED; - return mrc->rxlev; - } - return -ENODEV; -} - -/* obtain averaged rxlev for given neighbor */ -static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window) -{ - unsigned int i, idx; - int avg = 0; - - /* reduce window to the actual number of existing measurements */ - if (window < nmp->rxlev_cnt) - window = nmp->rxlev_cnt; - /* this should never happen */ - if (window == 0) - return 0; - - idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev), - nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev), - window); - - for (i = 0; i < window; i++) { - int j = (idx+i) % ARRAY_SIZE(nmp->rxlev); - - avg += nmp->rxlev[j]; - } - - return avg / window; -} - -/* find empty or evict bad neighbor */ -static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan) -{ - int j, worst = 999999; - struct neigh_meas_proc *nmp_worst = NULL; - - /* first try to find an empty/unused slot */ - for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; - if (!nmp->arfcn) - return nmp; - } - - /* no empty slot found. evict worst neighbor from list */ - for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; - int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG); - if (!nmp_worst || avg < worst) { - worst = avg; - nmp_worst = nmp; - } - } - - return nmp_worst; -} - -/* process neighbor cell measurement reports */ -static void process_meas_neigh(struct gsm_meas_rep *mr) -{ - int i, j, idx; - - /* for each reported cell, try to update global state */ - for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j]; - unsigned int idx; - int rxlev; - - /* skip unused entries */ - if (!nmp->arfcn) - continue; - - rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic); - idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); - if (rxlev >= 0) { - nmp->rxlev[idx] = rxlev; - nmp->last_seen_nr = mr->nr; - } else - nmp->rxlev[idx] = 0; - nmp->rxlev_cnt++; - } - - /* iterate over list of reported cells, check if we did not - * process all of them */ - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - struct neigh_meas_proc *nmp; - - if (mrc->flags & MRC_F_PROCESSED) - continue; - - nmp = find_evict_neigh(mr->lchan); - - nmp->arfcn = mrc->arfcn; - nmp->bsic = mrc->bsic; - - nmp->rxlev_cnt = 0; - idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); - nmp->rxlev[idx] = mrc->rxlev; - nmp->rxlev_cnt++; - nmp->last_seen_nr = mr->nr; - - mrc->flags |= MRC_F_PROCESSED; - } -} - -/* attempt to do a handover */ -static int attempt_handover(struct gsm_meas_rep *mr) -{ - struct gsm_bts *bts = mr->lchan->ts->trx->bts; - struct neigh_meas_proc *best_cell = NULL; - unsigned int best_better_db = 0; - int i, rc; - - /* find the best cell in this report that is at least RXLEV_HYST - * better than the current serving cell */ - - for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) { - struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i]; - int avg, better; - - /* skip empty slots */ - if (nmp->arfcn == 0) - continue; - - /* caculate average rxlev for this cell over the window */ - avg = neigh_meas_avg(nmp, bts->handover.win_rxlev_avg_neigh); - - /* check if hysteresis is fulfilled */ - if (avg < mr->dl.full.rx_lev + bts->handover.pwr_hysteresis) - continue; - - better = avg - mr->dl.full.rx_lev; - if (better > best_better_db) { - best_cell = nmp; - best_better_db = better; - } - } - - if (!best_cell) - return 0; - - LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ", - gsm_ts_name(mr->lchan->ts), best_cell->arfcn); - if (!bts->handover.ho_active) { - LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n"); - return 0; - } - - rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic); - switch (rc) { - case 0: - LOGPC(DHO, LOGL_INFO, "Starting handover\n"); - break; - case -ENOSPC: - LOGPC(DHO, LOGL_INFO, "No channel available\n"); - break; - case -EBUSY: - LOGPC(DHO, LOGL_INFO, "Handover already active\n"); - break; - default: - LOGPC(DHO, LOGL_ERROR, "Unknown error\n"); - } - return rc; -} - -/* process an already parsed measurement report and decide if we want to - * attempt a handover */ -static int process_meas_rep(struct gsm_meas_rep *mr) -{ - struct gsm_bts *bts = mr->lchan->ts->trx->bts; - int av_rxlev; - - /* we currently only do handover for TCH channels */ - switch (mr->lchan->type) { - case GSM_LCHAN_TCH_F: - case GSM_LCHAN_TCH_H: - break; - default: - return 0; - } - - /* parse actual neighbor cell info */ - if (mr->num_cell > 0 && mr->num_cell < 7) - process_meas_neigh(mr); - - av_rxlev = get_meas_rep_avg(mr->lchan, MEAS_REP_DL_RXLEV_FULL, - bts->handover.win_rxlev_avg); - - /* Interference HO */ - if (rxlev2dbm(av_rxlev) > -85 && - meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, - 3, 4, 5)) - return attempt_handover(mr); - - /* Bad Quality */ - if (meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, - 3, 4, 5)) - return attempt_handover(mr); - - /* Low Level */ - if (rxlev2dbm(av_rxlev) <= -110) - return attempt_handover(mr); - - /* Distance */ - if (mr->ms_l1.ta > bts->handover.max_distance) - return attempt_handover(mr); - - /* Power Budget AKA Better Cell */ - if ((mr->nr % bts->handover.pwr_interval) == 0) - return attempt_handover(mr); - - return 0; - -} - -static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct lchan_signal_data *lchan_data; - - if (subsys != SS_LCHAN) - return 0; - - lchan_data = signal_data; - switch (signal) { - case S_LCHAN_MEAS_REP: - process_meas_rep(lchan_data->mr); - break; - } - - return 0; -} - -void on_dso_load_ho_dec(void) -{ - osmo_signal_register_handler(SS_LCHAN, ho_dec_sig_cb, NULL); -} diff --git a/src/libbsc/handover_decision_1.c b/src/libbsc/handover_decision_1.c new file mode 100644 index 000000000..aae56f0d1 --- /dev/null +++ b/src/libbsc/handover_decision_1.c @@ -0,0 +1,305 @@ +/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This + * only implements the handover algorithm/decision, but not execution + * of it */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero 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 . + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* issue handover to a cell identified by ARFCN and BSIC */ +static int handover_to_arfcn_bsic(struct gsm_lchan *lchan, + uint16_t arfcn, uint8_t bsic) +{ + struct gsm_bts *new_bts; + + /* resolve the gsm_bts structure for the best neighbor */ + new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic); + if (!new_bts) { + LOGP(DHO, LOGL_NOTICE, "unable to determine neighbor BTS " + "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic); + return -EINVAL; + } + + /* and actually try to handover to that cell */ + return bsc_handover_start(lchan, new_bts); +} + +/* did we get a RXLEV for a given cell in the given report? */ +static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr, + uint16_t arfcn, uint8_t bsic) +{ + int i; + + for (i = 0; i < mr->num_cell; i++) { + struct gsm_meas_rep_cell *mrc = &mr->cell[i]; + + /* search for matching report */ + if (!(mrc->arfcn == arfcn && mrc->bsic == bsic)) + continue; + + mrc->flags |= MRC_F_PROCESSED; + return mrc->rxlev; + } + return -ENODEV; +} + +/* obtain averaged rxlev for given neighbor */ +static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window) +{ + unsigned int i, idx; + int avg = 0; + + /* reduce window to the actual number of existing measurements */ + if (window < nmp->rxlev_cnt) + window = nmp->rxlev_cnt; + /* this should never happen */ + if (window == 0) + return 0; + + idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev), + nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev), + window); + + for (i = 0; i < window; i++) { + int j = (idx+i) % ARRAY_SIZE(nmp->rxlev); + + avg += nmp->rxlev[j]; + } + + return avg / window; +} + +/* find empty or evict bad neighbor */ +static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan) +{ + int j, worst = 999999; + struct neigh_meas_proc *nmp_worst = NULL; + + /* first try to find an empty/unused slot */ + for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { + struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; + if (!nmp->arfcn) + return nmp; + } + + /* no empty slot found. evict worst neighbor from list */ + for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { + struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; + int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG); + if (!nmp_worst || avg < worst) { + worst = avg; + nmp_worst = nmp; + } + } + + return nmp_worst; +} + +/* process neighbor cell measurement reports */ +static void process_meas_neigh(struct gsm_meas_rep *mr) +{ + int i, j, idx; + + /* for each reported cell, try to update global state */ + for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) { + struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j]; + unsigned int idx; + int rxlev; + + /* skip unused entries */ + if (!nmp->arfcn) + continue; + + rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic); + idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); + if (rxlev >= 0) { + nmp->rxlev[idx] = rxlev; + nmp->last_seen_nr = mr->nr; + } else + nmp->rxlev[idx] = 0; + nmp->rxlev_cnt++; + } + + /* iterate over list of reported cells, check if we did not + * process all of them */ + for (i = 0; i < mr->num_cell; i++) { + struct gsm_meas_rep_cell *mrc = &mr->cell[i]; + struct neigh_meas_proc *nmp; + + if (mrc->flags & MRC_F_PROCESSED) + continue; + + nmp = find_evict_neigh(mr->lchan); + + nmp->arfcn = mrc->arfcn; + nmp->bsic = mrc->bsic; + + nmp->rxlev_cnt = 0; + idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); + nmp->rxlev[idx] = mrc->rxlev; + nmp->rxlev_cnt++; + nmp->last_seen_nr = mr->nr; + + mrc->flags |= MRC_F_PROCESSED; + } +} + +/* attempt to do a handover */ +static int attempt_handover(struct gsm_meas_rep *mr) +{ + struct gsm_bts *bts = mr->lchan->ts->trx->bts; + struct neigh_meas_proc *best_cell = NULL; + unsigned int best_better_db = 0; + int i, rc; + + /* find the best cell in this report that is at least RXLEV_HYST + * better than the current serving cell */ + + for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) { + struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i]; + int avg, better; + + /* skip empty slots */ + if (nmp->arfcn == 0) + continue; + + /* caculate average rxlev for this cell over the window */ + avg = neigh_meas_avg(nmp, bts->handover.win_rxlev_avg_neigh); + + /* check if hysteresis is fulfilled */ + if (avg < mr->dl.full.rx_lev + bts->handover.pwr_hysteresis) + continue; + + better = avg - mr->dl.full.rx_lev; + if (better > best_better_db) { + best_cell = nmp; + best_better_db = better; + } + } + + if (!best_cell) + return 0; + + LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ", + gsm_ts_name(mr->lchan->ts), best_cell->arfcn); + if (!bts->handover.ho_active) { + LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n"); + return 0; + } + + rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic); + switch (rc) { + case 0: + LOGPC(DHO, LOGL_INFO, "Starting handover\n"); + break; + case -ENOSPC: + LOGPC(DHO, LOGL_INFO, "No channel available\n"); + break; + case -EBUSY: + LOGPC(DHO, LOGL_INFO, "Handover already active\n"); + break; + default: + LOGPC(DHO, LOGL_ERROR, "Unknown error\n"); + } + return rc; +} + +/* process an already parsed measurement report and decide if we want to + * attempt a handover */ +static int process_meas_rep(struct gsm_meas_rep *mr) +{ + struct gsm_bts *bts = mr->lchan->ts->trx->bts; + int av_rxlev; + + /* we currently only do handover for TCH channels */ + switch (mr->lchan->type) { + case GSM_LCHAN_TCH_F: + case GSM_LCHAN_TCH_H: + break; + default: + return 0; + } + + /* parse actual neighbor cell info */ + if (mr->num_cell > 0 && mr->num_cell < 7) + process_meas_neigh(mr); + + av_rxlev = get_meas_rep_avg(mr->lchan, MEAS_REP_DL_RXLEV_FULL, + bts->handover.win_rxlev_avg); + + /* Interference HO */ + if (rxlev2dbm(av_rxlev) > -85 && + meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, + 3, 4, 5)) + return attempt_handover(mr); + + /* Bad Quality */ + if (meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, + 3, 4, 5)) + return attempt_handover(mr); + + /* Low Level */ + if (rxlev2dbm(av_rxlev) <= -110) + return attempt_handover(mr); + + /* Distance */ + if (mr->ms_l1.ta > bts->handover.max_distance) + return attempt_handover(mr); + + /* Power Budget AKA Better Cell */ + if ((mr->nr % bts->handover.pwr_interval) == 0) + return attempt_handover(mr); + + return 0; + +} + +static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct lchan_signal_data *lchan_data; + + if (subsys != SS_LCHAN) + return 0; + + lchan_data = signal_data; + switch (signal) { + case S_LCHAN_MEAS_REP: + process_meas_rep(lchan_data->mr); + break; + } + + return 0; +} + +void init_ho_1(void) +{ + osmo_signal_register_handler(SS_LCHAN, ho_dec_sig_cb, NULL); +} diff --git a/src/osmo-nitb/bsc_hack.c b/src/osmo-nitb/bsc_hack.c index 172fb8269..ea5d327a9 100644 --- a/src/osmo-nitb/bsc_hack.c +++ b/src/osmo-nitb/bsc_hack.c @@ -252,7 +252,6 @@ int main(int argc, char **argv) talloc_ctx_init(); on_dso_load_token(); on_dso_load_rrlp(); - on_dso_load_ho_dec(); libosmo_abis_init(tall_bsc_ctx); osmo_init_logging(&log_info); -- cgit v1.2.3