OpenSpecy Raman / (FT)IR .rds

Status: Supported (scoped) | Vendor: OpenSpecy (R) | Extensions: .rds | Feature flag: fmt-openspecy

OpenSpecy is an open R toolkit for Raman and (FT)IR spectroscopy of microplastics and environmental particles. It stores spectra and reference libraries as R saveRDS() objects (.rds). This reader decodes that container with the pure-Rust rds2rust parser - no R runtime, no subprocess - and maps the canonical OpenSpecy object onto records.

Instruments & software

Vendor-neutral: any FTIR or Raman spectrometer whose spectra are imported into the OpenSpecy R package (or the openspecy.org web app), including the bundled nobaseline reference libraries downloaded from OSF.

File structure

The reader is gated behind the fmt-openspecy feature, which pulls in the pure-Rust rds2rust crate. Dispatch is by .rds extension plus the R serialization container magic:

  • gzip RDS (1f 8b, probe Likely) - the saveRDS() default: a gzip-compressed XDR stream, decompressed and parsed by rds2rust.

  • uncompressed XDR RDS (X\n, probe Likely) - the same XDR stream without the gzip wrapper.

The .rds extension alone cannot prove a file is an OpenSpecy object (it is a generic R container), so the OpenSpecy shape is validated on read; a non-OpenSpecy .rds returns an actionable error.

Canonical object (OpenSpecy >= 1.0)

as_OpenSpecy() builds an S3-classed three-part list:

  • wavenumber - numeric vector, the shared x-axis (cm^-1), length W.

  • spectra - a data.table/data.frame with one column per spectrum and W rows; column names are spectrum identifiers.

  • metadata - a data.table/data.frame with one row per spectrum (spectrum_type = ftir/raman, spectrum_identity, sample_name, …).

What nirs4all-formats extracts

  • Signals - each spectra column becomes one record: a 1-D intensity signal over the shared wavenumber axis (AxisKind::Wavenumber, unit cm-1). The column name is carried as the signal source.

  • Axis - the wavenumber vector, shared by every spectrum. Raman shift and IR wavenumber are both reported in cm^-1, matching OpenSpecy.

  • Targets - the polymer / material identity (spectrum_identity, material_class) when present in the metadata.

  • Metadata & provenance - FTIR/Raman modality (from spectrum_type), the R class, the spectrum column id, the full metadata row under fields, container type, and source file + SHA-256.

Variants & support status

Variant

Status

Notes

Canonical OpenSpecy list (gzip RDS)

Supported

wavenumber + spectra + metadata; one record per spectrum.

Canonical OpenSpecy list (uncompressed XDR RDS)

Supported

Same mapping, no gzip wrapper.

Legacy single-spectrum data.frame (wavenumber + intensity)

Supported

Emitted as one record.

Attribute-stripped or metadata-free large library

Supported (degraded)

Parts located structurally by shape; spectra load, but metadata is only emitted if present in the RDS object.

bzip2 / xz-compressed .rds

Not supported

Only gzip + uncompressed XDR are decoded.

Limitations & known gaps

  • Only the default saveRDS() containers are decoded (gzip and uncompressed XDR). bzip2/xz-compressed .rds files are rejected with a parse error.

  • Intensity semantics are reported as unknown unless the metadata names them via an intensity_units field (mapped to absorbance/transmittance/…).

  • Some public nobaseline.rds exports contain a NULL metadata slot. The spectra and wavenumber axis still load via structural shape detection, but per-spectrum metadata (identity, modality) cannot be reconstructed from that RDS object alone.

Reference readers

OpenSpecy::read_spec() / readRDS() in R. nirs4all-formats adds container sniffing, structural fallback, axis/modality typing, target mapping and provenance on top of the rds2rust decode.

Samples & validation

A committed, network-free fixture under samples/openspecy/: synthetic_minilib.rds - a minimal canonical OpenSpecy object with 3 spectra (2 ftir, 1 raman) over 6 wavenumbers and a 4-column metadata table, written with the rds2rust RDS writer (CC0). It is golden-backed in crates/nirs4all-formats/tests/goldens/ and exercised end-to-end (path + bytes) in crates/nirs4all-formats/tests/openspecy.rs; the reader’s structural fallback and legacy-data.frame paths are unit-tested in the reader module. Real OpenSpecy nobaseline libraries are large and license-restricted, so they are validated out-of-tree rather than vendored.