aboutsummaryrefslogtreecommitdiffstats
path: root/doc/stats_dabmod_munin.py
diff options
context:
space:
mode:
Diffstat (limited to 'doc/stats_dabmod_munin.py')
-rwxr-xr-xdoc/stats_dabmod_munin.py315
1 files changed, 315 insertions, 0 deletions
diff --git a/doc/stats_dabmod_munin.py b/doc/stats_dabmod_munin.py
new file mode 100755
index 0000000..33745ad
--- /dev/null
+++ b/doc/stats_dabmod_munin.py
@@ -0,0 +1,315 @@
+#!/usr/bin/env python2
+#
+# present statistics from ODR-DabMod's
+# RC interface to munin
+
+import sys
+import json
+import zmq
+import os
+import re
+
+# Values monitored:
+
+config_all = ""
+
+#default data type is GAUGE
+
+# One GAUGE multigraph from 0% to 100% with
+# ofdm clip_stats clip_ratio
+# ofdm clip_stats errorclip_ratio
+config_all += """
+multigraph ofdm_clip_stats
+graph_title OFDM CFR clip stats
+graph_order clip_ratio errorclip_ratio
+graph_vlabel number of samples/errors clipped during last ${{graph_period}}
+graph_category dabmod
+graph_info This graph shows CFR clipping statistics
+
+clip_ratio.info Number of samples clipped
+clip_ratio.label Number of samples clipped
+clip_ratio.min 0
+clip_ratio.max 100
+errorclip_ratio.info Number of errors clipped
+errorclip_ratio.label Number of errors clipped
+errorclip_ratio.min 0
+errorclip_ratio.max 100"""
+
+# One GAUGE multigraph
+# ofdm clip_stats mer
+config_all += """
+multigraph ofdm_clip_stats_mer
+graph_title OFDM MER after CFR
+graph_order mer
+graph_vlabel MER in dB after CFR
+graph_category dabmod
+graph_info This graph shows MER after CFR
+
+mer.info MER dB
+mer.label MER dB
+mer.min 0
+mer.max 100"""
+
+# One GAUGE multigraph in dB for
+# ofdm papr before-cfr
+# ofdm papr after-cfr
+config_all += """
+multigraph ofdm_papr
+graph_title OFDM PAPR stats
+graph_order before_cfr after_cfr
+graph_args --base 1000
+graph_vlabel Averate PAPR before/after CFR during last ${{graph_period}}
+graph_category dabmod
+graph_info This graph shows the Peak-to-Average Power Ratio before and after CFR
+
+before_cfr.info PAPR before CFR
+before_cfr.label PAPR before CFR
+before_cfr.min 0
+after_cfr.info PAPR after CFR
+after_cfr.label PAPR after CFR
+after_cfr.min 0"""
+
+# One GAUGE graph for
+# tist offset
+config_all += """
+multigraph tist_offset
+graph_title TIST configured offset
+graph_order offset
+graph_args --base 1000
+graph_vlabel Configured offset
+graph_category dabmod
+graph_info This graph shows the configured TIST offset
+
+offset.info Configured offset
+offset.label Configured offset
+offset.min 0
+offset.max 300"""
+
+# One COUNTER (min 0, max 249) graph for
+# tist timestamp fct
+config_all += """
+multigraph frame_fct
+graph_title TIST FCT
+graph_order fct
+graph_args --base 1000
+graph_vlabel FCT value
+graph_category dabmod
+graph_info This graph shows the FCT value
+
+fct.info FCT
+fct.label FCT
+fct.type COUNTER
+fct.min 0
+fct.max 249"""
+
+# One DDERIVE graph for
+# tist timestamp timestamps
+config_all += """
+multigraph tist_timestamp
+graph_title TIST timestamp
+graph_order timestamp
+graph_args --base 1000
+graph_vlabel timestamp value
+graph_category dabmod
+graph_info This graph shows the timestamp value in seconds
+
+timestamp.info timestamp
+timestamp.label timestamp
+timestamp.type DDERIVE
+timestamp.min 0"""
+
+# One DERIVE (min 0) multigraph for
+# sdr underruns
+# sdr latepackets
+config_all += """
+multigraph sdr_stats
+graph_title SDR device statistics
+graph_order underruns latepackets
+graph_args --base 1000
+graph_vlabel Number of underruns and late packets
+graph_category dabmod
+graph_info This graph shows the number of underruns and late packets
+
+underruns.info Number of SoapySDR/UHD underruns
+underruns.label Number of SoapySDR/UHD underruns
+underruns.type DERIVE
+underruns.min 0
+latepackets.info Number of SoapySDR/UHD late packets
+latepackets.label Number of SoapySDR/UHD late packets
+latepackets.type DERIVE
+latepackets.min 0"""
+
+# One DERIVE (min 0) graph for
+# sdr frames
+config_all += """
+multigraph sdr_frames
+graph_title SDR number of frames transmitted
+graph_order frames
+graph_args --base 1000
+graph_vlabel Number of frames transmitted
+graph_category dabmod
+graph_info This graph shows the number of frames transmitted
+
+frames.info Number of SoapySDR/UHD frames
+frames.label Number of SoapySDR/UHD frames
+frames.type DERIVE
+frames.min 0"""
+
+ctx = zmq.Context()
+
+class RCException(Exception):
+ pass
+
+if not os.environ.get("MUNIN_CAP_MULTIGRAPH"):
+ sys.stderr.write("This needs munin version 1.4 at least\n")
+ sys.exit(1)
+
+def do_transaction(message_parts, sock):
+ """To a send + receive transaction, quit whole program on timeout"""
+ if isinstance(message_parts, str):
+ sys.stderr.write("do_transaction expects a list!\n");
+ sys.exit(1)
+
+ for i, part in enumerate(message_parts):
+ if i == len(message_parts) - 1:
+ f = 0
+ else:
+ f = zmq.SNDMORE
+ sock.send(part, flags=f)
+
+ poller = zmq.Poller()
+ poller.register(sock, zmq.POLLIN)
+
+ socks = dict(poller.poll(1000))
+ if socks:
+ if socks.get(sock) == zmq.POLLIN:
+ rxpackets = sock.recv_multipart()
+ return rxpackets
+
+ raise RCException("Could not receive data for command '{}'\n".format(
+ message_parts))
+
+def connect():
+ """Create a connection to the dabmod RC
+
+ returns: the socket"""
+
+ sock = zmq.Socket(ctx, zmq.REQ)
+ sock.set(zmq.LINGER, 5)
+ sock.connect("tcp://localhost:9400")
+
+ try:
+ ping_answer = do_transaction([b"ping"], sock)
+
+ if not ping_answer == [b"ok"]:
+ sys.stderr.write("Wrong answer to ping\n")
+ sys.exit(1)
+ except RCException as e:
+ print("connect failed because: {}".format(e))
+ sys.exit(1)
+
+ return sock
+
+def get_rc_value(module, name, sock):
+ try:
+ parts = do_transaction([b"get", module.encode(), name.encode()], sock)
+ if len(parts) != 1:
+ sys.stderr.write("Received unexpected multipart message {}\n".format(
+ parts))
+ sys.exit(1)
+ return parts[0].decode()
+ except RCException as e:
+ print("get {} {} fail: {}".format(module, name, e))
+ return ""
+
+def handle_re(graph_name, re, rc_value, group_number=1):
+ match = re.search(rc_value)
+ if match:
+ return "{} {}\n".format(graph_name, match.group(group_number))
+ else:
+ return "{} U\n".format(graph_name)
+
+re_double_value = re.compile(r"(\d+\.\d+)", re.X)
+re_int_value = re.compile(r"(\d+)", re.X)
+
+if len(sys.argv) == 1:
+ sock = connect()
+
+ munin_values = ""
+
+ munin_values += "multigraph ofdm_clip_stats\n"
+ ofdm_clip_stats = get_rc_value("ofdm", "clip_stats", sock)
+ re_clip_samples = re.compile(r"(\d+\.\d+)%\ samples\ clipped", re.X)
+ munin_values += handle_re("clip_ratio.value ", re_clip_samples, ofdm_clip_stats)
+
+ re_clip_errors = re.compile(r"(\d+\.\d+)%\ errors\ clipped", re.X)
+ munin_values += handle_re("errorclip_ratio.value",
+ re_clip_errors, ofdm_clip_stats)
+
+ munin_values += "multigraph ofdm_clip_stats_mer\n"
+ re_clip_mer = re.compile(r"MER\ after\ CFR:\ (\d+\.\d+)", re.X)
+ munin_values += handle_re("mer.value",
+ re_clip_mer, ofdm_clip_stats)
+
+ munin_values += "multigraph ofdm_papr\n"
+ ofdm_papr_stats = get_rc_value("ofdm", "papr", sock)
+
+ def muninise_papr(papr):
+ if "N/A" in papr:
+ return "U"
+ else:
+ return float(papr.strip())
+
+ # Format is as follows:
+ # "PAPR [dB]: " << std::fixed <<
+ # (papr_before == 0 ? string("N/A") : to_string(papr_before)) <<
+ # ", " <<
+ # (papr_after == 0 ? string("N/A") : to_string(papr_after));
+ try:
+ _, _, both_papr = ofdm_papr_stats.partition(":")
+ papr_before, papr_after = both_papr.split(",")
+ papr_before = muninise_papr(papr_before)
+ munin_values += "before_cfr.value {}\n".format(papr_before)
+ except:
+ munin_values += "before_cfr.value U\n"
+
+ try:
+ _, _, both_papr = ofdm_papr_stats.partition(":")
+ papr_before, papr_after = both_papr.split(",")
+ papr_after = muninise_papr(papr_after)
+ munin_values += "after_cfr.value {}\n".format(papr_after)
+ except:
+ munin_values += "after_cfr.value U\n"
+
+
+ munin_values += "multigraph tist_offset\n"
+ tist_offset = get_rc_value("tist", "offset", sock)
+ munin_values += handle_re("offset.value", re_double_value, tist_offset)
+
+ munin_values += "multigraph frame_fct\n"
+ tist_timestamp = get_rc_value("tist", "timestamp", sock)
+ re_tist_timestamp = re.compile(r"(\d+\.\d+)\ for\ frame\ FCT\ (\d+)", re.X)
+ munin_values += handle_re("fct.value", re_tist_timestamp, tist_timestamp, 2)
+
+ munin_values += "multigraph tist_timestamp\n"
+ munin_values += handle_re("timestamp.value", re_tist_timestamp, tist_timestamp, 1)
+
+ munin_values += "multigraph sdr_stats\n"
+ sdr_underruns = get_rc_value("sdr", "underruns", sock)
+ munin_values += handle_re("underruns.value", re_int_value, sdr_underruns)
+ sdr_latepackets = get_rc_value("sdr", "latepackets", sock)
+ munin_values += handle_re("latepacket.value", re_int_value, sdr_latepackets)
+
+ munin_values += "multigraph sdr_frames\n"
+ sdr_frames = get_rc_value("sdr", "frames", sock)
+ munin_values += handle_re("frames.value", re_int_value, sdr_frames)
+
+ print(munin_values)
+
+elif len(sys.argv) == 2 and sys.argv[1] == "config":
+ # No need to connect
+ print(config_all)
+else:
+ sys.stderr.write("Invalid command line arguments")
+ sys.exit(1)
+