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
|
int8_t cur_pow;
|
50
|
cur_pow = lchan->ms_power_ctrl.current;
|
51
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Current value: %d\n", cur_pow);
|
52
|
//if (!(chan_state->meas.clock & 1))
|
53
|
//return;
|
54
|
|
55
|
switch(rssi)
|
56
|
{
|
57
|
case -2:
|
58
|
ms_send = 25;
|
59
|
break;
|
60
|
case -3:
|
61
|
ms_send = 24;
|
62
|
break;
|
63
|
case -4:
|
64
|
ms_send = 23;
|
65
|
break;
|
66
|
case -5:
|
67
|
ms_send = 22;
|
68
|
break;
|
69
|
case -6:
|
70
|
ms_send = 21;
|
71
|
break;
|
72
|
case -7:
|
73
|
ms_send = 20;
|
74
|
break;
|
75
|
case -8:
|
76
|
ms_send = 19;
|
77
|
break;
|
78
|
case -9:
|
79
|
ms_send = 18;
|
80
|
break;
|
81
|
case -10:
|
82
|
ms_send = 17;
|
83
|
break;
|
84
|
case -11:
|
85
|
ms_send = 16;
|
86
|
break;
|
87
|
case -12:
|
88
|
ms_send = 15;
|
89
|
break;
|
90
|
case -13:
|
91
|
ms_send = 14;
|
92
|
break;
|
93
|
case -14:
|
94
|
ms_send = 14;
|
95
|
break;
|
96
|
case -15:
|
97
|
ms_send = 13;
|
98
|
break;
|
99
|
case -16:
|
100
|
ms_send = 13;
|
101
|
break;
|
102
|
case -17:
|
103
|
ms_send = 12;
|
104
|
break;
|
105
|
case -18:
|
106
|
ms_send = 12;
|
107
|
break;
|
108
|
case -19:
|
109
|
ms_send = 11;
|
110
|
break;
|
111
|
case -20:
|
112
|
ms_send = 11;
|
113
|
break;
|
114
|
case -21:
|
115
|
ms_send = 10;
|
116
|
break;
|
117
|
case -22:
|
118
|
ms_send = 10;
|
119
|
break;
|
120
|
case -23:
|
121
|
ms_send = 9;
|
122
|
break;
|
123
|
case -24:
|
124
|
ms_send = 9;
|
125
|
break;
|
126
|
case -25:
|
127
|
ms_send = 8;
|
128
|
break;
|
129
|
case -26:
|
130
|
ms_send = 8;
|
131
|
break;
|
132
|
case -27:
|
133
|
ms_send = 8;
|
134
|
break;
|
135
|
case -28:
|
136
|
ms_send = 7;
|
137
|
break;
|
138
|
case -29:
|
139
|
ms_send = 7;
|
140
|
break;
|
141
|
case -30:
|
142
|
ms_send = 7;
|
143
|
break;
|
144
|
case -31:
|
145
|
ms_send = 6;
|
146
|
break;
|
147
|
case -32:
|
148
|
ms_send = 6;
|
149
|
break;
|
150
|
case -33:
|
151
|
ms_send = 6;
|
152
|
break;
|
153
|
case -34:
|
154
|
ms_send = 6;
|
155
|
break;
|
156
|
case -35:
|
157
|
ms_send = 5;
|
158
|
default:
|
159
|
ms_send = 5;
|
160
|
break;
|
161
|
}
|
162
|
|
163
|
if (rssi > -2)
|
164
|
ms_send = 26;
|
165
|
|
166
|
//ms_send = 30; - min_power
|
167
|
lchan->ms_power_ctrl.current = ms_send;
|
168
|
LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "RSSI value: %d\n", rssi);
|
169
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Set ms_power_ctrl.current value: %d\n", ms_send);
|
170
|
|
171
|
}
|
172
|
|
173
|
/*! Process a single clock tick of the MS power control loop.
|
174
|
* \param lchan Logical channel to which the clock tick applies */
|
175
|
//static void ms_power_clock(struct gsm_lchan *lchan, struct l1sched_chan_state *chan_state)
|
176
|
//{
|
177
|
// struct gsm_bts_trx *trx = lchan->ts->trx;
|
178
|
// struct phy_instance *pinst = trx_phy_instance(trx);
|
179
|
// int rssi;
|
180
|
// int i;
|
181
|
|
182
|
/* skip every second clock, to prevent oscillating due to roundtrip
|
183
|
* delay */
|
184
|
// if (!(chan_state->meas.clock & 1))
|
185
|
// return;
|
186
|
|
187
|
// LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Got SACCH master clock at RSSI count %d\n",
|
188
|
// chan_state->meas.rssi_count);
|
189
|
|
190
|
/* wait for initial burst */
|
191
|
/* Отключить
|
192
|
if (!chan_state->meas.rssi_got_burst)
|
193
|
ms_power_diff(lchan, MS_RAISE_MAX);
|
194
|
return; */
|
195
|
|
196
|
/* if no burst was received from MS at clock */
|
197
|
/* Отключить
|
198
|
if (chan_state->meas.rssi_count == 0) {
|
199
|
LOGPLCHAN(lchan, DLOOP, LOGL_NOTICE, "LOST SACCH frame, so we raise MS power\n");
|
200
|
ms_power_diff(lchan, MS_RAISE_MAX);
|
201
|
return;
|
202
|
}*/
|
203
|
|
204
|
/* reset total counter */
|
205
|
// chan_state->meas.rssi_count = 0;
|
206
|
|
207
|
/* check the minimum level received after MS acknowledged the ordered
|
208
|
* power level */
|
209
|
// if (chan_state->meas.rssi_valid_count == 0)
|
210
|
// return;
|
211
|
// for (rssi = 999, i = 0; i < chan_state->meas.rssi_valid_count; i++) {
|
212
|
// if (rssi > chan_state->meas.rssi[i])
|
213
|
// rssi = chan_state->meas.rssi[i];
|
214
|
// }
|
215
|
|
216
|
/* reset valid counter */
|
217
|
// chan_state->meas.rssi_valid_count = 0;
|
218
|
|
219
|
/* change RSSI
|
220
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Lowest RSSI: %d Target RSSI: %d Current "
|
221
|
"MS power: %d (%d dBm)\n", rssi,
|
222
|
pinst->phy_link->u.osmotrx.trx_target_rssi, lchan->ms_power_ctrl.current,
|
223
|
ms_pwr_dbm(trx->bts->band, lchan->ms_power_ctrl.current));
|
224
|
ms_power_diff(lchan, pinst->phy_link->u.osmotrx.trx_target_rssi - rssi); */
|
225
|
//}
|
226
|
|
227
|
|
228
|
/* 90% of one bit duration in 1/256 symbols: 256*0.9 */
|
229
|
#define TOA256_9OPERCENT 230
|
230
|
|
231
|
/*
|
232
|
* Timing Advance loop
|
233
|
*/
|
234
|
|
235
|
void ta_val(struct gsm_lchan *lchan, struct l1sched_chan_state *chan_state, int16_t toa256)
|
236
|
{
|
237
|
/* check if the current L1 header acks to the current ordered TA */
|
238
|
if (lchan->meas.l1_info[1] != lchan->rqd_ta)
|
239
|
return;
|
240
|
|
241
|
/* sum measurement */
|
242
|
chan_state->meas.toa256_sum += toa256;
|
243
|
if (++(chan_state->meas.toa_num) < 16)
|
244
|
return;
|
245
|
|
246
|
/* complete set */
|
247
|
toa256 = chan_state->meas.toa256_sum / chan_state->meas.toa_num;
|
248
|
|
249
|
/* check for change of TOA */
|
250
|
if (toa256 < -TOA256_9OPERCENT && lchan->rqd_ta > 0) {
|
251
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "TOA is too early (%d), now lowering TA from %d to %d\n",
|
252
|
toa256, lchan->rqd_ta, lchan->rqd_ta - 1);
|
253
|
lchan->rqd_ta--;
|
254
|
} else if (toa256 > TOA256_9OPERCENT && lchan->rqd_ta < 63) {
|
255
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "TOA is too late (%d), now raising TA from %d to %d\n",
|
256
|
toa256, lchan->rqd_ta, lchan->rqd_ta + 1);
|
257
|
lchan->rqd_ta++;
|
258
|
} else
|
259
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "TOA is correct (%d), keeping current TA of %d\n",
|
260
|
toa256, lchan->rqd_ta);
|
261
|
|
262
|
chan_state->meas.toa_num = 0;
|
263
|
chan_state->meas.toa256_sum = 0;
|
264
|
}
|
265
|
|
266
|
/*! Process a SACCH event as input to the MS power control and TA loop. Function
|
267
|
* is called once every uplink SACCH block is received.
|
268
|
* \param l1t L1 TRX instance on which we operate
|
269
|
* \param chan_nr RSL channel number on which we operate
|
270
|
* \param chan_state L1 scheduler channel state of the channel on which we operate
|
271
|
* \param[in] rssi Receive Signal Strength Indication
|
272
|
* \param[in] toa256 Time of Arrival in 1/256 symbol periods */
|
273
|
void trx_loop_sacch_input(struct l1sched_trx *l1t, uint8_t chan_nr,
|
274
|
struct l1sched_chan_state *chan_state, int8_t rssi, int16_t toa256)
|
275
|
{
|
276
|
struct gsm_lchan *lchan = &l1t->trx->ts[L1SAP_CHAN2TS(chan_nr)]
|
277
|
.lchan[l1sap_chan2ss(chan_nr)];
|
278
|
struct phy_instance *pinst = trx_phy_instance(l1t->trx);
|
279
|
|
280
|
/* if MS power control loop is enabled, handle it */
|
281
|
if (pinst->phy_link->u.osmotrx.trx_ms_power_loop)
|
282
|
ms_power_val(lchan, chan_state, rssi);
|
283
|
|
284
|
/* if TA loop is enabled, handle it */
|
285
|
if (pinst->phy_link->u.osmotrx.trx_ta_loop)
|
286
|
ta_val(lchan, chan_state, toa256);
|
287
|
}
|
288
|
|
289
|
/*! Called once every downlink SACCH block needs to be sent. */
|
290
|
void trx_loop_sacch_clock(struct l1sched_trx *l1t, uint8_t chan_nr,
|
291
|
struct l1sched_chan_state *chan_state)
|
292
|
{
|
293
|
struct gsm_lchan *lchan = &l1t->trx->ts[L1SAP_CHAN2TS(chan_nr)]
|
294
|
.lchan[l1sap_chan2ss(chan_nr)];
|
295
|
struct phy_instance *pinst = trx_phy_instance(l1t->trx);
|
296
|
|
297
|
if (lchan->ms_power_ctrl.fixed)
|
298
|
return;
|
299
|
|
300
|
// if (pinst->phy_link->u.osmotrx.trx_ms_power_loop)
|
301
|
// ms_power_clock(lchan, chan_state);
|
302
|
|
303
|
/* count the number of SACCH clocks */
|
304
|
chan_state->meas.clock++;
|
305
|
}
|
306
|
|
307
|
void trx_loop_amr_input(struct l1sched_trx *l1t, uint8_t chan_nr,
|
308
|
struct l1sched_chan_state *chan_state, float ber)
|
309
|
{
|
310
|
struct gsm_bts_trx *trx = l1t->trx;
|
311
|
struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)]
|
312
|
.lchan[l1sap_chan2ss(chan_nr)];
|
313
|
|
314
|
/* check if loop is enabled */
|
315
|
if (!chan_state->amr_loop)
|
316
|
return;
|
317
|
|
318
|
/* wait for MS to use the requested codec */
|
319
|
if (chan_state->ul_ft != chan_state->dl_cmr)
|
320
|
return;
|
321
|
|
322
|
/* count bit errors */
|
323
|
if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
|
324
|
chan_state->ber_num += 2;
|
325
|
chan_state->ber_sum += (ber + ber);
|
326
|
} else {
|
327
|
chan_state->ber_num++;
|
328
|
chan_state->ber_sum += ber;
|
329
|
}
|
330
|
|
331
|
/* count frames */
|
332
|
if (chan_state->ber_num < 48)
|
333
|
return;
|
334
|
|
335
|
/* calculate average (reuse ber variable) */
|
336
|
ber = chan_state->ber_sum / chan_state->ber_num;
|
337
|
|
338
|
/* reset bit errors */
|
339
|
chan_state->ber_num = 0;
|
340
|
chan_state->ber_sum = 0;
|
341
|
|
342
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Current bit error rate (BER) %.6f "
|
343
|
"codec id %d\n", ber, chan_state->ul_ft);
|
344
|
|
345
|
/* degrade */
|
346
|
if (chan_state->dl_cmr > 0) {
|
347
|
/* degrade, if ber is above threshold FIXME: C/I */
|
348
|
if (ber >
|
349
|
lchan->tch.amr_mr.bts_mode[chan_state->dl_cmr-1].threshold) {
|
350
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Degrading due to BER %.6f "
|
351
|
"from codec id %d to %d\n", ber, chan_state->dl_cmr,
|
352
|
chan_state->dl_cmr - 1);
|
353
|
chan_state->dl_cmr--;
|
354
|
}
|
355
|
} else if (chan_state->dl_cmr < chan_state->codecs - 1) {
|
356
|
/* degrade, if ber is above threshold FIXME: C/I*/
|
357
|
if (ber <
|
358
|
lchan->tch.amr_mr.bts_mode[chan_state->dl_cmr].threshold
|
359
|
- lchan->tch.amr_mr.bts_mode[chan_state->dl_cmr].hysteresis) {
|
360
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Upgrading due to BER %.6f "
|
361
|
"from codec id %d to %d\n", ber, chan_state->dl_cmr,
|
362
|
chan_state->dl_cmr + 1);
|
363
|
chan_state->dl_cmr++;
|
364
|
}
|
365
|
}
|
366
|
}
|
367
|
|
368
|
void trx_loop_amr_set(struct l1sched_chan_state *chan_state, int loop)
|
369
|
{
|
370
|
if (chan_state->amr_loop && !loop) {
|
371
|
chan_state->amr_loop = 0;
|
372
|
return;
|
373
|
}
|
374
|
|
375
|
if (!chan_state->amr_loop && loop) {
|
376
|
chan_state->amr_loop = 1;
|
377
|
|
378
|
/* reset bit errors */
|
379
|
chan_state->ber_num = 0;
|
380
|
chan_state->ber_sum = 0;
|
381
|
|
382
|
return;
|
383
|
}
|
384
|
}
|