Skip to content

Signal1D #

Signal1D(
    time_points: FloatArray | None = None,
    amplitudes: NumericArray | None = None,
    time_unit: TimeUnit = MICROSECOND,
    metadata: dict[str, Any] | None = None,
    reference_frequency_hz: float | None = None,
    carrier_offset_hz: float | None = None,
)

Time-domain 1D signal.

Attributes:

Name Type Description
time_points FloatNdArray

Time coordinates of the signal

amplitudes

Signal amplitudes (can be complex)

time_unit

Unit of time coordinates

metadata dict[str, str]

Optional metadata dictionary

processing_history class-attribute instance-attribute #

processing_history: ProcessingHistory | None = (
    betterproto2.field(
        6, betterproto2.TYPE_MESSAGE, optional=True
    )
)

Processing history - read-only audit log of all operations applied to this signal

history property #

history: Sequence[ProcessingHistoryEntry]

Read-only access to processing history entries.

Returns:

Type Description
Sequence[ProcessingHistoryEntry]

Immutable sequence of history entries. Returns empty tuple if

Sequence[ProcessingHistoryEntry]

no history has been recorded yet.

metadata class-attribute instance-attribute #

metadata: dict[str, str] = betterproto2.field(
    3,
    betterproto2.TYPE_MAP,
    map_meta=betterproto2.map_meta(
        betterproto2.TYPE_STRING, betterproto2.TYPE_STRING
    ),
)

Optional metadata

axis_role property writable #

axis_role: AxisRole

Physical role of this signal's time axis.

nucleus property writable #

nucleus: Nucleus

Nucleus type for this signal's time axis.

_visualization_handler class-attribute instance-attribute #

_visualization_handler: str = ''

_time_axis class-attribute instance-attribute #

_time_axis: NdAxis | None = betterproto2.field(
    1, betterproto2.TYPE_MESSAGE, optional=True
)

Exactly one time axis (enforced by validation)

_amplitudes class-attribute instance-attribute #

_amplitudes: NdComplexArray | None = betterproto2.field(
    2, betterproto2.TYPE_MESSAGE, optional=True
)

1D complex amplitude data (shape = [N])

_points_view instance-attribute #

_points_view: ndarray | None

_np_dirty_points instance-attribute #

_np_dirty_points: bool

visualization_handler property writable #

visualization_handler: str

Plugin visualization handler ID (e.g., 'nanalysis:cosy_2d').

__module__ class-attribute instance-attribute #

__module__ = GrpcSignal1D.__module__

time_points instance-attribute #

time_points: FloatNdArray = np.asarray(
    time_points, dtype=np.float64
)

amplitudes instance-attribute #

amplitudes = np.asarray(amplitudes, dtype=np.complex128)

time_unit instance-attribute #

time_unit = time_unit

reference_frequency_hz instance-attribute #

reference_frequency_hz = float(reference_frequency_hz)

carrier_offset_hz instance-attribute #

carrier_offset_hz = float(carrier_offset_hz)

duration property #

duration: float

Total duration of the signal in current time units.

duration_seconds property #

duration_seconds: float

Total duration of the signal in seconds.

sampling_rate_hz property #

sampling_rate_hz: float

Sampling rate in Hz.

is_complex property #

is_complex: bool

Whether the signal has complex amplitudes.

_init_history #

_init_history() -> None

Initialize the processing history.

Called from init of Signal1D/Spectrum1D. Always creates a ProcessingHistory instance - history is always enabled.

_record_history_entry #

_record_history_entry(
    operation: str,
    parameters: dict[str, str],
    shape_before: tuple[int, ...],
    shape_after: tuple[int, ...],
    source: str = "tqt_nmr",
) -> None

Record a processing operation to the history.

This is an internal method - not exposed to users. Called by @track_operation decorator and TrackedArray.

Parameters:

Name Type Description Default
operation str

Name of the operation (e.g., "correct_phase_manual").

required
parameters dict[str, str]

Operation parameters as string key-value pairs.

required
shape_before tuple[int, ...]

Shape of amplitudes before operation.

required
shape_after tuple[int, ...]

Shape of amplitudes after operation.

required
source str

Source of the operation (e.g., "tqt_nmr", "numpy").

'tqt_nmr'

_copy_history_to #

_copy_history_to(other: Self) -> None

