aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/handover_decision.c
blob: 4c08c6664a83a34781699884c24969b1fd602e37 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* 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 <laforge@gnumonks.org>
 *
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#include <stdlib.h>
#include <errno.h>

#include <openbsc/msgb.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/meas_rep.h>
#include <openbsc/signal.h>
#include <openbsc/talloc.h>
#include <openbsc/handover.h>

static int handover_to_arfcn_bsic(struct gsm_lchan *lchan,
				  u_int16_t arfcn, u_int8_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);
}

#define RXLEV_HYST 3

/* process an already parsed measurement report */
static int process_meas_rep(struct gsm_meas_rep *mr)
{
	struct gsm_meas_rep_cell *mr_cell = NULL;
	unsigned int best_better_db;
	int i;

	/* 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;
	}

	/* FIXME: implement actual averaging over multiple measurement
	 * reports */

	if (mr->num_cell > 6)
		return 0;

	/* find the best cell in this report that is at least RXLEV_HYST
	 * better than the current serving cell */
	for (i = 0; i < mr->num_cell; i++) {
		unsigned int better;
		if (mr->cell[i].rxlev < mr->dl.full.rx_lev + RXLEV_HYST)
			continue;

		better = mr->cell[i].rxlev - mr->dl.full.rx_lev;
		if (better > best_better_db) {
			mr_cell = &mr->cell[i];
			best_better_db = better;
		}
	}

	if (!mr_cell) {
		DEBUGPC(DHO, "No better cell\n");
		return 0;
	}

	LOGP(DHO, LOGL_INFO, "Cell on ARFCN %u is better: ", mr_cell->arfcn);
	if (!mr->lchan->ts->trx->bts->network->handover.active) {
		LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n");
		return 0;
	}

	LOGPC(DHO, LOGL_INFO, "Starting handover\n");
	return handover_to_arfcn_bsic(mr->lchan, mr_cell->arfcn, mr_cell->bsic);
}

static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal,
			   void *handler_data, void *signal_data)
{
	struct gsm_meas_rep *mr;

	if (subsys != SS_LCHAN)
		return 0;

	switch (signal) {
	case S_LCHAN_MEAS_REP:
		mr = signal_data;
		process_meas_rep(mr);
		break;
	}

	return 0;
}

void on_dso_load_ho_dec(void)
{
	register_signal_handler(SS_LCHAN, ho_dec_sig_cb, NULL);
}