equser 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- equser-0.0.1/.gitignore +34 -0
- equser-0.0.1/CHANGELOG.md +42 -0
- equser-0.0.1/LICENSE +21 -0
- equser-0.0.1/PKG-INFO +247 -0
- equser-0.0.1/README.md +179 -0
- equser-0.0.1/equser/__init__.py +60 -0
- equser-0.0.1/equser/_version.py +8 -0
- equser-0.0.1/equser/analysis/__init__.py +25 -0
- equser-0.0.1/equser/analysis/waveform.py +221 -0
- equser-0.0.1/equser/api/__init__.py +26 -0
- equser-0.0.1/equser/api/client.py +135 -0
- equser-0.0.1/equser/api/streaming.py +96 -0
- equser-0.0.1/equser/cli.py +271 -0
- equser-0.0.1/equser/core/__init__.py +6 -0
- equser-0.0.1/equser/core/config.py +118 -0
- equser-0.0.1/equser/core/paths.py +120 -0
- equser-0.0.1/equser/core/system.py +40 -0
- equser-0.0.1/equser/data/__init__.py +47 -0
- equser-0.0.1/equser/data/cpow.py +98 -0
- equser-0.0.1/equser/data/pmon.py +33 -0
- equser-0.0.1/equser/data/timestamps.py +81 -0
- equser-0.0.1/equser/notebooks/__init__.py +115 -0
- equser-0.0.1/equser/notebooks/analysis/delta-analysis.ipynb +359 -0
- equser-0.0.1/equser/notebooks/analysis/harmonic-analysis.ipynb +437 -0
- equser-0.0.1/equser/notebooks/analysis/power-trends.ipynb +390 -0
- equser-0.0.1/equser/notebooks/tutorials/01-parquet-files.ipynb +242 -0
- equser-0.0.1/equser/notebooks/tutorials/02-backend-api.ipynb +405 -0
- equser-0.0.1/equser/notebooks/tutorials/03-live-streaming.ipynb +131 -0
- equser-0.0.1/equser/plotting/__init__.py +34 -0
- equser-0.0.1/equser/plotting/power_quality.py +376 -0
- equser-0.0.1/equser/pmon/__init__.py +141 -0
- equser-0.0.1/equser/pmon/__main__.py +10 -0
- equser-0.0.1/equser/pmon/daq.py +450 -0
- equser-0.0.1/equser/pmon/dataops.py +218 -0
- equser-0.0.1/equser/pmon/errors.py +11 -0
- equser-0.0.1/equser/pmon/schema.py +76 -0
- equser-0.0.1/equser/py.typed +0 -0
- equser-0.0.1/equser/snapshot.py +156 -0
- equser-0.0.1/equser/utils/__init__.py +6 -0
- equser-0.0.1/equser/utils/datetime.py +60 -0
- equser-0.0.1/equser/utils/logging.py +90 -0
- equser-0.0.1/equser/widgets.py +156 -0
- equser-0.0.1/pyproject.toml +145 -0
equser-0.0.1/.gitignore
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
*.egg
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
*.whl
|
|
10
|
+
|
|
11
|
+
# Virtual environments
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
|
|
15
|
+
# IDE
|
|
16
|
+
.idea/
|
|
17
|
+
.vscode/
|
|
18
|
+
*.swp
|
|
19
|
+
*.swo
|
|
20
|
+
*~
|
|
21
|
+
|
|
22
|
+
# Testing
|
|
23
|
+
.pytest_cache/
|
|
24
|
+
.coverage
|
|
25
|
+
htmlcov/
|
|
26
|
+
.mypy_cache/
|
|
27
|
+
|
|
28
|
+
# OS
|
|
29
|
+
.DS_Store
|
|
30
|
+
Thumbs.db
|
|
31
|
+
|
|
32
|
+
# AI assistant instructions (developer-local)
|
|
33
|
+
CLAUDE.md
|
|
34
|
+
CLAUDE.local.md
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-02-06
|
|
11
|
+
|
|
12
|
+
Initial public release. User toolkit for EQ Wave power quality data.
|
|
13
|
+
|
|
14
|
+
### Modules
|
|
15
|
+
- **equser.data** - Load CPOW and PMon Parquet files with automatic scaling, timestamp parsing
|
|
16
|
+
- **equser.analysis** - Waveform analysis: zero-crossing detection, AC cycle extraction
|
|
17
|
+
- **equser.api** - REST and WebSocket clients for EQ Synapse gateways (requires `[analysis]`)
|
|
18
|
+
- **equser.plotting** - Static matplotlib plots for PMon and CPOW data (requires `[analysis]`)
|
|
19
|
+
- **equser.pmon** - Live sensor acquisition and Avro-to-Parquet conversion (requires `[daq]`)
|
|
20
|
+
- **equser.core** - YAML configuration loading, XDG-compliant path resolution
|
|
21
|
+
- **equser.utils** - Logging with optional color, DateTime with floor-division
|
|
22
|
+
- **equser.notebooks** - Bundled reference notebooks with list/copy API
|
|
23
|
+
- **equser.widgets** - Interactive file selector for JupyterLab (requires `[jupyter]`)
|
|
24
|
+
- **equser.snapshot** - Waveform capture via gateway WebSocket
|
|
25
|
+
|
|
26
|
+
### Dependency Tiers
|
|
27
|
+
- **Base**: numpy, pyarrow, pyyaml, argcomplete, colorlog (data loading, analysis, CLI)
|
|
28
|
+
- **[daq]**: avro, fastavro (live sensor acquisition)
|
|
29
|
+
- **[analysis]**: matplotlib, requests, websocket-client (plotting + API)
|
|
30
|
+
- **[jupyter]**: `[analysis]` + jupyterlab, duckdb, ipywidgets, ipykernel, nbconvert
|
|
31
|
+
- **[full]**: all of the above
|
|
32
|
+
|
|
33
|
+
### CLI
|
|
34
|
+
- `equser pmon acquire` - Start power monitoring from EQ Wave sensor
|
|
35
|
+
- `equser pmon convert` - Convert Avro files to Parquet
|
|
36
|
+
- `equser plot` - Plot PMon or CPOW data files
|
|
37
|
+
- `equser notebooks list` - List bundled reference notebooks
|
|
38
|
+
- `equser notebooks copy` - Copy reference notebooks to a directory
|
|
39
|
+
- `equser snapshot` - Capture live waveform data to a Parquet file
|
|
40
|
+
|
|
41
|
+
[Unreleased]: https://github.com/Energy-Quotient/equser/compare/v0.1.0...HEAD
|
|
42
|
+
[0.1.0]: https://github.com/Energy-Quotient/equser/releases/tag/v0.1.0
|
equser-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EQ Systems Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
equser-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: equser
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: User toolkit for power quality data from EQ Wave sensors
|
|
5
|
+
Project-URL: Homepage, https://eq.systems
|
|
6
|
+
Project-URL: Documentation, https://equser.eq.systems
|
|
7
|
+
Project-URL: Repository, https://github.com/Energy-Quotient/equser
|
|
8
|
+
Project-URL: Changelog, https://github.com/Energy-Quotient/equser/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Issues, https://github.com/Energy-Quotient/equser/issues
|
|
10
|
+
Author-email: Energy Quotient <info@eq.systems>
|
|
11
|
+
Maintainer-email: Kevin Davies <kdavies@eq.systems>
|
|
12
|
+
License: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: continuous-waveform,cpow,data-acquisition,electrical,energy,eq-wave,monitoring,parquet,power-quality,pq,waveform
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Environment :: Console
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: Intended Audience :: Science/Research
|
|
19
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
20
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
27
|
+
Classifier: Topic :: System :: Monitoring
|
|
28
|
+
Classifier: Typing :: Typed
|
|
29
|
+
Requires-Python: >=3.10
|
|
30
|
+
Requires-Dist: argcomplete>=3.5
|
|
31
|
+
Requires-Dist: colorlog>=6.9
|
|
32
|
+
Requires-Dist: numpy>=1.24
|
|
33
|
+
Requires-Dist: pyarrow!=21.0.0,>=18.0
|
|
34
|
+
Requires-Dist: pyyaml>=6.0
|
|
35
|
+
Provides-Extra: analysis
|
|
36
|
+
Requires-Dist: matplotlib>=3.5; extra == 'analysis'
|
|
37
|
+
Requires-Dist: requests>=2.28; extra == 'analysis'
|
|
38
|
+
Requires-Dist: websocket-client>=1.6; extra == 'analysis'
|
|
39
|
+
Provides-Extra: daq
|
|
40
|
+
Requires-Dist: avro>=1.12; extra == 'daq'
|
|
41
|
+
Requires-Dist: fastavro>=1.9; extra == 'daq'
|
|
42
|
+
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
45
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
46
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
47
|
+
Provides-Extra: full
|
|
48
|
+
Requires-Dist: avro>=1.12; extra == 'full'
|
|
49
|
+
Requires-Dist: duckdb>=1.0; extra == 'full'
|
|
50
|
+
Requires-Dist: fastavro>=1.9; extra == 'full'
|
|
51
|
+
Requires-Dist: ipykernel>=6.29; extra == 'full'
|
|
52
|
+
Requires-Dist: ipywidgets>=8.1; extra == 'full'
|
|
53
|
+
Requires-Dist: jupyterlab>=4.4; extra == 'full'
|
|
54
|
+
Requires-Dist: matplotlib>=3.5; extra == 'full'
|
|
55
|
+
Requires-Dist: nbconvert>=7.16; extra == 'full'
|
|
56
|
+
Requires-Dist: requests>=2.28; extra == 'full'
|
|
57
|
+
Requires-Dist: websocket-client>=1.6; extra == 'full'
|
|
58
|
+
Provides-Extra: jupyter
|
|
59
|
+
Requires-Dist: duckdb>=1.0; extra == 'jupyter'
|
|
60
|
+
Requires-Dist: ipykernel>=6.29; extra == 'jupyter'
|
|
61
|
+
Requires-Dist: ipywidgets>=8.1; extra == 'jupyter'
|
|
62
|
+
Requires-Dist: jupyterlab>=4.4; extra == 'jupyter'
|
|
63
|
+
Requires-Dist: matplotlib>=3.5; extra == 'jupyter'
|
|
64
|
+
Requires-Dist: nbconvert>=7.16; extra == 'jupyter'
|
|
65
|
+
Requires-Dist: requests>=2.28; extra == 'jupyter'
|
|
66
|
+
Requires-Dist: websocket-client>=1.6; extra == 'jupyter'
|
|
67
|
+
Description-Content-Type: text/markdown
|
|
68
|
+
|
|
69
|
+
# equser
|
|
70
|
+
|
|
71
|
+
[](https://www.python.org/downloads/)
|
|
72
|
+
[](https://opensource.org/licenses/MIT)
|
|
73
|
+
|
|
74
|
+
User toolkit for power quality data from EQ Wave sensors.
|
|
75
|
+
|
|
76
|
+
## Overview
|
|
77
|
+
|
|
78
|
+
equser is a Python library for loading, analyzing, and visualizing continuous
|
|
79
|
+
waveform (CPOW) and power monitoring (PMon) data from EQ Wave hardware. It
|
|
80
|
+
provides:
|
|
81
|
+
|
|
82
|
+
- **Data loading** (`data`): Load CPOW and PMon Parquet files with automatic scaling
|
|
83
|
+
- **Waveform analysis** (`analysis`): Zero-crossing detection, cycle extraction
|
|
84
|
+
- **Visualization** (`plotting`): Static plots for power quality data (requires `[analysis]`)
|
|
85
|
+
- **API client** (`api`): REST and WebSocket clients for EQ Synapse gateways (requires `[analysis]`)
|
|
86
|
+
- **Live acquisition** (`pmon`): Real-time sensor data acquisition (requires `[daq]`)
|
|
87
|
+
- **CLI tools**: Command-line interface for monitoring and conversion
|
|
88
|
+
|
|
89
|
+
## Installation
|
|
90
|
+
|
|
91
|
+
### Base installation (data loading + analysis)
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pip install equser
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### With plotting and API support
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pip install equser[analysis]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### With JupyterLab notebook environment
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pip install equser[jupyter]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### With live sensor acquisition
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pip install equser[daq]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Full installation (all features)
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pip install equser[full]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Quick Start
|
|
122
|
+
|
|
123
|
+
### Load and explore CPOW data
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from equser.data import load_cpow_scaled
|
|
127
|
+
|
|
128
|
+
result = load_cpow_scaled('20250623_075056.parquet')
|
|
129
|
+
print(f"Voltage A peak: {result['VA'].max():.1f} V")
|
|
130
|
+
print(f"Start time: {result['start_time']}")
|
|
131
|
+
print(f"Sample rate: {result['sample_rate']} Hz")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Load PMon summary data
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from equser.data import load_pmon
|
|
138
|
+
|
|
139
|
+
table = load_pmon('20250623_0750.parquet')
|
|
140
|
+
print(table.column_names)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Analyze waveform zero crossings
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import numpy as np
|
|
147
|
+
from equser.data import load_cpow_scaled, SAMPLE_RATE_HZ
|
|
148
|
+
from equser.analysis import find_zero_crossings
|
|
149
|
+
|
|
150
|
+
result = load_cpow_scaled('cpow_data.parquet')
|
|
151
|
+
time = np.arange(len(result['VA'])) / SAMPLE_RATE_HZ
|
|
152
|
+
crossings, indices = find_zero_crossings(result['VA'], time)
|
|
153
|
+
print(f"Found {len(crossings)} zero crossings")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Plot data (requires `[analysis]`)
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from equser.plotting import PowerMonitorPlotter, WaveformPlotter
|
|
160
|
+
|
|
161
|
+
# Plot power monitor data
|
|
162
|
+
plotter = PowerMonitorPlotter()
|
|
163
|
+
plotter.plot_file('pmon_data.parquet')
|
|
164
|
+
|
|
165
|
+
# Plot waveform data
|
|
166
|
+
wf_plotter = WaveformPlotter()
|
|
167
|
+
wf_plotter.plot_file('cpow_data.parquet')
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Query a gateway (requires `[analysis]`)
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from equser.api import SynapseClient
|
|
174
|
+
|
|
175
|
+
client = SynapseClient('http://gateway:8080')
|
|
176
|
+
devices = client.list_devices()
|
|
177
|
+
table = client.get_pmon_data(devices[0]['id'])
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Command Line
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Start power monitoring (requires EQ Wave sensor + [daq])
|
|
184
|
+
equser pmon acquire -c config.yaml
|
|
185
|
+
|
|
186
|
+
# Convert Avro files to Parquet (requires [daq])
|
|
187
|
+
equser pmon convert data/*.avro --remove
|
|
188
|
+
|
|
189
|
+
# Plot data file (requires [analysis])
|
|
190
|
+
equser plot data.parquet
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Configuration
|
|
194
|
+
|
|
195
|
+
equser looks for configuration in the following locations (in order):
|
|
196
|
+
|
|
197
|
+
1. `EQUSER_CONFIG` environment variable
|
|
198
|
+
2. `./equser.yaml` (current directory)
|
|
199
|
+
3. `~/.config/equser/config.yaml` (XDG config)
|
|
200
|
+
4. `/etc/equser/config.yaml` (system-wide)
|
|
201
|
+
|
|
202
|
+
Example configuration:
|
|
203
|
+
|
|
204
|
+
```yaml
|
|
205
|
+
sensor:
|
|
206
|
+
address: "192.168.10.10"
|
|
207
|
+
port: 1535
|
|
208
|
+
|
|
209
|
+
pmon:
|
|
210
|
+
connection:
|
|
211
|
+
retry_delay: 3
|
|
212
|
+
parquet:
|
|
213
|
+
interval: 86400
|
|
214
|
+
compression:
|
|
215
|
+
method: ZSTD
|
|
216
|
+
level: 4
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Dependency Tiers
|
|
220
|
+
|
|
221
|
+
| Extra | Description | Key Packages |
|
|
222
|
+
|-------|-------------|--------------|
|
|
223
|
+
| *(base)* | Data loading, analysis, CLI | numpy, pyarrow, pyyaml, argcomplete, colorlog |
|
|
224
|
+
| `[daq]` | Live sensor acquisition | avro, fastavro |
|
|
225
|
+
| `[analysis]` | Plotting + API client | matplotlib, requests, websocket-client |
|
|
226
|
+
| `[jupyter]` | Full notebook environment | `[analysis]` + jupyterlab, duckdb, ipywidgets |
|
|
227
|
+
| `[dev]` | Development tools | pytest, ruff, mypy |
|
|
228
|
+
| `[full]` | All of the above (except dev) | - |
|
|
229
|
+
|
|
230
|
+
## Requirements
|
|
231
|
+
|
|
232
|
+
- Python 3.10 or later
|
|
233
|
+
- Linux (for hardware integration features)
|
|
234
|
+
|
|
235
|
+
## Documentation
|
|
236
|
+
|
|
237
|
+
- [API Documentation](https://equser.eq.systems)
|
|
238
|
+
- [Changelog](CHANGELOG.md)
|
|
239
|
+
|
|
240
|
+
## License
|
|
241
|
+
|
|
242
|
+
MIT License - Copyright (c) 2026 EQ Systems Inc.
|
|
243
|
+
|
|
244
|
+
## About
|
|
245
|
+
|
|
246
|
+
equser is developed by [Energy Quotient](https://eq.systems) as part of the
|
|
247
|
+
EQ Synapse platform for continuous waveform intelligence in power systems.
|
equser-0.0.1/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# equser
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org/downloads/)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
User toolkit for power quality data from EQ Wave sensors.
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
equser is a Python library for loading, analyzing, and visualizing continuous
|
|
11
|
+
waveform (CPOW) and power monitoring (PMon) data from EQ Wave hardware. It
|
|
12
|
+
provides:
|
|
13
|
+
|
|
14
|
+
- **Data loading** (`data`): Load CPOW and PMon Parquet files with automatic scaling
|
|
15
|
+
- **Waveform analysis** (`analysis`): Zero-crossing detection, cycle extraction
|
|
16
|
+
- **Visualization** (`plotting`): Static plots for power quality data (requires `[analysis]`)
|
|
17
|
+
- **API client** (`api`): REST and WebSocket clients for EQ Synapse gateways (requires `[analysis]`)
|
|
18
|
+
- **Live acquisition** (`pmon`): Real-time sensor data acquisition (requires `[daq]`)
|
|
19
|
+
- **CLI tools**: Command-line interface for monitoring and conversion
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### Base installation (data loading + analysis)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install equser
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### With plotting and API support
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install equser[analysis]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### With JupyterLab notebook environment
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install equser[jupyter]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### With live sensor acquisition
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install equser[daq]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Full installation (all features)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install equser[full]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
### Load and explore CPOW data
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from equser.data import load_cpow_scaled
|
|
59
|
+
|
|
60
|
+
result = load_cpow_scaled('20250623_075056.parquet')
|
|
61
|
+
print(f"Voltage A peak: {result['VA'].max():.1f} V")
|
|
62
|
+
print(f"Start time: {result['start_time']}")
|
|
63
|
+
print(f"Sample rate: {result['sample_rate']} Hz")
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Load PMon summary data
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from equser.data import load_pmon
|
|
70
|
+
|
|
71
|
+
table = load_pmon('20250623_0750.parquet')
|
|
72
|
+
print(table.column_names)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Analyze waveform zero crossings
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import numpy as np
|
|
79
|
+
from equser.data import load_cpow_scaled, SAMPLE_RATE_HZ
|
|
80
|
+
from equser.analysis import find_zero_crossings
|
|
81
|
+
|
|
82
|
+
result = load_cpow_scaled('cpow_data.parquet')
|
|
83
|
+
time = np.arange(len(result['VA'])) / SAMPLE_RATE_HZ
|
|
84
|
+
crossings, indices = find_zero_crossings(result['VA'], time)
|
|
85
|
+
print(f"Found {len(crossings)} zero crossings")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Plot data (requires `[analysis]`)
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from equser.plotting import PowerMonitorPlotter, WaveformPlotter
|
|
92
|
+
|
|
93
|
+
# Plot power monitor data
|
|
94
|
+
plotter = PowerMonitorPlotter()
|
|
95
|
+
plotter.plot_file('pmon_data.parquet')
|
|
96
|
+
|
|
97
|
+
# Plot waveform data
|
|
98
|
+
wf_plotter = WaveformPlotter()
|
|
99
|
+
wf_plotter.plot_file('cpow_data.parquet')
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Query a gateway (requires `[analysis]`)
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from equser.api import SynapseClient
|
|
106
|
+
|
|
107
|
+
client = SynapseClient('http://gateway:8080')
|
|
108
|
+
devices = client.list_devices()
|
|
109
|
+
table = client.get_pmon_data(devices[0]['id'])
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Command Line
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Start power monitoring (requires EQ Wave sensor + [daq])
|
|
116
|
+
equser pmon acquire -c config.yaml
|
|
117
|
+
|
|
118
|
+
# Convert Avro files to Parquet (requires [daq])
|
|
119
|
+
equser pmon convert data/*.avro --remove
|
|
120
|
+
|
|
121
|
+
# Plot data file (requires [analysis])
|
|
122
|
+
equser plot data.parquet
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Configuration
|
|
126
|
+
|
|
127
|
+
equser looks for configuration in the following locations (in order):
|
|
128
|
+
|
|
129
|
+
1. `EQUSER_CONFIG` environment variable
|
|
130
|
+
2. `./equser.yaml` (current directory)
|
|
131
|
+
3. `~/.config/equser/config.yaml` (XDG config)
|
|
132
|
+
4. `/etc/equser/config.yaml` (system-wide)
|
|
133
|
+
|
|
134
|
+
Example configuration:
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
sensor:
|
|
138
|
+
address: "192.168.10.10"
|
|
139
|
+
port: 1535
|
|
140
|
+
|
|
141
|
+
pmon:
|
|
142
|
+
connection:
|
|
143
|
+
retry_delay: 3
|
|
144
|
+
parquet:
|
|
145
|
+
interval: 86400
|
|
146
|
+
compression:
|
|
147
|
+
method: ZSTD
|
|
148
|
+
level: 4
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Dependency Tiers
|
|
152
|
+
|
|
153
|
+
| Extra | Description | Key Packages |
|
|
154
|
+
|-------|-------------|--------------|
|
|
155
|
+
| *(base)* | Data loading, analysis, CLI | numpy, pyarrow, pyyaml, argcomplete, colorlog |
|
|
156
|
+
| `[daq]` | Live sensor acquisition | avro, fastavro |
|
|
157
|
+
| `[analysis]` | Plotting + API client | matplotlib, requests, websocket-client |
|
|
158
|
+
| `[jupyter]` | Full notebook environment | `[analysis]` + jupyterlab, duckdb, ipywidgets |
|
|
159
|
+
| `[dev]` | Development tools | pytest, ruff, mypy |
|
|
160
|
+
| `[full]` | All of the above (except dev) | - |
|
|
161
|
+
|
|
162
|
+
## Requirements
|
|
163
|
+
|
|
164
|
+
- Python 3.10 or later
|
|
165
|
+
- Linux (for hardware integration features)
|
|
166
|
+
|
|
167
|
+
## Documentation
|
|
168
|
+
|
|
169
|
+
- [API Documentation](https://equser.eq.systems)
|
|
170
|
+
- [Changelog](CHANGELOG.md)
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
MIT License - Copyright (c) 2026 EQ Systems Inc.
|
|
175
|
+
|
|
176
|
+
## About
|
|
177
|
+
|
|
178
|
+
equser is developed by [Energy Quotient](https://eq.systems) as part of the
|
|
179
|
+
EQ Synapse platform for continuous waveform intelligence in power systems.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""EQ User Tools - Power Quality Data Toolkit for EQ Wave Sensors
|
|
2
|
+
|
|
3
|
+
A Python library for loading, analyzing, and visualizing power quality data
|
|
4
|
+
from EQ Wave continuous waveform sensors.
|
|
5
|
+
|
|
6
|
+
Modules (always available):
|
|
7
|
+
data: Load CPOW and PMon Parquet files, parse timestamps
|
|
8
|
+
analysis: Waveform analysis (zero crossings, cycle extraction)
|
|
9
|
+
pmon: Power monitoring errors, schema, field descriptions
|
|
10
|
+
core: Configuration and path utilities
|
|
11
|
+
utils: Logging and datetime utilities
|
|
12
|
+
|
|
13
|
+
Modules (always available, continued):
|
|
14
|
+
notebooks: Bundled reference notebooks (list, copy, path helpers)
|
|
15
|
+
|
|
16
|
+
Modules (require extras):
|
|
17
|
+
plotting: Visualization tools (requires ``[analysis]`` extra)
|
|
18
|
+
api: Gateway REST and WebSocket clients (requires ``[analysis]`` extra)
|
|
19
|
+
widgets: Interactive Jupyter widgets (requires ``[jupyter]`` extra)
|
|
20
|
+
|
|
21
|
+
Quick start::
|
|
22
|
+
|
|
23
|
+
from equser.data import load_cpow_scaled, load_pmon
|
|
24
|
+
result = load_cpow_scaled('cpow_data.parquet')
|
|
25
|
+
|
|
26
|
+
from equser.analysis import find_zero_crossings
|
|
27
|
+
crossings, indices = find_zero_crossings(result['VA'], time_array)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from equser._version import __version__, __version_info__
|
|
31
|
+
|
|
32
|
+
# Core modules always available
|
|
33
|
+
from equser import core
|
|
34
|
+
from equser import utils
|
|
35
|
+
from equser import pmon
|
|
36
|
+
from equser import data
|
|
37
|
+
from equser import analysis
|
|
38
|
+
from equser import notebooks
|
|
39
|
+
|
|
40
|
+
# Plotting requires [analysis] extra (matplotlib)
|
|
41
|
+
try:
|
|
42
|
+
from equser import plotting
|
|
43
|
+
_has_plotting = True
|
|
44
|
+
except ImportError:
|
|
45
|
+
_has_plotting = False
|
|
46
|
+
|
|
47
|
+
# API client requires [analysis] extra (requests, websocket-client)
|
|
48
|
+
try:
|
|
49
|
+
from equser import api
|
|
50
|
+
_has_api = True
|
|
51
|
+
except ImportError:
|
|
52
|
+
_has_api = False
|
|
53
|
+
|
|
54
|
+
__all__ = ['core', 'utils', 'pmon', 'data', 'analysis', 'notebooks',
|
|
55
|
+
'__version__', '__version_info__']
|
|
56
|
+
|
|
57
|
+
if _has_plotting:
|
|
58
|
+
__all__.append('plotting')
|
|
59
|
+
if _has_api:
|
|
60
|
+
__all__.append('api')
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Waveform analysis functions for CPOW data.
|
|
2
|
+
|
|
3
|
+
Uses only base dependencies (numpy). Plotting helpers that require matplotlib
|
|
4
|
+
are guarded and will raise ImportError with a helpful message if matplotlib
|
|
5
|
+
is not installed.
|
|
6
|
+
|
|
7
|
+
Usage::
|
|
8
|
+
|
|
9
|
+
from equser.analysis import find_zero_crossings, extract_complete_cycles
|
|
10
|
+
|
|
11
|
+
crossings, indices = find_zero_crossings(voltage_array, time_array)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from equser.analysis.waveform import (
|
|
15
|
+
extract_complete_cycles,
|
|
16
|
+
find_zero_crossings,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
'extract_complete_cycles',
|
|
21
|
+
'find_zero_crossings',
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
# plot_extracted_cycles is intentionally not in __all__; import it directly
|
|
25
|
+
# if needed: from equser.analysis.waveform import plot_extracted_cycles
|