Copy processing history to another object.

Used by copy() methods to preserve history lineage.

Parameters:

Name Type Description Default
other Self

Object to copy history to.

required

extract_echo_amplitudes #

extract_echo_amplitudes(
    echo_spacing_ms: float,
    n_echoes: int | None = None,
    use_real: bool = False,
) -> tuple[ndarray, ndarray]

Extract echo amplitudes from a pre-processed CPMG signal.

Each sample of amplitudes is treated as one echo amplitude, and the time axis is reconstructed from echo_spacing_ms.

Limitation

This does not handle raw interleaved data containing full echo shapes; it never searches for the maximum within an echo window. (An earlier version of this docstring claimed it did — it never has.) Raw echo trains must be reduced to one point per echo beforehand.

Parameters:

Name Type Description Default
echo_spacing_ms float

Time between echoes in milliseconds.

required
n_echoes int | None

Number of echoes to extract. If None, uses all data.

None
use_real bool

Use the phased real part instead of the magnitude. Default False (magnitude). Magnitude mode makes the noise floor Rician (strictly positive, non-zero mean), which biases a subsequent T2 distribution towards a spurious long-T2 component. If the data is properly phased, prefer use_real=True.

False

Returns:

Type Description
tuple[ndarray, ndarray]

(echo_times_s, echo_amplitudes) — times in seconds.

compute_t2_distribution #

compute_t2_distribution(
    echo_spacing_ms: float | None = None,
    t_min: float = 1e-05,
    t_max: float = 10.0,
    n_points: int = 200,
    alpha: float | None = None,
    use_real: bool = False,
) -> RelaxationDistribution

Compute T2 distribution via Inverse Laplace Transform.

Parameters:

Name Type Description Default
echo_spacing_ms float | None

Echo spacing in ms. If None, the echo times are taken from time_points and converted to seconds using the signal's own time_unit.

None
t_min float

Minimum T2 in seconds for the distribution grid.

1e-05
t_max float

Maximum T2 in seconds for the distribution grid.

10.0
n_points int

Number of points in the T2 grid.

200
alpha float | None

Regularization parameter (None = auto via GCV).

None
use_real bool

Use the phased real part instead of the magnitude. Default False (magnitude). Magnitude mode gives a Rician noise floor with a positive mean, which biases the recovered T2 distribution towards a spurious long-T2 component. Use use_real=True on phased data for an unbiased result.

False

Returns:

Type Description
RelaxationDistribution

RelaxationDistribution with the T2 spectrum.

fit_multiexponential #

fit_multiexponential(
    echo_spacing_ms: float | None = None,
    n_components: int | Literal["auto"] = "auto",
    max_components: int = 4,
    use_real: bool = False,
) -> MultiExponentialResult

Fit discrete multi-exponential decay to the echo train.

Parameters:

Name Type Description Default
echo_spacing_ms float | None

Echo spacing in ms. If None, the echo times are taken from time_points and converted to seconds using the signal's own time_unit.

None
n_components int | Literal['auto']

Number of components, or "auto" for BIC-based selection.

'auto'
max_components int

Maximum components to try when n_components="auto".

4
use_real bool

Use the phased real part instead of the magnitude (default False). See :meth:extract_echo_amplitudes for the magnitude-mode noise bias.

False

Returns:

Type Description
MultiExponentialResult

MultiExponentialResult with fitted time constants and amplitudes.

Raises:

Type Description
ImportError

If the nuclei_backend fitter is unavailable.

_echo_data #

_echo_data(use_real: bool) -> ndarray

Return the decay data as float, either magnitude or the real part.

Parameters:

Name Type Description Default
use_real bool

If True use amplitudes.real (requires the data to be phased); if False use abs(amplitudes).

required

_time_points_seconds #

_time_points_seconds() -> ndarray

Time points converted to seconds using the signal's own time unit.

pad_zeros #

pad_zeros(factor: float | None = None) -> Self

convert_time_unit #

convert_time_unit(target_unit: TimeUnit) -> Self

Convert signal to different time unit.

Parameters:

Name Type Description Default
target_unit TimeUnit

Target time unit

required

Returns:

Type Description
Self

Self for method chaining

trim_dead_time #

trim_dead_time(
    threshold_ratio: float = 0.1,
    max_dead_time: float | None = None,
    dead_time_unit: TimeUnit | None = None,
) -> Self

