Skip to content
This documentation describes version 4.0.0, which is not released yet. The current version on PyPI is 3.3.0 and does not carry everything described here.

vibration.immission.railway

Evaluating the vibration of a passing train (DIN 45672-2:1995-07).

A train passing a building puts vibration into the ground for twenty seconds and then leaves, and DIN 45672-2 is the procedure that turns those twenty seconds into numbers that can be compared: between two measuring points on the way from the track to the building, between two dates either side of a mitigation measure, and between one train and the next. It is measured with the meter of DIN 45669-1 in its railway working range, 4 Hz to 315 Hz, and it is an evaluation standard rather than an assessment one: it says how to reduce a record, not what the result may be.

Three stretches of one record (Clause 5). is about four seconds with the largest vibration in its middle, where the characteristic values of the passage are read. is the passage itself, from where the amplitude first reaches about a quarter of its usual maxima to where it falls back to it. is the whole event, and it is the one an event value is formed over. Every quantity below carries the index of the stretch it came from.

The quantities (Clause 6). The running r.m.s. with ms (Formula (1)) and its level against m/s (Formula (2)); the interval r.m.s. of Formula (4), or the approximation of Formula (5) formed from the running r.m.s.; and the event value of Formula (8), the interval r.m.s. of referred to one hour, , whose square adds across the passages of an hour (Formula (10)). The same quantities in third-octave bands from 4 Hz to 315 Hz give the interval and maximum third-octave levels of Formulae (6) and (7).

Narrow band (Clause 7.3). The one-sided power spectral density of Formula (12), blockwise with a Hanning window and at least half a block of overlap, at the resolution of 1,25 Hz the standard recommends because it resolves the resonance of an ordinary floor. Adding its lines back into third octaves is Formula (23), and Table 1 fixes how many lines each band takes: a number recommended for comparability rather than derived, which is why it is a table here.

What this does not do. The standard is a method of reduction, so there is no verdict anywhere in it, and none here: what the numbers mean for people and buildings is DIN 4150-2 and DIN 4150-3. The calibration checks of Clause 7.3.3 test an analyser, which this module is not.

Auto-generated from the source docstrings by scripts/generate_api_docs.py (make api-docs). Do not edit by hand.

amplitude_distribution(
velocity_mm_s: ArrayLike,
*,
bins: int = 101,
) -> AmplitudeDistribution

The amplitude distribution of a stretch of velocity (Clause 6.3).

Clause 6.3 forms it over by DIN 55350-23, which is a histogram normalised to unit area: equal bins across the range of the stretch.

Parameters

NameDescription
velocity_mm_sThe velocity of the stretch, in millimetres per second (1-D).
binsHow many equal bins span the range of the stretch (default 101, odd so that zero sits in the middle of one).

Returns: The density and its cumulative function, as an AmplitudeDistribution.

Raises

ExceptionWhen
ValueErrorFor a bad record or fewer than two bins.
AmplitudeDistribution(
edges_mm_s: NDArray[np.float64],
density_per_mm_s: NDArray[np.float64],
cumulative: NDArray[np.float64],
)

How often each velocity occurred in a stretch, Formula (11).

Attributes

NameDescription
edges_mm_sThe bin edges, in millimetres per second.
density_per_mm_s, the amplitude distribution density, one value per bin, in 1/(mm/s); it integrates to one.
cumulative, its integral up to the upper edge of each bin, rising from zero to one.
band_sum_level(levels_db: ArrayLike) -> float

The sum level of a spectrum, Annex B, Formula (B.3).

, the energy sum of the bands.

Parameters

NameDescription
levels_dbThe band levels, in decibels.

Returns: , in decibels.

Raises

ExceptionWhen
ValueErrorFor an empty or non-finite input.
centred_interval(
velocity_mm_s: ArrayLike,
fs_hz: float,
*,
duration_s: float = 4.0,
) -> tuple[float, float]

The stretch of Clause 5 a), centred on the largest amplitude.

Clause 5 a) asks for the largest vibration to sit about in the middle of , and this puts it exactly there, or as near as the record allows: a stretch that would run off either end is moved back inside it rather than cut, so it keeps its length.

