measurement: fix matching of SUB frames by TDMA FN

3GPP TS 45.008, section 8.3 defines active TDMA frame subsets for
TCH channels, which shall always be transmitted even during the
silence periods in DTX mode of operation.  Each frame number
listed in this section corresponds to a single burst.

The Uplink measurements always contain TDMA FN of the *first* burst
of a block, so it does not make sense to match the given FN against
all FNs in the respective subset.  Instead, we should match only
specific FNs in accordance with the block mapping rules defined in
3GPP TS 45.002, section 7, table 1.

In the active subset for TCH/F there is only one *complete* block
starting at FN=52.  Incomplete blocks {52, 53, 54, 55} and {56, 57,
58, 59} contain only 50% of the useful bits (partial SID) and thus
~50% BER, so we don't treat them as SUB.

In the active subsets for TCH/H there are two *complete* blocks
for each sub-slot.  Their respective first FNs can be efficiently
defined in a lookup table (see ts45008_dtx_tchh_fn_map[]).  Note
that we can use a single lookup table for both sub-slots of TCH/H
because their TDMA FNs do not overlap.

This patch fixes unexpected SUB-RxQual values > 0 on TCH channels
with DTXu enabled and other than AMR (HR, FR, EFR) codec in use.

Change-Id: I8cc3a755a8ad4dc564439aab2298c1e97ac0592d
Related: SYS#5853
This commit is contained in:
Vadim Yanitskiy 2022-04-28 01:35:54 +03:00
parent 991f3f64d9
commit 284f16e7d7
2 changed files with 32 additions and 69 deletions

View File

@ -16,10 +16,17 @@
#include <osmo-bts/power_control.h>
#include <osmo-bts/ta_control.h>
/* Tables as per TS 45.008 Section 8.3 */
static const uint8_t ts45008_83_tch_f[] = { 52, 53, 54, 55, 56, 57, 58, 59 };
static const uint8_t ts45008_83_tch_hs0[] = { 0, 2, 4, 6, 52, 54, 56, 58 };
static const uint8_t ts45008_83_tch_hs1[] = { 14, 16, 18, 20, 66, 68, 70, 72 };
/* Active TDMA frame subset for TCH/H in DTX mode (see 3GPP TS 45.008 Section 8.3).
* This mapping is used to determine if a L2 block starting at the given TDMA FN
* belongs to the SUB set and thus shall always be transmitted in DTX mode. */
static const uint8_t ts45008_dtx_tchh_fn_map[104] = {
/* TCH/H(0): 0, 2, 4, 6, 52, 54, 56, 58 */
[0] = 1, /* block { 0, 2, 4, 6} */
[52] = 1, /* block {52, 54, 56, 58} */
/* TCH/H(1): 14, 16, 18, 20, 66, 68, 70, 72 */
[14] = 1, /* block {14, 16, 18, 20} */
[66] = 1, /* block {66, 68, 70, 72} */
};
/* In cases where we less measurements than we expect we must assume that we
* just did not receive the block because it was lost due to bad channel
@ -36,17 +43,6 @@ static const struct bts_ul_meas measurement_dummy = {
.inv_rssi = MEASUREMENT_DUMMY_IRSSI
};
/* find out if an array contains a given key as element */
#define ARRAY_CONTAINS(arr, val) array_contains(arr, ARRAY_SIZE(arr), val)
static bool array_contains(const uint8_t *arr, unsigned int len, uint8_t val) {
int i;
for (i = 0; i < len; i++) {
if (arr[i] == val)
return true;
}
return false;
}
/* Decide if a given frame number is part of the "-SUB" measurements (true) or not (false)
* (this function is only used internally, it is public to call it from unit-tests) */
bool ts45008_83_is_sub(struct gsm_lchan *lchan, uint32_t fn)
@ -65,7 +61,11 @@ bool ts45008_83_is_sub(struct gsm_lchan *lchan, uint32_t fn)
switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_V1:
case GSM48_CMODE_SPEECH_EFR:
if (ARRAY_CONTAINS(ts45008_83_tch_f, fn104))
/* Active TDMA frame subset for TCH/F: 52, 53, 54, 55, 56, 57, 58, 59.
* There is only one *complete* block in this subset starting at FN=52.
* Incomplete blocks {... 52, 53, 54, 55} and {56, 57, 58, 59 ...}
* contain only 50% of the useful bits (partial SID) and thus ~50% BER. */
if (fn104 == 52)
return true;
break;
case GSM48_CMODE_SIGN:
@ -81,18 +81,8 @@ bool ts45008_83_is_sub(struct gsm_lchan *lchan, uint32_t fn)
case GSM_LCHAN_TCH_H:
switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_V1:
switch (lchan->nr) {
case 0:
if (ARRAY_CONTAINS(ts45008_83_tch_hs0, fn104))
return true;
break;
case 1:
if (ARRAY_CONTAINS(ts45008_83_tch_hs1, fn104))
return true;
break;
default:
OSMO_ASSERT(0);
}
if (ts45008_dtx_tchh_fn_map[fn104])
return true;
break;
case GSM48_CMODE_SIGN:
/* No DTX allowed; SUB=FULL, therefore measurements at all frame numbers are
@ -432,16 +422,16 @@ static unsigned int lchan_meas_sub_num_expected(const struct gsm_lchan *lchan)
/* 1 block SACCH, 24 blocks TCH (see note 1) */
return 25;
} else {
/* 1 block SACCH, 2 blocks TCH */
return 3;
/* 1 block SACCH, 1 block TCH */
return 2;
}
case GSM_PCHAN_TCH_H:
if (lchan->tch_mode == GSM48_CMODE_SIGN) {
/* 1 block SACCH, 12 blocks TCH (see ynote 1) */
return 13;
} else {
/* 1 block SACCH, 4 blocks TCH */
return 5;
/* 1 block SACCH, 2 blocks TCH */
return 3;
}
case GSM_PCHAN_SDCCH8_SACCH8C:
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:

View File

@ -379,44 +379,17 @@ static bool test_ts45008_83_is_sub_is_sub(const struct gsm_lchan *lchan, uint32_
switch (lchan->type) {
case GSM_LCHAN_TCH_F:
return (fn >= 52 && fn <= 59);
/* block {52, 53, 54, 55, 56, 57, 58, 59} */
return fn == 52;
case GSM_LCHAN_TCH_H:
if (lchan->nr == 0) {
if (fn == 0)
return true;
if (fn == 2)
return true;
if (fn == 4)
return true;
if (fn == 6)
return true;
if (fn == 52)
return true;
if (fn == 54)
return true;
if (fn == 56)
return true;
if (fn == 58)
return true;
} else if (lchan->nr == 1) {
if (fn == 14)
return true;
if (fn == 16)
return true;
if (fn == 18)
return true;
if (fn == 20)
return true;
if (fn == 66)
return true;
if (fn == 68)
return true;
if (fn == 70)
return true;
if (fn == 72)
return true;
} else
OSMO_ASSERT(false);
if (fn == 0) /* H0 block { 0, 2, 4, 6} */
return true;
if (fn == 52) /* H0 block {52, 54, 56, 58} */
return true;
if (fn == 14) /* H1 block {14, 16, 18, 20} */
return true;
if (fn == 66) /* H1 block {66, 68, 70, 72} */
return true;
return false;
default:
return false;