Commit eb9f1fa9 authored by Paul Graydon's avatar Paul Graydon

amari.kpi: Add DRB.UEActive measurement

parent 7164e99c
......@@ -29,6 +29,8 @@ from xlte import kpi
from xlte.amari import xlog
from golang import func
from math import floor
# LogMeasure takes enb.xlog (TODO and enb.log) as input, and produces kpi.Measurements on output.
#
......@@ -205,10 +207,14 @@ def _read(logm):
# _handle_stats handles next stats xlog entry upon _read request.
@func(LogMeasure)
def _handle_stats(logm, stats: xlog.Message, m_prev: kpi.Measurement):
# build Measurement from stats' counters.
# build Measurement from stats' values, by mapping Amarisoft non-counters
# (non-cumulative) and counters to their respective 3GPP value specified by
# kpi.Measurement.
#
# for non-counters, simply map the value to the corresponding 3GPP value.
#
# we take δ(stats_prev, stat) and process it mapping Amarisoft counters to
# 3GPP ones specified by kpi.Measurement. This approach has following limitations:
# for cumulative counters, we take δ(stats_prev, stat) and process it
# before performing the mapping. This approach has following limitations:
#
# - for most of the counters there is no direct mapping in between
# Amarisoft and 3GPP. For example we currently use s1_erab_setup_request for
......@@ -302,6 +308,17 @@ def _handle_stats(logm, stats: xlog.Message, m_prev: kpi.Measurement):
if m[fini] > m[init]:
m[fini] = m[init]
cells = set(stats['cells'].keys()) # NOTE cells are taken only from stats, not from stat_prev
# map non-counter values, which are independent between periods
# and do not need any special processing.
Σue_active_count_avg = 0
for cell in cells:
Σue_active_count_avg += _stats_cell_nc(stats, cell, 'ue_active_count_avg')
# flooring the sum of averages is more accurate to the 3GPP spec than summing the floors.
Σue_active_count_avg = int(floor(Σue_active_count_avg))
m['DRB.UEActive'] = Σue_active_count_avg
# compute δ for counters.
# any logic error in data will be reported via LogError.
try:
......@@ -315,7 +332,6 @@ def _handle_stats(logm, stats: xlog.Message, m_prev: kpi.Measurement):
# same whether we do aggregation here or in kpi.Calc.erab_accessibility().
#
# TODO rework to emit per-cell measurements when/if we need per-cell KPIs
cells = set(stats['cells'].keys()) # NOTE cells are taken only from stats, not from stat_prev
δΣcell_rrc_connection_request = 0 # (if a cell disappears its counters stop to be accounted)
δΣcell_rrc_connection_setup_complete = 0
for cell in cells:
......@@ -368,12 +384,25 @@ def _stats_check(stats: xlog.Message):
raise LogError(stats.timestamp, "stats: %s" % e) from None
return
# _stats_nc returns specified global non-counter from stats result.
#
# stats is assumed to be already verified by _stats_check.
def _stats_nc(stats: xlog.Message, non_counter: str):
return stats.get(non_counter, 0.0)
# _stats_cc returns specified global cumulative counter from stats result.
#
# stats is assumed to be already verified by _stats_check.
def _stats_cc(stats: xlog.Message, counter: str):
return stats['counters']['messages'].get(counter, 0)
# _stats_cell_nc is like _stats_nc but returns specified per-cell non-counter from stats result.
def _stats_cell_nc(stats: xlog.Message, cell: str, non_counter: str):
_ = stats['cells'].get(cell)
if _ is None:
return 0.0 # cell is absent in this stats
return _.get(non_counter, 0.0)
# _stats_cell_cc is like _stats_cc but returns specified per-cell cumulative counter from stats result.
def _stats_cell_cc(stats: xlog.Message, cell: str, counter: str):
_ = stats['cells'].get(cell)
......
This diff is collapsed.
......@@ -179,6 +179,9 @@ class Measurement(np.void):
('DRB.PdcpSduBitrateUl.QCI', np.float64),# bit/s 4.4.1.1 NOTE not kbit/s
('DRB.PdcpSduBitrateDl.QCI', np.float64),# bit/s 4.4.1.2 NOTE not kbit/s
('DRB.UEActive', np.int32), # 1 4.4.2.4 36.314:4.1.3.3
# XXX mean is not good for our model
# TODO mean -> total + npkt?
#('DRB.IPLatDl.QCI', Ttime), # s 4.4.5.1 32.450:6.3.2 NOTE not ms
......
......@@ -53,6 +53,8 @@ def test_Measurement():
assert m['S1SIG.ConnEstabAtt'] == 123
m['RRC.ConnEstabAtt.sum'] = 17
assert m['RRC.ConnEstabAtt.sum'] == 17
m['DRB.UEActive'] = 6
assert m['DRB.UEActive'] == 6
m['DRB.IPVolDl.QCI'][:] = 0
m['DRB.IPVolDl.5'] = 55
m['DRB.IPVolDl.7'] = NA(m['DRB.IPVolDl.7'].dtype)
......@@ -67,13 +69,14 @@ def test_Measurement():
assert m['DRB.IPVolDl.QCI'][k] == 0
# str/repr
assert repr(m) == "Measurement(RRC.ConnEstabAtt.sum=17, DRB.IPVolDl.QCI={5:55 7:ø 9:99}, S1SIG.ConnEstabAtt=123)"
assert repr(m) == "Measurement(RRC.ConnEstabAtt.sum=17, DRB.UEActive=6, DRB.IPVolDl.QCI={5:55 7:ø 9:99}, S1SIG.ConnEstabAtt=123)"
s = str(m)
assert s[0] == '('
assert s[-1] == ')'
v = s[1:-1].split(', ')
vok = ['ø'] * len(m._dtype0.names)
vok[m.dtype.names.index("RRC.ConnEstabAtt.sum")] = "17"
vok[m.dtype.names.index("DRB.UEActive")] = "6"
vok[m.dtype.names.index("S1SIG.ConnEstabAtt")] = "123"
vok[m.dtype.names.index("DRB.IPVolDl.QCI")] = "{5:55 7:ø 9:99}"
assert v == vok
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment