oml-plot-tools 0.9.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oml_plot_tools/__init__.py +24 -0
- oml_plot_tools/common.py +172 -0
- oml_plot_tools/consum.py +187 -0
- oml_plot_tools/main.py +49 -0
- oml_plot_tools/radio.py +184 -0
- oml_plot_tools/tests/__init__.py +22 -0
- oml_plot_tools/tests/common.py +126 -0
- oml_plot_tools/tests/common_test.py +133 -0
- oml_plot_tools/tests/consum_test.py +201 -0
- oml_plot_tools/tests/examples/consumption.oml +4179 -0
- oml_plot_tools/tests/examples/consumption_all.png +0 -0
- oml_plot_tools/tests/examples/consumption_clock.png +0 -0
- oml_plot_tools/tests/examples/consumption_current.png +0 -0
- oml_plot_tools/tests/examples/consumption_only_one.oml +10 -0
- oml_plot_tools/tests/examples/grenoble-iotlab.png +0 -0
- oml_plot_tools/tests/examples/radio.oml +2009 -0
- oml_plot_tools/tests/examples/radio_clock.png +0 -0
- oml_plot_tools/tests/examples/radio_separated_last.png +0 -0
- oml_plot_tools/tests/examples/radio_single.png +0 -0
- oml_plot_tools/tests/examples/robot.oml +1447 -0
- oml_plot_tools/tests/examples/trajectory_all.png +0 -0
- oml_plot_tools/tests/examples/trajectory_angle.png +0 -0
- oml_plot_tools/tests/examples/trajectory_circuit.png +0 -0
- oml_plot_tools/tests/examples/trajectory_clock.png +0 -0
- oml_plot_tools/tests/examples/trajectory_only.png +0 -0
- oml_plot_tools/tests/main_parser_test.py +51 -0
- oml_plot_tools/tests/radio_test.py +173 -0
- oml_plot_tools/tests/traj_test.py +223 -0
- oml_plot_tools/traj.py +352 -0
- oml_plot_tools-0.9.1.dist-info/METADATA +35 -0
- oml_plot_tools-0.9.1.dist-info/RECORD +35 -0
- oml_plot_tools-0.9.1.dist-info/WHEEL +4 -0
- oml_plot_tools-0.9.1.dist-info/entry_points.txt +5 -0
- oml_plot_tools-0.9.1.dist-info/licenses/AUTHORS +1 -0
- oml_plot_tools-0.9.1.dist-info/licenses/COPYING +518 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# This file is a part of IoT-LAB oml-plot-tools
|
|
4
|
+
# Copyright (C) 2015 INRIA (Contact: admin@iot-lab.info)
|
|
5
|
+
# Contributor(s) : see AUTHORS file
|
|
6
|
+
#
|
|
7
|
+
# This software is governed by the CeCILL license under French law
|
|
8
|
+
# and abiding by the rules of distribution of free software. You can use,
|
|
9
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
|
10
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
|
11
|
+
# http://www.cecill.info.
|
|
12
|
+
#
|
|
13
|
+
# As a counterpart to the access to the source code and rights to copy,
|
|
14
|
+
# modify and redistribute granted by the license, users are provided only
|
|
15
|
+
# with a limited warranty and the software's author, the holder of the
|
|
16
|
+
# economic rights, and the successive licensors have only limited
|
|
17
|
+
# liability.
|
|
18
|
+
#
|
|
19
|
+
# The fact that you are presently reading this means that you have had
|
|
20
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
|
21
|
+
|
|
22
|
+
"""List of tools to display iot-lab oml generated files"""
|
|
23
|
+
|
|
24
|
+
__version__ = "0.9.1"
|
oml_plot_tools/common.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
# This file is a part of IoT-LAB oml-plot-tools
|
|
5
|
+
# Copyright (C) 2015 INRIA (Contact: admin@iot-lab.info)
|
|
6
|
+
# Contributor(s) : see AUTHORS file
|
|
7
|
+
#
|
|
8
|
+
# This software is governed by the CeCILL license under French law
|
|
9
|
+
# and abiding by the rules of distribution of free software. You can use,
|
|
10
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
|
11
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
|
12
|
+
# http://www.cecill.info.
|
|
13
|
+
#
|
|
14
|
+
# As a counterpart to the access to the source code and rights to copy,
|
|
15
|
+
# modify and redistribute granted by the license, users are provided only
|
|
16
|
+
# with a limited warranty and the software's author, the holder of the
|
|
17
|
+
# economic rights, and the successive licensors have only limited
|
|
18
|
+
# liability.
|
|
19
|
+
#
|
|
20
|
+
# The fact that you are presently reading this means that you have had
|
|
21
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
|
22
|
+
|
|
23
|
+
"""Common functions for all oml types"""
|
|
24
|
+
|
|
25
|
+
# Issues with numpy
|
|
26
|
+
# pylint:disable=no-member
|
|
27
|
+
|
|
28
|
+
from collections import OrderedDict, namedtuple
|
|
29
|
+
|
|
30
|
+
import matplotlib.pyplot as plt
|
|
31
|
+
import numpy
|
|
32
|
+
|
|
33
|
+
OML_HEADER_LEN = 9
|
|
34
|
+
|
|
35
|
+
OML_TYPES = {
|
|
36
|
+
"consumption": 1,
|
|
37
|
+
"radio": 2,
|
|
38
|
+
"event": 3,
|
|
39
|
+
"sniffer": 4,
|
|
40
|
+
"robot_pose": 10,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
TIMESTAMP_LABEL = "Sample Time (sec)"
|
|
44
|
+
OML_FIELDS = [
|
|
45
|
+
("timestamp", float),
|
|
46
|
+
("type", numpy.str_, 16),
|
|
47
|
+
("num", int),
|
|
48
|
+
("t_s", int),
|
|
49
|
+
("t_us", int),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
MeasureTuple = namedtuple("MeasureTuple", ["name", "type", "label"])
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def measures_dict(*measures_tuples):
|
|
56
|
+
"""Create a dict of 'MeasuresTuple' with given measures"""
|
|
57
|
+
measures_list = [(m[0], MeasureTuple(*m)) for m in measures_tuples]
|
|
58
|
+
return OrderedDict(measures_list)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def oml_load(filename, meas_type, measures):
|
|
62
|
+
"""Load oml file
|
|
63
|
+
:returns: numpy array
|
|
64
|
+
:measures: list of MeasureTuple"""
|
|
65
|
+
|
|
66
|
+
meas_dtypes = [(m.name, m.type) for m in measures]
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
data = _oml_read(filename, meas_type, meas_dtypes)
|
|
70
|
+
except IOError as err:
|
|
71
|
+
raise ValueError(f"Error opening oml file:\n{err}\n")
|
|
72
|
+
except (ValueError, StopIteration, IndexError) as err:
|
|
73
|
+
raise ValueError(f"Error reading oml file:\n{err}\n")
|
|
74
|
+
except TypeError as err:
|
|
75
|
+
raise ValueError(f"{err}")
|
|
76
|
+
|
|
77
|
+
# No error when only one value
|
|
78
|
+
data = numpy.atleast_1d(data)
|
|
79
|
+
|
|
80
|
+
# No empty measures
|
|
81
|
+
# I think not reproducible anymore with genfromtxt however.
|
|
82
|
+
if array_empty(data): # pragma: no cover
|
|
83
|
+
raise ValueError("No values, not an oml file")
|
|
84
|
+
|
|
85
|
+
return data
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def oml_plot_clock(data, title="Clock time verification"):
|
|
89
|
+
"""Print clock diff between measures
|
|
90
|
+
:params data: oml_load returned array
|
|
91
|
+
"""
|
|
92
|
+
time = data["timestamp"]
|
|
93
|
+
clock_diff = numpy.diff(time) * 1000
|
|
94
|
+
|
|
95
|
+
print(f"Time from {time[0]:f} to {time[-1]:f}")
|
|
96
|
+
print("NB Points =", len(time))
|
|
97
|
+
print("Duration (s)=", time[-1] - time[0])
|
|
98
|
+
print("Steptime (ms)=", 1000 * (time[-1] - time[0]) / len(time))
|
|
99
|
+
print("Clock mean (ms)=", numpy.mean(clock_diff))
|
|
100
|
+
print("Clock std (ms)=", numpy.std(clock_diff))
|
|
101
|
+
print("Clock max (ms)=", numpy.max(clock_diff))
|
|
102
|
+
print("Clock min (ms)=", numpy.min(clock_diff))
|
|
103
|
+
|
|
104
|
+
plt.figure()
|
|
105
|
+
plt.title(title)
|
|
106
|
+
plt.grid()
|
|
107
|
+
plt.plot(clock_diff)
|
|
108
|
+
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def plot(data, title, field, ylabel, xlabel=TIMESTAMP_LABEL):
|
|
113
|
+
"""Plot data"""
|
|
114
|
+
plt.title(title)
|
|
115
|
+
plt.grid()
|
|
116
|
+
plt.xlabel(xlabel)
|
|
117
|
+
plt.ylabel(ylabel)
|
|
118
|
+
plt.plot(data["timestamp"], data[field])
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def plot_show():
|
|
122
|
+
"""Show image."""
|
|
123
|
+
plt.tight_layout()
|
|
124
|
+
plt.show()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# Help functions
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _oml_read(filename, meas_type, fields_dtypes=()):
|
|
131
|
+
"""Read oml file
|
|
132
|
+
:measures: list of MeasureTuple"""
|
|
133
|
+
|
|
134
|
+
# Select values
|
|
135
|
+
dtypes = OML_FIELDS + list(fields_dtypes)
|
|
136
|
+
names = [entry[0] for entry in dtypes]
|
|
137
|
+
|
|
138
|
+
# Read values from file
|
|
139
|
+
c_meas_type = {names.index("type"): _valid_oml_f(meas_type)}
|
|
140
|
+
data = numpy.genfromtxt(
|
|
141
|
+
filename,
|
|
142
|
+
skip_header=OML_HEADER_LEN,
|
|
143
|
+
names=names,
|
|
144
|
+
dtype=dtypes,
|
|
145
|
+
converters=c_meas_type,
|
|
146
|
+
invalid_raise=False,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Update 'timestamp' field with the cn calculated timestamp
|
|
150
|
+
for row in numpy.nditer(data, op_flags=["readwrite"]):
|
|
151
|
+
timestamp = row["t_s"] + row["t_us"] / 1e6
|
|
152
|
+
row["timestamp"] = timestamp
|
|
153
|
+
|
|
154
|
+
return data
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _valid_oml_f(meas_type):
|
|
158
|
+
"""Return a function that validates oml type"""
|
|
159
|
+
|
|
160
|
+
def _validate(value):
|
|
161
|
+
"""Check that 'meas_type' column matchs requested"""
|
|
162
|
+
value = int(value)
|
|
163
|
+
if int(value) == OML_TYPES[meas_type]:
|
|
164
|
+
return meas_type
|
|
165
|
+
raise TypeError(f"OML file is not: {meas_type}")
|
|
166
|
+
|
|
167
|
+
return _validate
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def array_empty(array):
|
|
171
|
+
"""Test if array is not None or not empty."""
|
|
172
|
+
return array is None or len(array) == 0
|
oml_plot_tools/consum.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
# This file is a part of IoT-LAB oml-plot-tools
|
|
5
|
+
# Copyright (C) 2015 INRIA (Contact: admin@iot-lab.info)
|
|
6
|
+
# Contributor(s) : see AUTHORS file
|
|
7
|
+
#
|
|
8
|
+
# This software is governed by the CeCILL license under French law
|
|
9
|
+
# and abiding by the rules of distribution of free software. You can use,
|
|
10
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
|
11
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
|
12
|
+
# http://www.cecill.info.
|
|
13
|
+
#
|
|
14
|
+
# As a counterpart to the access to the source code and rights to copy,
|
|
15
|
+
# modify and redistribute granted by the license, users are provided only
|
|
16
|
+
# with a limited warranty and the software's author, the holder of the
|
|
17
|
+
# economic rights, and the successive licensors have only limited
|
|
18
|
+
# liability.
|
|
19
|
+
#
|
|
20
|
+
# The fact that you are presently reading this means that you have had
|
|
21
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
usage: plot_oml_consum [-h] -i DATA [-l TITLE] [-b BEGIN] [-e END] [-a] [-p]
|
|
26
|
+
[-v] [-c] [-t]
|
|
27
|
+
|
|
28
|
+
Plot iot-lab consumption OML files
|
|
29
|
+
|
|
30
|
+
options:
|
|
31
|
+
-h, --help show this help message and exit
|
|
32
|
+
-i, --input DATA Node consumption values
|
|
33
|
+
-l, --label TITLE Graph title
|
|
34
|
+
-b, --begin BEGIN Sample start
|
|
35
|
+
-e, --end END Sample end
|
|
36
|
+
|
|
37
|
+
plot:
|
|
38
|
+
Plot selection
|
|
39
|
+
|
|
40
|
+
-a, --all Plot power/voltage/current on one figure (default)
|
|
41
|
+
-p, --power Plot power
|
|
42
|
+
-v, --voltage Plot voltage
|
|
43
|
+
-c, --current Plot current
|
|
44
|
+
-t, --time Plot time verification
|
|
45
|
+
""" # noqa: E501
|
|
46
|
+
|
|
47
|
+
import argparse
|
|
48
|
+
import sys
|
|
49
|
+
|
|
50
|
+
import matplotlib.pyplot as plt
|
|
51
|
+
|
|
52
|
+
from . import common
|
|
53
|
+
|
|
54
|
+
# Selection variables
|
|
55
|
+
_POWER = "power"
|
|
56
|
+
_VOLTAGE = "voltage"
|
|
57
|
+
_CURRENT = "current"
|
|
58
|
+
_ALL = "all"
|
|
59
|
+
_TIME = "time"
|
|
60
|
+
_TITLE = "Node"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
MEASURES_D = common.measures_dict(
|
|
64
|
+
(_POWER, float, "Power (W)"),
|
|
65
|
+
(_VOLTAGE, float, "Voltage (V)"),
|
|
66
|
+
(_CURRENT, float, "Current (A)"),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def oml_load(filename):
|
|
71
|
+
"""Load consumption oml file"""
|
|
72
|
+
data = common.oml_load(filename, "consumption", MEASURES_D.values())
|
|
73
|
+
return data
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
PARSER = argparse.ArgumentParser(
|
|
77
|
+
prog="plot_oml_consum", description="Plot iot-lab consumption OML files"
|
|
78
|
+
)
|
|
79
|
+
PARSER.add_argument(
|
|
80
|
+
"-i",
|
|
81
|
+
"--input",
|
|
82
|
+
dest="data",
|
|
83
|
+
type=oml_load,
|
|
84
|
+
required=True,
|
|
85
|
+
help="Node consumption values",
|
|
86
|
+
)
|
|
87
|
+
PARSER.add_argument("-l", "--label", dest="title", default=_TITLE, help="Graph title")
|
|
88
|
+
PARSER.add_argument("-b", "--begin", default=0, type=int, help="Sample start")
|
|
89
|
+
PARSER.add_argument("-e", "--end", default=-1, type=int, help="Sample end")
|
|
90
|
+
|
|
91
|
+
_PLOT = PARSER.add_argument_group("plot", "Plot selection")
|
|
92
|
+
_PLOT.add_argument(
|
|
93
|
+
"-a",
|
|
94
|
+
"--all",
|
|
95
|
+
dest="plot",
|
|
96
|
+
const=_ALL,
|
|
97
|
+
action="append_const",
|
|
98
|
+
help="Plot power/voltage/current on one figure (default)",
|
|
99
|
+
)
|
|
100
|
+
_PLOT.add_argument(
|
|
101
|
+
"-p", "--power", dest="plot", const=_POWER, action="append_const", help="Plot power"
|
|
102
|
+
)
|
|
103
|
+
_PLOT.add_argument(
|
|
104
|
+
"-v",
|
|
105
|
+
"--voltage",
|
|
106
|
+
dest="plot",
|
|
107
|
+
const=_VOLTAGE,
|
|
108
|
+
action="append_const",
|
|
109
|
+
help="Plot voltage",
|
|
110
|
+
)
|
|
111
|
+
_PLOT.add_argument(
|
|
112
|
+
"-c",
|
|
113
|
+
"--current",
|
|
114
|
+
dest="plot",
|
|
115
|
+
const=_CURRENT,
|
|
116
|
+
action="append_const",
|
|
117
|
+
help="Plot current",
|
|
118
|
+
)
|
|
119
|
+
_PLOT.add_argument(
|
|
120
|
+
"-t",
|
|
121
|
+
"--time",
|
|
122
|
+
dest="plot",
|
|
123
|
+
const=_TIME,
|
|
124
|
+
action="append_const",
|
|
125
|
+
help="Plot time verification",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def consumption_plot(data, title, selection):
|
|
130
|
+
"""Plot consumption values according to selection
|
|
131
|
+
|
|
132
|
+
:param data: numpy array returned by oml_read
|
|
133
|
+
:param title: Subplots title base
|
|
134
|
+
:param selection: with values in
|
|
135
|
+
'power', 'voltage', 'current': plot on different windows
|
|
136
|
+
'all': plot all three on the same window
|
|
137
|
+
'time': plot time verification
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
# Single selection of 'p/v/c'
|
|
141
|
+
for value in (_POWER, _VOLTAGE, _CURRENT):
|
|
142
|
+
if value in selection:
|
|
143
|
+
oml_plot(data, title, [MEASURES_D[value]])
|
|
144
|
+
|
|
145
|
+
# Plot all on the same window
|
|
146
|
+
if _ALL in selection:
|
|
147
|
+
oml_plot(data, title, MEASURES_D.values())
|
|
148
|
+
|
|
149
|
+
# Clock verification
|
|
150
|
+
if "time" in selection:
|
|
151
|
+
common.oml_plot_clock(data)
|
|
152
|
+
|
|
153
|
+
common.plot_show()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def oml_plot(data, title, meas_tuples):
|
|
157
|
+
"""Plot consumption value for 'meas_tuples'
|
|
158
|
+
|
|
159
|
+
:param data: numpy array returned by oml_read
|
|
160
|
+
:param title: Subplots title base
|
|
161
|
+
:param meas_tuples: numpy.dtypesplots separated on different windows
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
nbplots = len(meas_tuples)
|
|
165
|
+
plt.figure()
|
|
166
|
+
|
|
167
|
+
for num, meas in enumerate(meas_tuples, start=1):
|
|
168
|
+
plt.subplot(nbplots, 1, num)
|
|
169
|
+
|
|
170
|
+
_title = f"{title} {meas.name}"
|
|
171
|
+
common.plot(data, _title, meas.name, meas.label)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def main(args=None):
|
|
175
|
+
"""iotlab-plot consum command"""
|
|
176
|
+
args = args or sys.argv[1:]
|
|
177
|
+
|
|
178
|
+
opts = PARSER.parse_args(args)
|
|
179
|
+
# default to plot all
|
|
180
|
+
selection = opts.plot or (_ALL)
|
|
181
|
+
# select samples
|
|
182
|
+
data = opts.data[opts.begin : opts.end]
|
|
183
|
+
consumption_plot(data, opts.title, selection)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
if __name__ == "__main__":
|
|
187
|
+
main()
|
oml_plot_tools/main.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# This file is a part of IoT-LAB oml-plot-tools
|
|
4
|
+
# Copyright (C) 2015 INRIA (Contact: admin@iot-lab.info)
|
|
5
|
+
# Contributor(s) : see AUTHORS file
|
|
6
|
+
#
|
|
7
|
+
# This software is governed by the CeCILL license under French law
|
|
8
|
+
# and abiding by the rules of distribution of free software. You can use,
|
|
9
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
|
10
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
|
11
|
+
# http://www.cecill.info.
|
|
12
|
+
#
|
|
13
|
+
# As a counterpart to the access to the source code and rights to copy,
|
|
14
|
+
# modify and redistribute granted by the license, users are provided only
|
|
15
|
+
# with a limited warranty and the software's author, the holder of the
|
|
16
|
+
# economic rights, and the successive licensors have only limited
|
|
17
|
+
# liability.
|
|
18
|
+
#
|
|
19
|
+
# The fact that you are presently reading this means that you have had
|
|
20
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
|
21
|
+
|
|
22
|
+
"""Main parser."""
|
|
23
|
+
|
|
24
|
+
import sys
|
|
25
|
+
from argparse import ArgumentParser
|
|
26
|
+
|
|
27
|
+
import oml_plot_tools.consum
|
|
28
|
+
import oml_plot_tools.radio
|
|
29
|
+
import oml_plot_tools.traj
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main(args=None):
|
|
33
|
+
"""'iotlab' main function."""
|
|
34
|
+
args = args or sys.argv[1:]
|
|
35
|
+
|
|
36
|
+
commands = {
|
|
37
|
+
"consum": oml_plot_tools.consum.main,
|
|
38
|
+
"radio": oml_plot_tools.radio.main,
|
|
39
|
+
"traj": oml_plot_tools.traj.main,
|
|
40
|
+
"help": None,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
parser = ArgumentParser()
|
|
44
|
+
parser.add_argument("command", nargs="?", choices=commands.keys(), default="help")
|
|
45
|
+
commands["help"] = lambda args: parser.print_help()
|
|
46
|
+
|
|
47
|
+
opts, args = parser.parse_known_args(args)
|
|
48
|
+
|
|
49
|
+
return commands[opts.command](args)
|
oml_plot_tools/radio.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
# This file is a part of IoT-LAB oml-plot-tools
|
|
5
|
+
# Copyright (C) 2015 INRIA (Contact: admin@iot-lab.info)
|
|
6
|
+
# Contributor(s) : see AUTHORS file
|
|
7
|
+
#
|
|
8
|
+
# This software is governed by the CeCILL license under French law
|
|
9
|
+
# and abiding by the rules of distribution of free software. You can use,
|
|
10
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
|
11
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
|
12
|
+
# http://www.cecill.info.
|
|
13
|
+
#
|
|
14
|
+
# As a counterpart to the access to the source code and rights to copy,
|
|
15
|
+
# modify and redistribute granted by the license, users are provided only
|
|
16
|
+
# with a limited warranty and the software's author, the holder of the
|
|
17
|
+
# economic rights, and the successive licensors have only limited
|
|
18
|
+
# liability.
|
|
19
|
+
#
|
|
20
|
+
# The fact that you are presently reading this means that you have had
|
|
21
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
usage: plot_oml_radio [-h] -i DATA [-l TITLE] [-b BEGIN] [-e END] [-a] [-p]
|
|
26
|
+
[-t]
|
|
27
|
+
|
|
28
|
+
Plot iot-lab radio OML files
|
|
29
|
+
|
|
30
|
+
options:
|
|
31
|
+
-h, --help show this help message and exit
|
|
32
|
+
-i, --input DATA Node radio values
|
|
33
|
+
-l, --label TITLE Graph title
|
|
34
|
+
-b, --begin BEGIN Sample start
|
|
35
|
+
-e, --end END Sample end
|
|
36
|
+
|
|
37
|
+
plot:
|
|
38
|
+
Plot selection
|
|
39
|
+
|
|
40
|
+
-a, --all Plot all channels in one window (default)
|
|
41
|
+
-p, --plot Plot channels in different windows
|
|
42
|
+
-t, --time Plot time verification
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
import argparse
|
|
46
|
+
import sys
|
|
47
|
+
|
|
48
|
+
import matplotlib.pyplot as plt
|
|
49
|
+
|
|
50
|
+
from . import common
|
|
51
|
+
|
|
52
|
+
MEASURES_D = common.measures_dict(
|
|
53
|
+
("channel", int, "Channel"),
|
|
54
|
+
("rssi", int, "RSSI (dBm)"),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def oml_load(filename):
|
|
59
|
+
"""Load radio oml file"""
|
|
60
|
+
data = common.oml_load(filename, "radio", MEASURES_D.values())
|
|
61
|
+
return data
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# Selection variables
|
|
65
|
+
_JOINED = "joined"
|
|
66
|
+
_SEPARATED = "separated"
|
|
67
|
+
_TIME = "time"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
PARSER = argparse.ArgumentParser(
|
|
71
|
+
prog="plot_oml_radio", description="Plot iot-lab radio OML files"
|
|
72
|
+
)
|
|
73
|
+
PARSER.add_argument(
|
|
74
|
+
"-i", "--input", dest="data", type=oml_load, required=True, help="Node radio values"
|
|
75
|
+
)
|
|
76
|
+
PARSER.add_argument("-l", "--label", dest="title", default="Node", help="Graph title")
|
|
77
|
+
PARSER.add_argument("-b", "--begin", default=0, type=int, help="Sample start")
|
|
78
|
+
PARSER.add_argument("-e", "--end", default=-1, type=int, help="Sample end")
|
|
79
|
+
|
|
80
|
+
_PLOT = PARSER.add_argument_group("plot", "Plot selection")
|
|
81
|
+
_PLOT.add_argument(
|
|
82
|
+
"-a",
|
|
83
|
+
"--all",
|
|
84
|
+
dest="plot",
|
|
85
|
+
const=_JOINED,
|
|
86
|
+
action="append_const",
|
|
87
|
+
help="Plot all channels in one window (default)",
|
|
88
|
+
)
|
|
89
|
+
_PLOT.add_argument(
|
|
90
|
+
"-p",
|
|
91
|
+
"--plot",
|
|
92
|
+
dest="plot",
|
|
93
|
+
const=_SEPARATED,
|
|
94
|
+
action="append_const",
|
|
95
|
+
help="Plot channels in different windows",
|
|
96
|
+
)
|
|
97
|
+
_PLOT.add_argument(
|
|
98
|
+
"-t",
|
|
99
|
+
"--time",
|
|
100
|
+
dest="plot",
|
|
101
|
+
const=_TIME,
|
|
102
|
+
action="append_const",
|
|
103
|
+
help="Plot time verification",
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def radio_plot(data, title, selection):
|
|
108
|
+
"""Plot radio values according to selection
|
|
109
|
+
|
|
110
|
+
:param data: numpy array returnel by oml_read
|
|
111
|
+
:param title: Subplots title base
|
|
112
|
+
:param selection: with values in
|
|
113
|
+
'joined': plot on the same window
|
|
114
|
+
'separated': plot on different windows
|
|
115
|
+
'time': plot time verification
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
if _JOINED in selection:
|
|
119
|
+
oml_plot_rssi(data, title)
|
|
120
|
+
if _SEPARATED in selection:
|
|
121
|
+
oml_plot_rssi(data, title, separated=True)
|
|
122
|
+
|
|
123
|
+
# Clock verification
|
|
124
|
+
if _TIME in selection:
|
|
125
|
+
common.oml_plot_clock(data)
|
|
126
|
+
|
|
127
|
+
common.plot_show()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def list_channels(data):
|
|
131
|
+
"""List radio channels used in data"""
|
|
132
|
+
channels = list(set(data["channel"]))
|
|
133
|
+
return sorted(channels)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def with_channel(data, channel):
|
|
137
|
+
"""Extract data where measured channel == `channel`"""
|
|
138
|
+
select = data["channel"] == channel
|
|
139
|
+
return data[select]
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def oml_plot_rssi(data, title, separated=False):
|
|
143
|
+
"""Plot rssi for all channels.
|
|
144
|
+
|
|
145
|
+
:param data: numpy array returned by oml_read
|
|
146
|
+
:param title: Subplots title base
|
|
147
|
+
:param separated: plots separated on different windows
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
channels = list_channels(data)
|
|
151
|
+
nbplots = len(channels)
|
|
152
|
+
meas = MEASURES_D["rssi"]
|
|
153
|
+
|
|
154
|
+
# Only window for all
|
|
155
|
+
if not separated:
|
|
156
|
+
plt.figure()
|
|
157
|
+
|
|
158
|
+
for num, channel in enumerate(channels, start=1):
|
|
159
|
+
# Select data for channel
|
|
160
|
+
cdata = with_channel(data, channel)
|
|
161
|
+
_title = f"{title} Channel {channel}"
|
|
162
|
+
|
|
163
|
+
# One window per plot
|
|
164
|
+
if separated:
|
|
165
|
+
plt.figure()
|
|
166
|
+
|
|
167
|
+
plt.subplot(nbplots, 1, num)
|
|
168
|
+
common.plot(cdata, _title, meas.name, meas.label)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def main(args=None):
|
|
172
|
+
"""iotlab-plot radio command"""
|
|
173
|
+
args = args or sys.argv[1:]
|
|
174
|
+
|
|
175
|
+
opts = PARSER.parse_args(args)
|
|
176
|
+
# default to plot all
|
|
177
|
+
selection = opts.plot or (_JOINED)
|
|
178
|
+
# select samples
|
|
179
|
+
data = opts.data[opts.begin : opts.end]
|
|
180
|
+
radio_plot(data, opts.title, selection)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
if __name__ == "__main__":
|
|
184
|
+
main()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# This file is a part of IoT-LAB oml-plot-tools
|
|
4
|
+
# Copyright (C) 2015 INRIA (Contact: admin@iot-lab.info)
|
|
5
|
+
# Contributor(s) : see AUTHORS file
|
|
6
|
+
#
|
|
7
|
+
# This software is governed by the CeCILL license under French law
|
|
8
|
+
# and abiding by the rules of distribution of free software. You can use,
|
|
9
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
|
10
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
|
11
|
+
# http://www.cecill.info.
|
|
12
|
+
#
|
|
13
|
+
# As a counterpart to the access to the source code and rights to copy,
|
|
14
|
+
# modify and redistribute granted by the license, users are provided only
|
|
15
|
+
# with a limited warranty and the software's author, the holder of the
|
|
16
|
+
# economic rights, and the successive licensors have only limited
|
|
17
|
+
# liability.
|
|
18
|
+
#
|
|
19
|
+
# The fact that you are presently reading this means that you have had
|
|
20
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
|
21
|
+
|
|
22
|
+
"""oml_plot_tools tests"""
|