diff --git a/Sources/FormatoCore/AcquisitionMetadata.swift b/Sources/FormatoCore/AcquisitionMetadata.swift index 97461f1..e3c69e3 100644 --- a/Sources/FormatoCore/AcquisitionMetadata.swift +++ b/Sources/FormatoCore/AcquisitionMetadata.swift @@ -75,7 +75,7 @@ public nonisolated struct AcquisitionMetadata: Sendable, Equatable { /// The instrument, as the file names it. public var instrument: MetadataReading? /// The operating mode as the file states it: a name ("STEM", "TEM · DIFFRACTION") for Velox and DM; the stored - /// integer for MRC, labelled unvalidated (no pinned reference decodes it). + /// integer for MRC, with a note that no published reader decodes it. public var mode: MetadataReading? // MARK: Added in 0.3.1 (additive). Read by the `.rosettaSciIO` convention only, where RosettaSciIO 049e7d70 maps the key @@ -120,7 +120,7 @@ public nonisolated struct AcquisitionMetadata: Sendable, Equatable { /// RosettaSciIO uses it as the axis scale, not as metadata, so the `note` says what the file does not settle. public var pixelSize: MetadataReading? /// Convergence semi-angle in mrad. A Velox EELS spectrum image (`velox(eelsSpectrumImage:)`, `.rosettaSciIO` only) also fills - /// it, labelled unvalidated: no published reader reads the stored value. + /// it, with a note that no published reader reads the stored value. public var convergenceAngleMrad: MetadataReading? // MARK: Added after 0.11.0 (additive). Read by the DM reader from an image object (`AcquisitionMetadata.dm(_:convention:)`), and @@ -132,7 +132,7 @@ public nonisolated struct AcquisitionMetadata: Sendable, Equatable { /// is the first and `rows` the second, units "um" as the tag names them; the note says so when the two differ. Not the /// binned frame pixel (`Acquisition.Frame.CCD.Pixel Size (um)`). Nil when the tag is absent, does not hold exactly two /// numbers, or holds one that is not finite and positive. RosettaSciIO maps the tag for a CL signal only (its - /// `Detector.pixel_size`, also in `signalNumbers`); on any other signal `.rosettaSciIO` labels it `unvalidatedNote`. The + /// `Detector.pixel_size`, also in `signalNumbers`); on any other signal `.rosettaSciIO` notes that no published reader reads it. The /// `.app` convention reads it too (an extension of the consumer's rule set, which has no pitch), unlabelled. public var cameraPixelPitch: MetadataReading? @@ -206,7 +206,8 @@ public nonisolated struct AcquisitionMetadata: Sendable, Equatable { /// /// `.rosettaSciIO` (the default) follows RosettaSciIO at the pinned commit for every field RosettaSciIO maps: its tag /// choice, its mode tests and its unit arithmetic, with no positivity rule (a stored zero is 0, not nil). A field -/// RosettaSciIO does not map is read by the consumer's rule and carries the note ``AcquisitionMetadata/unvalidatedNote``. +/// RosettaSciIO does not map is read by the consumer's rule and carries the format's wording of +/// ``AcquisitionMetadata/noPublishedReaderNote``. /// `.app` is the consumer's own rule set (a to g above), pinned to its source; `docs/parity.md` lists the differences. public nonisolated enum AcquisitionMetadataConvention: Sendable, Equatable { case rosettaSciIO @@ -214,6 +215,9 @@ public nonisolated enum AcquisitionMetadataConvention: Sendable, Equatable { } extension AcquisitionMetadata { - /// The note of a value no RosettaSciIO mapping reads. - public static let unvalidatedNote = "unvalidated: no published reader reads it" + /// The note of a value no RosettaSciIO mapping reads: it is read as the file stores it. Each format's reader attaches + /// its own wording of this note, naming where in the file the value is stored. + public static let noPublishedReaderNote = "no published reader reads this field; read as the file stores it" + /// The same note under its older name. + public static let unvalidatedNote = noPublishedReaderNote } diff --git a/Sources/FormatoDM/AcquisitionMetadataDM.swift b/Sources/FormatoDM/AcquisitionMetadataDM.swift index 2f52409..475caf2 100644 --- a/Sources/FormatoDM/AcquisitionMetadataDM.swift +++ b/Sources/FormatoDM/AcquisitionMetadataDM.swift @@ -25,7 +25,7 @@ // the dwell with DigiScan tags, else the exposure; X-ray: neither; CL: the DigiScan sample time). The EELS frame number, // aperture and spectrometer, the X-ray (EDS) and CL groups and the SI group are read into `signalNumbers`/`signalTexts` under // RosettaSciIO's own path names, and the date and time take the last source that parses (DataBar, SI, then the signal group; for CL DataBar again, as the mapping ends). -// Fields RosettaSciIO does not map (the detector) are read by the consumer's rule with `unvalidatedNote`. The +// Fields RosettaSciIO does not map (the detector) are read by the consumer's rule with `DMNote.unvalidated`. The // signal-type mappings (EELS, X-ray, CL, SI) are applied from the tag maps (`Meta Data.Signal`), not from a signal object. // // `.app` is the consumer's rule set (AcquisitionMetadata.swift, rules a to g), pinned to its source: @@ -139,7 +139,8 @@ extension AcquisitionMetadata { nonisolated enum DMNote { static let cameraLengthUnit = "mm (assumed)" static let monthFirst = "month-first order assumed" - static let unvalidated = "unvalidated: no published reader reads it" + /// This format's wording of `noPublishedReaderNote`: the value is read as the DM image tags store it. + static let unvalidated = "no published reader reads this field; read as stored in the DM image tags" } /// The two ImageTags maps with the stack-builder fallback: the direct key, else the same key under @@ -267,10 +268,10 @@ extension AcquisitionMetadata { private static let dmCreationPrefix = "source.Tags at creation." /// `Acquisition.Device.CCD.Pixel Size (um)` as a pitch: exactly two finite positive numbers (x, y), else nil. `unvalidated` - /// adds `unvalidatedNote` (`.rosettaSciIO` on a signal RosettaSciIO does not map the tag for). + /// adds `DMNote.unvalidated` (`.rosettaSciIO` on a signal RosettaSciIO does not map the tag for). private static func dmCameraPitch(_ stored: [Double]?, unvalidated: Bool = false) -> MetadataReading? { guard let stored, stored.count == 2, stored.allSatisfy({ $0.isFinite && $0 > 0 }) else { return nil } - let notes = [stored[0] == stored[1] ? nil : unequalPitchNote, unvalidated ? unvalidatedNote : nil].compactMap { $0 } + let notes = [stored[0] == stored[1] ? nil : unequalPitchNote, unvalidated ? DMNote.unvalidated : nil].compactMap { $0 } return MetadataReading(PixelSize(rows: stored[1], columns: stored[0], units: "um"), note: notes.isEmpty ? nil : notes.joined(separator: "; ")) } @@ -463,7 +464,7 @@ extension AcquisitionMetadata { } // RosettaSciIO maps no detector: the camera name as stored, labelled. - if let name = text(string("Acquisition.Device.Name")) { meta.detector = MetadataReading(name, note: unvalidatedNote) } + if let name = text(string("Acquisition.Device.Name")) { meta.detector = MetadataReading(name, note: DMNote.unvalidated) } // The camera pitch, every signal type: the tag as stored. RosettaSciIO maps it for CL only (`Detector.pixel_size` above), // so on any other signal it is the consumer's rule, labelled. meta.cameraPixelPitch = dmCameraPitch(arrays[prefix + dmDevicePitchKey], unvalidated: signalType != "CL") diff --git a/Sources/FormatoEMD/AcquisitionMetadataVelox.swift b/Sources/FormatoEMD/AcquisitionMetadataVelox.swift index f69d907..20849ef 100644 --- a/Sources/FormatoEMD/AcquisitionMetadataVelox.swift +++ b/Sources/FormatoEMD/AcquisitionMetadataVelox.swift @@ -7,7 +7,7 @@ // field: `Optics.AccelerationVoltage` / 1e3 (kV), `Optics.CameraLength` * 1e3 (mm, in every mode), // `CustomProperties.StemMagnification.value` as the magnification (Velox stores an unset bool as 0; it is returned as // stored); no positivity rule. RosettaSciIO maps no detector, dwell, exposure, mode or date for an image; those are read -// by the consumer's rule with `unvalidatedNote`: the detector `BinaryResult.Detector`, the instrument +// by the consumer's rule with `veloxUnvalidated` (no published reader; read as stored): the detector `BinaryResult.Detector`, the instrument // `Instrument.InstrumentModel` (`.app`; under `.rosettaSciIO` it is `instrumentModel`, and `instrument` is RosettaSciIO's `InstrumentClass`), the dwell `Scan.DwellTime` only for a // ScanningDetector, the exposure of the detector the image names (`VeloxMetadata.exposureTimeSeconds`), the magnification // `Optics.NominalMagnification` when no `StemMagnification` is stored, the mode `Optics.OperatingMode` as the stored @@ -18,7 +18,7 @@ // the note says it is not a metadata mapping) under `.rosettaSciIO`, and `velox(spectrumImage:)`, the same fields for the metadata // of a spectrum image (`VeloxEMDReader.spectrumImageMetadata()`). `velox(eelsSpectrumImage:)` reads the same fields from one EELS // spectrum image's metadata (`VeloxEMDReader.eelsSpectrumImageMetadata()`) plus the collection and convergence semi-angles, where -// RosettaSciIO maps nothing (the collection angle as pyEELSMODEL reads it; the convergence angle labelled unvalidated). +// RosettaSciIO maps nothing (the collection angle as pyEELSMODEL reads it; the convergence angle noted as read by no published reader). // // `.app` is the consumer's reading of the same entry, pinned to its source: high tension, detector, instrument, start // date and dwell only; magnification, camera length, exposure and mode are `unread` (the consumer's reader predated the @@ -44,7 +44,7 @@ nonisolated extension AcquisitionMetadata { if let stem = m.stemMagnification { out.magnification = MetadataReading(stem) } else if let nominal = positive(m.nominalMagnification) { - out.magnification = MetadataReading(nominal, note: unvalidatedNote) + out.magnification = MetadataReading(nominal, note: veloxUnvalidated) } // The microscope is `Instrument.InstrumentClass` (`_get_mapping`, :947-950), as stored; the stored model is kept beside it. out.instrument = m.instrumentClass.map { MetadataReading($0) } // as stored, as RosettaSciIO passes it @@ -66,9 +66,9 @@ nonisolated extension AcquisitionMetadata { if let unitY = text(m.pixelUnitY), unitY != unit { note += "; PixelUnitY is '\(unitY)', RosettaSciIO uses PixelUnitX for both" } out.pixelSize = MetadataReading(PixelSize(rows: size.height, columns: size.width, units: unit), note: note) } - if let seconds = positive(m.exposureTimeSeconds) { out.exposureS = MetadataReading(seconds, note: unvalidatedNote) } + if let seconds = positive(m.exposureTimeSeconds) { out.exposureS = MetadataReading(seconds, note: veloxUnvalidated) } if let mode = text(m.operatingMode) { - out.mode = MetadataReading(mode, note: "the stored code, not decoded; " + unvalidatedNote) + out.mode = MetadataReading(mode, note: "the stored code, not decoded; " + veloxUnvalidated) } return out } @@ -87,7 +87,7 @@ nonisolated extension AcquisitionMetadata { /// `velox(spectrumImage:)`, among them the beam energy `Optics.AccelerationVoltage` / 1e3 (kV, RosettaSciIO's mapping for /// the EDS signal of the same file; pyEELSMODEL's `emd_reader` reads the same volts as E0), and the two angles in mrad: /// the collection semi-angle `EnergyFilter.CollectionAngleRange.End` × 1e3 (pyEELSMODEL's beta, radians as stored) and - /// the convergence semi-angle `Optics.BeamConvergence` × 1e3 (no published reader reads it: unvalidated). An angle that is + /// the convergence semi-angle `Optics.BeamConvergence` × 1e3 (no published reader reads it; noted so). An angle that is /// not a finite positive number is nil (pyEELSMODEL passes a stored 0 through as beta; here 0 is no collection angle). The /// angles are read under `.rosettaSciIO` only; `.app` gives what `velox(spectrumImage:)` gives. public static func velox(eelsSpectrumImage metadata: VeloxMetadata, timeZone: TimeZone = .current, @@ -126,10 +126,11 @@ nonisolated extension AcquisitionMetadata { return out } - static let veloxUnvalidated = "unvalidated: no published reader reads it" - /// The registered clause failed (2026-10-13, velox-image-parity): RosettaSciIO's title comes from the file's - /// Operations, not `BinaryResult.Detector`, and differs on 7 of 33 public and owner files ("Ceta" for "BM-Ceta"). - static let veloxDetectorNote = "unvalidated: RosettaSciIO names some detectors from another field" + /// This format's wording of `noPublishedReaderNote`: the value is read as the Velox metadata stores it. + static let veloxUnvalidated = "no published reader reads this field; read as stored in the Velox metadata" + /// RosettaSciIO's title comes from the file's Operations, not `BinaryResult.Detector`, and differs on 7 of 33 public + /// and owner files ("Ceta" for "BM-Ceta"). + static let veloxDetectorNote = "read from BinaryResult.Detector; RosettaSciIO names some detectors from another field" /// Unix seconds as `YYYY-MM-DD HH:MM:SS` on `timeZone`'s clock (whole seconds, truncated as RosettaSciIO's /// `isoformat` is); nil when not finite or out of the four-digit years. diff --git a/Sources/FormatoMRC/AcquisitionMetadataMRC.swift b/Sources/FormatoMRC/AcquisitionMetadataMRC.swift index b784678..72d1907 100644 --- a/Sources/FormatoMRC/AcquisitionMetadataMRC.swift +++ b/Sources/FormatoMRC/AcquisitionMetadataMRC.swift @@ -103,7 +103,7 @@ // STEM Detector name, else Camera name, else Detector commercial name (FEI2). DEVIATION from the first sketch // (detector: STEM Detector name, else Camera name): the third source is needed because 4DSTEMscan.mrc names its // detector only there ("Ceta-S speed"). Exposure = "Integration time" (the layout has no field called exposure; -// this is the camera field in seconds) — unvalidated and labelled so (`unvalidatedNote`): mrcfile declares the +// this is the camera field in seconds) — labelled `unvalidatedNote` (no published reader; read as stored): mrcfile declares the // field (f8 at offset 419, mask 2 bit 16) and the harness plants and compares it, but no pinned reader treats it // as an exposure and no real file records a positive value. // @@ -195,10 +195,10 @@ nonisolated struct FEIRecord { static let fei1Size = 768 static let fei2Size = 888 /// The label of a value whose path no pinned reference reads (exposure, mode). - static let unvalidatedNote = "unvalidated: no published reader reads it" + static let unvalidatedNote = "no published reader reads this field; read as stored in the FEI extended header, at the offset mrcfile declares" /// RULE (c), open deviation: the label of a camera length shown without a mode test (Instrument mode and /// Projection mode are enumerations no published reader decodes). - static let modeUncheckedNote = "unvalidated: the mode is not checked" + static let modeUncheckedNote = "shown as stored; the instrument mode is not checked, as no published reader decodes it" /// A field: its byte offset in the record and the (mask, bit) that says it was written. Masks are 0...3 for /// Bitmask 1...4; their offsets are `maskOffsets`. diff --git a/Sources/FormatoTIFF/AcquisitionMetadataTIFF.swift b/Sources/FormatoTIFF/AcquisitionMetadataTIFF.swift index 9abc8a1..cd65bad 100644 --- a/Sources/FormatoTIFF/AcquisitionMetadataTIFF.swift +++ b/Sources/FormatoTIFF/AcquisitionMetadataTIFF.swift @@ -11,7 +11,7 @@ // ports) joined into one stamp; no positivity rule (a stored 0 is 0), values as tifffile's `astype` makes them; // - Velox XML: the same schema's EMD mapping (`emd/_emd_velox.py` `_get_mapping`, :934-948): `Optics.AccelerationVoltage` // / 1e3 and `Optics.CameraLength` * 1e3 with NO mode gate and no positivity rule; every other value is read by the -// consumer's rule and keeps its `unvalidatedNote`; +// consumer's rule and keeps its `TIFFNote.veloxUnvalidated`; // - the DM tag stream (tag 65027) goes through `AcquisitionMetadata.dm` with the same convention. // // WHAT IS READ, first directory only, in the order FEI SEM, Velox, DM (the first two are the order @@ -61,7 +61,7 @@ // projector mode is read only to decide rule (c); it is not part of the mode shown. // Which Velox paths a published reader maps: RosettaSciIO 049e7d70 rsciio/emd/_emd_velox.py maps // Optics.AccelerationVoltage (lines 938–941, / 1e3) and Optics.CameraLength (942–945, × 1e3, in no mode gate): those -// two carry no `TIFFNote.unvalidated`, every other value does — the same schema, no reader. In particular the date: +// two carry no `TIFFNote.veloxUnvalidated`, every other value does — the same schema, no reader. In particular the date: // the reference reads Acquisition.AcquisitionStartDatetime.DateTime (902–903, 916–918, 934–937), the EMD's unix-time // sub-key shown in the VIEWER's zone and mapped to General.time_zone; the TIFF's XML text of that element is ISO // local text with no DateTime child (measured on the six Ceta files), a path no published reader reads. (It also @@ -94,7 +94,7 @@ // [PrivateFei] TimeOfCreation (a second stamp, dd.mm.yyyy hh:mm:ss, not a registered path). // RosettaSciIO 049e7d70 rsciio/tiff/_api.py `mapping_fei` (lines 1130–1159) maps Beam.HV (× 1e-3 via // `_parse_beam_energy_FEI`, lines 1081–1085: float() or None), Scan.Dwelltime (as is), User.Date and User.Time -// (dateutil) and System.SystemType; it does not map [Detectors] Name, so the detector carries `TIFFNote.unvalidated`. +// (dateutil) and System.SystemType; it does not map [Detectors] Name, so the detector carries `TIFFNote.feiUnvalidated`. // DEVIATION: HV is divided by 1e3 here, multiplied by 1e-3 there — the same number to within one ulp. // DEVIATION: a stored 0 (HV or Dwelltime) is 0.0 there and "not recorded" here (rule b). // DEVIATION: the dwell is stated in µs here, seconds there (the registered unit). @@ -125,7 +125,9 @@ nonisolated extension AcquisitionMetadata { /// The notes this reader attaches, worded once. Private: the other readers word their own, and the tests pin /// the texts as literals. private enum TIFFNote { - static let unvalidated = "unvalidated: no published reader reads it" + /// This format's wordings of `noPublishedReaderNote`, naming the block of the TIFF the value is stored in. + static let veloxUnvalidated = "no published reader reads this field; read as stored in the Velox XML of the TIFF" + static let feiUnvalidated = "no published reader reads this field; read as stored in the FEI header of the TIFF" } /// The acquisition values a TIFF records; an empty `AcquisitionMetadata` when it records none. Throws only @@ -189,7 +191,7 @@ nonisolated extension AcquisitionMetadata { if let current = sections["EBeam"]?["BeamCurrent"]?.asFloat { out.beamCurrentNA = MetadataReading(current * 1e9) } if case .string(let user)? = sections["User"]?["User"] { out.operatorName = MetadataReading(user) } if case .string(let name)? = sections["Detectors"]?["Name"], let name = text(name) { - out.detector = MetadataReading(name, note: unvalidatedNote) + out.detector = MetadataReading(name, note: TIFFNote.feiUnvalidated) } return out } @@ -241,12 +243,12 @@ nonisolated extension AcquisitionMetadata { func child(_ element: XMLElement, _ name: String) -> String? { GuardedXML.bounded(text(element.elements(forName: name).first?.stringValue)) } - // A reading with the notes that apply: its own, then "unvalidated" unless a published reader maps its path. + // A reading with the notes that apply: its own, then the no-published-reader note unless a published reader maps its path. let readByReference: Set = ["Optics/AccelerationVoltage", "Optics/CameraLength", "Instrument/InstrumentClass", "Stage/AlphaTilt", "Stage/BetaTilt", "Stage/Position/x", "Stage/Position/y", "Stage/Position/z"] func reading(_ value: V, _ path: String, note: String? = nil, otherCopy: V? = nil) -> MetadataReading { - MetadataReading(value, note: joined(note, readByReference.contains(path) ? nil : TIFFNote.unvalidated), + MetadataReading(value, note: joined(note, readByReference.contains(path) ? nil : TIFFNote.veloxUnvalidated), otherCopy: otherCopy) } @@ -326,9 +328,9 @@ nonisolated extension AcquisitionMetadata { private static func fei(_ sections: [String: [String: String]]) -> AcquisitionMetadata { var out = AcquisitionMetadata() func stored(_ section: String, _ key: String) -> String? { text(sections[section]?[key]) } - // "unvalidated" unless mapping_fei maps the path (Beam.HV, Scan.Dwelltime, User.Date/Time, System.SystemType). + // The no-published-reader note unless mapping_fei maps the path (Beam.HV, Scan.Dwelltime, User.Date/Time, System.SystemType). func reading(_ value: V, mapped: Bool, note: String? = nil) -> MetadataReading { - MetadataReading(value, note: joined(note, mapped ? nil : TIFFNote.unvalidated)) + MetadataReading(value, note: joined(note, mapped ? nil : TIFFNote.feiUnvalidated)) } // Rule (b) after the unit conversion, as for the Velox values: a stored 5e-324 V is 0.0 kV, a stored 1e306 s is diff --git a/Sources/FormatoTIFF/TIFFAppImage.swift b/Sources/FormatoTIFF/TIFFAppImage.swift index e0634e4..bd7166d 100644 --- a/Sources/FormatoTIFF/TIFFAppImage.swift +++ b/Sources/FormatoTIFF/TIFFAppImage.swift @@ -84,7 +84,9 @@ public nonisolated enum TIFFAppImage { /// The notes a reading can attach, worded once. public enum Note { public static let render = "8-bit render, not counts" - public static let veloxUnvalidated = TIFFVeloxCalibration.unvalidatedNote + public static let veloxStoredScale = TIFFVeloxCalibration.storedScaleNote + /// The same note under its older name. + public static let veloxUnvalidated = veloxStoredScale public static let veloxDisagree = TIFFVeloxCalibration.disagreeNote } diff --git a/Sources/FormatoTIFF/TIFFVeloxCalibration.swift b/Sources/FormatoTIFF/TIFFVeloxCalibration.swift index 2b4b6ee..5857bbd 100644 --- a/Sources/FormatoTIFF/TIFFVeloxCalibration.swift +++ b/Sources/FormatoTIFF/TIFFVeloxCalibration.swift @@ -21,8 +21,11 @@ public nonisolated enum TIFFVeloxCalibration: Sendable, Equatable { /// The XML and the resolution tags name different sizes. case disagree - /// The note of a calibrated reading: the scale comes from one convention seen on one acquisition. - public static let unvalidatedNote = "scale unvalidated (one acquisition)" + /// The note of a calibrated reading: the scale is the pixel size Velox wrote (`BinaryResult/PixelSize`), at most + /// cross-checked against the file's own resolution tags (metres, tag 65452 = 3); no calibration standard checks it. + public static let storedScaleNote = "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" + /// The same note under its older name. + public static let unvalidatedNote = storedScaleNote /// The note of `disagree`. public static let disagreeNote = "uncalibrated: the Velox XML and the resolution tags disagree" diff --git a/Tests/Fixtures/README.md b/Tests/Fixtures/README.md index e6ae230..187f3f6 100644 --- a/Tests/Fixtures/README.md +++ b/Tests/Fixtures/README.md @@ -66,7 +66,7 @@ others stay in SpectroKit or the first consumer, named in the rows. | `FormatoDMTests/Fixtures/acq-metadata/eels-scanning.dm4` | 1.3 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoDMTests/Fixtures/acq-metadata/negative-voltage.dm4` | 1.2 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoDMTests/Fixtures/acq-metadata/no-microscope-group.dm4` | 1.3 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | -| `FormatoDMTests/Fixtures/acq-metadata/pins.json` | 29.7 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | +| `FormatoDMTests/Fixtures/acq-metadata/pins.json` | 29.8 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoDMTests/Fixtures/acq-metadata/sem-stage.dm4` | 1.3 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoDMTests/Fixtures/acq-metadata/sem.dm4` | 1.3 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoDMTests/Fixtures/acq-metadata/si-group.dm4` | 1.5 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | @@ -152,7 +152,7 @@ others stay in SpectroKit or the first consumer, named in the rows. | `FormatoDMTests/Fixtures/rsciio-EELS_SI.truth.json` | 2.5 KB | `tools/eels-reader/truth.py` | RosettaSciIO 049e7d70's reading of its public file; from SpectroKit 0.3.0 | | `FormatoDMTests/Fixtures/rsciio-MonarcCL_spectrum-ccd.dm4` | 316.6 KB | *copied* | third party: RosettaSciIO public test data (GPL-3.0-or-later), copied unchanged (RosettaSciIO 8363d24 `digitalmicrograph/1D/`); sha256 equal to its `registry.txt` | | `FormatoDMTests/Fixtures/rsciio-MonoCL_spectrum-ccd.dm4` | 320.1 KB | *copied* | third party: RosettaSciIO public test data (GPL-3.0-or-later), copied unchanged (RosettaSciIO 8363d24 `digitalmicrograph/1D/`); sha256 equal to its `registry.txt` | -| `FormatoEMDTests/Fixtures/acq-metadata/acq-pins.json` | 6.5 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | +| `FormatoEMDTests/Fixtures/acq-metadata/acq-pins.json` | 6.8 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoEMDTests/Fixtures/acq-metadata/velox-acquisition.emd` | 48.7 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoEMDTests/Fixtures/acq-metadata/velox-no-unit.emd` | 48.7 KB | `tools/acq-metadata/no_unit.py` | planted (from velox-acquisition.emd); pins RosettaSciIO 049e7d70 | | `FormatoEMDTests/Fixtures/acq-metadata/velox-no-unit.pin.json` | 883 B | `tools/acq-metadata/no_unit.py` | planted (from velox-acquisition.emd); pins RosettaSciIO 049e7d70 | @@ -398,7 +398,7 @@ others stay in SpectroKit or the first consumer, named in the rows. | `FormatoMRCTests/Fixtures/acq-metadata/fei2-all-set.mrc` | 2.8 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoMRCTests/Fixtures/acq-metadata/fei2-zero-and-clear-bits.mrc` | 1.9 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoMRCTests/Fixtures/acq-metadata/no-extended-header.mrc` | 1.1 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | -| `FormatoMRCTests/Fixtures/acq-metadata/pins.json` | 3.2 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | +| `FormatoMRCTests/Fixtures/acq-metadata/pins.json` | 3.5 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoMRCTests/Fixtures/image-readers/axis-order.mrc` | 1.0 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoMRCTests/Fixtures/image-readers/big-endian.mrc` | 1.0 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoMRCTests/Fixtures/image-readers/complex64.mrc` | 1.0 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | @@ -434,7 +434,7 @@ others stay in SpectroKit or the first consumer, named in the rows. | `FormatoTIFFTests/Fixtures/acq-metadata/fei-helios.tif` | 340 B | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoTIFFTests/Fixtures/acq-metadata/fei-sfeg-zero.tif` | 333 B | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoTIFFTests/Fixtures/acq-metadata/fei-stage-beam.tif` | 473 B | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | -| `FormatoTIFFTests/Fixtures/acq-metadata/pins.json` | 9.9 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | +| `FormatoTIFFTests/Fixtures/acq-metadata/pins.json` | 11.2 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoTIFFTests/Fixtures/acq-metadata/plain.tif` | 126 B | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoTIFFTests/Fixtures/acq-metadata/velox-ceta-imaging.tif` | 972 B | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoTIFFTests/Fixtures/acq-metadata/velox-diffraction-zero-voltage.tif` | 966 B | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | @@ -527,7 +527,7 @@ others stay in SpectroKit or the first consumer, named in the rows. | `FormatoTIFFTests/Fixtures/app-image/no-width.tif` | 122 B | `tools/acq-metadata/generate.py` | planted; pins the consumer's readers | | `FormatoTIFFTests/Fixtures/app-image/not-tiff-letters.tif` | 16 B | `tools/acq-metadata/generate.py` | planted; pins the consumer's readers | | `FormatoTIFFTests/Fixtures/app-image/palette.tif` | 1.6 KB | `tools/acq-metadata/generate.py` | planted; pins the consumer's readers | -| `FormatoTIFFTests/Fixtures/app-image/pins.json` | 54.2 KB | `tools/acq-metadata/generate.py` | planted; pins the consumer's readers | +| `FormatoTIFFTests/Fixtures/app-image/pins.json` | 55.3 KB | `tools/acq-metadata/generate.py` | planted; pins the consumer's readers | | `FormatoTIFFTests/Fixtures/app-image/plain.tif` | 126 B | `tools/acq-metadata/generate.py` | planted; pins the consumer's readers | | `FormatoTIFFTests/Fixtures/app-image/rgb-16bit.tif` | 158 B | `tools/acq-metadata/generate.py` | planted; pins the consumer's readers | | `FormatoTIFFTests/Fixtures/app-image/rgb-4-samples.tif` | 160 B | `tools/acq-metadata/generate.py` | planted; pins the consumer's readers | @@ -591,7 +591,7 @@ others stay in SpectroKit or the first consumer, named in the rows. | `FormatoTIFFTests/Fixtures/app-image/white-is-zero.tif` | 134 B | `tools/acq-metadata/generate.py` | planted; pins the consumer's readers | | `FormatoTIFFTests/Fixtures/app-image/wrong-magic.tif` | 134 B | `tools/acq-metadata/generate.py` | planted; pins the consumer's readers | | `FormatoTIFFTests/Fixtures/app-image/zero-height.tif` | 134 B | `tools/acq-metadata/generate.py` | planted; pins the consumer's readers | -| `FormatoTIFFTests/Fixtures/image-readers/pins.json` | 8.3 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | +| `FormatoTIFFTests/Fixtures/image-readers/pins.json` | 9.0 KB | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoTIFFTests/Fixtures/image-readers/plain.tif` | 126 B | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoTIFFTests/Fixtures/image-readers/velox-agree.tif` | 362 B | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | | `FormatoTIFFTests/Fixtures/image-readers/velox-and-fei.tif` | 523 B | `tools/acq-metadata/generate.py` | planted; pins RosettaSciIO 049e7d70 and the consumer's readers | diff --git a/Tests/FormatoCoreTests/AcquisitionMetadataTests.swift b/Tests/FormatoCoreTests/AcquisitionMetadataTests.swift index bdb3419..0e8a3ed 100644 --- a/Tests/FormatoCoreTests/AcquisitionMetadataTests.swift +++ b/Tests/FormatoCoreTests/AcquisitionMetadataTests.swift @@ -79,4 +79,11 @@ import FormatoCore #expect(formatoDisplayFileName("") == "") #expect(formatoDisplayFileName("/") == "/") } + + @Test func theNoReferenceNoteStatesTheFactAndKeepsItsOldName() { + // Apps show the note verbatim: it says what is known (no published reader, read as stored), never "unvalidated". + #expect(AcquisitionMetadata.noPublishedReaderNote == "no published reader reads this field; read as the file stores it") + #expect(AcquisitionMetadata.unvalidatedNote == AcquisitionMetadata.noPublishedReaderNote) + #expect(!AcquisitionMetadata.noPublishedReaderNote.lowercased().contains("unvalidated")) + } } diff --git a/Tests/FormatoDMTests/AcquisitionMetadataDMTests.swift b/Tests/FormatoDMTests/AcquisitionMetadataDMTests.swift index 96a8424..297d5d0 100644 --- a/Tests/FormatoDMTests/AcquisitionMetadataDMTests.swift +++ b/Tests/FormatoDMTests/AcquisitionMetadataDMTests.swift @@ -162,7 +162,7 @@ import FormatoDM let names = try both("whitespace-names") #expect(names.reference.instrument?.value == " " && names.app.instrument?.value == "Titan") // The detector is mapped by neither RosettaSciIO nor the app's reference: labelled in both conventions' defaults. - #expect(tem.reference.detector == MetadataReading("K2", note: AcquisitionMetadata.unvalidatedNote)) + #expect(tem.reference.detector == MetadataReading("K2", note: "no published reader reads this field; read as stored in the DM image tags")) // The ambiguous day/month order is named in the note. let ambiguous = try both("ambiguous-date") #expect(ambiguous.reference.acquired?.value == "2021-03-04 00:00:00") diff --git a/Tests/FormatoDMTests/CameraPixelPitchTests.swift b/Tests/FormatoDMTests/CameraPixelPitchTests.swift index c2f73a5..92d4975 100644 --- a/Tests/FormatoDMTests/CameraPixelPitchTests.swift +++ b/Tests/FormatoDMTests/CameraPixelPitchTests.swift @@ -54,7 +54,8 @@ import FormatoDM let object = try Self.object("rsciio-EELS_SI.dm4") let meta = AcquisitionMetadata.dm(object) #expect(meta.cameraPixelPitch?.value == PixelSize(rows: 14, columns: 14, units: "um")) - #expect(meta.cameraPixelPitch?.note == AcquisitionMetadata.unvalidatedNote, "not mapped by RosettaSciIO outside CL") + #expect(meta.cameraPixelPitch?.note == "no published reader reads this field; read as stored in the DM image tags", + "not mapped by RosettaSciIO outside CL") #expect(meta.signalNumbers["Detector.pixel_size"] == nil, "RosettaSciIO maps the tag for CL only") let app = AcquisitionMetadata.dm(object, convention: .app) #expect(app.cameraPixelPitch?.value == PixelSize(rows: 14, columns: 14, units: "um")) @@ -179,7 +180,7 @@ import FormatoDM @Test func aPlantedSquarePitchOutsideCLHasNoCLKey() throws { let meta = AcquisitionMetadata.dm(try Self.file(tags: Self.device([15, 15]))) #expect(meta.cameraPixelPitch?.value == PixelSize(rows: 15, columns: 15, units: "um")) - #expect(meta.cameraPixelPitch?.note == AcquisitionMetadata.unvalidatedNote) + #expect(meta.cameraPixelPitch?.note == "no published reader reads this field; read as stored in the DM image tags") #expect(meta.signalNumbers["Detector.pixel_size"] == nil) let cl = AcquisitionMetadata.dm(try Self.file(tags: Self.device([15, 15], signal: "CL"))) #expect(cl.signalNumbers["Detector.pixel_size"]?.value == 15) diff --git a/Tests/FormatoDMTests/Fixtures/acq-metadata/pins.json b/Tests/FormatoDMTests/Fixtures/acq-metadata/pins.json index 3138cf3..2444968 100644 --- a/Tests/FormatoDMTests/Fixtures/acq-metadata/pins.json +++ b/Tests/FormatoDMTests/Fixtures/acq-metadata/pins.json @@ -14,7 +14,7 @@ "value": 130 }, "detector": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the DM image tags", "otherCopy": null, "value": "Orius" }, @@ -24,7 +24,7 @@ "value": 15 }, "exposureS": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the DM image tags", "otherCopy": null, "value": 0.5 }, @@ -89,7 +89,7 @@ }, "cameraLengthMM": null, "detector": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the DM image tags", "otherCopy": null, "value": "K2" }, diff --git a/Tests/FormatoEMDTests/AcquisitionMetadataVeloxTests.swift b/Tests/FormatoEMDTests/AcquisitionMetadataVeloxTests.swift index 82c79f5..ee1b28d 100644 --- a/Tests/FormatoEMDTests/AcquisitionMetadataVeloxTests.swift +++ b/Tests/FormatoEMDTests/AcquisitionMetadataVeloxTests.swift @@ -46,14 +46,14 @@ extension VeloxEMDSerialized { let ceta = AcquisitionMetadata.velox(try #require(entries["ceta"]), timeZone: Self.utc) // StemMagnification "0" is returned as stored (RosettaSciIO's float); the camera's exposure is the detector the image names. #expect(ceta.magnification == MetadataReading(0)) - #expect(ceta.exposureS == MetadataReading(0.5, note: AcquisitionMetadata.unvalidatedNote)) + #expect(ceta.exposureS == MetadataReading(0.5, note: "no published reader reads this field; read as stored in the Velox metadata")) #expect(ceta.mode?.value == "1") #expect(ceta.cameraLengthMM == MetadataReading(844.0)) // RosettaSciIO's microscope is InstrumentClass ("Talos"); the stored model is kept beside it, labelled. #expect(ceta.instrument == MetadataReading("Talos")) - #expect(ceta.instrumentModel == MetadataReading("Talos F200X", note: "unvalidated: no published reader reads it")) + #expect(ceta.instrumentModel == MetadataReading("Talos F200X", note: "no published reader reads this field; read as stored in the Velox metadata")) #expect(AcquisitionMetadata.velox(try #require(entries["ceta"]), timeZone: Self.utc, convention: .app).instrument - == MetadataReading("Talos F200X", note: "unvalidated: no published reader reads it")) + == MetadataReading("Talos F200X", note: "no published reader reads this field; read as stored in the Velox metadata")) #expect(ceta.dwellUS == nil) // an ImagingDetector's image carries the scan settings too, but no dwell let haadf = AcquisitionMetadata.velox(try #require(entries["haadf"]), timeZone: Self.utc) #expect(haadf.dwellUS?.value == 10 && haadf.magnification == MetadataReading(225000.0)) diff --git a/Tests/FormatoEMDTests/Fixtures/acq-metadata/acq-pins.json b/Tests/FormatoEMDTests/Fixtures/acq-metadata/acq-pins.json index 15e23ae..697b3d2 100644 --- a/Tests/FormatoEMDTests/Fixtures/acq-metadata/acq-pins.json +++ b/Tests/FormatoEMDTests/Fixtures/acq-metadata/acq-pins.json @@ -12,7 +12,7 @@ }, "cameraLengthMM": null, "detector": { - "note": "unvalidated: RosettaSciIO names some detectors from another field", + "note": "read from BinaryResult.Detector; RosettaSciIO names some detectors from another field", "otherCopy": null, "value": "BM-Ceta" }, @@ -24,7 +24,7 @@ "value": 200 }, "instrument": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox metadata", "otherCopy": null, "value": "Talos F200X" }, @@ -80,12 +80,12 @@ }, "cameraLengthMM": null, "detector": { - "note": "unvalidated: RosettaSciIO names some detectors from another field", + "note": "read from BinaryResult.Detector; RosettaSciIO names some detectors from another field", "otherCopy": null, "value": "HAADF" }, "dwellUS": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox metadata", "otherCopy": null, "value": 10 }, @@ -96,7 +96,7 @@ "value": 300 }, "instrument": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox metadata", "otherCopy": null, "value": "Talos F200X" }, @@ -157,12 +157,12 @@ }, "cameraLengthMM": null, "detector": { - "note": "unvalidated: RosettaSciIO names some detectors from another field", + "note": "read from BinaryResult.Detector; RosettaSciIO names some detectors from another field", "otherCopy": null, "value": "HAADF" }, "dwellUS": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox metadata", "otherCopy": null, "value": 10 }, @@ -173,7 +173,7 @@ "value": 200 }, "instrument": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox metadata", "otherCopy": null, "value": "Talos F200X" }, @@ -229,19 +229,19 @@ }, "cameraLengthMM": null, "detector": { - "note": "unvalidated: RosettaSciIO names some detectors from another field", + "note": "read from BinaryResult.Detector; RosettaSciIO names some detectors from another field", "otherCopy": null, "value": "HAADF" }, "dwellUS": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox metadata", "otherCopy": null, "value": 10 }, "exposureS": null, "highTensionKV": null, "instrument": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox metadata", "otherCopy": null, "value": "Talos F200X" }, diff --git a/Tests/FormatoEMDTests/VeloxEELSAcquisitionTests.swift b/Tests/FormatoEMDTests/VeloxEELSAcquisitionTests.swift index 85da9ec..195efeb 100644 --- a/Tests/FormatoEMDTests/VeloxEELSAcquisitionTests.swift +++ b/Tests/FormatoEMDTests/VeloxEELSAcquisitionTests.swift @@ -78,7 +78,8 @@ extension VeloxEMDSerialized { #expect(a.highTensionKV?.value == Double(cube.beamVoltageV) / 1e3) #expect(a.collectionAngleMrad.map { bits($0.value) } == bits(cube.collectionAngleEndRad.value * 1e3)) #expect(a.convergenceAngleMrad.map { bits($0.value) } == bits(pins.planted.beamConvergenceRad * 1e3)) - #expect(a.convergenceAngleMrad?.note?.contains("unvalidated") == true) + #expect(a.convergenceAngleMrad?.note + == "Optics.BeamConvergence in radians × 1e3; no published reader reads this field; read as stored in the Velox metadata") } } diff --git a/Tests/FormatoMRCTests/AcquisitionMetadataMRCTests.swift b/Tests/FormatoMRCTests/AcquisitionMetadataMRCTests.swift index b2b1436..12e8b72 100644 --- a/Tests/FormatoMRCTests/AcquisitionMetadataMRCTests.swift +++ b/Tests/FormatoMRCTests/AcquisitionMetadataMRCTests.swift @@ -53,7 +53,7 @@ import FormatoMRC #expect(try AcquisitionMetadata.mrc(data: data).isEmpty) let m = try AcquisitionMetadata.mrc(data: data, convention: .app) #expect(m.highTensionKV == MetadataReading(300.0)) - #expect(m.cameraLengthMM?.value == 844 && m.cameraLengthMM?.note == "unvalidated: the mode is not checked") + #expect(m.cameraLengthMM?.value == 844 && m.cameraLengthMM?.note == "shown as stored; the instrument mode is not checked, as no published reader decodes it") #expect(m.detector?.value == "Ceta" && m.instrument?.value == "Talos F200X" && m.dwellUS?.value == 10) #expect(m.acquired?.value == "2022-12-05 13:13:07") } diff --git a/Tests/FormatoMRCTests/Fixtures/acq-metadata/pins.json b/Tests/FormatoMRCTests/Fixtures/acq-metadata/pins.json index a8e8a20..de3e0d5 100644 --- a/Tests/FormatoMRCTests/Fixtures/acq-metadata/pins.json +++ b/Tests/FormatoMRCTests/Fixtures/acq-metadata/pins.json @@ -57,7 +57,7 @@ "value": "2022-12-05 13:13:07" }, "cameraLengthMM": { - "note": "unvalidated: the mode is not checked", + "note": "shown as stored; the instrument mode is not checked, as no published reader decodes it", "otherCopy": null, "value": 844 }, @@ -72,7 +72,7 @@ "value": 10 }, "exposureS": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the FEI extended header, at the offset mrcfile declares", "otherCopy": null, "value": 0.5 }, @@ -92,7 +92,7 @@ "value": 28500 }, "mode": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the FEI extended header, at the offset mrcfile declares", "otherCopy": null, "value": "3" }, @@ -124,7 +124,7 @@ }, "magnification": null, "mode": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the FEI extended header, at the offset mrcfile declares", "otherCopy": null, "value": "3" }, diff --git a/Tests/FormatoTIFFTests/AcquisitionMetadataTIFFTests.swift b/Tests/FormatoTIFFTests/AcquisitionMetadataTIFFTests.swift index 8025fe8..1ff863e 100644 --- a/Tests/FormatoTIFFTests/AcquisitionMetadataTIFFTests.swift +++ b/Tests/FormatoTIFFTests/AcquisitionMetadataTIFFTests.swift @@ -34,7 +34,7 @@ import FormatoTIFF let m = try AcquisitionMetadata.tiff(data: Self.data(name)) #expect(referenceDifferences(m, c.reference, strict: ["beam_energy", "dwell_time", "microscope", "stamp"]) == [], "\(name)") // [Detectors] Name is mapped by neither: labelled. - #expect(m.detector == MetadataReading("ETD", note: AcquisitionMetadata.unvalidatedNote)) + #expect(m.detector == MetadataReading("ETD", note: "no published reader reads this field; read as stored in the FEI header of the TIFF")) } // A stored zero is 0 (RosettaSciIO), not recorded (rule b). let zero = try AcquisitionMetadata.tiff(data: Self.data("fei-sfeg-zero")) @@ -54,8 +54,8 @@ import FormatoTIFF #expect(try AcquisitionMetadata.tiff(data: imaging, convention: .app).cameraLengthMM == nil) // The values RosettaSciIO does not map are read by the app's rules, labelled. let m = try AcquisitionMetadata.tiff(data: imaging) - #expect(m.detector == MetadataReading("BM-Ceta", note: AcquisitionMetadata.unvalidatedNote)) - #expect(m.exposureS == MetadataReading(0.5, note: AcquisitionMetadata.unvalidatedNote)) + #expect(m.detector == MetadataReading("BM-Ceta", note: "no published reader reads this field; read as stored in the Velox XML of the TIFF")) + #expect(m.exposureS == MetadataReading(0.5, note: "no published reader reads this field; read as stored in the Velox XML of the TIFF")) #expect(m.acquired?.value == "2026-06-05 13:05:07") // A zone designator is another quantity: not recorded. #expect(try AcquisitionMetadata.tiff(data: Self.data("velox-zone-designator")).acquired == nil) diff --git a/Tests/FormatoTIFFTests/Fixtures/acq-metadata/pins.json b/Tests/FormatoTIFFTests/Fixtures/acq-metadata/pins.json index b3bcfe2..ac194eb 100644 --- a/Tests/FormatoTIFFTests/Fixtures/acq-metadata/pins.json +++ b/Tests/FormatoTIFFTests/Fixtures/acq-metadata/pins.json @@ -4,19 +4,19 @@ { "app": { "acquired": { - "note": "local time, zone not stored; unvalidated: no published reader reads it", + "note": "local time, zone not stored; no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "2026-06-05 13:05:07" }, "cameraLengthMM": null, "detector": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "BM-Ceta" }, "dwellUS": null, "exposureS": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": 0.5 }, @@ -26,17 +26,17 @@ "value": 200 }, "instrument": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "Talos F200X" }, "magnification": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": 17500 }, "mode": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "TEM" }, @@ -52,7 +52,7 @@ { "app": { "acquired": { - "note": "local time, zone not stored; unvalidated: no published reader reads it", + "note": "local time, zone not stored; no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "2026-06-05 13:05:07" }, @@ -62,25 +62,25 @@ "value": 150 }, "detector": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "BM-Ceta" }, "dwellUS": null, "exposureS": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": 0.5 }, "highTensionKV": null, "instrument": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "Talos F200X" }, "magnification": null, "mode": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "TEM" }, @@ -96,7 +96,7 @@ { "app": { "acquired": { - "note": "local time, zone not stored; unvalidated: no published reader reads it", + "note": "local time, zone not stored; no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "2026-06-05 13:05:07" }, @@ -106,13 +106,13 @@ "value": 200 }, "detector": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "BM-Ceta" }, "dwellUS": null, "exposureS": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": 0.5 }, @@ -122,17 +122,17 @@ "value": 200 }, "instrument": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "Talos F200X" }, "magnification": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": 17500 }, "mode": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "STEM" }, @@ -150,13 +150,13 @@ "acquired": null, "cameraLengthMM": null, "detector": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "BM-Ceta" }, "dwellUS": null, "exposureS": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": 0.5 }, @@ -166,17 +166,17 @@ "value": 200 }, "instrument": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "Talos F200X" }, "magnification": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": 17500 }, "mode": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "TEM" }, @@ -192,19 +192,19 @@ { "app": { "acquired": { - "note": "local time, zone not stored; unvalidated: no published reader reads it", + "note": "local time, zone not stored; no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "2026-06-05 13:05:07" }, "cameraLengthMM": null, "detector": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "BM-Ceta" }, "dwellUS": null, "exposureS": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": 0.5 }, @@ -214,17 +214,17 @@ "value": 200 }, "instrument": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "Talos F200X" }, "magnification": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": 17500 }, "mode": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the Velox XML of the TIFF", "otherCopy": null, "value": "TEM" }, @@ -251,7 +251,7 @@ }, "cameraLengthMM": null, "detector": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the FEI header of the TIFF", "otherCopy": null, "value": "ETD" }, @@ -294,7 +294,7 @@ }, "cameraLengthMM": null, "detector": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the FEI header of the TIFF", "otherCopy": null, "value": "ETD" }, @@ -344,7 +344,7 @@ }, "cameraLengthMM": null, "detector": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the FEI header of the TIFF", "otherCopy": null, "value": "ETD" }, @@ -379,7 +379,7 @@ }, "cameraLengthMM": null, "detector": { - "note": "unvalidated: no published reader reads it", + "note": "no published reader reads this field; read as stored in the FEI header of the TIFF", "otherCopy": null, "value": "ETD" }, diff --git a/Tests/FormatoTIFFTests/Fixtures/app-image/pins.json b/Tests/FormatoTIFFTests/Fixtures/app-image/pins.json index 8d0756e..aa09520 100644 --- a/Tests/FormatoTIFFTests/Fixtures/app-image/pins.json +++ b/Tests/FormatoTIFFTests/Fixtures/app-image/pins.json @@ -2749,7 +2749,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1065353216, @@ -2757,7 +2757,7 @@ 1077936128, 1082130432 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": { @@ -2797,7 +2797,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1065353216, @@ -2805,7 +2805,7 @@ 1077936128, 1082130432 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": { @@ -2825,7 +2825,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1065353216, @@ -2833,7 +2833,7 @@ 1077936128, 1082130432 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": { @@ -2853,7 +2853,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1065353216, @@ -2861,7 +2861,7 @@ 1077936128, 1082130432 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": { @@ -2881,7 +2881,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1065353216, @@ -2889,7 +2889,7 @@ 1077936128, 1082130432 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": { @@ -2909,7 +2909,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1065353216, @@ -2917,7 +2917,7 @@ 1077936128, 1082130432 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": null, @@ -3013,7 +3013,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1065353216, @@ -3021,7 +3021,7 @@ 1077936128, 1082130432 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": { @@ -3111,7 +3111,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1065353216, @@ -3119,7 +3119,7 @@ 1077936128, 1082130432 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": { @@ -3139,7 +3139,7 @@ "app": { "height": 3, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 0, @@ -3155,7 +3155,7 @@ 1167867904, 1168891904 ], - "source": "TIFF \u00b7 uint16 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint16 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 4, "x": { diff --git a/Tests/FormatoTIFFTests/Fixtures/image-readers/pins.json b/Tests/FormatoTIFFTests/Fixtures/image-readers/pins.json index 152f33d..581c411 100644 --- a/Tests/FormatoTIFFTests/Fixtures/image-readers/pins.json +++ b/Tests/FormatoTIFFTests/Fixtures/image-readers/pins.json @@ -5,7 +5,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1, @@ -13,7 +13,7 @@ 3, 4 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": { @@ -101,7 +101,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1, @@ -109,7 +109,7 @@ 3, 4 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": { @@ -153,7 +153,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1, @@ -161,7 +161,7 @@ 3, 4 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": { @@ -205,7 +205,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1, @@ -213,7 +213,7 @@ 3, 4 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": { @@ -257,7 +257,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1, @@ -265,7 +265,7 @@ 3, 4 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": { @@ -309,7 +309,7 @@ "app": { "height": 2, "notes": [ - "scale unvalidated (one acquisition)" + "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard" ], "pixels": [ 1, @@ -317,7 +317,7 @@ 3, 4 ], - "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale unvalidated (one acquisition)", + "source": "TIFF \u00b7 uint8 \u00b7 Velox metadata \u00b7 scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard", "voltage": null, "width": 2, "x": null, diff --git a/Tests/FormatoTIFFTests/TIFFAppImageTests.swift b/Tests/FormatoTIFFTests/TIFFAppImageTests.swift index a07ae5c..6a0884a 100644 --- a/Tests/FormatoTIFFTests/TIFFAppImageTests.swift +++ b/Tests/FormatoTIFFTests/TIFFAppImageTests.swift @@ -88,7 +88,10 @@ import FormatoTIFF } #expect(TIFFAppImage.Refusal.compressed(scheme: 5).reason == "its pixels are compressed (scheme 5); only uncompressed TIFF is read.") #expect(TIFFAppImage.Note.render == "8-bit render, not counts") - #expect(TIFFAppImage.Note.veloxUnvalidated == "scale unvalidated (one acquisition)") + #expect(TIFFAppImage.Note.veloxUnvalidated == "scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard") + #expect(TIFFAppImage.Note.veloxStoredScale == TIFFAppImage.Note.veloxUnvalidated) + #expect(TIFFVeloxCalibration.storedScaleNote == TIFFVeloxCalibration.unvalidatedNote) + #expect(!TIFFVeloxCalibration.storedScaleNote.lowercased().contains("unvalidated")) #expect(throws: FormatoReadError.self) { try TIFFAppImage.read(path: Self.path("no-such-file")) } } diff --git a/Tests/FormatoTIFFTests/TIFFCameraPixelPitchTests.swift b/Tests/FormatoTIFFTests/TIFFCameraPixelPitchTests.swift index 051ed97..654d412 100644 --- a/Tests/FormatoTIFFTests/TIFFCameraPixelPitchTests.swift +++ b/Tests/FormatoTIFFTests/TIFFCameraPixelPitchTests.swift @@ -99,7 +99,7 @@ import FormatoTIFF let reference = try AcquisitionMetadata.tiff(data: data) #expect(reference.cameraPixelPitch?.value == Self.um14) // Not a CL signal: RosettaSciIO maps the tag for CL only (and reads no tag 65027), so `.rosettaSciIO` labels it. - #expect(reference.cameraPixelPitch?.note == AcquisitionMetadata.unvalidatedNote) + #expect(reference.cameraPixelPitch?.note == "no published reader reads this field; read as stored in the DM image tags") #expect(reference.signalNumbers["Detector.pixel_size"] == nil) let app = try AcquisitionMetadata.tiff(data: data, convention: .app) #expect(app.cameraPixelPitch?.value == Self.um14) @@ -141,7 +141,7 @@ import FormatoTIFF for convention in [AcquisitionMetadataConvention.rosettaSciIO, .app] { var expected = AcquisitionMetadata.dm(numbers: tree.numbers, strings: tree.strings, convention: convention) expected.cameraPixelPitch = convention == .app ? MetadataReading(Self.um14) - : MetadataReading(Self.um14, note: AcquisitionMetadata.unvalidatedNote) + : MetadataReading(Self.um14, note: "no published reader reads this field; read as stored in the DM image tags") #expect(try AcquisitionMetadata.tiff(data: Self.tiff(stream: stream), convention: convention) == expected) } } diff --git a/tools/acq-metadata/generate.py b/tools/acq-metadata/generate.py index 12368a2..a7fc80a 100644 --- a/tools/acq-metadata/generate.py +++ b/tools/acq-metadata/generate.py @@ -377,7 +377,39 @@ def build_harness(td): def run_harness(exe, kind, cases, td): cp = td / f'{kind}.json' cp.write_text(json.dumps(cases)) - return json.loads(subprocess.check_output([str(exe), kind, str(cp)])) + return kit_notes(kind, json.loads(subprocess.check_output([str(exe), kind, str(cp)]))) + + +# The kit words the notes as plain facts where the pinned app (APP) wrote "unvalidated": the same notes, attached to the +# same values, in the kit's wording. Applied to the app output only; no value changes. +OLD_NOTE = 'unvalidated: no published reader reads it' +KIT_NOTE = { + 'dm': 'no published reader reads this field; read as stored in the DM image tags', + 'velox': 'no published reader reads this field; read as stored in the Velox metadata', + 'mrc': 'no published reader reads this field; read as stored in the FEI extended header, at the offset mrcfile declares', + 'tiff-velox': 'no published reader reads this field; read as stored in the Velox XML of the TIFF', + 'tiff-fei': 'no published reader reads this field; read as stored in the FEI header of the TIFF', +} +KIT_PAIRS = [ + ('unvalidated: RosettaSciIO names some detectors from another field', 'read from BinaryResult.Detector; RosettaSciIO names some detectors from another field'), + ('unvalidated: the mode is not checked', 'shown as stored; the instrument mode is not checked, as no published reader decodes it'), + ('scale unvalidated (one acquisition)', 'scale as Velox stores it (BinaryResult/PixelSize), not checked against a calibration standard'), +] + + +def kit_notes(kind, out): + def sub(obj, pairs): + if isinstance(obj, str): + for a, b in pairs: obj = obj.replace(a, b) + return obj + if isinstance(obj, list): return [sub(x, pairs) for x in obj] + if isinstance(obj, dict): return {k: sub(v, pairs) for k, v in obj.items()} + return obj + if kind == 'tiff': # keyed by case name: the Velox XML cases, the FEI INI cases, the DM tag stream + prefix = {'velox': 'tiff-velox', 'fei': 'tiff-fei', 'dm': 'dm'} + return {n: sub(v, KIT_PAIRS + [(OLD_NOTE, KIT_NOTE[prefix[p]])] if (p := next((k for k in prefix if n.startswith(k)), None)) else KIT_PAIRS) + for n, v in out.items()} + return sub(out, KIT_PAIRS + ([(OLD_NOTE, KIT_NOTE[kind])] if kind in KIT_NOTE else [])) # ---------------------------------------------------------------------------------------------------------------- image readers