/tools/telemetry/telemetry/internal/platform/profiler/monsoon_profiler.py

https://gitlab.com/jonnialva90/iridium-browser · Python · 97 lines · 61 code · 16 blank · 20 comment · 10 complexity · b79afda909de7d766036070790c2b4bc MD5 · raw file

  1. # Copyright 2013 The Chromium Authors. All rights reserved.
  2. # Use of this source code is governed by a BSD-style license that can be
  3. # found in the LICENSE file.
  4. """Profiler using data collected from a Monsoon power meter.
  5. http://msoon.com/LabEquipment/PowerMonitor/
  6. Data collected is a namedtuple of (amps, volts), at 5000 samples/second.
  7. Output graph plots power in watts over time in seconds.
  8. """
  9. import csv
  10. import multiprocessing
  11. from telemetry.core import exceptions
  12. from telemetry.internal.platform import profiler
  13. from telemetry.internal.platform.profiler import monsoon
  14. from telemetry.util import statistics
  15. def _CollectData(output_path, is_collecting):
  16. mon = monsoon.Monsoon(wait=False)
  17. # Note: Telemetry requires the device to be connected by USB, but that
  18. # puts it in charging mode. This increases the power consumption.
  19. mon.SetUsbPassthrough(1)
  20. # Nominal Li-ion voltage is 3.7V, but it puts out 4.2V at max capacity. Use
  21. # 4.0V to simulate a "~80%" charged battery. Google "li-ion voltage curve".
  22. # This is true only for a single cell. (Most smartphones, some tablets.)
  23. mon.SetVoltage(4.0)
  24. samples = []
  25. try:
  26. mon.StartDataCollection()
  27. # Do one CollectData() to make the Monsoon set up, which takes about
  28. # 0.3 seconds, and only signal that we've started after that.
  29. mon.CollectData()
  30. is_collecting.set()
  31. while is_collecting.is_set():
  32. samples += mon.CollectData()
  33. finally:
  34. mon.StopDataCollection()
  35. # Add x-axis labels.
  36. plot_data = [(i / 5000., sample.amps * sample.volts)
  37. for i, sample in enumerate(samples)]
  38. # Print data in csv.
  39. with open(output_path, 'w') as output_file:
  40. output_writer = csv.writer(output_file)
  41. output_writer.writerows(plot_data)
  42. output_file.flush()
  43. power_samples = [s.amps * s.volts for s in samples]
  44. print 'Monsoon profile power readings in watts:'
  45. print ' Total = %f' % statistics.TrapezoidalRule(power_samples, 1/5000.)
  46. print (' Average = %f' % statistics.ArithmeticMean(power_samples) +
  47. '+-%f' % statistics.StandardDeviation(power_samples))
  48. print ' Peak = %f' % max(power_samples)
  49. print ' Duration = %f' % (len(power_samples) / 5000.)
  50. print 'To view the Monsoon profile, run:'
  51. print (' echo "set datafile separator \',\'; plot \'%s\' with lines" | '
  52. 'gnuplot --persist' % output_path)
  53. class MonsoonProfiler(profiler.Profiler):
  54. def __init__(self, browser_backend, platform_backend, output_path, state):
  55. super(MonsoonProfiler, self).__init__(
  56. browser_backend, platform_backend, output_path, state)
  57. # We collect the data in a separate process, so we can continuously
  58. # read the samples from the USB port while running the test.
  59. self._is_collecting = multiprocessing.Event()
  60. self._collector = multiprocessing.Process(
  61. target=_CollectData, args=(output_path, self._is_collecting))
  62. self._collector.start()
  63. if not self._is_collecting.wait(timeout=0.5):
  64. self._collector.terminate()
  65. raise exceptions.ProfilingException('Failed to start data collection.')
  66. @classmethod
  67. def name(cls):
  68. return 'monsoon'
  69. @classmethod
  70. def is_supported(cls, browser_type):
  71. try:
  72. monsoon.Monsoon(wait=False)
  73. except EnvironmentError:
  74. return False
  75. else:
  76. return True
  77. def CollectProfile(self):
  78. self._is_collecting.clear()
  79. self._collector.join()
  80. return [self._output_path]