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.
Files changed (35) hide show
  1. oml_plot_tools/__init__.py +24 -0
  2. oml_plot_tools/common.py +172 -0
  3. oml_plot_tools/consum.py +187 -0
  4. oml_plot_tools/main.py +49 -0
  5. oml_plot_tools/radio.py +184 -0
  6. oml_plot_tools/tests/__init__.py +22 -0
  7. oml_plot_tools/tests/common.py +126 -0
  8. oml_plot_tools/tests/common_test.py +133 -0
  9. oml_plot_tools/tests/consum_test.py +201 -0
  10. oml_plot_tools/tests/examples/consumption.oml +4179 -0
  11. oml_plot_tools/tests/examples/consumption_all.png +0 -0
  12. oml_plot_tools/tests/examples/consumption_clock.png +0 -0
  13. oml_plot_tools/tests/examples/consumption_current.png +0 -0
  14. oml_plot_tools/tests/examples/consumption_only_one.oml +10 -0
  15. oml_plot_tools/tests/examples/grenoble-iotlab.png +0 -0
  16. oml_plot_tools/tests/examples/radio.oml +2009 -0
  17. oml_plot_tools/tests/examples/radio_clock.png +0 -0
  18. oml_plot_tools/tests/examples/radio_separated_last.png +0 -0
  19. oml_plot_tools/tests/examples/radio_single.png +0 -0
  20. oml_plot_tools/tests/examples/robot.oml +1447 -0
  21. oml_plot_tools/tests/examples/trajectory_all.png +0 -0
  22. oml_plot_tools/tests/examples/trajectory_angle.png +0 -0
  23. oml_plot_tools/tests/examples/trajectory_circuit.png +0 -0
  24. oml_plot_tools/tests/examples/trajectory_clock.png +0 -0
  25. oml_plot_tools/tests/examples/trajectory_only.png +0 -0
  26. oml_plot_tools/tests/main_parser_test.py +51 -0
  27. oml_plot_tools/tests/radio_test.py +173 -0
  28. oml_plot_tools/tests/traj_test.py +223 -0
  29. oml_plot_tools/traj.py +352 -0
  30. oml_plot_tools-0.9.1.dist-info/METADATA +35 -0
  31. oml_plot_tools-0.9.1.dist-info/RECORD +35 -0
  32. oml_plot_tools-0.9.1.dist-info/WHEEL +4 -0
  33. oml_plot_tools-0.9.1.dist-info/entry_points.txt +5 -0
  34. oml_plot_tools-0.9.1.dist-info/licenses/AUTHORS +1 -0
  35. oml_plot_tools-0.9.1.dist-info/licenses/COPYING +518 -0
oml_plot_tools/traj.py ADDED
@@ -0,0 +1,352 @@
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_traj [-h] [-i DATA] [--circuit-file CIRCUIT] [--site-map SITE]
26
+ [-l TITLE] [-b BEGIN] [-e END] [-t] [-a] [-ti]
27
+
28
+ Plot iot-lab trajectory oml files
29
+
30
+ options:
31
+ -h, --help show this help message and exit
32
+ -i, --input DATA Robot trajectory values
33
+ --circuit-file CIRCUIT
34
+ Robot circuit file, '-' for stdin
35
+ --site-map SITE Site map
36
+ -l, --label TITLE Graph title
37
+ -b, --begin BEGIN Sample start
38
+ -e, --end END Sample end
39
+
40
+ plot:
41
+ Plot selection
42
+
43
+ -t, --traj Plot robot trajectory
44
+ -a, --angle Plot robot angle
45
+ -ti, --time Plot time verification
46
+ """ # noqa: E501, #pylint: disable=line-too-long
47
+
48
+ import argparse
49
+ import json
50
+ import sys
51
+ from collections import namedtuple
52
+ from io import BytesIO
53
+
54
+ import iotlabcli.robot
55
+ import matplotlib.pyplot as plt
56
+
57
+ # Issues with numpy and matplotlib.cm
58
+ # pylint:disable=no-member
59
+ import numpy as np
60
+ from matplotlib import cm, patches
61
+
62
+ # http://stackoverflow.com/a/26605247/395687
63
+ # pip install --no-index -f http://dist.plone.org/thirdparty/ -U PIL
64
+ # or 'apt-get install python-imaging'
65
+ from PIL import Image
66
+
67
+ from . import common
68
+
69
+ PACKAGE = __name__.split(".", maxsplit=1)[0]
70
+
71
+ DOCK_PLT = {
72
+ "color": "blue",
73
+ "marker": "s",
74
+ "s": 10,
75
+ }
76
+ CIRCUIT_EDGE_PLT = {
77
+ "linestyle": "dashed",
78
+ "linewidth": 2,
79
+ "edgecolor": "red",
80
+ "fill": False,
81
+ }
82
+ CIRCUIT_POINT_PLT = {
83
+ "marker": "o",
84
+ "color": "red",
85
+ }
86
+
87
+ MEASURES_D = common.measures_dict(
88
+ ("x", float, "X"),
89
+ ("y", float, "Y"),
90
+ ("theta", float, "yaw angle (rad)"),
91
+ )
92
+
93
+ Deco = namedtuple("Deco", ["marker", "color", "size", "x", "y"])
94
+ Map = namedtuple(
95
+ "Map", ["marker", "file", "ratio", "sizex", "sizey", "offsetx", "offsety"]
96
+ )
97
+
98
+ MapInfo = namedtuple("MapInfo", ["image", "ratio", "offsetx", "offsety", "docks"])
99
+ Dock = namedtuple("Dock", ["x", "y", "theta"])
100
+
101
+ # Selection variables
102
+ _TITLE = "Robot"
103
+ _TRAJ = "traj"
104
+ _ANGLE = "angle"
105
+ _TIME = "time"
106
+
107
+
108
+ def oml_load(filename):
109
+ """Load consumption oml file"""
110
+ data = common.oml_load(filename, "robot_pose", MEASURES_D.values())
111
+ return data
112
+
113
+
114
+ def get_site_map(site):
115
+ """Load infos for site"""
116
+ # Get map config and with cache
117
+ map_cfg = iotlabcli.robot.robot_get_map(site)
118
+ map_info = _mapinfo_from_cfg(map_cfg)
119
+
120
+ return map_info
121
+
122
+
123
+ def _mapinfo_from_cfg(map_cfg):
124
+ """MapInfo object from 'map_cfg' dict"""
125
+
126
+ # Load Image
127
+ image_fd = BytesIO(map_cfg["image"])
128
+ image = Image.open(image_fd).convert("L")
129
+
130
+ # Load Docks
131
+ docks = map_cfg["dock"].values()
132
+ docks = [Dock(d["x"], d["y"], d["theta"]) for d in docks]
133
+
134
+ # Create the whole MapInfo
135
+ cfg = map_cfg["config"]
136
+ map_info = MapInfo(image, cfg["ratio"], cfg["offset"][0], cfg["offset"][1], docks)
137
+ return map_info
138
+
139
+
140
+ def circuit_load(filename):
141
+ """Load robot circuit file
142
+
143
+ :returns: circuit json object
144
+ {
145
+ "coordinates": [
146
+ {
147
+ "name": "0",
148
+ "x": 9.5036359876481,
149
+ "y": -0.8644077467101,
150
+ "z": 0,
151
+ "w": -2.4504422620417
152
+ },
153
+ {
154
+ "name": "1",
155
+ "x": 0.88773354001121,
156
+ "y": 9.750401138047,
157
+ "z": 0,
158
+ "w": 0.46435151843117
159
+ }
160
+ ],
161
+ "points":["0", "1" ]
162
+ }
163
+ """
164
+ # Handle '-' for stdin
165
+ json_file = argparse.FileType("r")(filename) # pylint: disable=deprecated-class
166
+ return json.load(json_file)
167
+
168
+
169
+ PARSER = argparse.ArgumentParser(
170
+ prog="plot_oml_traj", description="Plot iot-lab trajectory oml files"
171
+ )
172
+ PARSER.add_argument(
173
+ "-i", "--input", dest="data", type=oml_load, help="Robot trajectory values"
174
+ )
175
+ PARSER.add_argument(
176
+ "--circuit-file",
177
+ dest="circuit",
178
+ type=circuit_load,
179
+ help="Robot circuit file, '-' for stdin",
180
+ )
181
+ PARSER.add_argument(
182
+ "--site-map", metavar="SITE", dest="mapinfo", type=get_site_map, help="Site map"
183
+ )
184
+
185
+ PARSER.add_argument("-l", "--label", dest="title", default=_TITLE, help="Graph title")
186
+ PARSER.add_argument("-b", "--begin", default=0, type=int, help="Sample start")
187
+ PARSER.add_argument("-e", "--end", default=-1, type=int, help="Sample end")
188
+
189
+ _PLOT = PARSER.add_argument_group("plot", "Plot selection")
190
+ _PLOT.add_argument(
191
+ "-t",
192
+ "--traj",
193
+ dest="plot",
194
+ const=_TRAJ,
195
+ action="append_const",
196
+ help="Plot robot trajectory",
197
+ )
198
+ _PLOT.add_argument(
199
+ "-a",
200
+ "--angle",
201
+ dest="plot",
202
+ const=_ANGLE,
203
+ action="append_const",
204
+ help="Plot robot angle",
205
+ )
206
+ _PLOT.add_argument(
207
+ "-ti",
208
+ "--time",
209
+ dest="plot",
210
+ const=_TIME,
211
+ action="append_const",
212
+ help="Plot time verification",
213
+ )
214
+
215
+
216
+ def trajectory_plot(data, title, mapinfo, circuit, selection):
217
+ """Plot trajectories infos"""
218
+
219
+ plot_data = False
220
+
221
+ if _TRAJ in selection:
222
+ plot_data |= oml_plot_map(data, title, mapinfo, circuit)
223
+
224
+ # Figure angle initialization
225
+ if _ANGLE in selection:
226
+ plot_data |= oml_plot_angle(data, title)
227
+
228
+ # Clock verification
229
+ if _TIME in selection:
230
+ plot_data |= common.oml_plot_clock(data)
231
+
232
+ if plot_data:
233
+ common.plot_show()
234
+ else:
235
+ print("Nothing to plot")
236
+
237
+
238
+ def oml_plot_angle(data, title, xlabel=common.TIMESTAMP_LABEL):
239
+ """Plot data 'angel' field"""
240
+ ylabel = MEASURES_D["theta"].label
241
+ title = f"{title} {'angle'}"
242
+
243
+ plt.figure()
244
+ plt.title(title)
245
+ plt.grid()
246
+ plt.plot(data["timestamp"], data["theta"])
247
+ plt.xlabel(xlabel)
248
+ plt.ylabel(ylabel)
249
+ return True
250
+
251
+
252
+ def _image_extent(mapinfo):
253
+ """Image 'imshow' extent values
254
+ Place image in the robot coordinates"""
255
+ left = mapinfo.offsetx
256
+ right = mapinfo.offsetx + mapinfo.image.size[0] * mapinfo.ratio
257
+
258
+ bottom = mapinfo.offsety
259
+ top = mapinfo.offsety + mapinfo.image.size[1] * mapinfo.ratio
260
+
261
+ return (left, right, bottom, top)
262
+
263
+
264
+ def oml_plot_map(data, title, mapinfo, circuit=None):
265
+ """Plot iot-lab oml data
266
+
267
+ :param data: numpy array with robot trajectory
268
+ :param title: plot title
269
+ :param mapinfo: MapInfo object
270
+ :param circuit: circuit json
271
+ """
272
+
273
+ if not (mapinfo or circuit or not common.array_empty(data)):
274
+ return False # nothing to graph
275
+
276
+ # Figure trajectory initialization
277
+ plt.figure()
278
+ plt.title(title + " trajectory")
279
+ plt.grid()
280
+ plt.axes().set_aspect("equal", "datalim")
281
+
282
+ # Map and dock background
283
+ _plot_mapinfo(mapinfo)
284
+ # Plot theorical circuit
285
+ _plot_circuit(circuit)
286
+ # Plot actual robot trajectory
287
+ _plot_robot_traj(data)
288
+
289
+ return True
290
+
291
+
292
+ def _plot_mapinfo(mapinfo):
293
+ """Plot map and docks background"""
294
+ if mapinfo is None:
295
+ return
296
+
297
+ # Plot map image in background
298
+ extent = _image_extent(mapinfo)
299
+ arr = np.asarray(mapinfo.image)
300
+ plt.imshow(arr, cmap=cm.Greys_r, aspect="equal", extent=extent)
301
+
302
+ # Plot docks
303
+ for dock in mapinfo.docks:
304
+ plt.scatter(dock.x, dock.y, **DOCK_PLT)
305
+
306
+
307
+ def _plot_circuit(circuit):
308
+ """Plot circuit, scaled to map if available"""
309
+ if circuit is None:
310
+ return
311
+
312
+ coords = [circuit["coordinates"][p] for p in circuit["points"]]
313
+
314
+ # Get coordinates
315
+ coords_x = [c["x"] for c in coords]
316
+ coords_y = [c["y"] for c in coords]
317
+ coords = (coords_x, coords_y)
318
+
319
+ # Get edges between checkpoints
320
+ edges = patches.Polygon(list(zip(*coords)), **CIRCUIT_EDGE_PLT)
321
+
322
+ # Plot
323
+ a_x = plt.gcf().add_subplot(111)
324
+ a_x.add_patch(edges)
325
+ plt.plot(*coords, **CIRCUIT_POINT_PLT)
326
+
327
+
328
+ def _plot_robot_traj(robot_traj):
329
+ """Plot robot trajectory"""
330
+ if robot_traj is None:
331
+ return
332
+
333
+ plt.plot(robot_traj["x"], robot_traj["y"])
334
+ plt.xlabel("X (m)")
335
+ plt.ylabel("Y (m)")
336
+
337
+
338
+ def main(args=None): # pylint:disable=too-many-statements
339
+ """Main command"""
340
+ args = args or sys.argv[1:]
341
+
342
+ opts = PARSER.parse_args(args)
343
+ # default to plot traj/map
344
+ selection = opts.plot or ("traj")
345
+ # select samples
346
+ data = opts.data[opts.begin : opts.end] if opts.data is not None else None
347
+
348
+ trajectory_plot(data, opts.title, opts.mapinfo, opts.circuit, selection)
349
+
350
+
351
+ if __name__ == "__main__":
352
+ main()
@@ -0,0 +1,35 @@
1
+ Metadata-Version: 2.4
2
+ Name: oml_plot_tools
3
+ Version: 0.9.1
4
+ Summary: IoT-LAB OML plot tools
5
+ Project-URL: Homepage, http://www.iot-lab.info
6
+ Project-URL: Download URL, http://github.com/iot-lab/oml-plot-tools/
7
+ Author-email: IoT-LAB Team <admin@iot-lab.info>
8
+ License: CeCILL v2.1
9
+ License-File: AUTHORS
10
+ License-File: COPYING
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: End Users/Desktop
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Utilities
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: iotlabcli>=3.3.0
19
+ Requires-Dist: matplotlib
20
+ Requires-Dist: numpy
21
+ Requires-Dist: pillow
22
+ Description-Content-Type: text/x-rst
23
+
24
+ OML Plot Tools
25
+ ==============
26
+
27
+ |CI| |Codecov|
28
+
29
+ .. |CI| image:: https://github.com/iot-lab/oml-plot-tools/workflows/CI/badge.svg
30
+ :target: https://github.com/iot-lab/oml-plot-tools/actions?query=workflow%3ACI+branch%3Amaster
31
+ :alt: CI status
32
+
33
+ .. |Codecov| image:: https://codecov.io/gh/iot-lab/oml-plot-tools/branch/master/graph/badge.svg
34
+ :target: https://codecov.io/gh/iot-lab/oml-plot-tools/branch/master
35
+ :alt: Codecov coverage status
@@ -0,0 +1,35 @@
1
+ oml_plot_tools/__init__.py,sha256=_MvmohqvFPZHN6-JmHucYLn12Whl0M7ljGmO748Wtkk,1003
2
+ oml_plot_tools/common.py,sha256=tY1G_GFFU6OI_dMbabwpqubE4MqfqKvXMdkdv2Vw88U,4782
3
+ oml_plot_tools/consum.py,sha256=aK28D16wHpVhjGGI8RKBrxu1afVISqC8pWAT-WeIWuI,4951
4
+ oml_plot_tools/main.py,sha256=KHKnxNm0HVzjQXnBhQSIpfx32JWrHhkRMFDN6Q7244I,1603
5
+ oml_plot_tools/radio.py,sha256=BQyjIxYDU2bPfvo_ApGIgtkd9NDsbSAFIbEPN4r7kQ8,4876
6
+ oml_plot_tools/traj.py,sha256=yIdJvWLGhWmFewuXUl_H8Vo1Gl0SBeszbj36ezT4-rw,9036
7
+ oml_plot_tools/tests/__init__.py,sha256=d-8OtmP7hkvRyDIj2iM7A1qfPS854yWfAyNdLEjtrZM,948
8
+ oml_plot_tools/tests/common.py,sha256=eU7_Fu9awavwl38MEM3IwjmifdP9C3Xt0_VgMLlUWiM,3975
9
+ oml_plot_tools/tests/common_test.py,sha256=iW90FG6J_JiYrmLR1f64oZBDC03nPBLt0JhEiFhTvcQ,4492
10
+ oml_plot_tools/tests/consum_test.py,sha256=5LmEuVMxQevpjC9yBrl86D5cD0wuRXtWbyzR50nQWG0,7589
11
+ oml_plot_tools/tests/main_parser_test.py,sha256=LsSV2OdNqOXuWkKNNOp16Hk9yv_Yy1JyvH3f7N8sKAI,1803
12
+ oml_plot_tools/tests/radio_test.py,sha256=miVhEqt-uXSB0FIR5W0EDF_o5SMscQbm0GU7A7PhbEg,6597
13
+ oml_plot_tools/tests/traj_test.py,sha256=Y6wvsDYjzWNwqPUYPbZxTyLRzmPSrq8IZcngHbxrJ4Q,8594
14
+ oml_plot_tools/tests/examples/consumption.oml,sha256=rZ4hM_WaMnMpJNwfNfBKzDDDaCYsWutA7c1N-Td7h2I,248277
15
+ oml_plot_tools/tests/examples/consumption_all.png,sha256=2ns8tv4RaRbq4Bz1SxNrZOC3iejsgZzHrQnR-WeUg4c,43920
16
+ oml_plot_tools/tests/examples/consumption_clock.png,sha256=pWb1S8-2PB0QEoj3Pjehv7OC1sfGKT5Ou9L6XSqIJi4,28793
17
+ oml_plot_tools/tests/examples/consumption_current.png,sha256=9tnfK5lS0tk79wKQf31mnzaa_n-XlmPaK8eHDDp1GAk,27865
18
+ oml_plot_tools/tests/examples/consumption_only_one.oml,sha256=XOL9TX4nVy50VsdgNZkoNreonO8R2epFpevYFPG71eM,364
19
+ oml_plot_tools/tests/examples/grenoble-iotlab.png,sha256=R1WQssTj8EXeRk5T9FD6jLARc0EikG4xl4Ot-XPBF_o,9596
20
+ oml_plot_tools/tests/examples/radio.oml,sha256=zTOk7A0YcP1JVJswD6Qfp4mj0YQNl5iibuD0x9V7bAs,80951
21
+ oml_plot_tools/tests/examples/radio_clock.png,sha256=QAw3t5jc5X7bFqubh1VH9rnJDG4yQZ-4H65LdIA66JM,25781
22
+ oml_plot_tools/tests/examples/radio_separated_last.png,sha256=NncQ_X43QFtt7ajprYCz8G1CfjkcOS1v8cCSIuhPt4Q,19017
23
+ oml_plot_tools/tests/examples/radio_single.png,sha256=_bjukE2tLNnjyWTJPGzSHMUfDvhiSveBuWBBHJdZYjo,59418
24
+ oml_plot_tools/tests/examples/robot.oml,sha256=tjUA_PNgf3iLR2BebeRP0nDaNKDeE2C2Jez1LVZvXHA,93544
25
+ oml_plot_tools/tests/examples/trajectory_all.png,sha256=limJq98lElgcfeer9K1tb6noBWbH3kJ2XlF9PmDOI3Y,19495
26
+ oml_plot_tools/tests/examples/trajectory_angle.png,sha256=4m_jzm6BkqZ2ap9CoRK7ntOwgYLmuiK1-t22yX_sAGU,30137
27
+ oml_plot_tools/tests/examples/trajectory_circuit.png,sha256=gZ7wbo0-SwQOSJ_BpnjIdE4ckz9LNEJ4TBtw8spHmMg,14779
28
+ oml_plot_tools/tests/examples/trajectory_clock.png,sha256=J1R6AeBRQM-Y2ik0E5GlOIRrh61wZ4qOe5zyexlzvpY,44326
29
+ oml_plot_tools/tests/examples/trajectory_only.png,sha256=FlzmBAbp6T2qBnh254wevzf1hqdSVtSnGiAe-QoTL8w,17975
30
+ oml_plot_tools-0.9.1.dist-info/METADATA,sha256=WW5GtN1PNFEsKcsrkaYU94sSsioUxHJjqzJZ554qBFY,1182
31
+ oml_plot_tools-0.9.1.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
32
+ oml_plot_tools-0.9.1.dist-info/entry_points.txt,sha256=PvzrQk6XXTAIkZ4v1XXtrAkxIyC60UN5DssFy9tx3ns,186
33
+ oml_plot_tools-0.9.1.dist-info/licenses/AUTHORS,sha256=WhV6rxBdD-ADQ0anXf0Uxe3ivK0p4fXUPkxnbVHNdIY,23
34
+ oml_plot_tools-0.9.1.dist-info/licenses/COPYING,sha256=SDQAoBFRVsYzgFV4tTa0x7aerPr20RNeckxctfxqWXI,21774
35
+ oml_plot_tools-0.9.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.31.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ iotlab-plot = oml_plot_tools.main:main
3
+ plot_oml_consum = oml_plot_tools.consum:main
4
+ plot_oml_radio = oml_plot_tools.radio:main
5
+ plot_oml_traj = oml_plot_tools.traj:main
@@ -0,0 +1 @@
1
+ roger.pissard@inria.fr