• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3#   Copyright 2020 Google, Inc.
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import os
18import logging
19import numpy
20import math
21
22from bokeh.layouts import layout
23from bokeh.models import CustomJS, ColumnDataSource
24from bokeh.models import tools as bokeh_tools
25from bokeh.models.widgets import DataTable, TableColumn
26from bokeh.plotting import figure, output_file, save
27
28
29def current_waveform_plot(samples, voltage, dest_path, plot_title):
30    """Plot the current data using bokeh interactive plotting tool.
31
32    Plotting power measurement data with bokeh to generate interactive plots.
33    You can do interactive data analysis on the plot after generating with the
34    provided widgets, which make the debugging much easier. To realize that,
35    bokeh callback java scripting is used.
36
37    Args:
38        samples: a list of tuples in which the first element is a timestamp and
39          the second element is the sampled current in milli amps at that time.
40        voltage: the voltage that was used during the measurement.
41        dest_path: destination path.
42        plot_title: a filename and title for the plot.
43    Returns:
44        plot: the plotting object of bokeh, optional, will be needed if multiple
45           plots will be combined to one html file.
46        dt: the datatable object of bokeh, optional, will be needed if multiple
47           datatables will be combined to one html file.
48    """
49    logging.info('Plotting the power measurement data.')
50
51    time_relative = [sample[0] for sample in samples]
52    duration = time_relative[-1] - time_relative[0]
53    current_data = [sample[1] * 1000 for sample in samples]
54    avg_current = sum(current_data) / len(current_data)
55
56    color = ['navy'] * len(samples)
57
58    # Preparing the data and source link for bokehn java callback
59    source = ColumnDataSource(
60        data=dict(x=time_relative, y=current_data, color=color))
61    s2 = ColumnDataSource(
62        data=dict(a=[duration],
63                  b=[round(avg_current, 2)],
64                  c=[round(avg_current * voltage, 2)],
65                  d=[round(avg_current * voltage * duration, 2)],
66                  e=[round(avg_current * duration, 2)]))
67    # Setting up data table for the output
68    columns = [
69        TableColumn(field='a', title='Total Duration (s)'),
70        TableColumn(field='b', title='Average Current (mA)'),
71        TableColumn(field='c', title='Average Power (4.2v) (mW)'),
72        TableColumn(field='d', title='Average Energy (mW*s)'),
73        TableColumn(field='e', title='Normalized Average Energy (mA*s)')
74    ]
75    dt = DataTable(source=s2,
76                   columns=columns,
77                   width=1300,
78                   height=60,
79                   editable=True)
80
81    output_file(os.path.join(dest_path, plot_title + '.html'))
82    tools = 'box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save'
83    # Create a new plot with the datatable above
84    plot = figure(plot_width=1300,
85                  plot_height=700,
86                  title=plot_title,
87                  tools=tools)
88    plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='width'))
89    plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='height'))
90    plot.line('x', 'y', source=source, line_width=2)
91    plot.circle('x', 'y', source=source, size=0.5, fill_color='color')
92    plot.xaxis.axis_label = 'Time (s)'
93    plot.yaxis.axis_label = 'Current (mA)'
94
95    # Callback JavaScript
96    source.selected.js_on_change(
97        "indices",
98        CustomJS(args=dict(source=source, mytable=dt),
99                 code="""
100        const inds = source.selected.indices;
101        const d1 = source.data;
102        const d2 = mytable.source.data;
103        var ym = 0
104        var ts = 0
105        var min=d1['x'][inds[0]]
106        var max=d1['x'][inds[0]]
107        d2['a'] = []
108        d2['b'] = []
109        d2['c'] = []
110        d2['d'] = []
111        d2['e'] = []
112        if (inds.length==0) {return;}
113        for (var i = 0; i < inds.length; i++) {
114        ym += d1['y'][inds[i]]
115        d1['color'][inds[i]] = "red"
116        if (d1['x'][inds[i]] < min) {
117          min = d1['x'][inds[i]]}
118        if (d1['x'][inds[i]] > max) {
119          max = d1['x'][inds[i]]}
120        }
121        ym /= inds.length
122        ts = max - min
123        d2['a'].push(Math.round(ts*1000.0)/1000.0)
124        d2['b'].push(Math.round(ym*100.0)/100.0)
125        d2['c'].push(Math.round(ym*4.2*100.0)/100.0)
126        d2['d'].push(Math.round(ym*4.2*ts*100.0)/100.0)
127        d2['e'].push(Math.round(ym*ts*100.0)/100.0)
128        source.change.emit();
129        mytable.change.emit();
130    """))
131
132    # Layout the plot and the datatable bar
133    save(layout([[dt], [plot]]))
134    return plot, dt
135
136
137def monsoon_histogram_plot(samples, dest_path, plot_title):
138    """ Creates a histogram from a monsoon result object.
139
140    Args:
141        samples: a list of tuples in which the first element is a timestamp and
142          the second element is the sampled current in milli amps at that time.
143        dest_path: destination path
144        plot_title: a filename and title for the plot.
145    Returns:
146        a tuple of arrays containing the values of the histogram and the
147        bin edges.
148    """
149    milli_amps = [sample[1] * 1000 for sample in samples]
150    hist, edges = numpy.histogram(milli_amps,
151                                  bins=math.ceil(max(milli_amps)),
152                                  range=(0, max(milli_amps)))
153
154    output_file(os.path.join(dest_path, plot_title + '.html'))
155
156    plot = figure(title=plot_title,
157                  y_axis_type='log',
158                  background_fill_color='#fafafa')
159
160    plot.quad(top=hist,
161              bottom=0,
162              left=edges[:-1],
163              right=edges[1:],
164              fill_color='navy')
165
166    plot.y_range.start = 0
167    plot.xaxis.axis_label = 'Instantaneous current [mA]'
168    plot.yaxis.axis_label = 'Count'
169    plot.grid.grid_line_color = 'white'
170
171    save(plot)
172
173    return hist, edges
174
175
176def monsoon_tx_power_sweep_plot(dest_path, plot_title, currents, txs):
177    """ Creates average current vs tx power plot
178
179    Args:
180        dest_path: destination path
181        plot_title: a filename and title for the plot.
182        currents: List of average currents measured during power sweep
183        txs: List of uplink input power levels specified for each measurement
184    """
185
186    output_file(os.path.join(dest_path, plot_title + '.html'))
187
188    plot = figure(title=plot_title,
189                  y_axis_label='Average Current [mA]',
190                  x_axis_label='Tx Power [dBm]',
191                  background_fill_color='#fafafa')
192
193    plot.line(txs, currents)
194    plot.circle(txs, currents, fill_color='white', size=8)
195    plot.y_range.start = 0
196
197    save(plot)
198