Remove dead time from the beginning of the signal.

The original time origin is preserved: the retained points keep their original time values, so the acquisition delay that first-order phase correction depends on is not silently discarded. The removed delay is also recorded in metadata["dead_time_removed"] (in the signal's time unit) and the number of dropped samples in metadata["dead_time_removed_samples"].

Limitation

An FID normally has its maximum at t=0, so abs(signal) > threshold is almost always already true at the very first sample and start_idx comes out as 0 — i.e. for ordinary FIDs this method is effectively a no-op. It is only useful for signals whose leading samples are genuinely suppressed (e.g. hardware-blanked receivers or echo-like data). Use max_dead_time to trim a known fixed delay instead of relying on the threshold test.

Parameters:

Name Type Description Default
threshold_ratio float

Signal threshold as ratio of max amplitude

0.1
max_dead_time float | None

Maximum dead time to consider

None
dead_time_unit TimeUnit | None

Unit for max_dead_time (defaults to signal's time unit)

None

Returns:

Type Description
Self

Self for method chaining

get_upper_envelope #

get_upper_envelope(
    smoothing_kernel_factor: float = 70,
) -> Self

Create an upper-envelope of the signal.

Parameters:

Name Type Description Default
smoothing_kernel_factor float

Factor to determine smoothing kernel size

70

Returns:

Type Description
Self

New signal containing the upper envelope

align_to #

align_to(
    reference: Self,
    max_shift_seconds: float = 0.02,
    resolution: int = 3,
    fast: bool = False,
) -> Self

Cut the signal to align with the reference.

Cross-correlates data within region of interest at a precision of 1/res. If data is cross-correlated at native resolution (i.e. res=1), this function can only achieve integer precision.

A positive shift means this signal lags the reference and leading samples are dropped. A negative shift means this signal leads the reference; |shift| samples are zero-padded at the front and the time axis extended backwards by the same amount.

Parameters:

Name Type Description Default
reference Self

Signal to align to.

required
max_shift_seconds float

Max shift in seconds. Defaults to 0.02.

0.02
resolution int

Resolution of phase alignment. Defaults to 3.

3
fast bool

Whether to use a faster version of the algorithm. Defaults to False.

False

Returns:

Type Description
Self

Self for method chaining

apodize #

apodize(func: Callable[[ndarray], ndarray]) -> Self
apodize(
    func: WindowType,
    *,
    lb: float | None = None,
    sigma: float = 0.3,
    alpha: float = 5.0,
    gb: float | None = None,
) -> Self
apodize(
    func: Callable[[ndarray], ndarray] | WindowType,
    *,
    lb: float | None = None,
    sigma: float = 0.3,
    alpha: float = 5.0,
    gb: float | None = None,
) -> Self

Apply apodization (window function) to the signal.

Parameters:

Name Type Description Default
func Callable[[ndarray], ndarray] | WindowType

Either a callable apodization function that takes range [0;1] and returns weights, or a string window type from: "none", "exponential", "gaussian", "hamming", "hann", "blackman", "blackmanharris", "kaiser", "bartlett", "cosine", "tukey"

required
lb float | None

Line broadening factor for exponential window, in Hz (Lorentzian FWHM). None (the default) takes the value from the signal's nucleus via :func:~tqt_nmr.core.nucleus.line_broadening_for.

None
sigma float

Gaussian width as a dimensionless fraction of the acquisition time (0-0.5 range). Default 0.3. Ignored when gb is given.

0.3
alpha float

Shape parameter for kaiser (beta) or tukey (alpha) windows. Default 5.0.

5.0
gb float | None

Gaussian line broadening in Hz (Gaussian FWHM). When provided, the gaussian window becomes physically parameterized (consistent with lb) and sigma is ignored.

None

Returns:

Type Description
Self

Self for method chaining.

to_spectrum #

to_spectrum(
    fft_method: Literal[
        "real", "full", "standard"
    ] = "real",
    window: WindowType | None = None,
    window_lb: float | None = None,
    window_sigma: float = 0.3,
    window_alpha: float = 5.0,
    zero_fill: float | None = None,
    first_point_scale: bool = True,
) -> Spectrum1D

Convert signal to frequency spectrum.

Parameters:

Name Type Description Default
fft_method Literal['real', 'full', 'standard']

FFT computation method ("real", "full", "standard"). Note: "standard" is an O(N^2) dense DFT and is refused above STANDARD_FFT_MAX_LENGTH points.

'real'
window WindowType | None

Optional window function to apply before FFT. Options: "none", "exponential", "gaussian", "hamming", "hann", "blackman", "blackmanharris", "kaiser", "bartlett", "cosine", "tukey".

None
window_lb float | None

Line broadening for exponential window (Hz). None (the default) takes the value from the nucleus of the transformed dimension.

None
window_sigma float

Sigma for gaussian window (0-0.5). Default 0.3.

0.3
window_alpha float

Alpha/beta for kaiser/tukey windows. Default 5.0.

5.0
zero_fill float | None

Optional zero-fill applied to a copy of the signal before the FFT, using pad_zeros semantics (a total-length multiplier; e.g. 2 doubles the length). Pass "pow2" semantics via pad_zeros(None) directly if you need next-power-of-two. Default None = no zero-filling.

None
first_point_scale bool

Halve fid[0] before the FFT (standard NMR first-point correction, removes a DC offset / baseline roll). Defaults to True. Pass False to transform the FID untouched.

True

Returns:

Type Description
Spectrum1D

Spectrum object.

apply_window #

apply_window(
    window_func: Callable[[ndarray], ndarray],
) -> Self

Apply windowing function to the signal.

Parameters:

Name Type Description Default
window_func Callable[[ndarray], ndarray]

Function that takes array of length N and returns window

required

Returns:

Type Description
Self

Self for method chaining

adaptive_line_broadening_hz #

adaptive_line_broadening_hz() -> float | None

Exponential line broadening (Hz) measured from this signal.

Returns None when the signal carries no line whose width can be measured.

_get_window #

_get_window(
    window_type: WindowType,
    n: int,
    *,
    lb: float = 1.0,
    sigma: float = 0.3,
    alpha: float = 5.0,
    gb: float | None = None,
) -> ndarray

Generate a window array for apodization.

Thin wrapper around :func:generate_apodization_window.

Parameters:

Name Type Description Default
window_type WindowType

Type of window function.

required
n int

Number of points.

required
lb float

Line broadening for exponential, in Hz.

1.0
sigma float

Dimensionless gaussian width (fraction of acquisition time).

0.3
alpha float

Alpha/beta for kaiser/tukey.

5.0
gb float | None

Gaussian line broadening in Hz (takes precedence over sigma).

None

Returns:

Type Description
ndarray

Window array of length n, starting at ~1 and decaying.

normalize #

normalize(
    method: Literal["max", "rms", "unit"] = "max",
) -> Self

Normalize signal amplitudes.

Parameters:

Name Type Description Default
method Literal['max', 'rms', 'unit']

Normalization method: - "max": Divide by maximum absolute value - "rms": Divide by RMS value - "unit": Divide by L2 norm

'max'

Returns:

Type Description
Self

Self for method chaining

__post_init__ #

__post_init__() -> None

__bytes__ #

__bytes__() -> bytes

load #

load(
    stream: SupportsRead[bytes], size: int | None = None
) -> Self

parse classmethod #

parse(data: bytes) -> Self

FromString classmethod #

FromString(s: bytes) -> Self

_pb_amplitudes #

_pb_amplitudes() -> NdComplexArray

_ensure_storage_initialized #

_ensure_storage_initialized() -> None

_mark_dirty #

_mark_dirty(field: str) -> None

_sync_numpy_to_proto #

_sync_numpy_to_proto() -> None

_sync_proto_to_numpy #

_sync_proto_to_numpy() -> None

_pb_axis #

_pb_axis() -> NdAxis

_ensure_axis_initialized #

_ensure_axis_initialized() -> None

to_nd #

to_nd(visualization_handler: str | None = None) -> SignalNd

copy #

copy() -> Self

Create a deep copy of this signal.

__len__ #

__len__() -> int

__getitem__ #

__getitem__(key: slice | int) -> Self

Get subset of signal.

__repr__ #

__repr__() -> str

__str__ #

__str__() -> str

from_proto classmethod #

from_proto(proto: Signal1D | SignalNd | None) -> Self

Shallow-copy underscored proto storage into our instance.