diff --git a/ovgenpy/ovgenpy.py b/ovgenpy/ovgenpy.py index c48ef35..bd603ab 100644 --- a/ovgenpy/ovgenpy.py +++ b/ovgenpy/ovgenpy.py @@ -16,12 +16,9 @@ RENDER_PROFILING = True class Config(NamedTuple): wave_dir: str - # TODO: if wave_dir is present, it should overwrite List[WaveConfig]. - # wave_dir will be commented out when writing to file. - master_wave: Optional[str] - fps: int + time_visible_ms: int trigger: TriggerConfig # Maybe overriden per Wave render: RendererConfig @@ -42,6 +39,8 @@ def main(wave_dir: str, master_wave: Optional[str], fps: int): wave_dir=wave_dir, master_wave=master_wave, fps=fps, + time_visible_ms=25, + trigger=CorrelationTrigger.Config( trigger_strength=10, use_edge_trigger=True, @@ -51,7 +50,6 @@ def main(wave_dir: str, master_wave: Optional[str], fps: int): ), render=RendererConfig( # todo 1280, 720, - samples_visible=1000, rows_first=False, ncols=1 ) @@ -68,12 +66,13 @@ class Ovgen: def __init__(self, cfg: Config): self.cfg = cfg self.waves: List[Wave] = [] + self.nwaves: int = None def write(self): - self.load_waves() # self.waves = - self.render() + self._load_waves() # self.waves = + self._render() - def load_waves(self): + def _load_waves(self): wave_dir = Path(self.cfg.wave_dir) for idx, path in enumerate(wave_dir.glob('*.wav')): @@ -89,13 +88,17 @@ class Ovgen: wave.set_trigger(trigger) self.waves.append(wave) - def render(self): + self.nwaves = len(self.waves) + + def _render(self): # Calculate number of frames (TODO master file?) + time_visible_ms = self.cfg.time_visible_ms fps = self.cfg.fps + nframes = fps * self.waves[0].get_s() nframes = int(nframes) + 1 - renderer = MatplotlibRenderer(self.cfg.render, self.waves) + renderer = MatplotlibRenderer(self.cfg.render, self.nwaves) if RENDER_PROFILING: begin = time.perf_counter() @@ -104,17 +107,22 @@ class Ovgen: for frame in range(nframes): time_seconds = frame / fps - center_smps = [] + datas = [] + # Get data from each wave for wave in self.waves: sample = round(wave.smp_s * time_seconds) + region_len = round(wave.smp_s * time_visible_ms / 1000) + trigger_sample = wave.trigger.get_trigger(sample) print(f'- {trigger_sample}') - center_smps.append(trigger_sample) + + datas.append(wave.get_around(trigger_sample, region_len)) print(frame) - renderer.render_frame(center_smps) + renderer.render_frame(datas) if RENDER_PROFILING: + # noinspection PyUnboundLocalVariable dtime = time.perf_counter() - begin render_fps = nframes / dtime print(f'FPS = {render_fps}') diff --git a/ovgenpy/renderer.py b/ovgenpy/renderer.py index 565d66f..b52229a 100644 --- a/ovgenpy/renderer.py +++ b/ovgenpy/renderer.py @@ -1,5 +1,4 @@ -from itertools import count -from typing import NamedTuple, Optional, List, Tuple, TYPE_CHECKING +from typing import NamedTuple, Optional, List, Tuple import numpy as np from matplotlib import pyplot as plt @@ -9,16 +8,11 @@ from matplotlib.lines import Line2D from ovgenpy.util import ceildiv -if TYPE_CHECKING: - from ovgenpy.wave import Wave - class RendererConfig(NamedTuple): width: int height: int - samples_visible: int - rows_first: bool nrows: Optional[int] = None # TODO set to 1 @@ -48,19 +42,19 @@ class MatplotlibRenderer: DPI = 96 - def __init__(self, cfg: RendererConfig, waves: List['Wave']): + def __init__(self, cfg: RendererConfig, nplots: int): self.cfg = cfg - self.waves = waves - self.nwaves = len(waves) + self.nplots = nplots self.fig: Figure = None # Setup layout self.nrows = 0 self.ncols = 0 + # Flat array of nrows*ncols elements, ordered by cfg.rows_first. - self.axes: List[Axes] = None - self.lines: List[Line2D] = None + self.axes: List[Axes] = None # set by set_layout() + self.lines: List[Line2D] = None # set by render_frame() first call self.set_layout() # mutates self @@ -95,17 +89,7 @@ class MatplotlibRenderer: if not self.cfg.rows_first: axes2d = axes2d.T - self.axes: List[Axes] = axes2d.flatten().tolist()[:self.nwaves] - - # Create oscilloscope line objects - self.lines = [] - for ax in self.axes: - # Setup axes limits - ax.set_xlim(0, self.cfg.samples_visible) - ax.set_ylim(-1, 1) - - line = ax.plot([0] * self.cfg.samples_visible)[0] - self.lines.append(line) + self.axes: List[Axes] = axes2d.flatten().tolist()[:self.nplots] # Setup figure geometry self.fig.set_dpi(self.DPI) @@ -126,26 +110,37 @@ class MatplotlibRenderer: nrows = cfg.nrows if nrows is None: raise ValueError('invalid cfg: rows_first is True and nrows is None') - ncols = ceildiv(self.nwaves, nrows) + ncols = ceildiv(self.nplots, nrows) else: ncols = cfg.ncols if ncols is None: raise ValueError('invalid cfg: rows_first is False and ncols is None') - nrows = ceildiv(self.nwaves, ncols) + nrows = ceildiv(self.nplots, ncols) return nrows, ncols - def render_frame(self, center_smps: List[int]) -> None: - ncenters = len(center_smps) - if self.nwaves != ncenters: + def render_frame(self, datas: List[np.ndarray]) -> None: + ndata = len(datas) + if self.nplots != ndata: raise ValueError( - f'incorrect wave offsets: {self.nwaves} waves but {ncenters} offsets') + f'incorrect data to plot: {self.nplots} plots but {ndata} datas') - for idx, wave, center_smp in zip(count(), self.waves, center_smps): - # Draw waveform data - line = self.lines[idx] - data = wave.get_around(center_smp, self.cfg.samples_visible) - line.set_ydata(data) + # Initialize axes and draw waveform data + if self.lines is None: + self.lines = [] + for idx, data in enumerate(datas): + ax = self.axes[idx] + ax.set_xlim(0, len(data) - 1) + ax.set_ylim(-1, 1) + + line = ax.plot(data)[0] + self.lines.append(line) + + # Draw waveform data + else: + for idx, data in enumerate(datas): + line = self.lines[idx] + line.set_ydata(data) self.fig.canvas.draw() self.fig.canvas.flush_events()