aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bts-trx/loops.c
blob: eb25f64ee430051b4575cff68848630774a2d84c (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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/* Loop control for OsmoBTS-TRX */

/* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
 *
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation; either version 3 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 Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <inttypes.h>

#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/l1sap.h>
#include <osmocom/core/bits.h>
#include <osmocom/gsm/gsm_utils.h>

#include "trx_if.h"
#include "l1_if.h"
#include "loops.h"

/*
 * Timing Advance loop
 */

/* 90% of one bit duration in 1/256 symbols: 256*0.9 */
#define TOA256_9OPERCENT	230

void ta_val(struct gsm_lchan *lchan, struct l1sched_chan_state *chan_state, int16_t toa256)
{
	/* check if the current L1 header acks to the current ordered TA */
	if (lchan->meas.l1_info[1] != lchan->rqd_ta)
		return;

	/* sum measurement */
	chan_state->meas.toa256_sum += toa256;
	if (++(chan_state->meas.toa_num) < 16)
		return;

	/* complete set */
	toa256 = chan_state->meas.toa256_sum / chan_state->meas.toa_num;

	/* check for change of TOA */
	if (toa256 < -TOA256_9OPERCENT && lchan->rqd_ta > 0) {
		LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "TOA is too early (%d), now lowering TA from %d to %d\n",
			toa256, lchan->rqd_ta, lchan->rqd_ta - 1);
		lchan->rqd_ta--;
	} else if (toa256 > TOA256_9OPERCENT && lchan->rqd_ta < 63) {
		LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "TOA is too late (%d), now raising TA from %d to %d\n",
			toa256, lchan->rqd_ta, lchan->rqd_ta + 1);
		lchan->rqd_ta++;
	} else
		LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "TOA is correct (%d), keeping current TA of %d\n",
			toa256, lchan->rqd_ta);

	chan_state->meas.toa_num = 0;
	chan_state->meas.toa256_sum = 0;
}

/*! Process a SACCH event as input to the MS power control and TA loop.  Function
 *  is called once every uplink SACCH block is received.
 * \param l1t L1 TRX instance on which we operate
 * \param chan_nr RSL channel number on which we operate
 * \param chan_state L1 scheduler channel state of the channel on which we operate
 * \param[in] rssi Receive Signal Strength Indication
 * \param[in] toa256 Time of Arrival in 1/256 symbol periods */
void trx_loop_sacch_input(struct l1sched_trx *l1t, uint8_t chan_nr,
	struct l1sched_chan_state *chan_state, int16_t toa256)
{
	struct gsm_lchan *lchan = &l1t->trx->ts[L1SAP_CHAN2TS(chan_nr)]
					.lchan[l1sap_chan2ss(chan_nr)];
	struct phy_instance *pinst = trx_phy_instance(l1t->trx);

	/* if TA loop is enabled, handle it */
	if (pinst->phy_link->u.osmotrx.trx_ta_loop)
		ta_val(lchan, chan_state, toa256);
}

void trx_loop_amr_input(struct l1sched_trx *l1t, uint8_t chan_nr,
	struct l1sched_chan_state *chan_state,
	int n_errors, int n_bits_total)
{
	struct gsm_bts_trx *trx = l1t->trx;
	struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)]
					.lchan[l1sap_chan2ss(chan_nr)];
	float ber;

	/* calculate BER (Bit Error Ratio) */
	if (n_bits_total == 0)
		ber = 1.0; /* 100% BER */
	else
		ber = (float) n_errors / (float) n_bits_total;

	/* check if loop is enabled */
	if (!chan_state->amr_loop)
		return;

	/* wait for MS to use the requested codec */
	if (chan_state->ul_ft != chan_state->dl_cmr)
		return;

	/* count bit errors */
	if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
		chan_state->ber_num += 2;
		chan_state->ber_sum += (ber + ber);
	} else {
		chan_state->ber_num++;
		chan_state->ber_sum += ber;
	}

	/* count frames */
	if (chan_state->ber_num < 48)
		return;

	/* calculate average (reuse ber variable) */
	ber = chan_state->ber_sum / chan_state->ber_num;

	/* reset bit errors */
	chan_state->ber_num = 0;
	chan_state->ber_sum = 0;

	LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Current bit error rate (BER) %.6f "
		"codec id %d\n", ber, chan_state->ul_ft);

	/* degrade */
	if (chan_state->dl_cmr > 0) {
		/* degrade, if ber is above threshold FIXME: C/I */
		if (ber >
		   lchan->tch.amr_mr.bts_mode[chan_state->dl_cmr-1].threshold) {
			LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Degrading due to BER %.6f "
				"from codec id %d to %d\n", ber, chan_state->dl_cmr,
				chan_state->dl_cmr - 1);
			chan_state->dl_cmr--;
		}
	} else if (chan_state->dl_cmr < chan_state->codecs - 1) {
		/* degrade, if ber is above threshold  FIXME: C/I*/
		if (ber <
		    lchan->tch.amr_mr.bts_mode[chan_state->dl_cmr].threshold
		  - lchan->tch.amr_mr.bts_mode[chan_state->dl_cmr].hysteresis) {
			LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Upgrading due to BER %.6f "
				"from codec id %d to %d\n", ber, chan_state->dl_cmr,
				chan_state->dl_cmr + 1);
			chan_state->dl_cmr++;
		}
	}
}

void trx_loop_amr_set(struct l1sched_chan_state *chan_state, int loop)
{
	if (chan_state->amr_loop && !loop) {
		chan_state->amr_loop = 0;
		return;
	}

	if (!chan_state->amr_loop && loop) {
		chan_state->amr_loop = 1;

		/* reset bit errors */
		chan_state->ber_num = 0;
		chan_state->ber_sum = 0;

		return;
	}
}