1
|
/* Loop control for OsmoBTS-TRX */
|
2
|
|
3
|
/* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
|
4
|
*
|
5
|
* All Rights Reserved
|
6
|
*
|
7
|
* This program is free software; you can redistribute it and/or modify
|
8
|
* it under the terms of the GNU Affero General Public License as published by
|
9
|
* the Free Software Foundation; either version 3 of the License, or
|
10
|
* (at your option) any later version.
|
11
|
*
|
12
|
* This program is distributed in the hope that it will be useful,
|
13
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
* GNU General Public License for more details.
|
16
|
*
|
17
|
* You should have received a copy of the GNU Affero General Public License
|
18
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
*
|
20
|
*/
|
21
|
|
22
|
#include <stdint.h>
|
23
|
#include <unistd.h>
|
24
|
#include <stdlib.h>
|
25
|
#include <errno.h>
|
26
|
|
27
|
#include <osmo-bts/gsm_data.h>
|
28
|
#include <osmo-bts/logging.h>
|
29
|
#include <osmo-bts/l1sap.h>
|
30
|
#include <osmocom/core/bits.h>
|
31
|
|
32
|
#include "trx_if.h"
|
33
|
#include "l1_if.h"
|
34
|
#include "loops.h"
|
35
|
|
36
|
/*
|
37
|
* MS Power loop
|
38
|
*/
|
39
|
|
40
|
|
41
|
|
42
|
/*! Input a new RSSI value into the MS power control loop for the given logical channel.
|
43
|
* \param lchan logical channel
|
44
|
* \param chan_state L1 channel state of the logical channel.
|
45
|
* \param rssi Received Signal Strength Indication (in dBm) */
|
46
|
static void ms_power_val(struct gsm_lchan *lchan, struct l1sched_chan_state *chan_state, int8_t rssi)
|
47
|
{
|
48
|
int8_t ms_send;
|
49
|
switch(rssi)
|
50
|
{
|
51
|
case -2:
|
52
|
ms_send = 13;
|
53
|
break;
|
54
|
case -3:
|
55
|
ms_send = 13;
|
56
|
break;
|
57
|
case -4:
|
58
|
ms_send = 13;
|
59
|
break;
|
60
|
case -5:
|
61
|
ms_send = 13;
|
62
|
break;
|
63
|
case -6:
|
64
|
ms_send = 14;
|
65
|
break;
|
66
|
case -7:
|
67
|
ms_send = 14;
|
68
|
break;
|
69
|
case -8:
|
70
|
ms_send = 14;
|
71
|
break;
|
72
|
case -9:
|
73
|
ms_send = 15;
|
74
|
break;
|
75
|
case -10:
|
76
|
ms_send = 15;
|
77
|
break;
|
78
|
case -11:
|
79
|
ms_send = 15;
|
80
|
break;
|
81
|
case -12:
|
82
|
ms_send = 15;
|
83
|
break;
|
84
|
case -13:
|
85
|
ms_send = 16;
|
86
|
break;
|
87
|
case -14:
|
88
|
ms_send = 16;
|
89
|
break;
|
90
|
case -15:
|
91
|
ms_send = 16;
|
92
|
break;
|
93
|
case -16:
|
94
|
ms_send = 17;
|
95
|
break;
|
96
|
case -17:
|
97
|
ms_send = 17;
|
98
|
break;
|
99
|
case -18:
|
100
|
ms_send = 18;
|
101
|
break;
|
102
|
case -19:
|
103
|
ms_send = 18;
|
104
|
break;
|
105
|
case -20:
|
106
|
ms_send = 19;
|
107
|
break;
|
108
|
case -21:
|
109
|
ms_send = 19;
|
110
|
break;
|
111
|
case -22:
|
112
|
ms_send = 20;
|
113
|
break;
|
114
|
case -23:
|
115
|
ms_send = 20;
|
116
|
break;
|
117
|
case -24:
|
118
|
ms_send = 21;
|
119
|
break;
|
120
|
case -25:
|
121
|
ms_send = 21;
|
122
|
break;
|
123
|
case -26:
|
124
|
ms_send = 22;
|
125
|
break;
|
126
|
case -27:
|
127
|
ms_send = 22;
|
128
|
break;
|
129
|
case -28:
|
130
|
ms_send = 23;
|
131
|
break;
|
132
|
case -29:
|
133
|
ms_send = 23;
|
134
|
break;
|
135
|
case -30:
|
136
|
ms_send = 23;
|
137
|
break;
|
138
|
case -31:
|
139
|
ms_send = 24;
|
140
|
break;
|
141
|
case -32:
|
142
|
ms_send = 24;
|
143
|
break;
|
144
|
case -33:
|
145
|
ms_send = 24;
|
146
|
break;
|
147
|
case -34:
|
148
|
ms_send = 25;
|
149
|
break;
|
150
|
case -35:
|
151
|
ms_send = 25;
|
152
|
break;
|
153
|
case -36:
|
154
|
ms_send = 25;
|
155
|
break;
|
156
|
case -37:
|
157
|
ms_send = 26;
|
158
|
break;
|
159
|
case -38:
|
160
|
ms_send = 26;
|
161
|
break;
|
162
|
case -39:
|
163
|
ms_send = 26;
|
164
|
break;
|
165
|
case -40:
|
166
|
ms_send = 27;
|
167
|
break;
|
168
|
case -41:
|
169
|
ms_send = 27;
|
170
|
break;
|
171
|
case -42:
|
172
|
ms_send = 27;
|
173
|
break;
|
174
|
case -43:
|
175
|
ms_send = 28;
|
176
|
break;
|
177
|
case -44:
|
178
|
ms_send = 28;
|
179
|
break;
|
180
|
case -45:
|
181
|
ms_send = 28;
|
182
|
break;
|
183
|
case -46:
|
184
|
ms_send = 29;
|
185
|
break;
|
186
|
case -47:
|
187
|
ms_send = 29;
|
188
|
break;
|
189
|
case -48:
|
190
|
ms_send = 29;
|
191
|
break;
|
192
|
case -49:
|
193
|
ms_send = 30;
|
194
|
break;
|
195
|
case -50:
|
196
|
ms_send = 30;
|
197
|
break;
|
198
|
case -51:
|
199
|
ms_send = 30;
|
200
|
break;
|
201
|
case -52:
|
202
|
ms_send = 31;
|
203
|
break;
|
204
|
case -53:
|
205
|
ms_send = 31;
|
206
|
break;
|
207
|
case -54:
|
208
|
ms_send = 31;
|
209
|
break;
|
210
|
case -55:
|
211
|
ms_send = 32;
|
212
|
break;
|
213
|
case -56:
|
214
|
ms_send = 32;
|
215
|
break;
|
216
|
case -57:
|
217
|
ms_send = 32;
|
218
|
break;
|
219
|
default:
|
220
|
ms_send = 33;
|
221
|
break;
|
222
|
}
|
223
|
if (rssi > -2)
|
224
|
ms_send = 13;
|
225
|
|
226
|
lchan->ms_power_ctrl.current = ms_send;
|
227
|
LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "RSSI value: %d\n", rssi);
|
228
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Set ms_power_ctrl.current value: %d\n", ms_send);
|
229
|
|
230
|
}
|
231
|
|
232
|
/*! Process a single clock tick of the MS power control loop.
|
233
|
* \param lchan Logical channel to which the clock tick applies */
|
234
|
//static void ms_power_clock(struct gsm_lchan *lchan, struct l1sched_chan_state *chan_state)
|
235
|
//{
|
236
|
// struct gsm_bts_trx *trx = lchan->ts->trx;
|
237
|
// struct phy_instance *pinst = trx_phy_instance(trx);
|
238
|
// int rssi;
|
239
|
// int i;
|
240
|
|
241
|
/* skip every second clock, to prevent oscillating due to roundtrip
|
242
|
* delay */
|
243
|
// if (!(chan_state->meas.clock & 1))
|
244
|
// return;
|
245
|
|
246
|
// LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Got SACCH master clock at RSSI count %d\n",
|
247
|
// chan_state->meas.rssi_count);
|
248
|
|
249
|
/* wait for initial burst */
|
250
|
/* Отключить
|
251
|
if (!chan_state->meas.rssi_got_burst)
|
252
|
ms_power_diff(lchan, MS_RAISE_MAX);
|
253
|
return; */
|
254
|
|
255
|
/* if no burst was received from MS at clock */
|
256
|
/* Отключить
|
257
|
if (chan_state->meas.rssi_count == 0) {
|
258
|
LOGPLCHAN(lchan, DLOOP, LOGL_NOTICE, "LOST SACCH frame, so we raise MS power\n");
|
259
|
ms_power_diff(lchan, MS_RAISE_MAX);
|
260
|
return;
|
261
|
}*/
|
262
|
|
263
|
/* reset total counter */
|
264
|
// chan_state->meas.rssi_count = 0;
|
265
|
|
266
|
/* check the minimum level received after MS acknowledged the ordered
|
267
|
* power level */
|
268
|
// if (chan_state->meas.rssi_valid_count == 0)
|
269
|
// return;
|
270
|
// for (rssi = 999, i = 0; i < chan_state->meas.rssi_valid_count; i++) {
|
271
|
// if (rssi > chan_state->meas.rssi[i])
|
272
|
// rssi = chan_state->meas.rssi[i];
|
273
|
// }
|
274
|
|
275
|
/* reset valid counter */
|
276
|
// chan_state->meas.rssi_valid_count = 0;
|
277
|
|
278
|
/* change RSSI
|
279
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Lowest RSSI: %d Target RSSI: %d Current "
|
280
|
"MS power: %d (%d dBm)\n", rssi,
|
281
|
pinst->phy_link->u.osmotrx.trx_target_rssi, lchan->ms_power_ctrl.current,
|
282
|
ms_pwr_dbm(trx->bts->band, lchan->ms_power_ctrl.current));
|
283
|
ms_power_diff(lchan, pinst->phy_link->u.osmotrx.trx_target_rssi - rssi); */
|
284
|
//}
|
285
|
|
286
|
|
287
|
/* 90% of one bit duration in 1/256 symbols: 256*0.9 */
|
288
|
#define TOA256_9OPERCENT 230
|
289
|
|
290
|
/*
|
291
|
* Timing Advance loop
|
292
|
*/
|
293
|
|
294
|
void ta_val(struct gsm_lchan *lchan, struct l1sched_chan_state *chan_state, int16_t toa256)
|
295
|
{
|
296
|
/* check if the current L1 header acks to the current ordered TA */
|
297
|
if (lchan->meas.l1_info[1] != lchan->rqd_ta)
|
298
|
return;
|
299
|
|
300
|
/* sum measurement */
|
301
|
chan_state->meas.toa256_sum += toa256;
|
302
|
if (++(chan_state->meas.toa_num) < 16)
|
303
|
return;
|
304
|
|
305
|
/* complete set */
|
306
|
toa256 = chan_state->meas.toa256_sum / chan_state->meas.toa_num;
|
307
|
|
308
|
/* check for change of TOA */
|
309
|
if (toa256 < -TOA256_9OPERCENT && lchan->rqd_ta > 0) {
|
310
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "TOA is too early (%d), now lowering TA from %d to %d\n",
|
311
|
toa256, lchan->rqd_ta, lchan->rqd_ta - 1);
|
312
|
lchan->rqd_ta--;
|
313
|
} else if (toa256 > TOA256_9OPERCENT && lchan->rqd_ta < 63) {
|
314
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "TOA is too late (%d), now raising TA from %d to %d\n",
|
315
|
toa256, lchan->rqd_ta, lchan->rqd_ta + 1);
|
316
|
lchan->rqd_ta++;
|
317
|
} else
|
318
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "TOA is correct (%d), keeping current TA of %d\n",
|
319
|
toa256, lchan->rqd_ta);
|
320
|
|
321
|
chan_state->meas.toa_num = 0;
|
322
|
chan_state->meas.toa256_sum = 0;
|
323
|
}
|
324
|
|
325
|
/*! Process a SACCH event as input to the MS power control and TA loop. Function
|
326
|
* is called once every uplink SACCH block is received.
|
327
|
* \param l1t L1 TRX instance on which we operate
|
328
|
* \param chan_nr RSL channel number on which we operate
|
329
|
* \param chan_state L1 scheduler channel state of the channel on which we operate
|
330
|
* \param[in] rssi Receive Signal Strength Indication
|
331
|
* \param[in] toa256 Time of Arrival in 1/256 symbol periods */
|
332
|
void trx_loop_sacch_input(struct l1sched_trx *l1t, uint8_t chan_nr,
|
333
|
struct l1sched_chan_state *chan_state, int8_t rssi, int16_t toa256)
|
334
|
{
|
335
|
struct gsm_lchan *lchan = &l1t->trx->ts[L1SAP_CHAN2TS(chan_nr)]
|
336
|
.lchan[l1sap_chan2ss(chan_nr)];
|
337
|
struct phy_instance *pinst = trx_phy_instance(l1t->trx);
|
338
|
|
339
|
/* if MS power control loop is enabled, handle it */
|
340
|
if (pinst->phy_link->u.osmotrx.trx_ms_power_loop)
|
341
|
ms_power_val(lchan, chan_state, rssi);
|
342
|
|
343
|
/* if TA loop is enabled, handle it */
|
344
|
if (pinst->phy_link->u.osmotrx.trx_ta_loop)
|
345
|
ta_val(lchan, chan_state, toa256);
|
346
|
}
|
347
|
|
348
|
/*! Called once every downlink SACCH block needs to be sent. */
|
349
|
void trx_loop_sacch_clock(struct l1sched_trx *l1t, uint8_t chan_nr,
|
350
|
struct l1sched_chan_state *chan_state)
|
351
|
{
|
352
|
struct gsm_lchan *lchan = &l1t->trx->ts[L1SAP_CHAN2TS(chan_nr)]
|
353
|
.lchan[l1sap_chan2ss(chan_nr)];
|
354
|
struct phy_instance *pinst = trx_phy_instance(l1t->trx);
|
355
|
|
356
|
if (lchan->ms_power_ctrl.fixed)
|
357
|
return;
|
358
|
|
359
|
// if (pinst->phy_link->u.osmotrx.trx_ms_power_loop)
|
360
|
// ms_power_clock(lchan, chan_state);
|
361
|
|
362
|
/* count the number of SACCH clocks */
|
363
|
chan_state->meas.clock++;
|
364
|
}
|
365
|
|
366
|
void trx_loop_amr_input(struct l1sched_trx *l1t, uint8_t chan_nr,
|
367
|
struct l1sched_chan_state *chan_state, float ber)
|
368
|
{
|
369
|
struct gsm_bts_trx *trx = l1t->trx;
|
370
|
struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)]
|
371
|
.lchan[l1sap_chan2ss(chan_nr)];
|
372
|
|
373
|
/* check if loop is enabled */
|
374
|
if (!chan_state->amr_loop)
|
375
|
return;
|
376
|
|
377
|
/* wait for MS to use the requested codec */
|
378
|
if (chan_state->ul_ft != chan_state->dl_cmr)
|
379
|
return;
|
380
|
|
381
|
/* count bit errors */
|
382
|
if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
|
383
|
chan_state->ber_num += 2;
|
384
|
chan_state->ber_sum += (ber + ber);
|
385
|
} else {
|
386
|
chan_state->ber_num++;
|
387
|
chan_state->ber_sum += ber;
|
388
|
}
|
389
|
|
390
|
/* count frames */
|
391
|
if (chan_state->ber_num < 48)
|
392
|
return;
|
393
|
|
394
|
/* calculate average (reuse ber variable) */
|
395
|
ber = chan_state->ber_sum / chan_state->ber_num;
|
396
|
|
397
|
/* reset bit errors */
|
398
|
chan_state->ber_num = 0;
|
399
|
chan_state->ber_sum = 0;
|
400
|
|
401
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Current bit error rate (BER) %.6f "
|
402
|
"codec id %d\n", ber, chan_state->ul_ft);
|
403
|
|
404
|
/* degrade */
|
405
|
if (chan_state->dl_cmr > 0) {
|
406
|
/* degrade, if ber is above threshold FIXME: C/I */
|
407
|
if (ber >
|
408
|
lchan->tch.amr_mr.bts_mode[chan_state->dl_cmr-1].threshold) {
|
409
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Degrading due to BER %.6f "
|
410
|
"from codec id %d to %d\n", ber, chan_state->dl_cmr,
|
411
|
chan_state->dl_cmr - 1);
|
412
|
chan_state->dl_cmr--;
|
413
|
}
|
414
|
} else if (chan_state->dl_cmr < chan_state->codecs - 1) {
|
415
|
/* degrade, if ber is above threshold FIXME: C/I*/
|
416
|
if (ber <
|
417
|
lchan->tch.amr_mr.bts_mode[chan_state->dl_cmr].threshold
|
418
|
- lchan->tch.amr_mr.bts_mode[chan_state->dl_cmr].hysteresis) {
|
419
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Upgrading due to BER %.6f "
|
420
|
"from codec id %d to %d\n", ber, chan_state->dl_cmr,
|
421
|
chan_state->dl_cmr + 1);
|
422
|
chan_state->dl_cmr++;
|
423
|
}
|
424
|
}
|
425
|
}
|
426
|
|
427
|
void trx_loop_amr_set(struct l1sched_chan_state *chan_state, int loop)
|
428
|
{
|
429
|
if (chan_state->amr_loop && !loop) {
|
430
|
chan_state->amr_loop = 0;
|
431
|
return;
|
432
|
}
|
433
|
|
434
|
if (!chan_state->amr_loop && loop) {
|
435
|
chan_state->amr_loop = 1;
|
436
|
|
437
|
/* reset bit errors */
|
438
|
chan_state->ber_num = 0;
|
439
|
chan_state->ber_sum = 0;
|
440
|
|
441
|
return;
|
442
|
}
|
443
|
}
|