blob: 37b9e56d46a50460d13c18e924953663aed25722 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2019 - The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import time
import os
import acts_contrib.test_utils.power.PowerBaseTest as PBT
from acts import base_test
from acts.controllers import monsoon
from bokeh.layouts import column, layout
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.models import tools as bokeh_tools
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.plotting import figure, output_file, save
from acts.controllers.monsoon_lib.api.common import PassthroughStates
from acts.controllers.monsoon_lib.api.common import MonsoonError
LOGTIME_RETRY_COUNT = 3
RESET_BATTERY_STATS = 'dumpsys batterystats --reset'
RECOVER_MONSOON_RETRY_COUNT = 3
MONSOON_RETRY_INTERVAL = 300
class PowerGnssBaseTest(PBT.PowerBaseTest):
"""
Base Class for all GNSS Power related tests
"""
def setup_class(self):
super().setup_class()
req_params = ['customjsfile', 'maskfile', 'dpooff_nv_dict',
'dpoon_nv_dict', 'mdsapp', 'modemparfile']
self.unpack_userparams(req_params)
def collect_power_data(self):
"""Measure power and plot."""
samples = super().collect_power_data()
plot_title = '{}_{}_{}_Power'.format(self.test_name, self.dut.model,
self.dut.build_info['build_id'])
self.monsoon_data_plot_power(samples, self.mon_voltage,
self.mon_info.data_path, plot_title)
return samples
def monsoon_data_plot_power(self, samples, voltage, dest_path, plot_title):
"""Plot the monsoon power data using bokeh interactive plotting tool.
Args:
samples: a list of tuples in which the first element is a timestamp
and the second element is the sampled current at that time
voltage: the voltage that was used during the measurement
dest_path: destination path
plot_title: a filename and title for the plot.
"""
logging.info('Plotting the power measurement data.')
time_relative = [sample[0] for sample in samples]
duration = time_relative[-1] - time_relative[0]
current_data = [sample[1] * 1000 for sample in samples]
avg_current = sum(current_data) / len(current_data)
power_data = [current * voltage for current in current_data]
color = ['navy'] * len(samples)
# Preparing the data and source link for bokehn java callback
source = ColumnDataSource(
data=dict(x0=time_relative, y0=power_data, color=color))
s2 = ColumnDataSource(
data=dict(
z0=[duration],
y0=[round(avg_current, 2)],
x0=[round(avg_current * voltage, 2)],
z1=[round(avg_current * voltage * duration, 2)],
z2=[round(avg_current * duration, 2)]))
# Setting up data table for the output
columns = [
TableColumn(field='z0', title='Total Duration (s)'),
TableColumn(field='y0', title='Average Current (mA)'),
TableColumn(field='x0', title='Average Power (4.2v) (mW)'),
TableColumn(field='z1', title='Average Energy (mW*s)'),
TableColumn(field='z2', title='Normalized Average Energy (mA*s)')
]
dt = DataTable(
source=s2, columns=columns, width=1300, height=60, editable=True)
output_file(os.path.join(dest_path, plot_title + '.html'))
tools = 'box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save'
# Create a new plot with the datatable above
plot = figure(
plot_width=1300,
plot_height=700,
title=plot_title,
tools=tools,
output_backend='webgl')
plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='width'))
plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='height'))
plot.line('x0', 'y0', source=source, line_width=2)
plot.circle('x0', 'y0', source=source, size=0.5, fill_color='color')
plot.xaxis.axis_label = 'Time (s)'
plot.yaxis.axis_label = 'Power (mW)'
plot.title.text_font_size = {'value': '15pt'}
jsscript = open(self.customjsfile, 'r')
customjsscript = jsscript.read()
# Callback Java scripting
source.callback = CustomJS(
args=dict(mytable=dt),
code=customjsscript)
# Layout the plot and the datatable bar
save(layout([[dt], [plot]]))
def disconnect_usb(self, ad, sleeptime):
"""Disconnect usb while device is on sleep and
connect the usb again once the sleep time completes
sleeptime: sleep time where dut is disconnected from usb
"""
self.dut.adb.shell(RESET_BATTERY_STATS)
time.sleep(1)
for _ in range(LOGTIME_RETRY_COUNT):
self.monsoons[0].usb(PassthroughStates.OFF)
if not ad.is_connected():
time.sleep(sleeptime)
self.monsoons[0].usb(PassthroughStates.ON)
break
else:
self.log.error('Test failed after maximum retry')
for _ in range(RECOVER_MONSOON_RETRY_COUNT):
if self.monsoon_recover():
break
else:
self.log.warning(
'Wait for {} second then try again'.format(
MONSOON_RETRY_INTERVAL))
time.sleep(MONSOON_RETRY_INTERVAL)
else:
self.log.error('Failed to recover monsoon')
def monsoon_recover(self):
"""Test loop to wait for monsoon recover from unexpected error.
Wait for a certain time duration, then quit.0
Args:
mon: monsoon object
Returns:
True/False
"""
try:
self.power_monitor.connect_usb()
logging.info('Monsoon recovered from unexpected error')
time.sleep(2)
return True
except MonsoonError:
try:
self.log.info(self.monsoons[0]._mon.ser.in_waiting)
except AttributeError:
# This attribute does not exist for HVPMs.
pass
logging.warning('Unable to recover monsoon from unexpected error')
return False