• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3#   Copyright 2019 - The Android Open Source Project
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
17from acts.signals import ControllerError
18
19
20class MonsoonError(ControllerError):
21    """Raised for exceptions encountered when interfacing with a Monsoon device.
22    """
23
24
25class PassthroughStates(object):
26    """An enum containing the values for power monitor's passthrough states."""
27    # "Off" or 0 means USB always off.
28    OFF = 0
29    # "On" or 1 means USB always on.
30    ON = 1
31    # "Auto" or 2 means USB is automatically turned off during sampling, and
32    # turned back on after sampling.
33    AUTO = 2
34
35
36PASSTHROUGH_STATES = {
37    'off': PassthroughStates.OFF,
38    'on': PassthroughStates.ON,
39    'auto': PassthroughStates.AUTO
40}
41
42
43class MonsoonDataRecord(object):
44    """A data class for Monsoon data points."""
45    def __init__(self, sample_time, relative_time, current):
46        """Creates a new MonsoonDataRecord.
47
48        Args:
49            sample_time: the unix timestamp of the sample.
50            relative_time: the time since the start of the measurement.
51            current: The current in Amperes as a string.
52        """
53        self._sample_time = sample_time
54        self._relative_time = relative_time
55        self._current = current
56
57    @property
58    def time(self):
59        """The time the record was fetched."""
60        return self._sample_time
61
62    @property
63    def relative_time(self):
64        """The time the record was fetched, relative to collection start."""
65        return self._relative_time
66
67    @property
68    def current(self):
69        """The amount of current in Amperes measured for the given record."""
70        return self._current
71
72
73class MonsoonResult(object):
74    """An object that contains aggregated data collected during sampling.
75
76    Attributes:
77        _num_samples: The number of samples gathered.
78        _sum_currents: The total sum of all current values gathered, in amperes.
79        _hz: The frequency sampling is being done at.
80        _voltage: The voltage output during sampling.
81    """
82
83    # The number of decimal places to round a value to.
84    ROUND_TO = 6
85
86    def __init__(self, num_samples, sum_currents, hz, voltage, datafile_path):
87        """Creates a new MonsoonResult.
88
89        Args:
90            num_samples: the number of samples collected.
91            sum_currents: the total summation of every current measurement.
92            hz: the number of samples per second.
93            voltage: the voltage used during the test.
94            datafile_path: the path to the monsoon data file.
95        """
96        self._num_samples = num_samples
97        self._sum_currents = sum_currents
98        self._hz = hz
99        self._voltage = voltage
100        self.tag = datafile_path
101
102    def get_data_points(self):
103        """Returns an iterator of MonsoonDataRecords."""
104        class MonsoonDataIterator:
105            def __init__(self, file):
106                self.file = file
107
108            def __iter__(self):
109                with open(self.file, 'r') as f:
110                    start_time = None
111                    for line in f:
112                        # Remove the newline character.
113                        line.strip()
114                        sample_time, current = map(float, line.split(' '))
115                        if start_time is None:
116                            start_time = sample_time
117                        yield MonsoonDataRecord(sample_time,
118                                                sample_time - start_time,
119                                                current)
120
121        return MonsoonDataIterator(self.tag)
122
123    @property
124    def num_samples(self):
125        """The number of samples recorded during the test."""
126        return self._num_samples
127
128    @property
129    def average_current(self):
130        """Average current in mA."""
131        if self.num_samples == 0:
132            return 0
133        return round(self._sum_currents * 1000 / self.num_samples,
134                     self.ROUND_TO)
135
136    @property
137    def total_charge(self):
138        """Total charged used in the unit of mAh."""
139        return round((self._sum_currents / self._hz) * 1000 / 3600,
140                     self.ROUND_TO)
141
142    @property
143    def total_power(self):
144        """Total power used."""
145        return round(self.average_current * self._voltage, self.ROUND_TO)
146
147    @property
148    def voltage(self):
149        """The voltage during the measurement (in Volts)."""
150        return self._voltage
151
152    def __str__(self):
153        return ('avg current: %s\n'
154                'total charge: %s\n'
155                'total power: %s\n'
156                'total samples: %s' % (self.average_current, self.total_charge,
157                                      self.total_power, self._num_samples))
158