Skip to content

Commit

Permalink
Extend plotter
Browse files Browse the repository at this point in the history
  • Loading branch information
nocturnalastro authored and jnunyez committed Oct 5, 2023
1 parent 6f65d7e commit 178d6fd
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 36 deletions.
44 changes: 26 additions & 18 deletions src/vse_sync_pp/analyzers/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,21 +379,28 @@ def _test_common(self, data):
return (False, "short test duration")
if len(data) - 1 < self._duration_min:
return (False, "short test samples")
if self._rate is None:
self._rate = self.calculate_rate(data)
if self._lpf_signal is None:
self._lpf_signal = calculate_filter(data, self._transient, self._rate)
return None

def _explain_common(self, data):
if len(data) == 0:
return {}
if self._rate is None:
self._rate = self.calculate_rate(data)
self._rate = self.calculate_rate(self._data)
if self._lpf_signal is None:
self._lpf_signal = calculate_filter(data, self._transient, self._rate)
return None

def toplot(self):
self.close()
self._generate_taus()
yield from zip(self._taus, self._samples)

def _generate_taus(self):
if self._rate is None:
self._rate = self.calculate_rate(self._data)
if self._lpf_signal is None:
self._lpf_signal = calculate_filter(self._data, self._transient, self._rate)
return None

class TimeDeviationAnalyzerBase(TimeIntervalErrorAnalyzerBase):
"""Analyze Time Deviation (TDEV).
Expand All @@ -412,12 +419,16 @@ def __init__(self, config):
self._taus = None
# TDEV samples
self._samples = None

def _generate_taus(self):
super()._generate_taus()
if self._samples is None:
self._taus, self._samples, errors, ns = allantools.tdev(self._lpf_signal, rate=self._rate, data_type="phase", taus=self._taus_list) # noqa

def test(self, data):
result = self._test_common(data)
if result is None:
if self._samples is None:
self._taus, self._samples, errors, ns = allantools.tdev(self._lpf_signal, rate=self._rate, data_type="phase", taus=self._taus_list) # noqa
self._generate_taus()
if out_of_range(self._taus, self._samples, self._accuracy, self._limit):
return (False, "unacceptable time deviation")
return (True, None)
Expand All @@ -426,14 +437,11 @@ def test(self, data):
def explain(self, data):
analysis = self._explain_common(data)
if analysis is None:
if self._samples is None:
self._taus, self._samples, errors, ns = allantools.tdev(self._lpf_signal, rate=self._rate, data_type="phase", taus=self._taus_list) # noqa
self._generate_taus()
return {
'timestamp': self._timestamp_from_dec(data.iloc[0].timestamp),
'duration': data.iloc[-1].timestamp - data.iloc[0].timestamp,
'tdev': self._statistics(self._samples, 'ns'),
'tdev_taus': self._taus.tolist(),
'tdev_samples': self._samples.tolist(),
}
return analysis

Expand All @@ -456,26 +464,26 @@ def __init__(self, config):
# MTIE samples
self._samples = None

def _generate_taus(self):
super()._generate_taus()
if self._samples is None:
self._taus, self._samples, errors, ns = allantools.mtie(self._lpf_signal, rate=self._rate, data_type="phase", taus=self._taus_list) # noqa

def test(self, data):
result = self._test_common(data)
if result is None:
if self._samples is None:
self._taus, self._samples, errors, ns = allantools.mtie(self._lpf_signal, rate=self._rate, data_type="phase", taus=self._taus_list) # noqa
self._generate_taus()
if out_of_range(self._taus, self._samples, self._accuracy, self._limit):
return (False, "unacceptable mtie")
return (True, None)
return result

def explain(self, data):
analysis = self._explain_common(data)
if analysis is None:
if self._samples is None:
self._taus, self._samples, errors, ns = allantools.mtie(self._lpf_signal, rate=self._rate, data_type="phase", taus=self._taus_list) # noqa
self._generate_taus()
return {
'timestamp': self._timestamp_from_dec(data.iloc[0].timestamp),
'duration': data.iloc[-1].timestamp - data.iloc[0].timestamp,
'mtie': self._statistics(self._samples, 'ns'),
'mtie_taus': self._taus.tolist(),
'mtie_samples': self._samples.tolist(),
}
return analysis
47 changes: 29 additions & 18 deletions src/vse_sync_pp/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,62 @@

import numpy as np
import matplotlib.pyplot as plt
from collections import namedtuple

from .common import open_input

from .parsers import PARSERS


Axis = namedtuple("Axis", ["desc", "attr", "scale", "scale_kwargs"], defaults=[None, None, None, None])
TIMESERIES = Axis("Time", "timestamp")


class Plotter():
"""Rudimentary plotter of data values against timestamp"""
def __init__(self, y_name, y_desc=None):
self._x_name = 'timestamp'
self._y_name = y_name
if y_desc is None:
self._y_desc = y_name
else:
self._y_desc = y_desc
def __init__(self, x, y):
self._x = x
self._y = y
self._x_data = []
self._y_data = []

@staticmethod
def _extract_attr(axis, data):
return getattr(data, axis.attr)

def _set_yscale(self, ax):
if self._y.scale is not None:
ax.set_yscale(self._y.scale, **(self._y.scale_kwargs or {}))
elif any((abs(v) > 10 for v in self._y_data)):
ax.set_yscale("symlog", linthresh=10)

def append(self, data):
"""Append x and y data points extracted from `data`"""
x_val = getattr(data, self._x_name)
self._x_data.append(x_val)
y_val = getattr(data, self._y_name)
self._y_data.append(y_val)
self._x_data.append(self._extract_attr(self._x, data))
self._y_data.append(self._extract_attr(self._y, data))

def plot(self, filename):
"""Plot data to `filename`"""
fig, (ax1, ax2) = plt.subplots(2, constrained_layout=True)
fig.set_size_inches(10, 8)
ax1.axhline(0, color='black')
if any((abs(v) > 10 for v in self._y_data)):
ax1.set_yscale('symlog', linthresh=10)
self._set_yscale(ax1)
if self._x.scale is not None:
ax1.set_xscale(self._x.scale, **(self._x.scale_kwargs or {}))
ax1.plot(self._x_data, self._y_data, '.')
ax1.grid()
ax1.set_title(f'{self._x_name} vs {self._y_desc}')
ax1.set_title(f'{self._x.desc} vs {self._y.desc}')
counts, bins = np.histogram(
np.array(self._y_data, dtype=float),
bins='scott',
)
ax2.hist(bins[:-1], bins, weights=counts)
ax2.set_yscale('symlog', linthresh=10)
ax2.set_title(f'Histogram of {self._y_desc}')
self._set_yscale(ax2)
if self._x.scale is not None:
ax2.set_xscale(self._x.scale, **(self._x.scale_kwargs or {}))
ax2.set_title(f'Histogram of {self._y.desc}')
plt.savefig(filename)


def main():
"""Plot data parsed from log messages from a single source.
Expand All @@ -75,7 +86,7 @@ def main():
)
args = aparser.parse_args()
parser = PARSERS[args.parser]()
plotter = Plotter(parser.y_name)
plotter = Plotter(TIMESERIES, Axis(parser.y_name, parser.y_name))
with open_input(args.input) as fid:
method = parser.canonical if args.canonical else parser.parse
for parsed in method(fid):
Expand Down

0 comments on commit 178d6fd

Please sign in to comment.