Parameters

NameDescription
velocity_mm_sThe velocity, in millimetres per second (1-D).
fs_hzSampling frequency, in hertz.
duration_sThe length of the stretch, in seconds (default 4 s).

Returns: (start, end), in seconds from the start of the record. A record shorter than duration_s is returned whole.

Raises

ExceptionWhen
ValueErrorFor a bad record or a non-positive rate or duration.
combined_event_velocity(event_velocities_mm_s: ArrayLike) -> float

The event value of every passage in one hour, Formula (10), in mm/s.

Event values add in square, because each is already the energy of its passage spread over the same hour: .

Parameters

NameDescription
event_velocities_mm_sThe event values of the passages, in millimetres per second.

Returns: , in millimetres per second.

Raises

ExceptionWhen
ValueErrorFor an empty, non-finite or negative input.
elastic_insertion_loss(
levels_before_db: ArrayLike,
levels_after_db: ArrayLike,
) -> NDArray[np.float64]

The insertion loss of Annex B, Formula (B.1), in dB.

The drop in the third-octave structure-borne level at one point of a transmission path when an elastic element is built in. Annex B says it and it bears repeating: the value belongs to the point it was measured at. The difference spectrum of Formula (B.2) is the same subtraction between any two spectra.

Parameters

NameDescription
levels_before_dbThe third-octave levels before, in decibels.
levels_after_dbThe levels after, at the same point, in decibels.

Returns: per band, positive where the element reduces the level.

Raises

ExceptionWhen
ValueErrorIf the two do not match band for band.
evaluate_train_passage(
velocity_mm_s: ArrayLike,
fs_hz: float,
*,
t2_s: tuple[float, float],
t1_s: tuple[float, float] | None = None,
t3_s: tuple[float, float] | None = None,
upper_band_hz: float = 315.0,
) -> TrainPassage

Reduce one passage to the quantities of Clauses 5 to 7.

The record goes through the band limitation of the DIN 45669-1 railway working range first, as it does inside the meter, and every quantity is read from what is left: the running r.m.s. and its maximum, the peak, the interval r.m.s. of the three stretches, the event value, the weighted severity DIN 4150-2 reads, and the third-octave levels of Figure 6.

Parameters

NameDescription
velocity_mm_sThe velocity of the passage as the transducer gives it, in millimetres per second (1-D).
fs_hzSampling frequency, in hertz: an integer, for the third-octave bank, and high enough to carry the 315 Hz range.
t2_s, the passage itself, (start, end) in seconds. Clause 5 b) reads it off the record, from about a quarter of the most frequent maxima back down to the same amplitude, which is a judgement about the record, so it is asked for rather than guessed.
t1_s; None (default) centres four seconds on the largest amplitude inside , or all of when that is shorter. An explicit one may not be longer than .
t3_s; None (default) takes the whole record.
upper_band_hzThe highest third octave, a nominal centre in hertz: 315 (default) or down to 80, the special case of DIN 45672-1.

Returns: The reduced passage, as a TrainPassage.

Raises

ExceptionWhen
ValueErrorFor a bad record, a rate the chain or the upper band cannot take, a stretch outside the record, stretches that do not nest or a longer than , or an upper band that is not a nominal centre from 80 Hz to 315 Hz.

Constant (float).

EVENT_REFERENCE_DURATION_S = 3600.0
event_velocity(interval_rms_mm_s: float, duration_s: float) -> float

The event value of Formula (8), in mm/s.

The interval r.m.s. of the whole event referred to one hour, : the constant velocity that, held for an hour, carries the energy the passage carried.

Parameters

NameDescription
interval_rms_mm_s, in millimetres per second. Clause 6.2 allows of Formula (5) in its place.
duration_s, in seconds.

Returns: , in millimetres per second.

Raises

ExceptionWhen
ValueErrorFor a negative velocity or a non-positive duration.
event_velocity_level(interval_rms_mm_s: float, duration_s: float) -> float

The event level of Formula (9), in dB.

, which is event_velocity as a level against VELOCITY_LEVEL_REFERENCE_MM_S.

Parameters

NameDescription
interval_rms_mm_s, in millimetres per second, positive.
duration_s, in seconds.

