Skip to content

Signal2D #

Signal2D(
    t1: FloatArray | None = None,
    t2: FloatArray | None = None,
    amplitudes: ndarray | None = None,
    t1_unit: TimeUnit = MICROSECOND,
    t2_unit: TimeUnit = MICROSECOND,
    metadata: dict[str, Any] | None = None,
    reference_frequency_hz: float | None = None,
    carrier_offset_hz_1: float | None = None,
    carrier_offset_hz_2: float | None = None,
    reference_signal_f1: Signal1D | None = None,
    reference_signal_f2: Signal1D | None = None,
    f1_reference_frequency_hz: float | None = None,
    f2_reference_frequency_hz: float | None = None,
)

2D time-domain signal for NMR experiments (e.g., COSY, HSQC).

Attributes:

Name Type Description
t1 FloatArray

Time coordinates for indirect dimension (axis 1)

t2 FloatArray

Time coordinates for direct dimension (axis 2)

amplitudes ndarray

2D complex amplitude array with shape (len(t1), len(t2))

t1_unit, t2_unit

Time units for each axis

processing_history class-attribute instance-attribute #

processing_history: ProcessingHistory | None = (
    betterproto2.field(
        10, 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.

amplitudes property writable #

amplitudes: ndarray

metadata class-attribute instance-attribute #

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

Optional metadata

nucleus_t1 property writable #

nucleus_t1: Nucleus

Nucleus type for the T1 (indirect) axis.

nucleus_t2 property writable #

nucleus_t2: Nucleus

Nucleus type for the T2 (direct) axis.

role_t1 property writable #

role_t1: AxisRole

Physical role of the T1 (indirect) axis.

role_t2 property writable #

role_t2: AxisRole

Physical role of the T2 (direct) axis.

_axis_1 class-attribute instance-attribute #

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

Exactly two time/spatial axes (enforced by validation)

e.g., t1

_axis_2 class-attribute instance-attribute #

_axis_2: NdAxis | None = betterproto2.field(
    2, betterproto2.TYPE_MESSAGE, optional=True
)

e.g., t2

_visualization_handler class-attribute instance-attribute #

_visualization_handler: str = ''

_amplitudes class-attribute instance-attribute #

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

2D complex amplitude data (shape = [N, M])

_points_f1_view instance-attribute #

_points_f1_view: ndarray | None

_points_f2_view instance-attribute #

_points_f2_view: ndarray | None

_np_dirty_points_f1 instance-attribute #

_np_dirty_points_f1: bool

_np_dirty_points_f2 instance-attribute #

_np_dirty_points_f2: bool

points_f1 property writable #

points_f1: FloatArray

points_f2 property writable #

points_f2: FloatArray

visualization_handler property writable #

visualization_handler: str

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

x property writable #

x: FloatArray

x_units property #

x_units: TimeUnit

y property writable #

y: FloatArray

y_units property writable #

y_units: TimeUnit

z property writable #

z: FloatArray

__module__ class-attribute instance-attribute #

__module__ = GrpcSignal2D.__module__

t1_unit property writable #

t1_unit: TimeUnit

Time unit for t1 (indirect) dimension.

t2_unit property writable #

t2_unit: TimeUnit

Time unit for t2 (direct) dimension.

reference_frequency_hz instance-attribute #

reference_frequency_hz = float(reference_frequency_hz)

carrier_offset_hz_1 instance-attribute #

carrier_offset_hz_1 = float(carrier_offset_hz_1)

carrier_offset_hz_2 instance-attribute #

carrier_offset_hz_2 = float(carrier_offset_hz_2)

f1_reference_frequency_hz property writable #

f1_reference_frequency_hz: float | None

Reference frequency for F1 (indirect) dimension, for heteronuclear PPM conversion.

f2_reference_frequency_hz property writable #

f2_reference_frequency_hz: float | None

Reference frequency for F2 (direct) dimension, for heteronuclear PPM conversion.

reference_signal_f1 instance-attribute #

reference_signal_f1 = reference_signal_f1

reference_signal_f2 instance-attribute #

reference_signal_f2 = reference_signal_f2

t1 property writable #

t1: FloatArray

Time coordinates for indirect dimension (axis 1).

t2 property writable #

t2: FloatArray

Time coordinates for direct dimension (axis 2).

shape property #

shape: tuple[int, int]

is_complex property #

is_complex: bool

Whether the signal has complex amplitudes.

sampling_rate_hz_t1 property #

sampling_rate_hz_t1: float

Sampling rate in Hz for t1 (indirect) dimension.

sampling_rate_hz_t2 property #

sampling_rate_hz_t2: float

Sampling rate in Hz for t2 (direct) dimension.

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 property writable #

visualization_handler: str

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

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.

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.

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.

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

load #

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

parse classmethod #

parse(data: bytes) -> Self

FromString classmethod #

FromString(s: bytes) -> Self

to_nd #

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

copy #

copy() -> Self

Create a deep copy of this signal.

from_proto classmethod #

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

Shallow-copy underscored proto storage into our instance.

_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

apodize #

apodize(
    window: WindowType = "exponential",
    axis: Literal["t1", "t2", "both"] = "both",
    *,
    lb: float | None = None,
    sigma: float = 0.3,
    alpha: float = 5.0,
    gb: float | None = None,
) -> Self

Apply apodization (window function) to the 2D signal.

Uses the same one-sided decaying windows as the 1D path, so the first FID point is preserved.

Parameters:

Name Type Description Default
window WindowType

Window type from: "none", "exponential", "gaussian", "hamming", "hann", "blackman", "blackmanharris", "kaiser", "bartlett", "cosine", "tukey".

'exponential'
axis Literal['t1', 't2', 'both']

Which axis to apply window to ("t1", "t2", or "both").

'both'
lb float | None

Line broadening for exponential window, in Hz. None (the default) takes the value for each apodized axis from that axis' nucleus via :func:~tqt_nmr.core.nucleus.line_broadening_for.

None
sigma float

Dimensionless gaussian width (fraction of acquisition time, 0-0.5). Default 0.3. Ignored when gb is given.

0.3
alpha float

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

5.0
gb float | None

Gaussian line broadening in Hz; takes precedence over sigma.

None

Returns:

Type Description
Self

Self for method chaining.

to_spectrum #

to_spectrum(
    reference_frequency_hz: float | None = None,
    window: WindowType | None = None,
    window_axis: Literal["t1", "t2", "both"] = "both",
    window_lb: float | None = None,
    window_sigma: float = 0.3,
    window_alpha: float = 5.0,
    zero_fill_f1: int | None = None,
    zero_fill_f2: int | None = None,
    shift_f1: bool = True,
    shift_f2: bool = True,
    tppi_mode: Literal[
        "none", "correct", "correct_and_half", "half_only"
    ]
    | None = None,
    tppi_keep_upper: bool = False,
    fft_axes: Literal["both", "t2", "t1", "auto"] = "auto",
    fft_method: Literal["real", "full"] = "full",
    first_point_scale: bool = True,
    auto_phase: bool = False,
    f1_mode: F1AcquisitionMode | None = None,
) -> Spectrum2D

Convert 2D signal to 2D frequency spectrum via FFT.

Parameters:

Name Type Description Default
reference_frequency_hz float | None

Reference frequency for PPM calculations.

None
window WindowType | None

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

None
window_axis Literal['t1', 't2', 'both']

Which axis to apply window to ("t1", "t2", or "both").

'both'
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_f1 int | None

Total-length zero-fill factor for the F1 dimension. None = auto: 4x when F1 has fewer than 128 points, otherwise no zero-filling.

None
zero_fill_f2 int | None

Total-length zero-fill factor for the F2 dimension. None = no zero-filling (F2 is normally already well sampled).

None
shift_f1 bool

Apply fftshift to F1 axis (default True). Set False for TPPI acquisition.

True
shift_f2 bool

Apply fftshift to F2 axis (default True).

True
tppi_mode Literal['none', 'correct', 'correct_and_half', 'half_only'] | None

TPPI/half-spectrum processing mode: - None or "none": No TPPI processing (default) - "correct": Apply TPPI correction only (multiply odd rows by -1) - "correct_and_half": Apply TPPI correction AND extract half of F1 spectrum - "half_only": Extract half of F1 spectrum without TPPI correction (for non-phase-sensitive acquisitions like CPMG, JRES)

None
tppi_keep_upper bool

Which half of F1 to keep when extracting: - False (default): keep lower half (frequencies < carrier) - for MIRRORIMAGE=0 - True: keep upper half (frequencies >= carrier) - for MIRRORIMAGE=1

False
fft_axes Literal['both', 't2', 't1', 'auto']

Which axes to FFT: - "auto" (default): auto-detect from axis roles. If t1 has PARAMETER role, only FFT along t2 (row-wise 1D). Otherwise full 2D FFT. - "both": Full 2D FFT (standard for COSY, HSQC, etc.) - "t2": FFT only along t2 (direct dimension). Keeps t1 as-is. Use for relaxation experiments (T1, T2, CPMG, DOSY). - "t1": FFT only along t1 (indirect dimension). Keeps t2 as-is.

'auto'
fft_method Literal['real', 'full']

How the transformed dimension is Fourier transformed, matching Signal1D.to_spectrum: - "full" (default): complex transform, both signs of frequency. - "real": real-input transform of the transformed axis, keeping only non-negative frequencies. A complex signal carries information in both signs of frequency, so it is transformed with "full" regardless of this argument.

'full'
first_point_scale bool

Halve the first time-domain point of each transformed dimension before the FFT (standard NMR first-point correction, removing a DC offset / baseline roll). Defaults to True. Pass False to transform the FID untouched.

True
auto_phase bool

Run correct_phase_auto(axis="both", method="acme") on the result. Defaults to False: this is a transform, and phase correction belongs to the preprocessing pipeline, where the user chooses the algorithm and can inspect the result.

False
f1_mode F1AcquisitionMode | None

How the indirect dimension was quadrature-detected -- one of "states", "states_tppi", "echo_antiecho", "tppi", "qf", "qseq", "undefined". None (the default) takes it from the signal's fft_f1_acquisition_mode metadata, which the import handlers write from the instrument's own parameters. The phase-sensitive modes are transformed hypercomplex (F2 first, real part along F2, then F1), which is the only way to get a 2D absorption lineshape; the others keep the plain complex 2D transform.

None

Returns:

Type Description
Spectrum2D

Spectrum2D object.

_to_spectrum_1d_rows #

_to_spectrum_1d_rows(
    fft_axis: Literal["t2", "t1"],
    reference_frequency_hz: float | None = None,
    window: WindowType | None = None,
    window_lb: float | None = None,
    window_sigma: float = 0.3,
    window_alpha: float = 5.0,
    fft_method: Literal["real", "full"] = "full",
    first_point_scale: bool = True,
) -> Spectrum2D

FFT along one axis only, keeping the other as parameter values.

For relaxation experiments (T1/T2/CPMG/DOSY), the indirect axis is a parameter (delay, gradient strength), not a time dimension. Each row (or column) is an independent FID that gets its own 1D FFT.

Parameters:

Name Type Description Default
fft_axis Literal['t2', 't1']

Which axis to FFT ("t2" = row-wise, "t1" = column-wise).

required
reference_frequency_hz float | None

Reference frequency for PPM calculations.

None
window WindowType | None

Window function for apodization before FFT.

None
window_lb float | None

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

None
window_sigma float

Gaussian sigma parameter.

0.3
window_alpha float

Kaiser/tukey alpha parameter.

5.0
fft_method Literal['real', 'full']

"full" (complex transform) or "real" (real-input transform of the FFT'd axis, keeping only non-negative frequencies). A complex signal is always transformed with "full": both signs of frequency carry information.

'full'
first_point_scale bool

Halve the first point along the transformed axis before the FFT (standard NMR first-point correction). Defaults to True.

True

_split_interleaved staticmethod #

_split_interleaved(
    signal: Signal2D, mode: F1AcquisitionMode
) -> Signal2D

Split an interleaved hypercomplex signal in place, returning its partner.

signal is left holding the cosine-modulated interferograms on a t1 axis of every other original point (the acquisition increments t1 once per pair of rows, so the retained points are exactly the real t1 increments). The returned signal holds the sine-modulated partner on the same axis.

Parameters:

Name Type Description Default
signal Signal2D

The signal to split, modified in place.

required
mode F1AcquisitionMode

One of the interleaved modes.

required

Returns:

Type Description
Signal2D

The sine-modulated partner signal.

adaptive_line_broadening_hz #

adaptive_line_broadening_hz() -> float | None

Exponential line broadening (Hz) measured along the direct dimension.

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

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_proto_to_numpy #

_sync_proto_to_numpy() -> None

_pb_axis_f1 #

_pb_axis_f1() -> NdAxis

_pb_axis_f2 #

_pb_axis_f2() -> NdAxis

_ensure_axes_initialized #

_ensure_axes_initialized() -> None

to_nd #

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

__len__ #

__len__() -> int

copy #

copy() -> Self

Create a deep copy of this signal.

pad_zeros #

pad_zeros(
    factor_t1: float | None = None,
    factor_t2: float | None = None,
) -> Self

Pad signal with zeros to increase resolution after FFT.

Factors are total-length multipliers (matching Signal1D.pad_zeros): factor_t1=k gives a total t1 length of k * n1. None pads to the next power of two >= n. The result is never shorter than the input.

Parameters:

Name Type Description Default
factor_t1 float | None

Total-length zero-fill factor for t1 (None = next power of 2)

None
factor_t2 float | None

Total-length zero-fill factor for t2 (None = next power of 2)

None

Returns:

Type Description
Self

Self for method chaining

correct_tppi #

correct_tppi() -> Self

Apply TPPI (Time-Proportional Phase Incrementation) correction.

Undoes the half-spectral-width F1 shift that TPPI's 90° per-increment phase alternation produces. Call before the FFT, on TPPI data only.

Returns:

Type Description
Self

Self for method chaining

get_base_signal #

get_base_signal() -> Self

Get the main signal without reference data.

Returns a copy of this signal with reference fields set to None.

get_reference_f1 #

get_reference_f1() -> Signal1D | None

Get F1 (indirect dimension) reference signal.

get_reference_f2 #

get_reference_f2() -> Signal1D | None

Get F2 (direct dimension) reference signal.

has_references #

has_references() -> bool

Check if any reference signals are attached.

__repr__ #

__repr__() -> str

__str__ #

__str__() -> str

from_proto classmethod #

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

_setup_from_arrays #

_setup_from_arrays(
    t1: FloatArray,
    t2: FloatArray,
    amplitudes: ndarray,
    t1_unit: TimeUnit,
    t2_unit: TimeUnit,
) -> None

_sync_numpy_to_proto #

_sync_numpy_to_proto() -> None

Override to sync reference signals before serialization.