Changelog
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
[0.2.7] - 2026-05-16
Changed
-
CI: added an optional HDF5 feature job to
.github/workflows/ci.yml. The job now probes for a system HDF5 library viapkg-configand runscargo test -p biodream --features hdf5only when available; otherwise it emits an explicit skip notice instead of failing. -
Docs: expanded HDF5 installation guidance with
HDF5_DIRandPKG_CONFIG_PATHoverrides plus apkg-configverification command.
[0.2.6] - 2026-05-16
Added
-
biopac info— lazy header load: for file-path arguments,infonow usesLazyDatafile(headers + markers only) instead of reading all sample data. Channel sample-rate is derived fromfrequency_divider;--jsonoutput includesduration_secondscomputed from sample counts. -
biopac markers— lazy header load: same optimisation asinfo; for file-path arguments, markers are read viaLazyDatafile.markerswithout loading any sample data. Stdin input continues to use the fullread_acqpath. -
biopac convert— channel selection by name:--channel-name <NAME>— select channels by exact name; may be specified multiple times. Resolves indices via a lazy header scan.--channel-contains <NEEDLE>— select the first channel whose name contains the substring (case-insensitive). Resolves indices via a lazy header scan. Conflicts with--channelsand--channel-name.
-
biopac convert— CSV output options: the CSV export now exposes the fullCsvOptionssurface through CLI flags:--time-format <seconds|milliseconds|hms>— time-column format (default:seconds)--precision <N>— decimal places for float values (default:6)--delimiter <CHAR>— field separator; accepts a single ASCII character ortab(default:,)--include-raw— emit a<name>_rawinteger column alongside each scaled column--fill-value <STR>— value written for absent samples (default: empty string)
-
biopac signalssubcommand (--features physio): new command group for physiological signal processing:biopac signals detect-peaks --channel <IDX> --fs <HZ> [--json]— loads one ECG channel, runs the Pan-Tompkins R-peak detector, and prints each peak assample<TAB>time_s(or JSON array).biopac signals ptt --ecg <IDX> --ppg <IDX> --fs <HZ> [--json]— loads ECG and PPG channels, computes per-beat PTT, and prints a summary table with median PTT and mean heart rate (or JSON object).
Added
-
physiofeature — newbiodream::signalsmodule with pure-Rust physiological signal processing algorithms (no external dependencies,no_std-compatible):rising_edges/falling_edges— detect digital trigger-pulse edges in a Sync channel by threshold crossingsync_window— returns the(start, end)sample span between the first two rising edges; useful for gating analysis to the recording windowdetect_r_peaks— Pan-Tompkins–inspired QRS R-peak detector (5-point derivative → square → 150 ms moving-window integration → adaptive percentile threshold)detect_ppg_feet— PPG pulse-onset detector (100 ms smoothing → 1 s baseline removal → local-minimum search with 300 ms minimum distance)beat_ptt— per-beat pulse-transit time (ms) from ECG R-peaks to the next PPG foot within a configurable search windowmedian_ptt— convenience wrapper returning the median PTT across all matched beats (default search window: 30–380 ms)heart_rate_bpm— mean heart rate in BPM derived from RR intervals, with physiological bounds filtering (0.25–2.0 s)
-
LazyDatafile::find_channel_by_name— returns the zero-based index of the first channel whose name exactly matches the given string without triggering a sample-data load. -
LazyDatafile::find_channel_containing— case-insensitive substring variant offind_channel_by_name; useful when channel names vary slightly across recordings (e.g."ECG - Filtered"vs"ECG"). -
LazyDatafile::load_channel_by_name— loads and returns a channel by exact name; errors include the full list of available channel names. -
LazyDatafile::load_channel_containing— loads and returns the first channel whose name contains the given substring (case-insensitive).
0.2.4 - 2026-05-16
Fixed
- Parser: corrected foreign data section length interpretation for Post-4
files. The
lLengthfield inForeignDataRawis the total byte count of the section (including the 4-bytelLengthfield itself), not the payload byte count. biodream was readinglLengthbytes as payload and then consuming the 4-byte field on top, overreading by 4 bytes. For a typical Post-4 big-endian file (lLength = 8), this shifted every subsequent dtype header by 4 bytes, causing channel 5's dtype to be read from garbage data (nType = 456instead ofnType = 1, f64). The payload count is now computed as(n_length - 4).max(0).
0.2.3 - 2026-05-15
Fixed
-
Parser: corrected
ChannelHeaderRawbinary layout for the V_20a channel-header format. The struct previously placedlBufLength,dAmplScale,dAmplOffset, andnVarSampleDividerat the wrong offsets; the correct layout is:szCommentTextat offset 6,lBufLengthat 88,dAmplScaleat 92,dAmplOffsetat 100 (total fixed region = 112 bytes).CHANNEL_HEADER_MIN_LENupdated from 86 → 112. -
Parser: added support for the
hExpectedPaddingsfield in Post-4 graph headers (AcqKnowledge≥ 4.3.0, file revision ≥ 124). Files from BIOPAC hardware in big-endian mode include one or more 40-byteUnknownPaddingHeaderblocks between the graph header and the first channel header. biodream previously read the first padding block as a channel header, sawlChanHeaderLen = 40 < 112, and returned a parse error. The parser now readshExpectedPaddingsfrom graph-header offset 2398 and skips that many padding blocks before reading channel headers. -
Parser:
nVarSampleDivider(per-channel variable sample divider) is now read from its correct version-dependent offset instead of the channel-header fixed struct: offset 152 for Post-4 files (revision ≥ 68, channel header ≥ 154 bytes), offset 250 for Pre-4 files (revision ≥ 44, channel header ≥ 252 bytes). Older files default to divider = 1.
0.2.2 - 2026-05-15
Added
- CLI:
biopac plot <file.acq>— renders channel waveforms as a tiled PNG or SVG image using plotters. Supports--output,--format png|svg,--width,--height-per-channel,--channels(by name or 0-based index), and--start/--endtime-window clipping. Gated behind the optionalplotfeature.
Changed
- Dependencies: upgraded
arrowandparquetfrom 54 → 58.
Fixed
- CI: Four clippy lint errors in
examples/write_file.rs(single_match_else,option_if_let_else,cast_precision_loss×2) that were causing theLintjob to fail since thewritefeature was added. - CI:
build.rstriggeredclippy::map_unwrap_or; replaced.map(…).unwrap_or(false)withis_ok_and(…). - CI: Restored
RUSTSEC-2024-0436advisory ignore indeny.tomlthat was inadvertently dropped during the arrow/parquet 54 → 58 upgrade;paste 1.0.15remains a transitive dependency viaparquetand has no patched version available. - Packaging: excluded
.mcp.json,.vscode/,.github/,deny.toml,plan.toml,docs/, andreference_projects/from the crates.io package manifest to prevent publish failures caused by dangling symlinks in the working tree.
0.2.1 - 2026-05-15
Added
- CLI:
--versionnow reports the git commit SHA and commit date (e.g.biopac 0.2.1 (git:abc12345 2026-05-15)). Falls back tocrates.iowhen installed from the registry.
Fixed
- CLI:
biopacwith no arguments now prints help instead of an error. Unknown flags and subcommands exit 2 with a--helphint; parse errors are handled explicitly viatry_parse()rather than clap's internal exit. - CI: Silenced
cargo-denyfalse-positive forRUSTSEC-2024-0436(pasteunmaintained); the crate is a transitive dependency viaparquet→ahashand is not directly actionable.
0.2.0 - 2026-05-15
Fixed
- Parser: corrected file-version offset — skips the unused
i16prefix at byte offset 0 that was being misread as part of the version field, fixing version detection on all v30+ files.
Changed
- Security (T16–T18):
deny.tomlhardened with stricter advisory, license, and source policies;cargo-denyandcargo-auditadded as scheduled CI checks viasecurity.yml. - Style:
rustfmtformatting pass across the writer, inspect, andarrow_exportmodules.
Added
- CI/CD pipeline: complete GitHub Actions workflow suite —
ci.ymlextended withfmt,docs(RUSTDOCFLAGS=-D warnings), andmsrv(1.95.0) gates alongside the existing test and deny jobs.auto-tag.yml: creates an annotated semver tag after CI passes on achore(release):commit, usingcargo metadatato read the version.release.yml: builds cross-platformbiopacbinaries (Linux x86-64, macOS ARM/x86, Windows x86-64), publishes to crates.io, and creates a GitHub Release with checksums. Guarded by averify-cipolling step.security.yml: weekly secret scan (gitleaks),cargo audit, andcargo denyon a schedule and on Cargo file changes.dependabot-automerge.yml+dependabot.yml: auto-merge patch/minor Dependabot PRs for both Cargo and GitHub Actions ecosystems.
- Local secret scanning:
gitleaks protect --stagedpre-commit hook.
0.1.0 - 2025-07-01
Added
Parser & Core (T01–T06)
- Binary parser for BIOPAC AcqKnowledge
.acqfiles across all known format versions (v30 through v84+) using declarativebinrw-based header structs. - Version-dispatched parsing:
FileRevisiondetermines which header layout is read; single code path handles all variants cleanly. - Support for both uncompressed and zlib-compressed data payloads.
- Mixed sampling-rate support: each channel carries its own
samples_per_secondandfrequency_divider, correctly computed from the global rate stored in the graph header. - Event-marker parsing:
Marker,MarkerStyle, andTimestampdomain types with full textual label support. - Journal section parsing: raw journal text exposed as
Journal::as_text(). - Foreign-data section detection and graceful skip-forward with a
Warning. ParseResult<T>wrapper that accumulates non-fatalWarnings alongside the value; callers iterateresult.warningsbefore callingresult.into_value().
Domain Model (T02–T03)
- Rich domain types:
Datafile,GraphMetadata,Channel,ChannelData,Marker,MarkerStyle,Journal,Timestamp,FileRevision,ByteOrder. Channel::scaled_samples()converts rawi16integers tof64via per- channel scale and offset; linear-interpolation upsampling for sub-rate channels.ChannelDataenum:Scaled { raw, scale, offset }for the common case;Raw(Vec<i16>)for unprocessed access.- Typed error hierarchy via
thiserror:BiopacErrorwith variants carrying byte offsets and expected-vs-actual values for triage of corrupt files.
Write Support (T07, feature write)
- Round-trip write support:
write_fileserialises aDatafileback to the BIOPAC binary format with bitwise fidelity on read-modify-write cycles. WriteOptionsfor controlling output behaviour (byte order, version).- Feature-gated behind
writeto keep the default dependency footprint minimal.
Export (T08–T10)
- CSV (
default):to_csvwithCsvOptions(delimiter, time column,TimeFormatenum for elapsed seconds vs. sample index). - Arrow IPC (feature
arrow):export::arrow::to_arrow_ipcwrites an Arrow IPC stream compatible with Polars, Rarrow, and Julia. - Parquet (feature
parquet):export::parquet::to_parquetwrites a Parquet file suitable for direct loading in DuckDB, Spark, or Pandas. - HDF5 (feature
hdf5):export::hdf5::to_hdf5writes a hierarchical HDF5 dataset per channel.
CLI (T11)
biodreambinary with sub-commands:info,csv,arrow,parquet.info: human-readable summary of file metadata and channel list.csv/arrow/parquet: batch conversion with feature-gated availability.- Colorised output via
owo-colors; structured error reporting withanyhow.
Lazy / Streaming Reader (T12)
LazyDatafile/ReadOptionsfor deferred channel loading: reads only the channel headers on open, then streams individual channels on demand without buffering the entire file.
Testing (T13–T14)
- 222-test suite covering: unit tests, integration tests against 14 synthetic
fixture
.acqbinary files (v30–v84+ with and without compression), write round-trip tests, and property-based tests viaproptest. - Proptest strategies generate arbitrary valid
Datafilestructures and verifywrite → read → writeproduces bitwise-identical output. cargo test --workspace --all-featuresruns the full suite in CI.
Documentation & Publishing (T15)
- Full rustdoc coverage (
#![warn(missing_docs)]);cargo doc --all-features --no-depsproduces zero warnings. - Four runnable examples:
read_file,convert_csv,arrow_export,write_file. README.mdwith feature comparison table, installation instructions, feature flag reference, quick-start code,no_stdusage notes, and CLI examples.Cargo.tomlpublish metadata: description, repository, license, keywords, and categories.
Architecture
no_std-compatible core (parser + domain) withalloc;stdrequired only by I/O adapters and the CLI binary.- Feature gates:
default = ["read", "csv"]; optional:write,arrow,parquet,hdf5,serde. - MSRV: Rust 1.95.0 (edition 2024, stable toolchain only).
- Full Clippy
-W pedantic / nursery / cargo / perfprofile with zero warnings.