blob: 8d7e047b74a29befad7041ce788823fef0f9878b [file] [log] [blame]
Harald Welte8d77b952009-12-17 00:31:10 +01001/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This
2 * only implements the handover algorithm/decision, but not execution
3 * of it */
4
5/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
6 *
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
Harald Welte9af6ddf2011-01-01 15:25:50 +010010 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
Harald Welte8d77b952009-12-17 00:31:10 +010012 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte9af6ddf2011-01-01 15:25:50 +010017 * GNU Affero General Public License for more details.
Harald Welte8d77b952009-12-17 00:31:10 +010018 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010019 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Harald Welte8d77b952009-12-17 00:31:10 +010021 *
22 */
23
24#include <stdlib.h>
25#include <errno.h>
26
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010027#include <osmocom/core/msgb.h>
Harald Welte8d77b952009-12-17 00:31:10 +010028#include <openbsc/debug.h>
29#include <openbsc/gsm_data.h>
30#include <openbsc/meas_rep.h>
31#include <openbsc/signal.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010032#include <osmocom/core/talloc.h>
Harald Welte8d77b952009-12-17 00:31:10 +010033#include <openbsc/handover.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010034#include <osmocom/gsm/gsm_utils.h>
Harald Welte8d77b952009-12-17 00:31:10 +010035
Neels Hofmeyr0e5a7c42017-05-08 15:12:20 +020036/* Get reference to a neighbor cell on a given BCCH ARFCN */
37static struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts,
38 uint16_t arfcn, uint8_t bsic)
39{
40 struct gsm_bts *neigh;
41 /* FIXME: use some better heuristics here to determine which cell
42 * using this ARFCN really is closest to the target cell. For
43 * now we simply assume that each ARFCN will only be used by one
44 * cell */
45
46 llist_for_each_entry(neigh, &bts->network->bts_list, list) {
47 /* FIXME: this is probably returning the same bts again!? */
48 if (neigh->c0->arfcn == arfcn &&
49 neigh->bsic == bsic)
50 return neigh;
51 }
52
53 return NULL;
54}
55
56
Harald Weltef7c28b02009-12-21 13:30:17 +010057/* issue handover to a cell identified by ARFCN and BSIC */
Harald Welte8d77b952009-12-17 00:31:10 +010058static int handover_to_arfcn_bsic(struct gsm_lchan *lchan,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020059 uint16_t arfcn, uint8_t bsic)
Harald Welte8d77b952009-12-17 00:31:10 +010060{
61 struct gsm_bts *new_bts;
62
63 /* resolve the gsm_bts structure for the best neighbor */
64 new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic);
65 if (!new_bts) {
Harald Welteb1d4c8e2009-12-17 23:10:46 +010066 LOGP(DHO, LOGL_NOTICE, "unable to determine neighbor BTS "
67 "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic);
Harald Welte8d77b952009-12-17 00:31:10 +010068 return -EINVAL;
69 }
70
71 /* and actually try to handover to that cell */
72 return bsc_handover_start(lchan, new_bts);
73}
74
Harald Weltef7c28b02009-12-21 13:30:17 +010075/* did we get a RXLEV for a given cell in the given report? */
76static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020077 uint16_t arfcn, uint8_t bsic)
Harald Weltef7c28b02009-12-21 13:30:17 +010078{
79 int i;
Harald Welte8d77b952009-12-17 00:31:10 +010080
Harald Weltef7c28b02009-12-21 13:30:17 +010081 for (i = 0; i < mr->num_cell; i++) {
82 struct gsm_meas_rep_cell *mrc = &mr->cell[i];
83
84 /* search for matching report */
85 if (!(mrc->arfcn == arfcn && mrc->bsic == bsic))
86 continue;
87
88 mrc->flags |= MRC_F_PROCESSED;
89 return mrc->rxlev;
90 }
91 return -ENODEV;
92}
93
94/* obtain averaged rxlev for given neighbor */
95static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window)
96{
97 unsigned int i, idx;
98 int avg = 0;
99
100 idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev),
101 nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev),
102 window);
103
104 for (i = 0; i < window; i++) {
105 int j = (idx+i) % ARRAY_SIZE(nmp->rxlev);
106
107 avg += nmp->rxlev[j];
108 }
109
110 return avg / window;
111}
112
113/* find empty or evict bad neighbor */
114static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan)
115{
116 int j, worst = 999999;
Holger Hans Peter Freyther80352e02011-04-18 17:16:56 +0200117 struct neigh_meas_proc *nmp_worst = NULL;
Harald Weltef7c28b02009-12-21 13:30:17 +0100118
119 /* first try to find an empty/unused slot */
120 for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) {
121 struct neigh_meas_proc *nmp = &lchan->neigh_meas[j];
122 if (!nmp->arfcn)
123 return nmp;
124 }
125
126 /* no empty slot found. evict worst neighbor from list */
127 for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) {
128 struct neigh_meas_proc *nmp = &lchan->neigh_meas[j];
129 int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG);
Holger Hans Peter Freyther80352e02011-04-18 17:16:56 +0200130 if (!nmp_worst || avg < worst) {
Harald Weltef7c28b02009-12-21 13:30:17 +0100131 worst = avg;
132 nmp_worst = nmp;
133 }
134 }
135
136 return nmp_worst;
137}
138
139/* process neighbor cell measurement reports */
140static void process_meas_neigh(struct gsm_meas_rep *mr)
141{
142 int i, j, idx;
143
144 /* for each reported cell, try to update global state */
145 for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) {
146 struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j];
147 unsigned int idx;
148 int rxlev;
149
150 /* skip unused entries */
151 if (!nmp->arfcn)
152 continue;
153
154 rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic);
155 idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev);
156 if (rxlev >= 0) {
157 nmp->rxlev[idx] = rxlev;
158 nmp->last_seen_nr = mr->nr;
159 } else
160 nmp->rxlev[idx] = 0;
161 nmp->rxlev_cnt++;
162 }
163
164 /* iterate over list of reported cells, check if we did not
165 * process all of them */
166 for (i = 0; i < mr->num_cell; i++) {
167 struct gsm_meas_rep_cell *mrc = &mr->cell[i];
168 struct neigh_meas_proc *nmp;
169
170 if (mrc->flags & MRC_F_PROCESSED)
171 continue;
172
173 nmp = find_evict_neigh(mr->lchan);
174
175 nmp->arfcn = mrc->arfcn;
176 nmp->bsic = mrc->bsic;
177
178 idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev);
179 nmp->rxlev[idx] = mrc->rxlev;
180 nmp->rxlev_cnt++;
181 nmp->last_seen_nr = mr->nr;
182
183 mrc->flags |= MRC_F_PROCESSED;
184 }
185}
186
187/* attempt to do a handover */
188static int attempt_handover(struct gsm_meas_rep *mr)
189{
190 struct gsm_network *net = mr->lchan->ts->trx->bts->network;
191 struct neigh_meas_proc *best_cell = NULL;
192 unsigned int best_better_db = 0;
193 int i, rc;
194
195 /* find the best cell in this report that is at least RXLEV_HYST
196 * better than the current serving cell */
197
198 for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) {
199 struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i];
200 int avg, better;
201
202 /* skip empty slots */
203 if (nmp->arfcn == 0)
204 continue;
205
206 /* caculate average rxlev for this cell over the window */
207 avg = neigh_meas_avg(nmp, net->handover.win_rxlev_avg_neigh);
208
209 /* check if hysteresis is fulfilled */
210 if (avg < mr->dl.full.rx_lev + net->handover.pwr_hysteresis)
211 continue;
212
213 better = avg - mr->dl.full.rx_lev;
214 if (better > best_better_db) {
215 best_cell = nmp;
216 best_better_db = better;
217 }
218 }
219
220 if (!best_cell)
221 return 0;
222
223 LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ",
224 gsm_ts_name(mr->lchan->ts), best_cell->arfcn);
225 if (!net->handover.active) {
226 LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n");
227 return 0;
228 }
229
230 rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic);
231 switch (rc) {
232 case 0:
233 LOGPC(DHO, LOGL_INFO, "Starting handover\n");
234 break;
235 case -ENOSPC:
236 LOGPC(DHO, LOGL_INFO, "No channel available\n");
237 break;
238 case -EBUSY:
239 LOGPC(DHO, LOGL_INFO, "Handover already active\n");
240 break;
241 default:
242 LOGPC(DHO, LOGL_ERROR, "Unknown error\n");
243 }
244 return rc;
245}
246
247/* process an already parsed measurement report and decide if we want to
248 * attempt a handover */
Harald Welte8d77b952009-12-17 00:31:10 +0100249static int process_meas_rep(struct gsm_meas_rep *mr)
250{
Harald Weltef7c28b02009-12-21 13:30:17 +0100251 struct gsm_network *net = mr->lchan->ts->trx->bts->network;
Max8a4d2e72016-05-17 15:56:49 +0200252 enum meas_rep_field dlev, dqual;
Harald Weltef7c28b02009-12-21 13:30:17 +0100253 int av_rxlev;
Harald Welte8d77b952009-12-17 00:31:10 +0100254
Harald Welte386cd2b2009-12-18 11:49:20 +0100255 /* we currently only do handover for TCH channels */
256 switch (mr->lchan->type) {
257 case GSM_LCHAN_TCH_F:
258 case GSM_LCHAN_TCH_H:
259 break;
260 default:
261 return 0;
262 }
263
Max8a4d2e72016-05-17 15:56:49 +0200264 if (mr->flags & MEAS_REP_F_DL_DTX) {
265 dlev = MEAS_REP_DL_RXLEV_SUB;
266 dqual = MEAS_REP_DL_RXQUAL_SUB;
267 } else {
268 dlev = MEAS_REP_DL_RXLEV_FULL;
269 dqual = MEAS_REP_DL_RXQUAL_FULL;
270 }
271
Harald Weltef7c28b02009-12-21 13:30:17 +0100272 /* parse actual neighbor cell info */
273 if (mr->num_cell > 0 && mr->num_cell < 7)
274 process_meas_neigh(mr);
Harald Welte8d77b952009-12-17 00:31:10 +0100275
Max8a4d2e72016-05-17 15:56:49 +0200276 av_rxlev = get_meas_rep_avg(mr->lchan, dlev,
Harald Weltef7c28b02009-12-21 13:30:17 +0100277 net->handover.win_rxlev_avg);
Harald Weltee786c322009-12-19 21:29:19 +0100278
Harald Weltef7c28b02009-12-21 13:30:17 +0100279 /* Interference HO */
280 if (rxlev2dbm(av_rxlev) > -85 &&
Max8a4d2e72016-05-17 15:56:49 +0200281 meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5))
Harald Weltef7c28b02009-12-21 13:30:17 +0100282 return attempt_handover(mr);
Harald Welte8d77b952009-12-17 00:31:10 +0100283
Harald Weltef7c28b02009-12-21 13:30:17 +0100284 /* Bad Quality */
Max8a4d2e72016-05-17 15:56:49 +0200285 if (meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5))
Harald Weltef7c28b02009-12-21 13:30:17 +0100286 return attempt_handover(mr);
Harald Welte8d77b952009-12-17 00:31:10 +0100287
Harald Weltef7c28b02009-12-21 13:30:17 +0100288 /* Low Level */
289 if (rxlev2dbm(av_rxlev) <= -110)
290 return attempt_handover(mr);
Harald Welte8d77b952009-12-17 00:31:10 +0100291
Harald Weltef7c28b02009-12-21 13:30:17 +0100292 /* Distance */
293 if (mr->ms_l1.ta > net->handover.max_distance)
294 return attempt_handover(mr);
Harald Weltebc814502009-12-19 21:41:52 +0100295
Harald Weltef7c28b02009-12-21 13:30:17 +0100296 /* Power Budget AKA Better Cell */
297 if ((mr->nr % net->handover.pwr_interval) == 0)
298 return attempt_handover(mr);
299
300 return 0;
301
Harald Welte8d77b952009-12-17 00:31:10 +0100302}
303
304static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal,
305 void *handler_data, void *signal_data)
306{
Holger Hans Peter Freyther08eebd52010-12-27 13:28:20 +0100307 struct lchan_signal_data *lchan_data;
Harald Welte8d77b952009-12-17 00:31:10 +0100308
309 if (subsys != SS_LCHAN)
310 return 0;
311
Holger Hans Peter Freyther08eebd52010-12-27 13:28:20 +0100312 lchan_data = signal_data;
Harald Welte8d77b952009-12-17 00:31:10 +0100313 switch (signal) {
314 case S_LCHAN_MEAS_REP:
Holger Hans Peter Freyther08eebd52010-12-27 13:28:20 +0100315 process_meas_rep(lchan_data->mr);
Harald Welte8d77b952009-12-17 00:31:10 +0100316 break;
317 }
318
319 return 0;
320}
321
322void on_dso_load_ho_dec(void)
323{
Pablo Neira Ayusobbc5b992011-05-06 12:12:31 +0200324 osmo_signal_register_handler(SS_LCHAN, ho_dec_sig_cb, NULL);
Harald Welte8d77b952009-12-17 00:31:10 +0100325}