Changelog
All notable changes to this project are documented here. The format follows
Keep a Changelog, and the project uses
semantic versioning. Each entry is one line: what changed and what to do about it. The full
old → new API table and output-format detail for the 0.1.0 break are in the migration guide
(Migrating to 0.1.0 in the docs; docs/source/migrating_0_1_0.rst).
[Unreleased]
[0.2.0] - 2026-10-24
Breaking changes
Density-family output values change where the basestation mislabelled their units. The basestation writes
density,density_insitu,sigma_tand (on basestation 2.8)sigma_thetaasg/m^3while holding kg m-3-magnitude values; 0.1.0 trusted the label, applied a factor, and wrote them 1000x too small. The label is now corrected tokg m-3before conversion, so no factor runs and the output equals the basestation value, with the basestation label kept asoriginal_unitsand noted incomment:variable
basestation label
0.1.0 wrote
now
DENSITY(sg271)g/m^31.027
1027
SIGMA_T(sg271, sg005)g/m^30.027
27.3 / 27.1
SIGTHETA(sg005)g/m^30.027
1027 (full density; see below)
A file read from 0.1.0 differs; reconvert. On basestation 2.8
sigma_thetaholds full density (~1027), not the anomaly its name implies; the value is not altered (it is left as-is and flagged on the conversion page, never silently changed), soSIGTHETAcarries density magnitude on those files — correct it withdrop_variablesor amission.yamloverride.gsw_sigma0is no longer mapped toPOTDENS0.gsw_sigma0is the density anomaly (sigma0 = potential density − 1000); OG1POTDENS0is full potential density, so the mapping produced aPOTDENS0~1000 out of range.gsw_sigma0now passes through under its own name;POTDENS0is absent until it is derived properly in a later release. The conversion page recommends droppinggsw_sigma0.A basestation units spelling that no view resolves is now refused with
UnitsErrorrather than written through unchecked. Every source units string resolves to the OG1 variable’s canonical spelling and a factor through one view (vocabularies.UNITS) over the vendored units layer; add a new spelling there (with its source and a verified factor) or correct the label in the mission config.The wrong-quantity oxygen mappings are removed:
dissolved_oxygen → DOXYanddissolved_oxygen_sat → OXYSAT. The sources were a per-mass concentration and a saturation mapped onto OG1 quantities they are not; they now pass through under their own names and are recommended for drop.DOXYcomes only fromoxygen_concentration,OXYSATonly from the percentO2_satsource.The three ad-hoc unit tables in
vocabularies(unit_str_format,unit1_to_unit2,preferred_units) are removed; access raises namingvocabularies.UNITS. The threetoolsunit helpers (reformat_units_str,reformat_units_var,convert_units_var) are deprecated (warn, removed 0.3.0) and now resolve throughvocabularies.UNITS: a spelling the view cannot relate raisesUnitsErrorinstead of the old silent pass-through, and theunit_format/unit1_to_unit2dict arguments are gone.
Added
unit_correctionsmission.yaml key: per-mission units-label corrections keyed by the basestation variable name ({from, to}, withfrom: nullfor an unlabelled source), applied before conversion in origin terms and layered over the packageOG1_unit_corrections.yamlso a mission entry wins on a shared key. For a one-off mislabel on one mission; the package file carries the systematic cross-mission defects.Conversion report “Unit corrections” section: one row per variable the conversion corrected (label fixed, value unchanged), converted (values scaled by the shown factor), or adopted an OG1 label for (source carried no units).
drop_variablesmission.yaml key: a list of variables to leave out of the output, by OG1 or basestation name (matched throughoriginal_name), the counterpart ofkeep_variables. OG1-mandatory variables are never dropped; a dropped variable’s_QCcompanion goes with it; dropped names are recorded inconversion_dropped_variables.The conversion page recommends a
drop_variablesblock for variables OG1 defines differently (first entrygsw_sigma0), as a copyablemission.yamlsnippet.
Fixed
The basestation units-label corrections live in
config/OG1_unit_corrections.yaml, keyed by the basestation variable and guarded by the label as written (so sg271’s correctly-labelledsigma_thetais untouched). A value a unit conversion moved outside the OG1 variable’svalid_min/valid_max— undoing the conversion lands it back in range — raisesUnitsError(new, inseagliderOG1.exceptions) rather than shipping a file off by a unit factor. Values out of range for other reasons are left as-is and flagged on the page, never silently changed. Every correction is logged once per variable.DELTA_TIME_Skeeps itssecondsunit.encode_times_og1stripped the unit from every variable whose name containedtime, including the numericDELTA_TIME_Sduration; it now acts only on datetime variables, whose CF units belong in encoding.
[0.1.0] - 2026-10-22
Breaking changes
The public API is a config-first set of verbs on the top-level package:
read,convert,write,report,enrich,process,process_missions, plusmission.load/mission.check.process("mission.yaml")loads a configuration and runs read + convert + write (+ report) for one mission, so a Python user no longer hand-merges the three global-attribute sections or passes a dozen keyword arguments.to_og1is renamedconvertand takes amission=path or mapping;enrichmoved to the package (andwriters) and takesmission=; the batch primitiveprocess_missionis now internal. The conversion moduleconvert.pyis renamed_convert.py, the mission-config schema moved to a newseagliderOG1.mission, and the conversion-record readers are no longer a public API. See the migration guide.mission.yamlgroups the user-set global attributes into three sections named for the conversion page’s convention groups —identity_and_discovery:,rights_and_access:,provenance_and_processing:— replacing the singledeployment:block, so the keys you fill name the page group they land in.config_versionis now3;validaterejects an older version (and a straydeployment:block), naming the sections, andseagliderOG1 initregenerates a file.program,program_vocabularyandnetworkleave the sections for the forthcoming local-config overlay (set them underglobal_attributes:until it lands); alocal_config:key is reserved (must benull—validateerrors on a non-null value, since a dropped override would make a wrong file; noconfig_versionbump when the feature lands).convert/enrichkeep their singledeployment=mapping argument.The data mode defaults to
realtime(id and file name suffixR) when neither the config norprocess --modesets it; it wasdelayed.convertdefaults tomode="realtime", andinitwritesmode: realtime. Setmode: delayedfor a delayed-mode file.The basestation
naming_authority(edu.washington.apl),uuid(one dive file’s, written asuri),summary(written assite) andfile_versionare no longer carried into the OG1 file: they describe the basestation files, not the OG1 dataset.naming_authorityis anidentity_and_discovery:key (written byinit).When the config lists
institutions, the basestationinstitutionglobal (free text, a postal address on sg005) is no longer written as a role-less contributing institution beside them; the conversion page offers its registry match as aninstitutions:entry instead. Without config institutions it is kept as before.The basestation sensor-string variables keep their names (
sbe41,sbe43,wlbb2f); they were written asSeaBird SBE 41 CTD,Seabird SBE43F,Wetlabs BB2FL-VMT(names with spaces). The sensor-key map moved fromOG1_variable_names.yamltoOG1_sensor_names.yaml(vocabularies.sensor_names).writers.writerefuses an attribute netCDF cannot store (a dict, a bool) with aTypeErrornaming it, before anything is written; it used to convert such attributes to text and retry.CLI flags in the family shape:
init [dest](a directory getsmission.yaml, a.yamlpath is that file) replacesinit -o FILE;--raw-dirreplacesinit --fromandprocess --source; themission.yamlkey israw_dir(source:is read with a deprecation warning for one release; both set is an error).inspectprints text only: the HTML page isseagliderOG1 report(inspect -o/--titleexit 2 and say so).The conversion record binds to its file by
id+tracking_id(wasid+date_created; an older conversion record is still read) and records the file’s SHA-256.Raw WET Labs counts are no longer written as
BBP700/BBP470(they were labelled m-1); they keep their basestation names (eng_wlbb2f_redCount,eng_wlbb2f_blueCount) withunits: counts. A volume scattering function (*_bb700, m-1 sr-1) is no longer written asBBP700either.A second or later basestation variable mapping to the same OG1 name is numbered with an underscore (
DEPTH_2, wasDEPTH2); the first keeps the plain name.log_VBD_MINis no longer mapped toVBD_MIN_CNTS(whichsg_cal_vbd_min_cntsholds); it is written under its basestation name (wasVBD_MIN_CNTS2).Public API reshaped to the OceanGliders family convention — modules, functions and arguments renamed (
convert.to_og1,writers.write,readers.read_mission, …). See the migration guide for the full old → new table.Output variable dtypes changed: QC flags
int8(6for unsampled, no_FillValue),PHASEint8,PROFILE_NUMBER/DIVE_NUMBER/VBD_MIN_CNTSint16,DEPTH/DEPTH_Zfloat32. A reader that assumes the old dtypes or testsnp.isnanon a QC flag must adapt.Time units are
seconds since 1970-01-01T00:00:00Z/gregorian(was… 00:00:00/standard); the instant is unchanged. TEMP units aredegree_Celsius(wasCelsius).Output
featureTypeistrajectory(wastrajectoryProfile);ConventionsisCF-1.10, ACDD-1.3, OG-1.0(wasCF-1.10,OG-1.0).Minimum Python raised to 3.11.
Platform, contributor and institution metadata come from
mission.yaml, not package defaults: a missingPLATFORM_SERIAL_NUMBERraises (wassg000); other platform fields are"UNK"with a warning (was0000000/ a hard-coded model); noPIrole default; no default EDMO code.A missing OG1-mandatory global is no longer written as a placeholder (
null/null@null.com);convert.to_og1raisesMetadataError. A config that relied on the fabricated email must set a realcontributor_email.global_attributesmay not set a key with a structured home in one of the three global-attribute sections (validateand the converter refuse it, naming the section);idis overridable underidentity_and_discovery:.Per-dive-varying scalars are written as a JSON-string scalar variable instead of broadcast onto
N_MEASUREMENTS(#117); decode the JSON to read them.
Added
A bundled sg271 sample mission (three dives, Drygalski Trough, PycnMix) ships inside the package:
readers.load_sample_basestation()returns it,readers.sample_mission_dir()gives its path, andseagliderOG1 init --examplewrites its config pointing at the packaged dives so the demo runs from apip install. Theinittemplate lists the OG1tracking_systemandbattery_typevocabularies.A
deployment_information:block inmission.yaml(DEPLOYMENT_TIME,DEPLOYMENT_LATITUDE,DEPLOYMENT_LONGITUDE) sets the OG1 deployment variables from the cruise log instead of deriving them from the first in-water fix; aDEPLOYMENT_TIMEafter the first data sample warns.The v0.0.2 names are importable for one release and warn: a renamed name (e.g.
readers.load_basestation_files,convertOG1.convert_to_OG1,writers.save_dataset) delegates to its 0.1.0 spelling and emits aSeagliderDeprecationWarningnaming it; a removed name (e.g.convertOG1.process_and_save_data,vocabularies.load_default_contributors) raises with a message pointing at the migration guide. All of it is removed in 0.3.0. See Migrating to 0.1.0.sensors:inmission.yaml(to_og1(sensors=...)): correct a basestation sensor record’sserial_numberandcalibration_dateby its sensor key; the corrected serial names theSENSOR_*variable, and itscommentrecords the replaced value.Conversion page: a Sensors named by OG1 table (type, maker, model, serial, calibration date, the variables using each sensor; amber for a placeholder or shared serial); suggestions for a
identity_and_discovery.idfrom a conversion starting at dive 1, and for registry entries close to an unmatched institution name.creator_*/publisher_*are no longer suggested (OG1 usescontributor_*). The missing-globals box also lists the OG1 highly-desirable globalsvalidatewarns about, each labelled with its tier.validatereports aglobal_attributeskey that belongs in a global-attribute section (naming the section and the lines to write) or that the converter computes, before any file is read;processused to refuse it only after loading the mission.Conversion page: mission.yaml against the data, copyable
mission.yamllines where the requesteddivesor a config platform field (serial, firmware, basestation version, WMO id) differs from the basestation files, where platform fields were written asUNKor omitted, where the EDMO registry resolved an institution id the config left null, and for missing ACDD globals (under a global-attribute section orglobal_attributes:). The conversion record records thedives,platformandinstitutionsvaluesprocess_missionwas given.Three ACDD globals derived from the data:
geospatial_bounds(lat/lon bounding box as a WKT polygon, latitude first),geospatial_vertical_positive(downorup, from the sign of mean DEPTH), andtime_coverage_duration(end − start as an ISO 8601 duration). Recorded asderivedinconversion_attribute_sources.CONTRIBUTING.md: licensing of contributions, credit and authorship, and scope.seagliderOG1 draft --raw-dir DIR: convert with no config to see what the basestation files hold —<id>_draft.nc(adraftglobal, noOG-1.0inConventions), a draft conversion page with the basestation’s own global attributes, the conversion record, and a proposed<id>_draft_mission.yaml.convert.process_mission(draft=True).seagliderOG1 report FILE.nc [-o DIR | --report-dir ROOT]writes the conversion page;process --report-dir ROOTwrites it toROOT/<id>/, apart from the data.seagliderOG1 enrich FILE.nc [CONFIG] [--doi DOI]/convert.enrich: set globals (e.g. a DOI) on a written file in place; setsdate_modified, appendshistory, renewstracking_id, keepsdate_created, rebinds the conversion record.A
tracking_idglobal (UUID, new on every write);writers.file_sha256,writers.write_sidecar.Conversion page: a conventions box under the global attributes — what
Conventionsclaims, ACDD 1.3 highly recommended / recommended present (lists from the IOOS compliance-checker 6.1.0), and dimensioned floating-point variables withoutunits.seagliderOG1CLI:init,validate,process,inspect,list;-v/-vvfor diagnostics.seagliderOG1.contributorsmodule andconfig/institution_registry.yaml(EDMO codes).convert.to_og1gainscontributors=,institutions=,platform=,deployment=,global_attributes=,mode=;convert.name_mapping(datasets)returns the variable-mapping table.Three global-attribute sections in
mission.yaml—identity_and_discovery:,rights_and_access:,provenance_and_processing:— withconfig_version: 3.Optional platform variables:
PLATFORM_NAME,TELECOM_TYPE,TRACKING_SYSTEM,BATTERY_TYPE,BATTERY_PACK,platform_maker_vocabulary.required:tier block inconfig/OG1_global_attributes.yaml→vocabularies.REQUIRED_GLOBALS.seagliderOG1.exceptions.MetadataError.conversion_attribute_sourcesglobal: JSON provenance of each written global (names only).Conversion record: per-variable
original_nameon every variable, and theconversion_dimensions,conversion_derived_variablesandconversion_dropped_variablesglobals record each dimension’s fate, the converter-created variables, and anykeep_variablesdrops;inspect --conversionprints them from the file.Scalar parameters carry
storage_type(constant_scalarorper_dive_scalar);inspect --conversionlists them in a Scalar parameters section andinspect --parameter NAMEdecodes a per-dive JSON scalar to a dive/value table.Template lines name each key’s destination (variable / attribute / global);
validategives a case hint on a mis-cased key.writers.writeapplies lossless zlib compression to numeric variables.convert.process_mission(source, output_dir, ...): one mission read → convert → write, the library form ofprocess; the CLI calls it per mission.seagliderOG1 list globals | platform | variables [NAME] | sensors: the OG1 globals with tier andmission.yamlhome, theplatform:keys, the OG1 variables with their attributes and basestation sources, and the sensor catalogue, read from the converter’s own tables.process --report(andreport:inmission.yaml) writes the HTML conversion page<id>_conversion.htmlbeside each file.processwrites<id>_conversion.jsonbeside each OG1 file: the input files and the dives each basestation variable is present and valid in. The conversion page shows it as a Presence across dives section;processprints a one-line summary when the dives disagree.convert.presence_by_dive,presence_patterns,presence_summary,conversion_sidecar.data/demo_sg005/mission.yaml: a filled-in example configuration for the bundled sample (writes to the gitignoredoutputs/); the demo notebook runs it throughconvert.process_mission.Conversion page “The OG1 file” section: the output read by what OG1 requires and names rather than by basestation origin — Required by OG1 (present/absent + where each came from), Named by OG1, a Candidate-for-OG1 tier the project curates, Carried-through families, and a “same quantity, more than once” comparison.
vocabularies.OG1_REQUIRED_VARS(the upstream checker’s list) andvocabularies.OG1_CANDIDATE_VARS(the project’s uplift list) back it.
Changed
The readers (
read_mission,read_file,list_files,discover_missions,repair_files) accept apathlib.Pathas well as a string.vocabularies.VARIABLES_TO_DROPis empty: the 24 basestation variables it removed (e.g.density,theta,dissolved_oxygen_sat, the*_gsmflight-model set) are now in the output.depthandeng_depthmap toDEPTH, whichctd_depthholds, and are written asDEPTH_2/DEPTH_3.Conversion page reordered for a first read: a Before → after summary and the Reconciliation with a Meaning column first, scalar parameters last and collapsed; Presence and Dimensions gain percentage and Samples in columns, and each Variables group states the Valid % baseline its dimension sets.
<id>_conversion.jsoninput records gain each file’s dimension lengths and global-attribute count (additive; an older conversion record still renders).seagliderOG1 listprints each registry as an aligned table with a header and a one-line summary; bareseagliderOG1 listnames the registries;--search TEXTfilters rows.Packaged config files and
vocabulariestables renamed to whole words (package data, no API change):OG1_variable_names.yaml,OG1_variable_attributes.yaml,OG1_sensor_attributes.yaml,OG1_global_attributes.yaml;DIMENSION_RENAMES,VARIABLES_TO_DROP,VARIABLES_AS_IS,ATTRIBUTE_ORDER.Conversion retains basestation variables previously dropped, merged onto a shared time axis with
original_name/original_dimensionprovenance (#117).Basestation
commentis no longer renamed tohistory;historyis a single conversion provenance line (the basestation per-dive processing log is not carried).Coverage gate floor raised from 75 to 82 (
mainis at 83%).Dependencies in
pyproject.tomlonly (requirements*.txt/environment.ymlremoved); ruff-family config,ruff formatreplacesblack, CI coverage gate.
Fixed
Conversion page: the presence-strips colorbar sits in its own column; it overlapped the strips (and warned “Axes that are not compatible with tight_layout”).
The
inittemplate no longer saysprovenance_and_processing.commentoverrides a comment “derived from the file’s history”: none is derived; it overrides acommentglobal the basestation files carry.data/demo_sg005/mission.yamlnames its OperatorUniversity of Washington School of Oceanography, a registry variant, so it carries the EDMO id; asUniversity of Washingtonit matched no registry entry.The EDMO registry resolves
University of Hamburgto EDMO 3323 (the current university) rather than 1156 (the dissolved Institute of Oceanography); the institute names remain as aliases.Conversion page:
contributing_institutions_roleand_role_vocabularyare grouped under People & institutions (were under Other).date_createdis the conversion time; the basestation files’ owndate_created(sg005: 2014-03-11) was written instead, becauseOG1_global_attributes.yamlkept it as-is.date_modified(and sodate_created) is UTC; it was the converting machine’s local time.LATITUDE_GPS,LONGITUDE_GPSandTIME_GPScarryoriginal_dimension: gps_info;TIME_GPSkeeps its vocabularylong_name(all attributes were removed).PHASE,PROFILE_NUMBERandDIVE_NUMBERtake their attributes from the vocabulary (PHASEandPROFILE_NUMBERhad none);PHASE_QC’slong_namewas" quality flag".Conversion is about 2.5 times faster (sg005, 5 dives: 7.5 s → 2.9 s), output unchanged: splitting a dive by dimension, the dtype pass and the scalar parameters rebuilt a dataset once per variable.
The fate figure on the conversion page writes
->, avoiding a missing-glyph warning.A sensor key in the
instrumentglobal with no entry inOG1_sensor_names.yamlis skipped with a warning (noSENSOR_variable); the conversion failed withKeyError.init --fromand the converter read dotted version strings (base_station_version: "3.0.6") from newer basestation files instead of failing;init --fromalso accepts onepSSSDDDD*.ncfile.inspect --conversion -o DIRaccepts a directory and writes<id>_conversion.htmlinto it (was a file path only, failing withIsADirectoryError).Raw instrument count channels with no units (
*redCount,*blueCount,*fluorCount,*redRef,*blueRef) are written withunits: counts.The “dimensions not merged” warning is read from the conversion record and names only dimensions whose variables are absent (it listed merged basestation-3 dimensions).
A unit pair with no conversion warns once, not once per dive.
A version attribute stored as a string is written as is (
"2.10"stayed2.10, was2.1).Conversion page: the Before → after verdict counts required OG1 variables as well as globals; the Reconciliation Meaning texts name sections instead of numbers (they pointed one section off after the reorder); the OG1-file caption names the Orig var / Orig dim columns.
inspect --conversion -o DIR/creates the directory, and a write error exits 1 with a message instead of a traceback.The
inittemplate’sidline no longer saysinit --fromproposes an id (it never did); null means the derived<serial>_<start>_<mode>.config/OG1_variable_attributes.yamlno longer carriesplatform_model_vocabulary/platform_maker_vocabularydefaults (never written to files; the maker term named Teledyne Webb Research, the Slocum maker). Set both per glider inmission.yaml; the sg005 example now does.start_date,TRAJECTORYandidare seeded from the earliest measurement time, so they agree withtime_coverage_startregardless of input order.A null or wholly-empty global is never written;
date_createdis always the conversion time.Scalar parameters no longer carry an empty
original_dimension; the attribute is omitted when there is no source dimension.Whitespace stripped from vocabulary
long_namestrings written into output attributes.writers.writepreserves_FillValue/scale_factor/add_offset, does not mutate the caller’s dataset, and reuses one encoding on the retry path.Dtype optimisation runs once post-concat (QC flags stay
int8);set_best_dtypevisits coordinates and respects an existing_FillValue;find_best_dtypeno longer truncates*rawfloats; the writer andencode_times_og1share one canonical time-units constant.