Sampled Coordinates#
Overview#
A SampledCoordinate describes a coordinate whose values are
spaced by a fixed sampling interval. Unlike InterpCoordinate,
which uses piecewise linear interpolation between arbitrary tie points,
SampledCoordinate exploits the regularity of the grid and stores only:
tie_values— the start value of each contiguous segment.tie_lengths— the number of samples in each segment.sampling_interval— the fixed step shared by all segments.
This makes it more compact and numerically stable than the interpolated variant, and it maps directly to the block-based time representation used in the miniSEED and SEED formats.
import numpy as np
import xdas as xd
from xdas.coordinates import SampledCoordinate
coord = SampledCoordinate(
{
"tie_values": [0.0, 200.0],
"tie_lengths": [10, 8],
"sampling_interval": 10.0,
}
)
coord
0.000 to 280.000
The two segments start at 0 and 200, each stepping by 10. The gap between them (from 100 to 200) is explicit — there is simply no segment covering that range.
Materialising values#
Calling .values returns the full coordinate vector as a dense NumPy array:
coord.values
array([ 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 200.,
210., 220., 230., 240., 250., 260., 270.])
Individual values are obtained from indices with .get_value and the reverse mapping
(index from value) with .to_index:
coord.get_value(3)
np.float64(30.0)
coord.to_index(30.0)
np.int64(3)
Datetime coordinates#
The most common use of SampledCoordinate is for the time axis of DAS data. The
sampling_interval must be a numpy.timedelta64:
t0 = np.datetime64("2024-01-01T00:00:00.000", "ms")
dt = np.timedelta64(4, "ms") # 250 Hz
coord = SampledCoordinate(
{
"tie_values": [t0, t0 + np.timedelta64(1, "s")],
"tie_lengths": [250, 250],
"sampling_interval": dt,
}
)
coord
2024-01-01T00:00:00.000 to 2024-01-01T00:00:02.000
Gaps and multi-segment coordinates#
Multiple segments represent an acquisition with gaps. Each element of tie_values marks
the start of one contiguous block, and tie_lengths gives its duration in samples:
t0 = np.datetime64("2024-01-01T00:00:00.000", "ms")
dt = np.timedelta64(4, "ms")
segments = [
(t0, 500), # 2 s block
(t0 + np.timedelta64(3, "s"), 500), # another 2 s block after a 1 s gap
]
coord = SampledCoordinate(
{
"tie_values": [s[0] for s in segments],
"tie_lengths": [s[1] for s in segments],
"sampling_interval": dt,
}
)
coord
2024-01-01T00:00:00.000 to 2024-01-01T00:00:05.000
Simplifying near-regular coordinates#
When opening files whose timestamps are not perfectly aligned (e.g. NTP-synchronized
acquisitions), small drifts create many short segments. The simplify method merges
segments whose start time is within tolerance of the expected position:
t0 = np.datetime64("2024-01-01T00:00:00.000", "ms")
dt = np.timedelta64(4, "ms")
# Simulate a one-sample drift at the boundary
t1_drifted = t0 + np.timedelta64(2000, "ms") + np.timedelta64(1, "ms")
coord = SampledCoordinate(
{
"tie_values": [t0, t1_drifted],
"tie_lengths": [500, 500],
"sampling_interval": dt,
}
)
print("Before:", len(coord.tie_values), "segments")
tol = np.timedelta64(10, "ms")
coord = coord.simplify(tol)
print("After: ", len(coord.tie_values), "segments")
coord
Before: 2 segments
After: 1 segments
2024-01-01T00:00:00.000 to 2024-01-01T00:00:04.000
When to use SampledCoordinate vs InterpCoordinate#
|
|
|
|---|---|---|
Sampling |
Strictly uniform (one |
Variable (piecewise linear) |
Memory |
Very compact |
Compact |
Use case |
DAS / seismic time axes, uniform grids |
Non-uniform grids, GPS-corrected time |
miniSEED/SEED compatible |
Yes |
No |