Returns: , in decibels.

Raises

ExceptionWhen
ValueErrorFor a non-positive velocity or duration.
interval_rms(
values: ArrayLike,
fs_hz: float,
*,
interval_s: tuple[float, float],
) -> float

The interval r.m.s. of Formula (4), or of Formula (5).

Formula (4) averages the square of the velocity over the stretch. Handed the running r.m.s. of running_velocity_rms instead, the same average is Formula (5), which Clause 6.1 calls a good approximation of Formula (4) and which is what a meter reading its display produces.

Parameters

NameDescription
valuesThe velocity or its running r.m.s., in any unit (1-D).
fs_hzSampling frequency, in hertz.
interval_sThe stretch (start, end), in seconds from the start of the record.

Returns: , in the unit of values.

Raises

ExceptionWhen
ValueErrorFor a bad record, a non-positive rate, or an interval that is empty or runs outside the record.
narrowband_psd(
velocity_mm_s: ArrayLike,
fs_hz: float,
*,
resolution_hz: float = 1.25,
) -> SpectralDensityResult

The one-sided power spectral density of Formula (12), in (mm/s)²/Hz.

Clause 7.3.1 asks for blocks of , a Hanning window (Formula (18)), at least half a block of overlap and a linear average of the blocks, which is the Welch estimate power_spectral_density makes. Its lines add back to the mean square of the stretch, which is Formula (14).

Parameters

NameDescription
velocity_mm_sThe velocity of the stretch, in millimetres per second (1-D).
fs_hzSampling frequency, in hertz.
resolution_hzThe line spacing , in hertz (default 1,25 Hz). The block holds round(fs / resolution) samples, so the spacing actually used is fs over that count.

Returns: The spectrum, as a SpectralDensityResult.

Raises

ExceptionWhen
ValueErrorFor a bad record, a non-positive rate or resolution, or a stretch shorter than one block.

Constant (float).

NARROWBAND_RESOLUTION_HZ = 1.25
passage_average_level(
levels_db: ArrayLike,
*,
axis: int | None = None,
) -> float | NDArray[np.float64]

The energy average of levels over repeated passages (Clause 9), in dB.

, the level of passage_average_velocity.

Parameters

NameDescription
levels_dbThe levels of the passages, in decibels. A 2-D array of third-octave spectra, one row per passage, averages band by band with axis=0.
axisThe axis the passages run along; None (default) averages every value.

Returns: The average level: a float, or one level per band.

Raises

ExceptionWhen
ValueErrorFor an empty or non-finite input.
passage_average_velocity(interval_rms_mm_s: ArrayLike) -> float

The energy average of r.m.s. values over repeated passages (Clause 9).

Clause 9 averages the results of a class of trains energetically, which for r.m.s. values is the root of the mean square, . The same holds for narrow-band spectra, whose power spectral densities average arithmetically line by line.

Parameters

NameDescription
interval_rms_mm_sOne r.m.s. value per passage, in millimetres per second.

Returns: The energy average, in millimetres per second.

Raises

ExceptionWhen
ValueErrorFor an empty, non-finite or negative input.

Constant (tuple).

PASSAGE_BANDS_HZ = (4.0, 315.0)
passage_energy_spectral_density(
psd: ArrayLike,
duration_s: float,
) -> NDArray[np.float64]

The energy spectral density of , Formula (13), in (mm/s)²/Hz².

Formula (13) writes with the one-sided Fourier spectrum of Formula (16), which is , the two-sided density. Set beside Formula (12) that is , and it means the lines of over positive frequencies add up to half the energy of the stretch where the lines of add up to all of its mean square. This returns the formula as printed.

Parameters

NameDescription
psd, the power spectral density of the stretch, in (mm/s)²/Hz, as narrowband_psd gives it.
duration_s, in seconds.

Returns: at the same lines, in (mm/s)²/Hz².

Raises

ExceptionWhen
ValueErrorFor a negative or non-finite density or a non-positive duration.
running_acceleration_level(
acceleration_m_s2: ArrayLike,
fs_hz: float,
*,
time_constant_s: float = 0.125,
) -> NDArray[np.float64]

