1
0
mirror of https://github.com/ARM-software/devlib.git synced 2025-01-31 02:00:45 +00:00

instrument/daq: Add an explicit time column to the DAQ measurements

Add the monotonic clock time to the energy measurements to help
correlate the measurement with those of other collectors, like
FtraceCollector or LogcatCollector.
This commit is contained in:
Javi Merino 2020-02-11 18:18:22 +00:00 committed by Marc Bonnici
parent 72ded188fa
commit 92e16ee873
5 changed files with 67 additions and 5 deletions

Binary file not shown.

Binary file not shown.

View File

@ -16,8 +16,10 @@
import os import os
import shutil import shutil
import tempfile import tempfile
import time
from itertools import chain, zip_longest from itertools import chain, zip_longest
from devlib.host import PACKAGE_BIN_DIRECTORY
from devlib.instrument import Instrument, MeasurementsCsv, CONTINUOUS from devlib.instrument import Instrument, MeasurementsCsv, CONTINUOUS
from devlib.exception import HostError from devlib.exception import HostError
from devlib.utils.csvutil import csvwriter, create_reader from devlib.utils.csvutil import csvwriter, create_reader
@ -45,7 +47,8 @@ class DaqInstrument(Instrument):
dv_range=0.2, dv_range=0.2,
sample_rate_hz=10000, sample_rate_hz=10000,
channel_map=(0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23), channel_map=(0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23),
keep_raw=False keep_raw=False,
time_as_clock_monotonic=True
): ):
# pylint: disable=no-member # pylint: disable=no-member
super(DaqInstrument, self).__init__(target) super(DaqInstrument, self).__init__(target)
@ -53,6 +56,7 @@ class DaqInstrument(Instrument):
self._need_reset = True self._need_reset = True
self._raw_files = [] self._raw_files = []
self.tempdir = None self.tempdir = None
self.target_monotonic_clock_at_start = 0.0
if DaqClient is None: if DaqClient is None:
raise HostError('Could not import "daqpower": {}'.format(import_error_mesg)) raise HostError('Could not import "daqpower": {}'.format(import_error_mesg))
if labels is None: if labels is None:
@ -76,11 +80,30 @@ class DaqInstrument(Instrument):
channel_map=channel_map, channel_map=channel_map,
labels=labels) labels=labels)
self.sample_rate_hz = sample_rate_hz self.sample_rate_hz = sample_rate_hz
self.time_as_clock_monotonic = time_as_clock_monotonic
self.add_channel('Time', 'time')
for label in labels: for label in labels:
for kind in ['power', 'voltage']: for kind in ['power', 'voltage']:
self.add_channel(label, kind) self.add_channel(label, kind)
if time_as_clock_monotonic:
host_path = os.path.join(PACKAGE_BIN_DIRECTORY, self.target.abi,
'get_clock_monotonic')
self.clock_monotonic_cmd = self.target.install_if_needed(host_path,
search_system_binaries=False)
def calculate_monotonic_offset(self):
time_before = time.time()
out = self.target.execute(self.clock_monotonic_cmd)
time_after = time.time()
remote_clock_monotonic = float(out)
propagation_delay = (time_after - time_before) / 2
monotonic_at_end = remote_clock_monotonic + propagation_delay
return time_after - monotonic_at_end
def reset(self, sites=None, kinds=None, channels=None): def reset(self, sites=None, kinds=None, channels=None):
super(DaqInstrument, self).reset(sites, kinds, channels) super(DaqInstrument, self).reset(sites, kinds, channels)
self.daq_client.close() self.daq_client.close()
@ -90,9 +113,19 @@ class DaqInstrument(Instrument):
def start(self): def start(self):
if self._need_reset: if self._need_reset:
self.reset() # Preserve channel order
self.reset(channels=self.channels.keys())
if self.time_as_clock_monotonic:
target_monotonic_offset = self.calculate_monotonic_offset()
time_start = time.time()
self.daq_client.start() self.daq_client.start()
if self.time_as_clock_monotonic:
time_end = time.time()
self.target_monotonic_clock_at_start = (time_start + time_end) / 2 - target_monotonic_offset
def stop(self): def stop(self):
self.daq_client.stop() self.daq_client.stop()
self._need_reset = True self._need_reset = True
@ -118,11 +151,12 @@ class DaqInstrument(Instrument):
site_readers[site] = reader site_readers[site] = reader
file_handles.append(fh) file_handles.append(fh)
except KeyError: except KeyError:
message = 'Could not get DAQ trace for {}; Obtained traces are in {}' if not site.startswith("Time"):
raise HostError(message.format(site, self.tempdir)) message = 'Could not get DAQ trace for {}; Obtained traces are in {}'
raise HostError(message.format(site, self.tempdir))
# The first row is the headers # The first row is the headers
channel_order = [] channel_order = ['Time_time']
for site, reader in site_readers.items(): for site, reader in site_readers.items():
channel_order.extend(['{}_{}'.format(site, kind) channel_order.extend(['{}_{}'.format(site, kind)
for kind in next(reader)]) for kind in next(reader)])
@ -131,7 +165,11 @@ class DaqInstrument(Instrument):
row_iter = zip_longest(*site_readers.values(), fillvalue=(None, None)) row_iter = zip_longest(*site_readers.values(), fillvalue=(None, None))
for raw_row in row_iter: for raw_row in row_iter:
raw_row = list(chain.from_iterable(raw_row)) raw_row = list(chain.from_iterable(raw_row))
raw_row.insert(0, _read_rows.row_time_s)
yield raw_row yield raw_row
_read_rows.row_time_s += 1.0 / self.sample_rate_hz
_read_rows.row_time_s = self.target_monotonic_clock_at_start
with csvwriter(outfile) as writer: with csvwriter(outfile) as writer:
field_names = [c.label for c in self.active_channels] field_names = [c.label for c in self.active_channels]

View File

@ -0,0 +1,6 @@
CFLAGS=-Wall --pedantic-errors -O2 -static
all: get_clock_monotonic
get_clock_monotonic: get_clock_monotonic.c
$(CC) $(CFLAGS) $^ -o $@

View File

@ -0,0 +1,18 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void) {
int ret;
struct timespec tp;
ret = clock_gettime(CLOCK_MONOTONIC, &tp);
if (ret) {
perror("clock_gettime()");
return EXIT_FAILURE;
}
printf("%ld.%ld\n", tp.tv_sec, tp.tv_nsec);
return EXIT_SUCCESS;
}