trxcon/scheduler: refactor Downlink measurement processing
So far we used to store the sums of ToA and RSSI measurements in the logical channel state, and after decoding of a block, we did calculate the average. This approach works fine for xCCH and PDTCH, but when it comes to block-diagonal interleaving (which is used on TCH/F and TCH/H channels), the results are incorrect. The problem is that a burst on TCH may carry 57 bits of one encoded frame and 57 bits of another. Instead of calculating the sum of measurements on the fly, let's push them into a circular buffer (the measurement history), and keep them there even after decoding of a block. This would allow us to calculate the average of N last measurements depending on the interleaving type. A single circular buffer can hold up to 8 unique measurements, so the recent measurements would basically override the oldest ones. Change-Id: I211ee3314f0a284112a4deddc0e93028f4a27cef
This commit is contained in:
parent
d534d43fc1
commit
2060b5b7cc
|
@ -126,9 +126,9 @@ int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len,
|
struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len,
|
||||||
int bit_error_count, bool dec_failed, bool traffic)
|
int bit_error_count, bool dec_failed, bool traffic)
|
||||||
{
|
{
|
||||||
|
const struct trx_meas_set *meas = &lchan->meas_avg;
|
||||||
const struct trx_lchan_desc *lchan_desc;
|
const struct trx_lchan_desc *lchan_desc;
|
||||||
struct l1ctl_info_dl dl_hdr;
|
struct l1ctl_info_dl dl_hdr;
|
||||||
int dbm_avg = 0;
|
|
||||||
|
|
||||||
/* Set up pointers */
|
/* Set up pointers */
|
||||||
lchan_desc = &trx_lchan_desc[lchan->type];
|
lchan_desc = &trx_lchan_desc[lchan->type];
|
||||||
|
@ -140,15 +140,8 @@ int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
dl_hdr.frame_nr = htonl(lchan->rx_first_fn);
|
dl_hdr.frame_nr = htonl(lchan->rx_first_fn);
|
||||||
dl_hdr.num_biterr = bit_error_count;
|
dl_hdr.num_biterr = bit_error_count;
|
||||||
|
|
||||||
/* Convert average RSSI to RX level */
|
/* RX level: 0 .. 63 in typical GSM notation (dBm + 110) */
|
||||||
if (lchan->meas.num) {
|
dl_hdr.rx_level = dbm2rxlev(meas->rssi);
|
||||||
/* RX level: 0 .. 63 in typical GSM notation (dBm + 110) */
|
|
||||||
dbm_avg = lchan->meas.rssi_sum / lchan->meas.num;
|
|
||||||
dl_hdr.rx_level = dbm2rxlev(dbm_avg);
|
|
||||||
} else {
|
|
||||||
/* No measurements, assuming the worst */
|
|
||||||
dl_hdr.rx_level = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: set proper values */
|
/* FIXME: set proper values */
|
||||||
dl_hdr.snr = 0;
|
dl_hdr.snr = 0;
|
||||||
|
@ -162,7 +155,7 @@ int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
/* Optional GSMTAP logging */
|
/* Optional GSMTAP logging */
|
||||||
if (l2_len > 0 && (!traffic || lchan_desc->chan_nr == RSL_CHAN_OSMO_PDCH)) {
|
if (l2_len > 0 && (!traffic || lchan_desc->chan_nr == RSL_CHAN_OSMO_PDCH)) {
|
||||||
sched_gsmtap_send(lchan->type, lchan->rx_first_fn, ts->index,
|
sched_gsmtap_send(lchan->type, lchan->rx_first_fn, ts->index,
|
||||||
trx->band_arfcn, dbm_avg, 0, l2, l2_len);
|
trx->band_arfcn, meas->rssi, 0, l2, l2_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -31,35 +31,35 @@
|
||||||
/* Forward declaration of handlers */
|
/* Forward declaration of handlers */
|
||||||
int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
||||||
sbit_t *bits, int8_t rssi, int16_t toa256);
|
sbit_t *bits, const struct trx_meas_set *meas);
|
||||||
|
|
||||||
int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
|
||||||
|
|
||||||
int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
||||||
sbit_t *bits, int8_t rssi, int16_t toa256);
|
sbit_t *bits, const struct trx_meas_set *meas);
|
||||||
|
|
||||||
int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
|
||||||
|
|
||||||
int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
||||||
sbit_t *bits, int8_t rssi, int16_t toa256);
|
sbit_t *bits, const struct trx_meas_set *meas);
|
||||||
|
|
||||||
int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
|
||||||
|
|
||||||
int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
||||||
sbit_t *bits, int8_t rssi, int16_t toa256);
|
sbit_t *bits, const struct trx_meas_set *meas);
|
||||||
|
|
||||||
int tx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int tx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
|
||||||
|
|
||||||
int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
||||||
sbit_t *bits, int8_t rssi, int16_t toa256);
|
sbit_t *bits, const struct trx_meas_set *meas);
|
||||||
|
|
||||||
int tx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int tx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* OsmocomBB <-> SDR connection bridge
|
* OsmocomBB <-> SDR connection bridge
|
||||||
* TDMA scheduler: handlers for DL / UL bursts on logical channels
|
* TDMA scheduler: handlers for DL / UL bursts on logical channels
|
||||||
*
|
*
|
||||||
* (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
|
* (C) 2018-2020 by Vadim Yanitskiy <axilirator@gmail.com>
|
||||||
*
|
*
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*
|
*
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
|
|
||||||
int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
||||||
sbit_t *bits, int8_t rssi, int16_t toa256)
|
sbit_t *bits, const struct trx_meas_set *meas)
|
||||||
{
|
{
|
||||||
const struct trx_lchan_desc *lchan_desc;
|
const struct trx_lchan_desc *lchan_desc;
|
||||||
uint8_t l2[GPRS_L2_MAX_LEN], *mask;
|
uint8_t l2[GPRS_L2_MAX_LEN], *mask;
|
||||||
|
@ -62,9 +62,6 @@ int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
|
|
||||||
/* Reset internal state */
|
/* Reset internal state */
|
||||||
if (bid == 0) {
|
if (bid == 0) {
|
||||||
/* Clean up old measurements */
|
|
||||||
memset(&lchan->meas, 0x00, sizeof(lchan->meas));
|
|
||||||
|
|
||||||
*first_fn = fn;
|
*first_fn = fn;
|
||||||
*mask = 0x0;
|
*mask = 0x0;
|
||||||
}
|
}
|
||||||
|
@ -72,10 +69,8 @@ int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
/* Update mask */
|
/* Update mask */
|
||||||
*mask |= (1 << bid);
|
*mask |= (1 << bid);
|
||||||
|
|
||||||
/* Update measurements */
|
/* Store the measurements */
|
||||||
lchan->meas.toa256_sum += toa256;
|
sched_trx_meas_push(lchan, meas);
|
||||||
lchan->meas.rssi_sum += rssi;
|
|
||||||
lchan->meas.num++;
|
|
||||||
|
|
||||||
/* Copy burst to buffer of 4 bursts */
|
/* Copy burst to buffer of 4 bursts */
|
||||||
offset = buffer + bid * 116;
|
offset = buffer + bid * 116;
|
||||||
|
@ -86,6 +81,9 @@ int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
if (bid != 3)
|
if (bid != 3)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* Calculate AVG of the measurements */
|
||||||
|
sched_trx_meas_avg(lchan, 4);
|
||||||
|
|
||||||
/* Check for complete set of bursts */
|
/* Check for complete set of bursts */
|
||||||
if ((*mask & 0xf) != 0xf) {
|
if ((*mask & 0xf) != 0xf) {
|
||||||
LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) data frame at "
|
LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) data frame at "
|
||||||
|
|
|
@ -70,7 +70,7 @@ static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info)
|
||||||
|
|
||||||
int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
||||||
sbit_t *bits, int8_t rssi, int16_t toa256)
|
sbit_t *bits, const struct trx_meas_set *meas)
|
||||||
{
|
{
|
||||||
sbit_t payload[2 * 39];
|
sbit_t payload[2 * 39];
|
||||||
struct gsm_time time;
|
struct gsm_time time;
|
||||||
|
@ -117,7 +117,7 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
data->link_id = trx_lchan_desc[lchan->type].link_id;
|
data->link_id = trx_lchan_desc[lchan->type].link_id;
|
||||||
data->band_arfcn = htons(trx->band_arfcn);
|
data->band_arfcn = htons(trx->band_arfcn);
|
||||||
data->frame_nr = htonl(fn);
|
data->frame_nr = htonl(fn);
|
||||||
data->rx_level = -rssi;
|
data->rx_level = -(meas->rssi);
|
||||||
|
|
||||||
/* FIXME: set proper values */
|
/* FIXME: set proper values */
|
||||||
data->num_biterr = 0;
|
data->num_biterr = 0;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* OsmocomBB <-> SDR connection bridge
|
* OsmocomBB <-> SDR connection bridge
|
||||||
* TDMA scheduler: handlers for DL / UL bursts on logical channels
|
* TDMA scheduler: handlers for DL / UL bursts on logical channels
|
||||||
*
|
*
|
||||||
* (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
|
* (C) 2017-2020 by Vadim Yanitskiy <axilirator@gmail.com>
|
||||||
*
|
*
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*
|
*
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
|
|
||||||
int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
||||||
sbit_t *bits, int8_t rssi, int16_t toa256)
|
sbit_t *bits, const struct trx_meas_set *meas)
|
||||||
{
|
{
|
||||||
const struct trx_lchan_desc *lchan_desc;
|
const struct trx_lchan_desc *lchan_desc;
|
||||||
int n_errors = -1, n_bits_total, rc;
|
int n_errors = -1, n_bits_total, rc;
|
||||||
|
@ -64,9 +64,6 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
|
|
||||||
/* Reset internal state */
|
/* Reset internal state */
|
||||||
if (bid == 0) {
|
if (bid == 0) {
|
||||||
/* Clean up old measurements */
|
|
||||||
memset(&lchan->meas, 0x00, sizeof(lchan->meas));
|
|
||||||
|
|
||||||
*first_fn = fn;
|
*first_fn = fn;
|
||||||
*mask = 0x00;
|
*mask = 0x00;
|
||||||
}
|
}
|
||||||
|
@ -74,10 +71,8 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
/* Update mask */
|
/* Update mask */
|
||||||
*mask |= (1 << bid);
|
*mask |= (1 << bid);
|
||||||
|
|
||||||
/* Update mask and RSSI */
|
/* Store the measurements */
|
||||||
lchan->meas.rssi_sum += rssi;
|
sched_trx_meas_push(lchan, meas);
|
||||||
lchan->meas.toa256_sum += toa256;
|
|
||||||
lchan->meas.num++;
|
|
||||||
|
|
||||||
/* Copy burst to end of buffer of 8 bursts */
|
/* Copy burst to end of buffer of 8 bursts */
|
||||||
offset = buffer + bid * 116 + 464;
|
offset = buffer + bid * 116 + 464;
|
||||||
|
@ -88,6 +83,9 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
if (bid != 3)
|
if (bid != 3)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* Calculate AVG of the measurements */
|
||||||
|
sched_trx_meas_avg(lchan, 8);
|
||||||
|
|
||||||
/* Check for complete set of bursts */
|
/* Check for complete set of bursts */
|
||||||
if ((*mask & 0xf) != 0xf) {
|
if ((*mask & 0xf) != 0xf) {
|
||||||
LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) traffic frame at "
|
LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) traffic frame at "
|
||||||
|
@ -150,9 +148,14 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
n_errors, false, true);
|
n_errors, false, true);
|
||||||
|
|
||||||
bfi:
|
bfi:
|
||||||
/* Didn't try to decode */
|
/* Didn't try to decode, fake measurements */
|
||||||
if (n_errors < 0)
|
if (n_errors < 0) {
|
||||||
|
lchan->meas_avg = (struct trx_meas_set) {
|
||||||
|
.toa256 = 0,
|
||||||
|
.rssi = -110,
|
||||||
|
};
|
||||||
n_errors = 116 * 4;
|
n_errors = 116 * 4;
|
||||||
|
}
|
||||||
|
|
||||||
/* BFI is not applicable in signalling mode */
|
/* BFI is not applicable in signalling mode */
|
||||||
if (lchan->tch_mode == GSM48_CMODE_SIGN)
|
if (lchan->tch_mode == GSM48_CMODE_SIGN)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* OsmocomBB <-> SDR connection bridge
|
* OsmocomBB <-> SDR connection bridge
|
||||||
* TDMA scheduler: handlers for DL / UL bursts on logical channels
|
* TDMA scheduler: handlers for DL / UL bursts on logical channels
|
||||||
*
|
*
|
||||||
* (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
|
* (C) 2018-2020 by Vadim Yanitskiy <axilirator@gmail.com>
|
||||||
* (C) 2018 by Harald Welte <laforge@gnumonks.org>
|
* (C) 2018 by Harald Welte <laforge@gnumonks.org>
|
||||||
*
|
*
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
|
@ -200,7 +200,7 @@ uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan,
|
||||||
|
|
||||||
int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
||||||
sbit_t *bits, int8_t rssi, int16_t toa256)
|
sbit_t *bits, const struct trx_meas_set *meas)
|
||||||
{
|
{
|
||||||
const struct trx_lchan_desc *lchan_desc;
|
const struct trx_lchan_desc *lchan_desc;
|
||||||
int n_errors = -1, n_bits_total, rc;
|
int n_errors = -1, n_bits_total, rc;
|
||||||
|
@ -234,16 +234,8 @@ int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
/* Update mask */
|
/* Update mask */
|
||||||
*mask |= (1 << bid);
|
*mask |= (1 << bid);
|
||||||
|
|
||||||
/**
|
/* Store the measurements */
|
||||||
* FIXME: properly update measurements
|
sched_trx_meas_push(lchan, meas);
|
||||||
*
|
|
||||||
* Since TCH/H channel is using block-diagonal interleaving,
|
|
||||||
* a single burst may carry 57 bits of one encoded frame,
|
|
||||||
* and 57 bits of another. This should be taken into account.
|
|
||||||
*/
|
|
||||||
lchan->meas.rssi_sum += rssi;
|
|
||||||
lchan->meas.toa256_sum += toa256;
|
|
||||||
lchan->meas.num++;
|
|
||||||
|
|
||||||
/* Copy burst to the end of buffer of 6 bursts */
|
/* Copy burst to the end of buffer of 6 bursts */
|
||||||
offset = buffer + bid * 116 + 464;
|
offset = buffer + bid * 116 + 464;
|
||||||
|
@ -303,6 +295,9 @@ int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
"fn=%u on %s (rc=%d)\n", burst_mask2str(mask, 6),
|
"fn=%u on %s (rc=%d)\n", burst_mask2str(mask, 6),
|
||||||
fn, lchan_desc->name, rc);
|
fn, lchan_desc->name, rc);
|
||||||
|
|
||||||
|
/* Calculate AVG of the measurements (assuming 4 bursts) */
|
||||||
|
sched_trx_meas_avg(lchan, 4);
|
||||||
|
|
||||||
/* Send BFI */
|
/* Send BFI */
|
||||||
goto bfi;
|
goto bfi;
|
||||||
} else if (rc == GSM_MACBLOCK_LEN) {
|
} else if (rc == GSM_MACBLOCK_LEN) {
|
||||||
|
@ -313,6 +308,9 @@ int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,
|
lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,
|
||||||
fn, true); /* FACCH/H */
|
fn, true); /* FACCH/H */
|
||||||
|
|
||||||
|
/* Calculate AVG of the measurements (FACCH/H takes 6 bursts) */
|
||||||
|
sched_trx_meas_avg(lchan, 6);
|
||||||
|
|
||||||
/* FACCH/H received, forward to the higher layers */
|
/* FACCH/H received, forward to the higher layers */
|
||||||
sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN,
|
sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN,
|
||||||
n_errors, false, false);
|
n_errors, false, false);
|
||||||
|
@ -322,6 +320,9 @@ int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
} else {
|
} else {
|
||||||
/* A good TCH frame received */
|
/* A good TCH frame received */
|
||||||
l2_len = rc;
|
l2_len = rc;
|
||||||
|
|
||||||
|
/* Calculate AVG of the measurements (traffic takes 4 bursts) */
|
||||||
|
sched_trx_meas_avg(lchan, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate TDMA frame number of the first burst */
|
/* Calculate TDMA frame number of the first burst */
|
||||||
|
@ -341,9 +342,14 @@ bfi_shift:
|
||||||
*mask = *mask << 2;
|
*mask = *mask << 2;
|
||||||
|
|
||||||
bfi:
|
bfi:
|
||||||
/* Didn't try to decode */
|
/* Didn't try to decode, fake measurements */
|
||||||
if (n_errors < 0)
|
if (n_errors < 0) {
|
||||||
|
lchan->meas_avg = (struct trx_meas_set) {
|
||||||
|
.toa256 = 0,
|
||||||
|
.rssi = -110,
|
||||||
|
};
|
||||||
n_errors = 116 * 2;
|
n_errors = 116 * 2;
|
||||||
|
}
|
||||||
|
|
||||||
/* Calculate TDMA frame number of the first burst */
|
/* Calculate TDMA frame number of the first burst */
|
||||||
lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,
|
lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* OsmocomBB <-> SDR connection bridge
|
* OsmocomBB <-> SDR connection bridge
|
||||||
* TDMA scheduler: handlers for DL / UL bursts on logical channels
|
* TDMA scheduler: handlers for DL / UL bursts on logical channels
|
||||||
*
|
*
|
||||||
* (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
|
* (C) 2017-2020 by Vadim Yanitskiy <axilirator@gmail.com>
|
||||||
*
|
*
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*
|
*
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
|
|
||||||
int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
|
int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
|
||||||
sbit_t *bits, int8_t rssi, int16_t toa256)
|
sbit_t *bits, const struct trx_meas_set *meas)
|
||||||
{
|
{
|
||||||
const struct trx_lchan_desc *lchan_desc;
|
const struct trx_lchan_desc *lchan_desc;
|
||||||
uint8_t l2[GSM_MACBLOCK_LEN], *mask;
|
uint8_t l2[GSM_MACBLOCK_LEN], *mask;
|
||||||
|
@ -61,9 +61,6 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
|
|
||||||
/* Reset internal state */
|
/* Reset internal state */
|
||||||
if (bid == 0) {
|
if (bid == 0) {
|
||||||
/* Clean up old measurements */
|
|
||||||
memset(&lchan->meas, 0x00, sizeof(lchan->meas));
|
|
||||||
|
|
||||||
*first_fn = fn;
|
*first_fn = fn;
|
||||||
*mask = 0x0;
|
*mask = 0x0;
|
||||||
}
|
}
|
||||||
|
@ -71,10 +68,8 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
/* Update mask */
|
/* Update mask */
|
||||||
*mask |= (1 << bid);
|
*mask |= (1 << bid);
|
||||||
|
|
||||||
/* Update measurements */
|
/* Store the measurements */
|
||||||
lchan->meas.rssi_sum += rssi;
|
sched_trx_meas_push(lchan, meas);
|
||||||
lchan->meas.toa256_sum += toa256;
|
|
||||||
lchan->meas.num++;
|
|
||||||
|
|
||||||
/* Copy burst to buffer of 4 bursts */
|
/* Copy burst to buffer of 4 bursts */
|
||||||
offset = buffer + bid * 116;
|
offset = buffer + bid * 116;
|
||||||
|
@ -85,6 +80,9 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
|
||||||
if (bid != 3)
|
if (bid != 3)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/* Calculate AVG of the measurements */
|
||||||
|
sched_trx_meas_avg(lchan, 4);
|
||||||
|
|
||||||
/* Check for complete set of bursts */
|
/* Check for complete set of bursts */
|
||||||
if ((*mask & 0xf) != 0xf) {
|
if ((*mask & 0xf) != 0xf) {
|
||||||
LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) data frame at "
|
LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) data frame at "
|
||||||
|
|
|
@ -613,7 +613,7 @@ static void sched_trx_a5_burst_enc(struct trx_lchan_state *lchan,
|
||||||
|
|
||||||
int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
|
int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
|
||||||
uint32_t burst_fn, sbit_t *bits, uint16_t nbits,
|
uint32_t burst_fn, sbit_t *bits, uint16_t nbits,
|
||||||
int8_t rssi, int16_t toa256)
|
const struct trx_meas_set *meas)
|
||||||
{
|
{
|
||||||
struct trx_lchan_state *lchan;
|
struct trx_lchan_state *lchan;
|
||||||
const struct trx_frame *frame;
|
const struct trx_frame *frame;
|
||||||
|
@ -674,7 +674,7 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
|
||||||
sched_trx_a5_burst_dec(lchan, fn, bits);
|
sched_trx_a5_burst_dec(lchan, fn, bits);
|
||||||
|
|
||||||
/* Put burst to handler */
|
/* Put burst to handler */
|
||||||
handler(trx, ts, lchan, fn, bid, bits, rssi, toa256);
|
handler(trx, ts, lchan, fn, bid, bits, meas);
|
||||||
}
|
}
|
||||||
|
|
||||||
next_frame:
|
next_frame:
|
||||||
|
@ -710,3 +710,50 @@ int sched_trx_handle_tx_burst(struct trx_instance *trx,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MEAS_HIST_FIRST(hist) \
|
||||||
|
(&hist->buf[0])
|
||||||
|
#define MEAS_HIST_LAST(hist) \
|
||||||
|
(MEAS_HIST_FIRST(hist) + ARRAY_SIZE(hist->buf) - 1)
|
||||||
|
|
||||||
|
/* Add a new set of measurements to the history */
|
||||||
|
void sched_trx_meas_push(struct trx_lchan_state *lchan, const struct trx_meas_set *meas)
|
||||||
|
{
|
||||||
|
struct trx_lchan_meas_hist *hist = &lchan->meas_hist;
|
||||||
|
|
||||||
|
/* Find a new position where to store the measurements */
|
||||||
|
if (hist->head == MEAS_HIST_LAST(hist) || hist->head == NULL)
|
||||||
|
hist->head = MEAS_HIST_FIRST(hist);
|
||||||
|
else
|
||||||
|
hist->head++;
|
||||||
|
|
||||||
|
*hist->head = *meas;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the AVG of n measurements from the history */
|
||||||
|
void sched_trx_meas_avg(struct trx_lchan_state *lchan, unsigned int n)
|
||||||
|
{
|
||||||
|
struct trx_lchan_meas_hist *hist = &lchan->meas_hist;
|
||||||
|
struct trx_meas_set *meas = hist->head;
|
||||||
|
int toa256_sum = 0;
|
||||||
|
int rssi_sum = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
OSMO_ASSERT(n > 0 && n <= ARRAY_SIZE(hist->buf));
|
||||||
|
OSMO_ASSERT(meas != NULL);
|
||||||
|
|
||||||
|
/* Traverse backwards up to n entries, calculate the sum */
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
toa256_sum += meas->toa256;
|
||||||
|
rssi_sum += meas->rssi;
|
||||||
|
|
||||||
|
if (meas == MEAS_HIST_FIRST(hist))
|
||||||
|
meas = MEAS_HIST_LAST(hist);
|
||||||
|
else
|
||||||
|
meas--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the AVG */
|
||||||
|
lchan->meas_avg.toa256 = toa256_sum / n;
|
||||||
|
lchan->meas_avg.rssi = rssi_sum / n;
|
||||||
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
/* Forward declaration to avoid mutual include */
|
/* Forward declaration to avoid mutual include */
|
||||||
struct trx_lchan_state;
|
struct trx_lchan_state;
|
||||||
|
struct trx_meas_set;
|
||||||
struct trx_instance;
|
struct trx_instance;
|
||||||
struct trx_ts;
|
struct trx_ts;
|
||||||
|
|
||||||
|
@ -99,7 +100,7 @@ enum trx_lchan_type {
|
||||||
typedef int trx_lchan_rx_func(struct trx_instance *trx,
|
typedef int trx_lchan_rx_func(struct trx_instance *trx,
|
||||||
struct trx_ts *ts, struct trx_lchan_state *lchan,
|
struct trx_ts *ts, struct trx_lchan_state *lchan,
|
||||||
uint32_t fn, uint8_t bid, sbit_t *bits,
|
uint32_t fn, uint8_t bid, sbit_t *bits,
|
||||||
int8_t rssi, int16_t toa256);
|
const struct trx_meas_set *meas);
|
||||||
|
|
||||||
typedef int trx_lchan_tx_func(struct trx_instance *trx,
|
typedef int trx_lchan_tx_func(struct trx_instance *trx,
|
||||||
struct trx_ts *ts, struct trx_lchan_state *lchan,
|
struct trx_ts *ts, struct trx_lchan_state *lchan,
|
||||||
|
@ -157,6 +158,19 @@ struct trx_multiframe {
|
||||||
const struct trx_frame *frames;
|
const struct trx_frame *frames;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct trx_meas_set {
|
||||||
|
/*! \brief ToA256 (Timing of Arrival, 1/256 of a symbol) */
|
||||||
|
int16_t toa256;
|
||||||
|
/*! \brief RSSI (Received Signal Strength Indication) */
|
||||||
|
int8_t rssi;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Simple ring buffer (up to 8 unique measurements) */
|
||||||
|
struct trx_lchan_meas_hist {
|
||||||
|
struct trx_meas_set buf[8];
|
||||||
|
struct trx_meas_set *head;
|
||||||
|
};
|
||||||
|
|
||||||
/* States each channel on a multiframe */
|
/* States each channel on a multiframe */
|
||||||
struct trx_lchan_state {
|
struct trx_lchan_state {
|
||||||
/*! \brief Channel type */
|
/*! \brief Channel type */
|
||||||
|
@ -190,14 +204,10 @@ struct trx_lchan_state {
|
||||||
/*! \brief pending FACCH/H blocks on Uplink */
|
/*! \brief pending FACCH/H blocks on Uplink */
|
||||||
uint8_t ul_facch_blocks;
|
uint8_t ul_facch_blocks;
|
||||||
|
|
||||||
struct {
|
/*! \brief Downlink measurements history */
|
||||||
/*! \brief Number of measurements */
|
struct trx_lchan_meas_hist meas_hist;
|
||||||
unsigned int num;
|
/*! \brief AVG measurements of the last received block */
|
||||||
/*! \brief Sum of RSSI values */
|
struct trx_meas_set meas_avg;
|
||||||
float rssi_sum;
|
|
||||||
/*! \brief Sum of TOA values */
|
|
||||||
int32_t toa256_sum;
|
|
||||||
} meas;
|
|
||||||
|
|
||||||
/*! \brief SACCH state */
|
/*! \brief SACCH state */
|
||||||
struct {
|
struct {
|
||||||
|
@ -347,7 +357,7 @@ void sched_prim_flush_queue(struct llist_head *list);
|
||||||
|
|
||||||
int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
|
int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
|
||||||
uint32_t burst_fn, sbit_t *bits, uint16_t nbits,
|
uint32_t burst_fn, sbit_t *bits, uint16_t nbits,
|
||||||
int8_t rssi, int16_t toa256);
|
const struct trx_meas_set *meas);
|
||||||
int sched_trx_handle_tx_burst(struct trx_instance *trx,
|
int sched_trx_handle_tx_burst(struct trx_instance *trx,
|
||||||
struct trx_ts *ts, struct trx_lchan_state *lchan,
|
struct trx_ts *ts, struct trx_lchan_state *lchan,
|
||||||
uint32_t fn, ubit_t *bits);
|
uint32_t fn, ubit_t *bits);
|
||||||
|
@ -381,3 +391,7 @@ bool sched_tchh_block_map_fn(enum trx_lchan_type chan,
|
||||||
sched_tchh_block_map_fn(chan, fn, ul, 1, 1)
|
sched_tchh_block_map_fn(chan, fn, ul, 1, 1)
|
||||||
#define sched_tchh_facch_end(chan, fn, ul) \
|
#define sched_tchh_facch_end(chan, fn, ul) \
|
||||||
sched_tchh_block_map_fn(chan, fn, ul, 1, 0)
|
sched_tchh_block_map_fn(chan, fn, ul, 1, 0)
|
||||||
|
|
||||||
|
/* Measurement history */
|
||||||
|
void sched_trx_meas_push(struct trx_lchan_state *lchan, const struct trx_meas_set *meas);
|
||||||
|
void sched_trx_meas_avg(struct trx_lchan_state *lchan, unsigned int n);
|
||||||
|
|
|
@ -555,6 +555,7 @@ rsp_error:
|
||||||
static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
|
static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
|
||||||
{
|
{
|
||||||
struct trx_instance *trx = ofd->data;
|
struct trx_instance *trx = ofd->data;
|
||||||
|
struct trx_meas_set meas;
|
||||||
uint8_t buf[256];
|
uint8_t buf[256];
|
||||||
sbit_t bits[148];
|
sbit_t bits[148];
|
||||||
int8_t rssi, tn;
|
int8_t rssi, tn;
|
||||||
|
@ -595,8 +596,14 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
|
||||||
LOGP(DTRXD, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%d\n",
|
LOGP(DTRXD, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%d\n",
|
||||||
tn, fn, rssi, toa256);
|
tn, fn, rssi, toa256);
|
||||||
|
|
||||||
|
/* Group the measurements together */
|
||||||
|
meas = (struct trx_meas_set) {
|
||||||
|
.toa256 = toa256,
|
||||||
|
.rssi = rssi,
|
||||||
|
};
|
||||||
|
|
||||||
/* Poke scheduler */
|
/* Poke scheduler */
|
||||||
sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, rssi, toa256);
|
sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, &meas);
|
||||||
|
|
||||||
/* Correct local clock counter */
|
/* Correct local clock counter */
|
||||||
if (fn % 51 == 0)
|
if (fn % 51 == 0)
|
||||||
|
|
Loading…
Reference in New Issue