The running acceleration level of Formula (3), in dB.

Parameters

NameDescription
acceleration_m_s2The acceleration, in metres per second squared (1-D).
fs_hzSampling frequency, in hertz.
time_constant_sSee running_velocity_rms.

Returns: with m/s², the reference DIN EN 21683 gives and the one the rest of this package’s acceleration levels use.

Raises

ExceptionWhen
ValueErrorFor a bad record or a non-positive rate or constant.
running_velocity_level(
velocity_mm_s: ArrayLike,
fs_hz: float,
*,
time_constant_s: float = 0.125,
) -> NDArray[np.float64]

The running velocity level of Formula (2), in dB.

Parameters

NameDescription
velocity_mm_sThe velocity, in millimetres per second (1-D).
fs_hzSampling frequency, in hertz.
time_constant_sSee running_velocity_rms.

Returns: with = VELOCITY_LEVEL_REFERENCE_MM_S, one value per sample. A sample whose running r.m.s. is still exactly zero, before the first nonzero sample, is -inf.

Raises

ExceptionWhen
ValueErrorFor a bad record or a non-positive rate or constant.
running_velocity_rms(
velocity_mm_s: ArrayLike,
fs_hz: float,
*,
time_constant_s: float = 0.125,
) -> NDArray[np.float64]

The running r.m.s. of Formula (1), in mm/s.

The exponential average the formula integrates, started from rest. Clause 4 says what that costs at the start of a record and so why the averaging has to be running before the train arrives. It puts the cost at 14 % after and 2 % after , which are the shortfalls of the mean square, and ; the running r.m.s. itself, which is what Figure 3 draws, is 7 % and 0,9 % short.

Parameters

NameDescription
velocity_mm_sThe velocity, in millimetres per second (1-D), as measured or in one third-octave band of it.
fs_hzSampling frequency, in hertz.
time_constant_sThe time constant, in seconds (default 0,125 s, “Fast”).

Returns: , one value per sample, in mm/s.

Raises

ExceptionWhen
ValueErrorFor a bad record or a non-positive rate or constant.
spectral_density_level(psd: ArrayLike) -> NDArray[np.float64]

The level of a power spectral density, as Figure 7 draws it, in dB.

with , the square of VELOCITY_LEVEL_REFERENCE_MM_S per hertz.

Parameters

NameDescription
psdThe density, in (mm/s)²/Hz.

Returns: The level at each line, in decibels; -inf where the density is exactly zero.

Raises

ExceptionWhen
ValueErrorFor a negative or non-finite density.

Constant (float).

T1_DURATION_S = 4.0

Constant (mapping).

THIRD_OCTAVE_LINES = {4.0: 1, 5.0: 1, 6.3: 1, 8.0: 2, 10.0: 2, 12.5: 2, 16.0: 3, 20.0: 3, 25.0: 5, 31.5: 6, 40.0: 7, 50.0: 9, 63.0: 12, 80.0: 14, 100.0: 18, 125.0: 23, 160.0: 29, 200.0: 37, 250.0: 46, 315.0: 58, 400.0: 74, 500.0: 92}
third_octaves_from_narrowband(
frequencies_hz: ArrayLike,
psd: ArrayLike,
) -> tuple[NDArray[np.float64], NDArray[np.float64]]

Add a narrow-band spectrum back into third octaves, Formula (23).

Each band takes the number of lines Table 1 gives it, and the band r.m.s. is the square root of their summed power, . The table gives the counts and not the lines, so the choice is stated here: the lines nearest the nominal centre on a logarithmic axis. Wherever the count is what a sixth of an octave either side of the centre holds, which is every band but two, those are exactly the lines inside the band’s nominal edges; at 10 Hz and 12,5 Hz, where the table departs from the edges, they are 10 and 11,25 Hz, then 12,5 and 13,75 Hz.

It is a rule of nominal bands and it behaves like one. Nominal edges do not meet: six lines fall between two bands and are in neither (71,25 Hz, 141,25 Hz and 142,5 Hz, and 353,75 Hz to 356,25 Hz), and five are in two (178,75 Hz, 223,75 Hz, and 446,25 Hz to 448,75 Hz). The bands therefore do not add up to the spectrum exactly, and a tone that sits in one of the gaps is in no band at all; that is the comparability the table buys.

