Pneumonitor
Python toolkit for analysing cardiorespiratory data recorded with the Pneumonitor 4 wearable device. The device simultaneously acquires ECG and impedance pneumography signals, enabling synchronised analysis of cardiac and respiratory activity.
Project structure
Pneumonitor/
├── pneumonitor/
│ ├── load.py # Data loading and timestamp normalisation
│ ├── preprocess.py # Signal processing (ECG, respiration, IMU)
│ ├── filters.py # Digital filter building blocks
│ └── plots.py # Interactive Plotly visualisations
├── <recording_id>/ # One folder per recording (e.g. 00001/)
│ ├── bio.txt # ECG + bioimpedance (I/Q) at 500 Hz
│ ├── imu.txt # 3-axis accelerometer at 50 Hz
│ ├── mark.txt # User-triggered event markers
│ ├── stat.txt # Battery and device status
│ ├── imp.txt # Empty in the current version
│ └── info.txt # Recording metadata and error log
└── experiments.py # Example analysis notebook (%-cell format)
Recording format
Each recording folder contains semicolon-delimited text files:
| File | Columns | Description |
|---|---|---|
bio.txt |
timestamp[us], ECG[uV], BiozI[uV], BiozQ[uV] |
ECG and bioimpedance in-phase / quadrature components |
imu.txt |
timestamp[us], X[mq], Y[mg], Z[mg] |
3-axis accelerometer in mg |
mark.txt |
timestamp[us] |
Timestamps of manual markers |
stat.txt |
timestamp[us], BatLevel[%], BatVoltage[mV], BatCurrent[mA], Status[NONE] |
Device telemetry |
info.txt |
Key-value pairs | Hardware/firmware version, sample counts, error list |
Timestamps are in microseconds from device boot. load_data() normalises them to seconds relative to the first biosignal sample.
Usage
1. Load a recording
from pneumonitor.load import load_data
df_bio, df_acc, df_markers, df_stats, errors = load_data('00001')
df_bio already contains the derived Amplitude (√(I²+Q²)) and Phase (arctan2(Q,I)) columns computed from the bioimpedance I/Q pair.
2. Process IMU data
from pneumonitor.preprocess import process_imu
df_acc = process_imu(df_acc, rms_window_sec=1, sampling_rate=50)
Adds Acc_Magnitude[g] (gravity-removed vector magnitude) and Acc_RMS[g] (rolling RMS over the specified window).
3. Preprocess cardiorespiratory signals
from pneumonitor.preprocess import preprocess_cardio_resp
df_bio = preprocess_cardio_resp(df_bio, sampling_rate=500)
Uses NeuroKit2 internally and appends the following columns to df_bio:
| Column | Description |
|---|---|
ECG_clean[uV] |
Cleaned ECG signal |
ECG_R_Peaks |
Binary mask of R-peak locations |
ECG_Rate |
Instantaneous heart rate (bpm) |
Amplitude_clean |
Cleaned respiratory amplitude |
RSP_Rate |
Instantaneous respiratory rate (bpm) |
RSP_Peaks |
Binary mask of respiration peaks |
RSP_Phase |
Respiratory phase (0 = exhalation, 1 = inhalation) |
4. Visualise
from pneumonitor.plots import plot_cardio_resp_data, plot_accelerometer_data
# Cardiorespiratory overview — add extra derived signals as extra subplots
plot_cardio_resp_data(df_bio, df_markers, include_raw=True,
additional_signals=['ECG_Rate', 'RSP_Rate'])
# Accelerometer overview
plot_accelerometer_data(df_acc, df_markers)
Both functions return a Plotly Figure and call .show(). The cardiorespiratory plot shades the respiratory amplitude subplot green (inhalation) / red (exhalation) based on RSP_Phase, and overlays R-peak markers on the ECG subplot.
5. Filters (optional low-level use)
from pneumonitor.filters import bandpass_filter, notch_filter, resp_filter
ecg_filtered = notch_filter(df_bio['ECG[uV]'].values) # remove 50 Hz mains
ecg_filtered = bandpass_filter(ecg_filtered, lowcut=0.5, highcut=25)
resp_filtered = resp_filter(df_bio['Amplitude'].values) # 3–180 bpm bandpass
Installation
Once published to PyPI, install it into any project with:
pip install pneumonitor
# or
uv add pneumonitor
Development
This project uses uv for dependency management.
Installing dependencies
Install all dependencies specified in pyproject.toml:
uv sync
Adding new dependencies
To add a new package to the project:
uv add <package-name>
This will update both pyproject.toml and the virtual environment automatically.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pneumonitor-0.1.1.tar.gz.
File metadata
- Download URL: pneumonitor-0.1.1.tar.gz
- Upload date:
- Size: 2.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.4.28
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d02fc9be6d4d50e73c9ece73124287a5de1cc0b82bf2f09345583c6123423ade
|
|
| MD5 |
0e0f930af6151f94243e89e14b0543a8
|
|
| BLAKE2b-256 |
86cda4b78cfc894f1bb142caf0e9f0fc98e111336c0c17132d7aa433ee6f89e0
|
File details
Details for the file pneumonitor-0.1.1-py3-none-any.whl.
File metadata
- Download URL: pneumonitor-0.1.1-py3-none-any.whl
- Upload date:
- Size: 9.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.4.28
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c260d716514ded2a022f9e5311cb2665a25fcf1ca2d5a135639dc32f1a71606b
|
|
| MD5 |
bdab92e286bbf4baa4bb85c23a8b3993
|
|
| BLAKE2b-256 |
37acaecee1606e7f6755b646ad250d4cbd6f9f7017f0f6fa9231a69d66a498a2
|