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 my_target;
|
50
|
int8_t target_hi;
|
51
|
my_target = trx_phy_instance(lchan->ts->trx)->phy_link->u.osmotrx.trx_target_rssi;
|
52
|
target_hi = my_target - 15;
|
53
|
//LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Target check: %d\n", target_hi);
|
54
|
ms_send = lchan->ms_power_ctrl.current;
|
55
|
|
56
|
if (rssi > target_hi)
|
57
|
{
|
58
|
ms_send = ms_send + 1;
|
59
|
LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "RSSI value: %d, Set ms_power_ctrl.current +1 (DOWN power) value: %d\n", rssi, ms_send);
|
60
|
}
|
61
|
if (rssi < my_target)
|
62
|
{
|
63
|
ms_send = ms_send - 1;
|
64
|
LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "RSSI value: %d, Set ms_power_ctrl.current -1 (UP power) value: %d\n", rssi, ms_send);
|
65
|
}
|
66
|
//Protect 30..5:
|
67
|
if (ms_send > 30)
|
68
|
ms_send = 30;
|
69
|
if (ms_send < 5)
|
70
|
ms_send = 5;
|
71
|
//Write
|
72
|
lchan->ms_power_ctrl.current = ms_send;
|
73
|
}
|
74
|
|
75
|
/* 90% of one bit duration in 1/256 symbols: 256*0.9 */
|
76
|
#define TOA256_9OPERCENT 230
|
77
|
|
78
|
/*
|
79
|
* Timing Advance loop
|
80
|
*/
|
81
|
|
82
|
void ta_val(struct gsm_lchan *lchan, struct l1sched_chan_state *chan_state, int16_t toa256)
|
83
|
{
|
84
|
/* check if the current L1 header acks to the current ordered TA */
|
85
|
if (lchan->meas.l1_info[1] != lchan->rqd_ta)
|
86
|
return;
|
87
|
|
88
|
/* sum measurement */
|
89
|
chan_state->meas.toa256_sum += toa256;
|
90
|
if (++(chan_state->meas.toa_num) < 16)
|
91
|
return;
|
92
|
|
93
|
/* complete set */
|
94
|
toa256 = chan_state->meas.toa256_sum / chan_state->meas.toa_num;
|
95
|
|
96
|
/* check for change of TOA */
|
97
|
if (toa256 < -TOA256_9OPERCENT && lchan->rqd_ta > 0) {
|
98
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "TOA is too early (%d), now lowering TA from %d to %d\n",
|
99
|
toa256, lchan->rqd_ta, lchan->rqd_ta - 1);
|
100
|
lchan->rqd_ta--;
|
101
|
} else if (toa256 > TOA256_9OPERCENT && lchan->rqd_ta < 63) {
|
102
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "TOA is too late (%d), now raising TA from %d to %d\n",
|
103
|
toa256, lchan->rqd_ta, lchan->rqd_ta + 1);
|
104
|
lchan->rqd_ta++;
|
105
|
} else
|
106
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "TOA is correct (%d), keeping current TA of %d\n",
|
107
|
toa256, lchan->rqd_ta);
|
108
|
|
109
|
chan_state->meas.toa_num = 0;
|
110
|
chan_state->meas.toa256_sum = 0;
|
111
|
|
112
|
}
|
113
|
|
114
|
/*! Process a SACCH event as input to the MS power control and TA loop. Function
|
115
|
* is called once every uplink SACCH block is received.
|
116
|
* \param l1t L1 TRX instance on which we operate
|
117
|
* \param chan_nr RSL channel number on which we operate
|
118
|
* \param chan_state L1 scheduler channel state of the channel on which we operate
|
119
|
* \param[in] rssi Receive Signal Strength Indication
|
120
|
* \param[in] toa256 Time of Arrival in 1/256 symbol periods */
|
121
|
void trx_loop_sacch_input(struct l1sched_trx *l1t, uint8_t chan_nr,
|
122
|
struct l1sched_chan_state *chan_state, int8_t rssi, int16_t toa256)
|
123
|
{
|
124
|
struct gsm_lchan *lchan = &l1t->trx->ts[L1SAP_CHAN2TS(chan_nr)]
|
125
|
.lchan[l1sap_chan2ss(chan_nr)];
|
126
|
struct phy_instance *pinst = trx_phy_instance(l1t->trx);
|
127
|
|
128
|
/* if MS power control loop is enabled, handle it */
|
129
|
if (pinst->phy_link->u.osmotrx.trx_ms_power_loop)
|
130
|
ms_power_val(lchan, chan_state, rssi);
|
131
|
|
132
|
/* if TA loop is enabled, handle it */
|
133
|
if (pinst->phy_link->u.osmotrx.trx_ta_loop)
|
134
|
ta_val(lchan, chan_state, toa256);
|
135
|
}
|
136
|
|
137
|
/*! Called once every downlink SACCH block needs to be sent. */
|
138
|
void trx_loop_sacch_clock(struct l1sched_trx *l1t, uint8_t chan_nr,
|
139
|
struct l1sched_chan_state *chan_state)
|
140
|
{
|
141
|
struct gsm_lchan *lchan = &l1t->trx->ts[L1SAP_CHAN2TS(chan_nr)]
|
142
|
.lchan[l1sap_chan2ss(chan_nr)];
|
143
|
// struct phy_instance *pinst = trx_phy_instance(l1t->trx);
|
144
|
|
145
|
if (lchan->ms_power_ctrl.fixed)
|
146
|
return;
|
147
|
|
148
|
// if (pinst->phy_link->u.osmotrx.trx_ms_power_loop)
|
149
|
// ms_power_clock(lchan, chan_state);
|
150
|
|
151
|
/* count the number of SACCH clocks */
|
152
|
chan_state->meas.clock++;
|
153
|
}
|
154
|
|
155
|
void trx_loop_amr_input(struct l1sched_trx *l1t, uint8_t chan_nr,
|
156
|
struct l1sched_chan_state *chan_state, float ber)
|
157
|
{
|
158
|
struct gsm_bts_trx *trx = l1t->trx;
|
159
|
struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)]
|
160
|
.lchan[l1sap_chan2ss(chan_nr)];
|
161
|
|
162
|
/* check if loop is enabled */
|
163
|
if (!chan_state->amr_loop)
|
164
|
return;
|
165
|
|
166
|
/* wait for MS to use the requested codec */
|
167
|
if (chan_state->ul_ft != chan_state->dl_cmr)
|
168
|
return;
|
169
|
|
170
|
/* count bit errors */
|
171
|
if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
|
172
|
chan_state->ber_num += 2;
|
173
|
chan_state->ber_sum += (ber + ber);
|
174
|
} else {
|
175
|
chan_state->ber_num++;
|
176
|
chan_state->ber_sum += ber;
|
177
|
}
|
178
|
|
179
|
/* count frames */
|
180
|
if (chan_state->ber_num < 48)
|
181
|
return;
|
182
|
|
183
|
/* calculate average (reuse ber variable) */
|
184
|
ber = chan_state->ber_sum / chan_state->ber_num;
|
185
|
|
186
|
/* reset bit errors */
|
187
|
chan_state->ber_num = 0;
|
188
|
chan_state->ber_sum = 0;
|
189
|
|
190
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Current bit error rate (BER) %.6f "
|
191
|
"codec id %d\n", ber, chan_state->ul_ft);
|
192
|
|
193
|
/* degrade */
|
194
|
if (chan_state->dl_cmr > 0) {
|
195
|
/* degrade, if ber is above threshold FIXME: C/I */
|
196
|
if (ber >
|
197
|
lchan->tch.amr_mr.bts_mode[chan_state->dl_cmr-1].threshold) {
|
198
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Degrading due to BER %.6f "
|
199
|
"from codec id %d to %d\n", ber, chan_state->dl_cmr,
|
200
|
chan_state->dl_cmr - 1);
|
201
|
chan_state->dl_cmr--;
|
202
|
}
|
203
|
} else if (chan_state->dl_cmr < chan_state->codecs - 1) {
|
204
|
/* degrade, if ber is above threshold FIXME: C/I*/
|
205
|
if (ber <
|
206
|
lchan->tch.amr_mr.bts_mode[chan_state->dl_cmr].threshold
|
207
|
- lchan->tch.amr_mr.bts_mode[chan_state->dl_cmr].hysteresis) {
|
208
|
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Upgrading due to BER %.6f "
|
209
|
"from codec id %d to %d\n", ber, chan_state->dl_cmr,
|
210
|
chan_state->dl_cmr + 1);
|
211
|
chan_state->dl_cmr++;
|
212
|
}
|
213
|
}
|
214
|
}
|
215
|
|
216
|
void trx_loop_amr_set(struct l1sched_chan_state *chan_state, int loop)
|
217
|
{
|
218
|
if (chan_state->amr_loop && !loop) {
|
219
|
chan_state->amr_loop = 0;
|
220
|
return;
|
221
|
}
|
222
|
|
223
|
if (!chan_state->amr_loop && loop) {
|
224
|
chan_state->amr_loop = 1;
|
225
|
|
226
|
/* reset bit errors */
|
227
|
chan_state->ber_num = 0;
|
228
|
chan_state->ber_sum = 0;
|
229
|
|
230
|
return;
|
231
|
}
|
232
|
}
|