A band is returned only if the spectrum holds all its lines: a spectrum that starts above the lowest bands or stops inside a band leaves those bands out rather than filling them with the lines of a neighbour.

Parameters

NameDescription
frequencies_hzThe line frequencies of the spectrum, in hertz, evenly spaced at 1,25 Hz.
psdThe one-sided power spectral density at those lines, in (mm/s)²/Hz.

Returns: (centres, rms): the nominal centres of the bands the spectrum holds whole, from THIRD_OCTAVE_LINES, in hertz, and the band r.m.s. in each, in mm/s.

Raises

ExceptionWhen
ValueErrorFor mismatched or non-finite inputs, a negative density, uneven spacing, or a spacing Table 1 is not written for.
TrainPassage(
velocity_mm_s: NDArray[np.float64],
running_rms_mm_s: NDArray[np.float64],
peak_velocity_mm_s: float,
running_rms_max_mm_s: float,
kbf_max: float,
interval_rms_mm_s: tuple[float, float, float],
intervals_s: tuple[tuple[float, float], ...],
event_velocity_mm_s: float,
event_level_db: float,
band_centres_hz: NDArray[np.float64],
band_interval_levels_db: NDArray[np.float64],
band_max_levels_db: NDArray[np.float64],
fs_hz: float,
)

One passage reduced the way Clauses 5 to 7 reduce it.

Attributes

NameDescription
velocity_mm_sThe velocity the meter’s railway band limitation leaves, one value per sample.
running_rms_mm_s of it.
peak_velocity_mm_s, the largest absolute velocity of the passage (Clause 6.3).
running_rms_max_mm_s, the maximum of the running r.m.s.
kbf_max, the maximum of the weighted severity of DIN 45669-1 over the same stretch, which DIN 4150-2 reads.
interval_rms_mm_s, and of Formula (4), in that order.
intervals_sThe three stretches (start, end), in seconds.
event_velocity_mm_s of Formula (8), from .
event_level_db of Formula (9).
band_centres_hzThe nominal third-octave centres analysed.
band_interval_levels_db of Formula (6), one row per stretch, to .
band_max_levels_db of Formula (7), over .
fs_hzThe sampling frequency of the record.
TrainPassage.plot(
ax: Axes | None = None,
*,
language: str = 'en',
**kwargs: Any,
) -> Axes

Draw the running level with the three stretches marked on it.

Requires matplotlib (pip install phonometry[plot]). The spectra of Figure 6 are plot_spectrum.

Parameters

NameDescription
axExisting axes, or None to create a figure.
languageLabel language, "en" (default) or "es".
kwargsForwarded to phonometry._plot.vibration.plot_train_passage.

Returns: The Axes.

TrainPassage.plot_spectrum(
ax: Axes | None = None,
*,
language: str = 'en',
**kwargs: Any,
) -> Axes

Draw the interval and maximum third-octave levels, as Figure 6 does.

Parameters

NameDescription
axExisting axes, or None to create a figure.
languageLabel language, "en" (default) or "es".
kwargsForwarded to phonometry._plot.vibration.plot_train_passage_spectrum.

Returns: The Axes.

Constant (float).

VELOCITY_LEVEL_REFERENCE_MM_S = 5e-05
velocity_psd_from_voltage(
psd_v2_per_hz: ArrayLike,
*,
sensitivity_v_per_mm_s: float,
gain: float,
) -> NDArray[np.float64]

A density measured in volts, in the unit of the velocity (Annex A).

The transducer’s transfer coefficient and the amplifier’s gain multiply the velocity, so they divide its density in square: .

Parameters

NameDescription
psd_v2_per_hzThe density the analyser shows, in V²/Hz.
sensitivity_v_per_mm_s, in volts per millimetre per second (Annex A uses 0,030).
gain, the amplifier gain, in volts per volt (Annex A uses 10).

Returns: The density of the velocity, in (mm/s)²/Hz.

Raises

ExceptionWhen
ValueErrorFor a negative or non-finite density or a non-positive coefficient.