Quick Start

Load CPOW waveform data

CPOW (Continuous Point-on-Wave) files contain high-resolution waveform captures at 32 kHz across 7 channels.

from equser.data import load_cpow_scaled

result = load_cpow_scaled('20250623_075056.parquet')

# Scaled voltage and current arrays (numpy float64)
print(f"Voltage A peak: {result['VA'].max():.1f} V")
print(f"Current A peak: {result['IA'].max():.3f} A")
print(f"Start time: {result['start_time']}")
print(f"Sample rate: {result['sample_rate']} Hz")

The function handles both data formats automatically:

  • int32 (current format): raw ADC counts scaled by vscale/iscale from Parquet metadata.
  • float (legacy format): values already in volts/amps.

Load PMon summary data

PMon (Power Monitor) files contain 10/12-cycle RMS measurements (~200 ms intervals).

from equser.data import load_pmon

table = load_pmon('20250623_0750.parquet')
print(table.column_names)
# ['time_us', 'FREQ', 'AVRMS', 'BVRMS', 'CVRMS', 'AIRMS', ...]

Analyze zero crossings

Zero-crossing detection is useful for frequency estimation, cycle extraction, and identifying waveform distortions.

import numpy as np
from equser.data import load_cpow_scaled, SAMPLE_RATE_HZ
from equser.analysis import find_zero_crossings

result = load_cpow_scaled('cpow_data.parquet')
time = np.arange(len(result['VA'])) / SAMPLE_RATE_HZ

crossings, indices = find_zero_crossings(result['VA'], time)
print(f"Found {len(crossings)} zero crossings")

# Estimate frequency from crossing intervals
periods = np.diff(crossings)
freq = 1.0 / np.mean(periods)
print(f"Estimated frequency: {freq:.2f} Hz")

Extract complete cycles

from equser.analysis import extract_complete_cycles

cycles = extract_complete_cycles(result['VA'], time, [0.0, 0.05], num_cycles=1)

for cycle_time, cycle_signal, start, end in cycles:
    duration_ms = (end - start) * 1000
    print(f"Cycle at {start:.6f}s, duration: {duration_ms:.2f} ms")

Plot data (requires [analysis])

from equser.plotting import PowerMonitorPlotter, WaveformPlotter

# PMon data
plotter = PowerMonitorPlotter()
plotter.plot_file('pmon_data.parquet')

# CPOW waveforms
wf_plotter = WaveformPlotter()
wf_plotter.plot_file('cpow_data.parquet')

Query a gateway (requires [analysis])

from equser.api import SynapseClient

client = SynapseClient('http://gateway:8080')
devices = client.list_devices()
table = client.get_pmon_data(devices[0]['id'])

# SQL queries
rows = client.query_sql("SELECT * FROM pmon ORDER BY time_us DESC", limit=10)

Stream real-time data (requires [analysis])

from equser.api import connect_cpow_stream

for batch_or_gap in connect_cpow_stream('http://gateway:8080'):
    if isinstance(batch_or_gap, dict):
        print(f"Gap: {batch_or_gap['skipped_samples']} samples")
    else:
        print(f"Batch: {batch_or_gap.num_rows} rows")

CLI tools

# Start power monitoring (requires [daq] + EQ Wave hardware)
equser pmon acquire -c config.yaml

# Convert Avro files to Parquet (requires [daq])
equser pmon convert data/*.avro --remove

# Plot data file (requires [analysis])
equser plot data.parquet