Base the whole-body A(8) on the Directive 2002/44/EC Part B dominant axis and tighten the vibration fiches (#352)
Directive 2002/44/EC bases the whole-body daily exposure A(8) on the
highest of the frequency-weighted axis values 1,4*a_wx, 1,4*a_wy, a_wz
(Annex, Part B, point 1), not on the ISO 2631-1 Eq. (10) vector total
a_v; the hand-arm side keeps the Part A vector total a_hv.
- Relabel the whole-body magnitude of the daily-exposure fiche as
a_w,max with a printed note stating the Part B basis (EN/ES); add
vibration.wbv_exposure_basis() returning the dominant-axis value and
document in daily_vibration_exposure which magnitude each kind must
be fed with; state the same basis in the human-vibration guide
(EN/ES) and the measurement-chain diagram.
- Derive the boxed exposure zone of the fiche from the same
displayed-rounded comparisons as the assessment rows and the verdict,
so an A(8) printing exactly at a threshold cannot show a zone one
step below its own Exceeded row.
- ISO 10848 Kij fiche: distinguish an empty single-number mean caused
by every in-range band being bracketed (M < 0,25) from a spectrum
with no bands in the Annex A range (EN/ES).
- Spanish ISO 2631-5 outputs: translate the subject sex in the
injury-probability plot title and the word Formula in the fiche's
clause references.
- Correct the running_rms comment: the linear window divides by the
full window length (zero-padded front), it does not average over the
available samples.
Base the whole-body A(8) on the Directive 2002/44/EC Part B dominant axis and tighten the vibration fiches (#352)
Directive 2002/44/EC bases the whole-body daily exposure A(8) on the
highest of the frequency-weighted axis values 1,4*a_wx, 1,4*a_wy, a_wz
(Annex, Part B, point 1), not on the ISO 2631-1 Eq. (10) vector total
a_v; the hand-arm side keeps the Part A vector total a_hv.
- Relabel the whole-body magnitude of the daily-exposure fiche as
a_w,max with a printed note stating the Part B basis (EN/ES); add
vibration.wbv_exposure_basis() returning the dominant-axis value and
document in daily_vibration_exposure which magnitude each kind must
be fed with; state the same basis in the human-vibration guide
(EN/ES) and the measurement-chain diagram.
- Derive the boxed exposure zone of the fiche from the same
displayed-rounded comparisons as the assessment rows and the verdict,
so an A(8) printing exactly at a threshold cannot show a zone one
step below its own Exceeded row.
- ISO 10848 Kij fiche: distinguish an empty single-number mean caused
by every in-range band being bracketed (M < 0,25) from a spectrum
with no bands in the Annex A range (EN/ES).
- Spanish ISO 2631-5 outputs: translate the subject sex in the
injury-probability plot title and the word Formula in the fiche's
clause references.
- Correct the running_rms comment: the linear window divides by the
full window length (zero-padded front), it does not average over the
available samples.
fix(vibration): use the 1 kHz reference frequency in the wave-approach Kij (Hopkins Eq. 5.116) (#270)
The wave-approach vibration reduction index was computed as
Kij = 10 lg(1/tau_ij) + 5 lg(fc_j/fc_i). Hopkins (2007) Eq. 5.116 defines
the correction term as 5 lg(fc_j/f_ref) with f_ref = 1000 Hz. With the
Eq. 5.7 reciprocity of the angular-average transmission coefficients
(which the coefficients already satisfy) the f_ref form is exactly
symmetric, Kij = Kji, as EN 12354 and ISO 10848 require of the junction
descriptor; the shipped form was asymmetric. For an L-junction of 100 mm
(cL 3800 m/s, 220 kg/m2) and 215 mm (cL 3200 m/s, 430 kg/m2) concrete
plates it returned 6.81 dB one way and 8.09 dB the other, where the
correct symmetric value is 2.97 dB.
- wave_vibration_reduction_index now takes the receiving plate's
critical_frequency_receiver and applies 5 lg(fc_j/1000).
- junction_transmission computes both plates' thin-plate critical
frequencies, fc = sqrt(12) c0^2 / (2 pi h cL) with c0 = 343 m/s, and
stores them on JunctionTransmissionResult (critical_frequency1/2);
corner_reduction_index uses the receiving plate's value.
- Tests: Kij = Kji property test over random plate pairs for X, L and
the T1/T2 pair; the concrete L-junction oracle above; closed-form and
validation cases. The tau reciprocity test is unchanged.
- Docs: module header, guide pages (EN/ES), curated API table, generated
API reference and llms-full regenerated; the conformance row now
states fc_j = f_ref for the pinned 10 lg(12) value.
Add structural-vibration FRF reports via .report() (ISO 7626, ISO 10846) (#334)
* Add structural-vibration FRF reports via .report() (ISO 7626, ISO 10846)
Add a one-page PDF .report() fiche to the two structural-vibration
frequency-response result types.
MobilityResult.report() renders a mechanical-mobility measurement fiche
(ISO 7626-1:2011 frequency-response-function definitions; measurement per
ISO 7626-2:2015). Mechanical mobility is a continuous frequency-response
function, not an octave-band quantity, so the sheet presents it honestly as the
mobility magnitude spectrum |Y(f)| plus a compact table of the FRF's
characteristic points (the FRF type, driving-point or transfer, the frequency
range, the peak frequency, the peak mobility magnitude and the phase there),
with a boxed peak mobility |Y| at the frequency it occurs at. It is a
characterisation, so there is no pass/fail verdict.
TransferStiffnessResult.report() renders a dynamic-transfer-stiffness
characterisation fiche for a resilient element (ISO 10846-1:2008 definition;
determined by the direct method, ISO 10846-2:2008, or the indirect
blocking-mass method, ISO 10846-3:2002). The transfer stiffness is a continuous
frequency-response function, so the sheet presents it as the transfer-stiffness
level spectrum Lk(f) plus a compact table of characteristic points (the
determination method, the blocking mass for the indirect method, the frequency
range, and the low-frequency stiffness plateau |k2,1|, its level Lk and the loss
factor there), with a boxed low-frequency Lk. It too is a characterisation, with
no verdict.
Both fiches share a common FRF body (_report/_frf_fiche.py): the title and
basis line, the optional metadata header, the two-panel body with the
characteristic-point table beside the result's own spectrum plot, the boxed
representative value and the footer. English and Spanish both render; example
inputs reuse the modules' oracle-validated closed forms.
* Reuse the result loss_factor in the transfer-stiffness fiche
Share the single ISO 10846-1 (3.8) loss-factor definition by reading the
result's own loss_factor property at the low-frequency index, instead of
recomputing eta = Im/Re in the renderer.
* Drop the unused low-frequency frequency in the stiffness table
The characteristic-point table only needs the magnitude, level and loss
factor at the low-frequency plateau, not its frequency.
Base the whole-body A(8) on the Directive 2002/44/EC Part B dominant axis and tighten the vibration fiches (#352)
Directive 2002/44/EC bases the whole-body daily exposure A(8) on the
highest of the frequency-weighted axis values 1,4*a_wx, 1,4*a_wy, a_wz
(Annex, Part B, point 1), not on the ISO 2631-1 Eq. (10) vector total
a_v; the hand-arm side keeps the Part A vector total a_hv.
- Relabel the whole-body magnitude of the daily-exposure fiche as
a_w,max with a printed note stating the Part B basis (EN/ES); add
vibration.wbv_exposure_basis() returning the dominant-axis value and
document in daily_vibration_exposure which magnitude each kind must
be fed with; state the same basis in the human-vibration guide
(EN/ES) and the measurement-chain diagram.
- Derive the boxed exposure zone of the fiche from the same
displayed-rounded comparisons as the assessment rows and the verdict,
so an A(8) printing exactly at a threshold cannot show a zone one
step below its own Exceeded row.
- ISO 10848 Kij fiche: distinguish an empty single-number mean caused
by every in-range band being bracketed (M < 0,25) from a spectrum
with no bands in the Annex A range (EN/ES).
- Spanish ISO 2631-5 outputs: translate the subject sex in the
injury-probability plot title and the word Formula in the fiche's
clause references.
- Correct the running_rms comment: the linear window divides by the
full window length (zero-padded front), it does not average over the
available samples.
feat: theoretical panel sound reduction, radiation efficiency and point mobilities (#233)
* feat: theoretical panel sound insulation, plate radiation efficiency and point mobilities
Predict the airborne sound reduction index R(f) of building elements from
their physical properties, closing the EN 12354 chain from panel physics to
the single-number rating without a laboratory measurement.
building.panel_transmission adds the mass law and coincidence dip by Sharp's
method (single_panel_transmission_loss), the mass-spring-mass double wall with
an optional porous cavity fill (double_wall_transmission_loss,
mass_spring_mass_resonance), all from Bies, Hansen & Howard 5e Section 7.2.
building.aperture_transmission adds transmission through slits (Gomperts) and
circular holes (Wilson & Soroka) and their area-weighted composition with the
wall (Hopkins Section 4.3.10, Eq. 4.92), so a bare opening caps the composite
at 10 lg(S/Sa).
vibration.radiation_efficiency predicts the Leppington/Maidanik radiation
efficiency of a bending plate (Hopkins Section 2.9), the radiation factor
ISO 7849 otherwise measures; vibration.point_mobility adds the closed-form
point impedances and mobilities of infinite plates, beams and rods and the
injected power (Cremer, Heckl & Petersson 3e Table 5.1).
Every prediction exposes .plot() and, for R(f), .rating() (ISO 717-1). Anchored
by closed-form oracles (exact mass law, coincidence and mass-air-mass
frequencies, aperture area limit) and digitized curves from the source books.
* docs: panel sound insulation guide, theory, conformance, figure and API reference
Add the Predicting Panel Sound Insulation guide (EN/ES), the concept figure
(single/double wall, radiation efficiency, composite aperture), theory sections
in the rooms-buildings and vibration references, eleven conformance checks
(mass law slope, coincidence and mass-air-mass frequencies, aperture area
limit, slit resonance, plate/beam mobilities), the generated API reference
pages and the curated api-reference table, llms.txt and the CHANGELOG entry.
* refactor: address PR #233 review (Sonar S1192, slit numerical stability)
- Extract the duplicated 'Frequency [Hz]' axis label (_plot/building.py) and the
'frequency must be positive' message (panel_transmission.py) into module
constants (SonarQube S1192).
- Reformulate the Gomperts slit transmission coefficient (Eq. 4.99) by
multiplying numerator and denominator by cos^2(Ke): mathematically identical
(tau unchanged to 2.5e-16) but finite where cos(Ke) crosses zero, instead of
dividing by it.
- Guard slit_resonance_frequencies against a slit so wide that the effective
depth d + 2e turns non-positive, raising a clear error instead of a NaN.
- Propagate the earlier em-dash prose fix into the embedded llms-full.txt.
- Add regression tests for the wide-slit guard and the cos(Ke)=0 sweep.
feat: theoretical panel sound reduction, radiation efficiency and point mobilities (#233)
* feat: theoretical panel sound insulation, plate radiation efficiency and point mobilities
Predict the airborne sound reduction index R(f) of building elements from
their physical properties, closing the EN 12354 chain from panel physics to
the single-number rating without a laboratory measurement.
building.panel_transmission adds the mass law and coincidence dip by Sharp's
method (single_panel_transmission_loss), the mass-spring-mass double wall with
an optional porous cavity fill (double_wall_transmission_loss,
mass_spring_mass_resonance), all from Bies, Hansen & Howard 5e Section 7.2.
building.aperture_transmission adds transmission through slits (Gomperts) and
circular holes (Wilson & Soroka) and their area-weighted composition with the
wall (Hopkins Section 4.3.10, Eq. 4.92), so a bare opening caps the composite
at 10 lg(S/Sa).
vibration.radiation_efficiency predicts the Leppington/Maidanik radiation
efficiency of a bending plate (Hopkins Section 2.9), the radiation factor
ISO 7849 otherwise measures; vibration.point_mobility adds the closed-form
point impedances and mobilities of infinite plates, beams and rods and the
injected power (Cremer, Heckl & Petersson 3e Table 5.1).
Every prediction exposes .plot() and, for R(f), .rating() (ISO 717-1). Anchored
by closed-form oracles (exact mass law, coincidence and mass-air-mass
frequencies, aperture area limit) and digitized curves from the source books.
* docs: panel sound insulation guide, theory, conformance, figure and API reference
Add the Predicting Panel Sound Insulation guide (EN/ES), the concept figure
(single/double wall, radiation efficiency, composite aperture), theory sections
in the rooms-buildings and vibration references, eleven conformance checks
(mass law slope, coincidence and mass-air-mass frequencies, aperture area
limit, slit resonance, plate/beam mobilities), the generated API reference
pages and the curated api-reference table, llms.txt and the CHANGELOG entry.
* refactor: address PR #233 review (Sonar S1192, slit numerical stability)
- Extract the duplicated 'Frequency [Hz]' axis label (_plot/building.py) and the
'frequency must be positive' message (panel_transmission.py) into module
constants (SonarQube S1192).
- Reformulate the Gomperts slit transmission coefficient (Eq. 4.99) by
multiplying numerator and denominator by cos^2(Ke): mathematically identical
(tau unchanged to 2.5e-16) but finite where cos(Ke) crosses zero, instead of
dividing by it.
- Guard slit_resonance_frequencies against a slit so wide that the effective
depth d + 2e turns non-positive, raising a clear error instead of a NaN.
- Propagate the earlier em-dash prose fix into the embedded llms-full.txt.
- Add regression tests for the wide-slit guard and the cos(Ke)=0 sweep.
feat: rotorcraft single-event noise (ECAC Doc 32): flight-condition interpolation, kinematics, SEL/LASmax/EPNL and contours (#213)
* feat: rotorcraft flight-condition interpolation, kinematics and single-event metrics (ECAC Doc 32 / NORAH2)
Complete the hemisphere chain into the single-event method:
- flight_condition_weights / interpolated_source_level: the guidance
Eq. 3-10 interpolation across a hemisphere database (span-normalised
conditions with Ffc = 2 on the path angle, distance-scaled Delaunay
triangle blend inside the convex hull, nearest neighbour outside, and
optional database lookup triangulations, which the shipped NORAH
tables require because they triangulate the raw (V, gamma) plane).
- flight_path_kinematics: ground speed, airspeed, heading, curvature,
bank and path angle by central differences (Eq. 16-21; the path angle
follows Doc 32 Eq. 10, whose atan corrects the guidance's acos print).
- rotorcraft_event_level: the received one-third-octave history at
recorded time tr = te + r/c (Eq. 22, c = 346.1 m/s) with the
hemisphere frame oriented by heading and tilted by bank (pitch is
implicit in the hemispheres), integrated into LASmax, full-history
and 10 dB-down SEL (Doc 32 Eq. 27) and ICAO Annex 16 EPNL (Doc 32
Eq. 28) with the helicopter 50 Hz tone-correction start band.
Per-point airspeed/path-angle/heading/bank overrides serve radar-track
workflows and per-point level offsets the Eq. 2 class substitutions.
- rotorcraft_noise_contour: the single-event SEL/LASmax ground grid in
one vectorised pass per emission step.
- RotorcraftHemisphere.mirrored() for mirrored-rotor class members;
vectorised hemisphere lookup and ground-effect cores (bit-identical
to the previous scalar paths).
Every result is a frozen dataclass with .plot(). Validated end to end
against the NORAH2 reference implementation's ARP verification cases:
emission angles to 0.01 deg, hard-ground step levels to 0.08 dB(A) out
to 18 km, LASmax to 0.03 dB, SEL to 0.05-0.4 dB and PNLTM to 0.1 dB;
plus hand-checked interpolation simplices, closed-form kinematics and
the Lorentzian flyover SEL integral.
* test: NORAH2 ARP end-to-end oracle suite for the rotorcraft event chain
Local-only validation against the NORAH2 V2.0.74 prototype outputs
(skips cleanly when the archive is absent; NORAH2_DATA points at an
existing extraction). The ARP cases' .his/.onl/.inp files provide the
oracle for the emission angles, retarded times, hemisphere selection,
per-step levels, event metrics and the 187-microphone contour grid:
- Case 4 (sigma 1e6, 0.2 m microphone) and the Case 2 radar track
reproduce every step to 0.08-0.15 dB(A) out to 18 km; SEL/LASmax to
0.03-0.1 dB, PNLTM to 0.09 dB.
- Case 2 soft-ground events (sigma 2e5, 1.2 m microphone) reproduce the
metrics while bounding the documented far-tail divergence: the
prototype damps the coherent two-ray interference of guidance Eq. 30
towards the incoherent sum where the in-band comb phase is large, a
term neither ECAC Doc 32 nor the guidance contains; this
implementation follows the published Eq. 28-35.
- The A600 events pin the per-condition Eq. 2 certification offsets and
the contour test covers both ground classes of the .onl grid.
No NORAH data is committed; the parsers are test-side plumbing.
* feat: conformance checks for the rotorcraft interpolation, kinematics and event
Four hand-checkable additions to the Doc 32 domain: the Eq. 8
distance-scaled triangle blend of three uniform hemispheres (97.0367 dB
by hand), the Eq. 17 airspeed of a straight climbing track, the Eq. 22
recorded-time delay at 100 m slant distance, and the closed-form
Lorentzian SEL minus LASmax of a constant-speed level flyover
(10 lg(pi d / V), with the class-H ground holding dLg at the +6 dB
pressure doubling along the whole path). 261/261 checks pass.
* docs: rotorcraft single-event guide, flyover figure and errata entries
Extend the rotorcraft guide (docs tree and site EN/ES) with the
flight-condition interpolation, the track kinematics, the single-event
SEL/LASmax/EPNL and the ground-grid contours, each with runnable
modular-API snippets, and add the flyover time-history figure (four
language and theme variants with its reproduction snippet). Refresh the
aircraft section landing pages, the API quick reference and the
generated API pages, and record two NORAH2 guidance defects in the
errata registry: Eq. 21 prints acos for the path angle where Doc 32
Eq. 10 prints the correct atan, and the shipped triangulation lookup
tables triangulate the raw (V, gamma) plane instead of the normalised
one prescribed by section A.3.1.
* fix: drop runtime asserts from the rotorcraft event chain (Bandit B101)
The event histories now always carry the first receiver's unweighted
band levels (the single-receiver event needs them for the
perceived-noise metrics and the cost is negligible next to the (K, G)
histories), so the Optional return and its narrowing assert disappear;
the per-point level-offset narrowing spells out its zero fallback
instead of asserting. bandit -r src is clean and the behaviour is
unchanged.
* test: record the expected library warnings instead of leaking them
pytest-xdist forwards every uncaught warning from a worker to the
controller, which re-materialises it by importing the warning class's
module; a warning class defined inside phonometry therefore makes the
controller import the package concurrently from several receiver
threads, and the import can be observed partially initialised
(ImportError on phonometry.hearing.sti) or deadlock on Windows. The
suite leaked exactly five such warnings, all expected by their tests:
the two extra STIWarning advisories of the pure-tone STIPA call (the
matched pytest.warns re-emitted the non-matching ones), the
FilterBankWarning detrending advisory of the steady-state initial
conditions, and the ISO 10846-3 validity advisory of the synthetic
transfer-stiffness plot data. Each is now asserted where it is
expected; the remaining run-summary warnings all carry builtin
categories, which the controller reconstructs without touching
phonometry.
* refactor: rotorcraft quality-gate and review-round fixes
SonarCloud:
- The bank tilt of the emission frame applies unconditionally: at zero
bank the rotation is the IEEE identity (cos 0 = 1, sin 0 = 0), so the
float-equality branch disappears with bit-identical output (verified
against the NORAH2 ARP residuals, unchanged to the last digit).
- The lookup-table search of the enveloping triangle moves to its own
helper, cutting the cognitive complexity of _enveloping_simplex.
- The single-event internals now flow through one validated _EventSetup
(source database, track state, ground and atmosphere) built by a
shared boundary that both public functions call, removing their
duplicated validation; the public signatures keep the ECAC Doc 32
parameter set one for one, covered by the same per-file S107 record
the Doc 29 airport API already uses in sonar-project.properties.
- The band-grid validation test builds its hemispheres outside the
pytest.raises blocks so each block holds one failing call.
Review round:
- The kinematics bank angle evaluates (dTheta/dt)*Vg/g, equal to
K*Vg^2/g without the intermediate 1/Vg division that could overflow
at minute ground speeds; the curvature output is unchanged.
- flight_path_kinematics reuses the shared track validation helper.
- atmospheric_adjustment reuses the shared pure-tone coefficient
helper, leaving a single suppression site for the sub-50 Hz advisory.
- The flight-condition docstring states the guidance Eq. 3-6 scaling
exactly: spans only, no minima subtracted (a shared offset cancels in
the Eq. 7 distances); the generated API page follows.
Add structural-vibration FRF reports via .report() (ISO 7626, ISO 10846) (#334)
* Add structural-vibration FRF reports via .report() (ISO 7626, ISO 10846)
Add a one-page PDF .report() fiche to the two structural-vibration
frequency-response result types.
MobilityResult.report() renders a mechanical-mobility measurement fiche
(ISO 7626-1:2011 frequency-response-function definitions; measurement per
ISO 7626-2:2015). Mechanical mobility is a continuous frequency-response
function, not an octave-band quantity, so the sheet presents it honestly as the
mobility magnitude spectrum |Y(f)| plus a compact table of the FRF's
characteristic points (the FRF type, driving-point or transfer, the frequency
range, the peak frequency, the peak mobility magnitude and the phase there),
with a boxed peak mobility |Y| at the frequency it occurs at. It is a
characterisation, so there is no pass/fail verdict.
TransferStiffnessResult.report() renders a dynamic-transfer-stiffness
characterisation fiche for a resilient element (ISO 10846-1:2008 definition;
determined by the direct method, ISO 10846-2:2008, or the indirect
blocking-mass method, ISO 10846-3:2002). The transfer stiffness is a continuous
frequency-response function, so the sheet presents it as the transfer-stiffness
level spectrum Lk(f) plus a compact table of characteristic points (the
determination method, the blocking mass for the indirect method, the frequency
range, and the low-frequency stiffness plateau |k2,1|, its level Lk and the loss
factor there), with a boxed low-frequency Lk. It too is a characterisation, with
no verdict.
Both fiches share a common FRF body (_report/_frf_fiche.py): the title and
basis line, the optional metadata header, the two-panel body with the
characteristic-point table beside the result's own spectrum plot, the boxed
representative value and the footer. English and Spanish both render; example
inputs reuse the modules' oracle-validated closed forms.
* Reuse the result loss_factor in the transfer-stiffness fiche
Share the single ISO 10846-1 (3.8) loss-factor definition by reading the
result's own loss_factor property at the low-frequency index, instead of
recomputing eta = Im/Re in the renderer.
* Drop the unused low-frequency frequency in the stiffness table
The characteristic-point table only needs the magnitude, level and loss
factor at the low-frequency plateau, not its frequency.
Base the whole-body A(8) on the Directive 2002/44/EC Part B dominant axis and tighten the vibration fiches (#352)
Directive 2002/44/EC bases the whole-body daily exposure A(8) on the
highest of the frequency-weighted axis values 1,4*a_wx, 1,4*a_wy, a_wz
(Annex, Part B, point 1), not on the ISO 2631-1 Eq. (10) vector total
a_v; the hand-arm side keeps the Part A vector total a_hv.
- Relabel the whole-body magnitude of the daily-exposure fiche as
a_w,max with a printed note stating the Part B basis (EN/ES); add
vibration.wbv_exposure_basis() returning the dominant-axis value and
document in daily_vibration_exposure which magnitude each kind must
be fed with; state the same basis in the human-vibration guide
(EN/ES) and the measurement-chain diagram.
- Derive the boxed exposure zone of the fiche from the same
displayed-rounded comparisons as the assessment rows and the verdict,
so an A(8) printing exactly at a threshold cannot show a zone one
step below its own Exceeded row.
- ISO 10848 Kij fiche: distinguish an empty single-number mean caused
by every in-range band being bracketed (M < 0,25) from a spectrum
with no bands in the Annex A range (EN/ES).
- Spanish ISO 2631-5 outputs: translate the subject sex in the
injury-probability plot title and the word Formula in the fiche's
clause references.
- Correct the running_rms comment: the linear window divides by the
full window length (zero-padded front), it does not average over the
available samples.