Skip to content

Unit Conversion for Signal and Image Axes #295

@PierreRaybaut

Description

@PierreRaybaut

Summary

Add a unit conversion feature allowing users to convert axis units on signals (X/Y) and images (X/Y/Z) through a dedicated dialog with predefined unit categories and custom conversion support.

Motivation

Currently, DataLab stores axis units as free-form strings (xunit, yunit, zunit) with no semantic meaning. When users need to convert units (e.g., wavelength from nm to µm, time from s to ms), they must:

  1. Manually apply a calibration (a*x + b) via the existing calibration dialog
  2. Separately edit the unit string through the object properties editor

This two-step workflow is error-prone and unintuitive. A dedicated unit conversion feature would streamline this into a single operation.

Scope

In scope

  • New "Convert units..." action in the Processing > Axis transformation submenu for both Signal and Image panels
  • Conversion dialog with:
    • Axis selector (X, Y for signals; X, Y, Z for images)
    • Predefined unit categories with conversion factors (see below)
    • Custom conversion mode (user-defined factor and target unit string)
    • Preview of the conversion factor to be applied
  • Predefined unit categories:
    • Length: nm, µm, mm, cm, m, km, in, ft
    • Time: ns, µs, ms, s, min, h
    • Frequency: Hz, kHz, MHz, GHz, THz
    • Wavelength/Energy (spectroscopy): nm ↔ µm ↔ cm⁻¹ ↔ eV ↔ THz
    • Angle: rad, deg, mrad
  • Automatic data scaling: Multiply/divide axis data by the appropriate conversion factor
  • Automatic unit string update: Replace the unit string on the target axis
  • (Optional) Enhance existing calibration dialog: Add "source unit" / "target unit" fields to XYCalibrateParam / XYZCalibrateParam so that calibration also updates the unit string

Out of scope

  • Integration with external unit libraries (pint, astropy.units)
  • Automatic unit propagation across all processing functions
  • Dimensional analysis or unit compatibility checks on operations
  • Unit-aware arithmetic (e.g., warning when adding signals with different units)

Proposed Implementation

Architecture

The implementation follows the standard DataLab/Sigima pattern:

  1. Sigima layer (sigima.proc): Computation function + parameter class
  2. DataLab layer: Processor registration + menu action

Sigima: Unit conversion engine

New module: sigima/proc/common/units.py (or extend existing calibration modules)

# Unit registry: category → {unit_name: factor_to_base_unit}
UNIT_CATEGORIES = {
    "Length": {"nm": 1e-9, "µm": 1e-6, "mm": 1e-3, "cm": 1e-2, "m": 1.0, "km": 1e3, "in": 0.0254, "ft": 0.3048},
    "Time": {"ns": 1e-9, "µs": 1e-6, "ms": 1e-3, "s": 1.0, "min": 60.0, "h": 3600.0},
    "Frequency": {"Hz": 1.0, "kHz": 1e3, "MHz": 1e6, "GHz": 1e9, "THz": 1e12},
    "Angle": {"rad": 1.0, "deg": 0.017453292519943, "mrad": 1e-3},
    # Spectroscopy: non-linear conversions handled separately
}

Parameter class: ConvertUnitsParam

  • axis: Choice (X / Y / Z)
  • category: Choice (Length / Time / Frequency / Angle / Spectroscopy / Custom)
  • source_unit: Auto-detected from current object or user-selected
  • target_unit: User-selected from category
  • custom_factor: Float (only for Custom mode)
  • custom_unit: String (only for Custom mode)

Computation function: convert_units(src, p) → dst

  • Computes conversion factor from source → target
  • Scales axis data accordingly
  • Updates unit string on the result object
  • Handles spectroscopy non-linear conversions (e.g., nm → cm⁻¹ requires 1e7 / x)

DataLab: Registration

  • Register as 1_to_1 in both SignalProcessor and ImageProcessor
  • Add to Processing > Axis transformation submenu
  • Icon: existing or new unit-related icon

UX Flow

  1. User selects one or more signal/image objects
  2. Processing > Axis transformation > Convert units...
  3. Dialog opens:
    • Axis: X / Y (or X / Y / Z for images)
    • Current unit is auto-filled from the object's xunit/yunit/zunit
    • Category dropdown → filters available source/target units
    • If current unit matches a known unit → auto-selects category and source
    • If not → defaults to "Custom" mode
    • Target unit dropdown (filtered by category)
    • Preview: shows the conversion factor (e.g., "×1000" or "÷1e-6")
  4. User confirms → conversion applied to all selected objects (1-to-1 pattern)

Acceptance Criteria

  • "Convert units..." action available in Processing > Axis transformation for both Signal and Image panels
  • Predefined conversions work correctly for all listed unit categories (linear)
  • Spectroscopy conversions (non-linear: nm ↔ cm⁻¹ ↔ eV) work correctly
  • Custom conversion mode allows arbitrary factor + unit string
  • Unit string is automatically updated on the converted axis
  • Current unit is auto-detected and pre-filled in the dialog
  • Works with multi-object selection (applied independently to each, 1-to-1 pattern)
  • Uncertainties (dx/dy) are scaled appropriately when present
  • Unit tests cover all predefined categories and edge cases
  • UI strings are internationalized (_() wrapped)
  • French translations provided

Technical Notes

  • For linear conversions (most categories), the factor is simply source_factor / target_factor
  • For spectroscopy (wavelength ↔ wavenumber ↔ energy ↔ frequency), conversions are non-linear and require transforming the data values (e.g., wavenumber_cm⁻¹ = 1e7 / wavelength_nm). This also reverses the X-axis ordering.
  • The existing TIME_UNIT_FACTORS in sigima/objects/signal/constants.py and FreqUnits in sigima/objects/signal/creation.py could be consolidated into the new unit registry.
  • Consider reusing the same unit registry for the FFT/IFFT unit propagation (currently hardcoded "s"→"Hz")

Related Existing Features

  • Linear calibration (Processing > Axis transformation > Linear calibration...)
  • Polynomial calibration (Image panel)
  • FFT/IFFT automatic unit swapping ("s" ↔ "Hz")
  • Transpose (swaps X/Y units)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions