Metadata-Version: 2.4
Name: oasis-api
Version: 4.0.0b1
Summary: Python API for the Open Acquisition System for IEPE Sensors (OASIS)
Author-email: Johannes Maierhofer <j.maierhofer@tum.de>, Oliver Zobel <oliver.zobel@tum.de>
Project-URL: Homepage, https://gitlab.com/oasis-acquisition/oasis-api
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyserial
Requires-Dist: psutil
Requires-Dist: numpy
Requires-Dist: scipy
Requires-Dist: h5py
Requires-Dist: matplotlib
Dynamic: license-file

# OASIS Board Python Package

Used in the following packages:
- oasis-gui
- oasis-tui

```
                    +-------------------+
                    |     oasis-api     |
                    |  (Python library) |
                    +-------------------+
                             ^
         +-------------------+---------------------+
         |                   |                     |
  +----------------+   +----------------+   +----------------+
  |   oasis-cli    |   |   oasis-tui    |   |   oasis-gui    |
  | (flags & args) |   | (guided menus) |   | (desktop/web)  |
  +----------------+   +----------------+   +----------------+
```

## HDF5 File Format for OASIS Measurements

### **Overview**

Measurement data is stored in [HDF5](https://www.hdfgroup.org/solutions/hdf5/) files in a **tidy data** format, making it easy to analyze, visualize, and share.

### **Structure**

* The main dataset is stored at the root as `/data`.
* The file contains **attributes** describing metadata and the measurement setup.

#### **Data Table: `/data`**

* Shape: `(N, )` where `N = number of channels × number of time points`
* Each row represents a single observation:

  * **channel**: Integer (1–8), channel number
  * **time**: Float (seconds), timestamp of the sample
  * **signal**: Float (volts), measured signal value

Example (first few rows):

| channel | time    | signal |
| ------- | ------- | ------- |
| 1       | 0.00000 | 0.0324  |
| 1       | 0.00100 | 0.0451  |
| ...     | ...     | ...     |
| 8       | 9.99900 | 0.0078  |

* **Units:**

  * The `signal` column is stored in volts.

#### **File Attributes (Metadata)**

* `schema_version`: HDF5 schema version used by the file
* `duration_s`: Duration of acquisition (in seconds)
* `sampling_frequency_hz`: Sampling frequency (in Hz)
* `voltage_ranges`: Array of voltage ranges for each channel (in volts)
* `channels`: Number of channels (typically 8)
* `board_count`: Number of contributing boards in the file
* `channels_per_board`: Channel counts per contributing board
* `format description`: Human-readable schema description
* Merged files additionally store `source_files`, `source_labels`, and `merge_align_mode`

#### **Why Tidy Data?**

* **Each variable forms a column:** channel, time, signal
* **Each observation forms a row:** one reading from one channel at one time
* **Each type of observational unit forms a table:** all data is in `/data`
* This format makes the data easy to filter, aggregate, and visualize in Python (Pandas), R, MATLAB, etc.

---

### **How to Read the File (Python Example)**

```python
import h5py
import numpy as np

with h5py.File("measurement.h5", "r") as f:
    data = f["data"][:]
    print("Fields:", data.dtype.names)  # ('channel', 'time', 'signal')
    print("First row:", data[0])
    print("Sampling frequency:", f.attrs["sampling_frequency_hz"])
    print("Voltage ranges:", f.attrs["voltage_ranges"])
```

---

### **Viewing**

* Use [HDFView](https://www.hdfgroup.org/downloads/hdfview/) or Vitables to inspect your data and metadata visually.

## Compatibility

- `oasis-api` `v4.0.0-b1` targets OASIS firmware v4 boards.
- Stored serial acquisitions require USB CDC+MSC firmware support (`>= 4.0.0.a2`).
- The current serial acquisition path does not support legacy v3 CDC payload transfer boards.
- `load_h5()` expects the current tidy schema with the `signal` field; legacy v3 HDF5 files written with `voltage` are not supported by this release.

## Device Information

Device information is written with one direct API call; the firmware no longer
opens an interactive serial menu for `OASIS.SetDeviceInfo()`.

```python
board.set_device_info(
    architecture_id=2,  # 0=Original OASIS, 1=OASIS-UROS, 2=OASIS-ERIS
    hw_major=1,
    hw_minor=0,
    adc_bits=18,
    teds=0,
    wss=0,
    device_name="OASIS-01",
)
```

## Distributed Acquisition Helpers

`OasisBoard` now includes helpers for multi-board distributed workflows:

- This release line is intended for firmware v4 boards.
- Stored acquisitions in `mode="serial"` require firmware `>= 4.0.0.a2` on OASIS USB CDC+MSC devices.
- Measurement payload is copied from USB MSC volumes, not transferred over CDC.
- Live streaming remains CDC-based.

- `acquire(...)`
  - Standard acquisition call; on firmware >= `4.0.0.a2` this also performs one-wire
    distributed primary-board signaling automatically.
- `collect_distributed_serial_files(...)`
  - Copies `.OASISraw/.OASISmeta` from multiple board-specific USB MSC volumes and writes local files.
- `stack_distributed_measurements(...)`
  - Loads multiple distributed files and stacks them into a single array.
- `collect_and_stack_distributed(...)`
  - One-shot helper for USB MSC collection by board serial port + optional manual file sources + stacking.
- `merge_h5_files(...)`
  - Merges multiple OASIS `.h5` files into one consistent dataset with global channel numbering.
  - Stores merge metadata (`board_count`, `channels_per_board`, `source_files`, `source_labels`).

Example:

```python
from oasis_api import OasisBoard

primary = OasisBoard(mode="serial", port="/dev/cu.usbmodem11101", baudrate=1000000)
resolved = "dist_run"
primary.acquire(t_sample=0.25, f_sample=2000, custom_filename=resolved)

result = primary.collect_and_stack_distributed(
    filename=resolved,
    serial_ports=["/dev/cu.usbmodem11101", "/dev/cu.usbmodem11201"],
    t_sample=0.25,
    f_sample=2000,
    output_dir="./distributed_pull",
)

stacked = result["stacked"]      # (n_devices, 8, n_samples)
flattened = result["flattened"]  # (n_devices*8, n_samples)
```

### Merge Existing H5 Files

```python
from oasis_api import OasisBoard

result = OasisBoard.merge_h5_files(
    input_files=["board1.h5", "board2.h5"],
    output_file="merged.h5",
    align="truncate",  # or "pad"
    overwrite=True,
)
print(result)
```

Merged files can be loaded and plotted without prior knowledge of board count:

```python
board = OasisBoard(mode="offline")
board.load_h5("merged.h5")
board.plot_data()  # adapts to total channel count with board-aware labels (e.g. board_1:CH1)
```
