Correct fiche verdict rules and mandatory report items across the .report() layer (#281)
* fix(report): apply the current R 128 tolerance rules and the standards' mandatory report items across the fiches
The EBU R 128 programme-loudness fiche passed its verdict on a blanket
+/-0.5 LU about the target, the June 2014 (V3) rule that R 128 dropped in
its August 2020 revision. ProgramLoudnessResult.report() now selects the
tolerance explicitly: tolerance="qc" (default) applies the +/-0.2 LU
measurement-error allowance of item i) for loudness workflows such as
Quality Control, tolerance="live" applies the +/-1.0 LU tolerance of
item h) for programmes where the Target Level is not achievable
practically; the fiche prints the applied rule and its R 128 item, and the
verdict is evaluated on the loudness rounded to the displayed 0.1 LU
(Tech 3341 display precision), so the printed numbers can never contradict
it at the boundary. The committed example signal is trimmed 0.4 dB onto
the -23.0 LUFS target so it passes the QC rule it demonstrates.
Also fixed across the fiche layer:
- language="es" now reaches the embedded chart: the fiche language is
forwarded to the result's own plot(), so Spanish fiches embed Spanish
axis labels and legends instead of English ones.
- ISO 532-1: the fiche states the clause 7 mandatory items: the method
used (stationary, clause 5, or time-varying, clause 6), the sound field
(ZwickerLoudness gains a field attribute set by the constructors), the
loudness-versus-time function N(t) for time-varying sounds and the
"Maximum loudness Nmax" labelling of the reported maximum.
- ICAO Annex 16: effective_perceived_noise_level() gains
procedure="aeroplane"|"helicopter", plumbing the App. 2 4.3.1 Step 1
tone-correction start band (50 Hz for helicopters and tilt-rotors)
through the public chain; the verdict determines the EPNL once to one
decimal place, as the Annex states it, before the level, margin and
status are derived; the reference-conditions strip cites Part II
3.6.1.5 in full (25 C i.e. ISA + 10 C, 70 % RH, 1013.25 hPa, zero wind).
- ISO 717: the caption declares the band set actually rated (an
octave-band rating was captioned one-third-octave, against the clause
4.4 statement requirement); report() gains symbol= so field quantities
(DnT,w, L'nT,w, ...) are not mislabelled with the laboratory Rw / Ln,w
descriptor; adaptation terms print a sign only when negative, the style
of the standard's own examples.
- IEC 61260-1: verify_filter_class() reports range_limited (and per-band
checked_to_omega) when the stop-band mask extends beyond a band's
processing Nyquist, and the fiche qualifies its COMPLIES statement
accordingly; bands are labelled with nominal mid-band frequencies; the
1995 fiche cites that edition's relative-attenuation definition (3.13
Note); a required_class absent from the verified edition is rejected.
- ISO 11654: the shape indicator is written in the clause 5.3 style
(0.60(M), no space) and the 5.3 NOTE recommendation is printed when an
indicator applies.
- Header grids reprint client-supplied metadata verbatim (1.23 m2 no
longer becomes 1.2), values that round to zero never print -0.0, and
the remaining English fragments on the Spanish IEC 61260 fiche are
localised.
Every fiche gains at least one pypdf content test pinning the exact
displayed values, plus boundary tests for the display-rounded verdicts
and the helicopter start band.
* chore: regenerate example fiches, API reference pages and llms files
The example fiches pick up the corrected verdict rules and report items
(the programme-loudness example now demonstrates the default +/-0.2 LU QC
rule on target), and the generated API pages and llms files pick up the
new report keywords and result attributes.
* fix(report): integrate the fiche corrections with the field-report engine
The rebase onto the ISO 16283 fiches left two seams: the verdict strings were
added by both branches (duplicate i18n keys) and the new field renderer's
embedded-plot closure did not accept the language argument the layout now
forwards. Dedupe the table, accept the keyword, and re-verify the rendered
examples.
* fix(report): draw the LRA band as a pale opaque tint so the curves stay visible
The report pipeline renders through svglib, which drops alpha, so the
translucent LRA span came out as saturated orange and hid the momentary
trace. Use the 15 percent composite of the accent over white as an opaque
fill below the curves, matching the other fiches' band treatment.
* fix(report): keep fiche charts full width and legends tidy
Cap the relocated legend at three columns: a single four-entry row (the
impact fiches add the 500 Hz read marker) grew wider than the axes and
tight_layout shrank the chart, leaving the impact fiche visibly narrower
than its airborne sibling. Also compact the field-fiche y-axis labels to
the quantity symbol (the full name already heads the page and the table),
regenerate the affected examples, and drop the now-unused secondary color
import.
* review: remove float equality, reduce complexity and hoist raise-block setup
Sonar flagged the signed-zero normalisation in format_number and the
display rounding's zero check as float equality: normalise the sign by
inspecting the formatted text and compare the non-negative magnitude with
an order relation instead. Extract the per-band evaluation of
verify_filter_class and the loudness fiche's basis line into helpers to
bring both functions under the complexity threshold. Hoist the setup calls
out of four pytest.raises blocks, print the live tolerance through
format_number so it keeps one decimal, and round the loudness metrics row
with the same display rule as the boxed statement.
Correct fiche verdict rules and mandatory report items across the .report() layer (#281)
* fix(report): apply the current R 128 tolerance rules and the standards' mandatory report items across the fiches
The EBU R 128 programme-loudness fiche passed its verdict on a blanket
+/-0.5 LU about the target, the June 2014 (V3) rule that R 128 dropped in
its August 2020 revision. ProgramLoudnessResult.report() now selects the
tolerance explicitly: tolerance="qc" (default) applies the +/-0.2 LU
measurement-error allowance of item i) for loudness workflows such as
Quality Control, tolerance="live" applies the +/-1.0 LU tolerance of
item h) for programmes where the Target Level is not achievable
practically; the fiche prints the applied rule and its R 128 item, and the
verdict is evaluated on the loudness rounded to the displayed 0.1 LU
(Tech 3341 display precision), so the printed numbers can never contradict
it at the boundary. The committed example signal is trimmed 0.4 dB onto
the -23.0 LUFS target so it passes the QC rule it demonstrates.
Also fixed across the fiche layer:
- language="es" now reaches the embedded chart: the fiche language is
forwarded to the result's own plot(), so Spanish fiches embed Spanish
axis labels and legends instead of English ones.
- ISO 532-1: the fiche states the clause 7 mandatory items: the method
used (stationary, clause 5, or time-varying, clause 6), the sound field
(ZwickerLoudness gains a field attribute set by the constructors), the
loudness-versus-time function N(t) for time-varying sounds and the
"Maximum loudness Nmax" labelling of the reported maximum.
- ICAO Annex 16: effective_perceived_noise_level() gains
procedure="aeroplane"|"helicopter", plumbing the App. 2 4.3.1 Step 1
tone-correction start band (50 Hz for helicopters and tilt-rotors)
through the public chain; the verdict determines the EPNL once to one
decimal place, as the Annex states it, before the level, margin and
status are derived; the reference-conditions strip cites Part II
3.6.1.5 in full (25 C i.e. ISA + 10 C, 70 % RH, 1013.25 hPa, zero wind).
- ISO 717: the caption declares the band set actually rated (an
octave-band rating was captioned one-third-octave, against the clause
4.4 statement requirement); report() gains symbol= so field quantities
(DnT,w, L'nT,w, ...) are not mislabelled with the laboratory Rw / Ln,w
descriptor; adaptation terms print a sign only when negative, the style
of the standard's own examples.
- IEC 61260-1: verify_filter_class() reports range_limited (and per-band
checked_to_omega) when the stop-band mask extends beyond a band's
processing Nyquist, and the fiche qualifies its COMPLIES statement
accordingly; bands are labelled with nominal mid-band frequencies; the
1995 fiche cites that edition's relative-attenuation definition (3.13
Note); a required_class absent from the verified edition is rejected.
- ISO 11654: the shape indicator is written in the clause 5.3 style
(0.60(M), no space) and the 5.3 NOTE recommendation is printed when an
indicator applies.
- Header grids reprint client-supplied metadata verbatim (1.23 m2 no
longer becomes 1.2), values that round to zero never print -0.0, and
the remaining English fragments on the Spanish IEC 61260 fiche are
localised.
Every fiche gains at least one pypdf content test pinning the exact
displayed values, plus boundary tests for the display-rounded verdicts
and the helicopter start band.
* chore: regenerate example fiches, API reference pages and llms files
The example fiches pick up the corrected verdict rules and report items
(the programme-loudness example now demonstrates the default +/-0.2 LU QC
rule on target), and the generated API pages and llms files pick up the
new report keywords and result attributes.
* fix(report): integrate the fiche corrections with the field-report engine
The rebase onto the ISO 16283 fiches left two seams: the verdict strings were
added by both branches (duplicate i18n keys) and the new field renderer's
embedded-plot closure did not accept the language argument the layout now
forwards. Dedupe the table, accept the keyword, and re-verify the rendered
examples.
* fix(report): draw the LRA band as a pale opaque tint so the curves stay visible
The report pipeline renders through svglib, which drops alpha, so the
translucent LRA span came out as saturated orange and hid the momentary
trace. Use the 15 percent composite of the accent over white as an opaque
fill below the curves, matching the other fiches' band treatment.
* fix(report): keep fiche charts full width and legends tidy
Cap the relocated legend at three columns: a single four-entry row (the
impact fiches add the 500 Hz read marker) grew wider than the axes and
tight_layout shrank the chart, leaving the impact fiche visibly narrower
than its airborne sibling. Also compact the field-fiche y-axis labels to
the quantity symbol (the full name already heads the page and the table),
regenerate the affected examples, and drop the now-unused secondary color
import.
* review: remove float equality, reduce complexity and hoist raise-block setup
Sonar flagged the signed-zero normalisation in format_number and the
display rounding's zero check as float equality: normalise the sign by
inspecting the formatted text and compare the non-negative magnitude with
an order relation instead. Extract the per-band evaluation of
verify_filter_class and the loudness fiche's basis line into helpers to
bring both functions under the complexity threshold. Hoist the setup calls
out of four pytest.raises blocks, print the live tolerance through
format_number so it keeps one decimal, and round the loudness metrics row
with the same display rule as the boxed statement.
Correct the framing and the guards of the perception and noise-control fiches (#355)
A review pass over the psychoacoustics, hearing, speech, environmental and
noise-control fiches. The numerics held up; what did not were the statements
printed around them, plus one wrong table lookup.
ISO 1999 validated domain. nipts()/htlan() applied Formula (2) wherever they
were asked to: L_EX,8h = 130 dB over 60 years at the 0,99 fractile returned a
357,9 dB threshold shift with nothing marking it as an extrapolation. Since the
formula is quadratic in (L_EX,8h - L0) that number is meaningless, not merely
uncertain. Both now warn (NoiseInducedHearingLossWarning) outside the stated
ranges while still computing: durations outside 1-40 years (clause 6.3.1
validates Formula (2) over 10-40 and Formula (3) over 1-10), fractiles in the
tails clause 6.3.2 says "should not be estimated" (Q below 5 % or above 95 %),
and levels above the 100 dB of Annex D that the Scope's NOTE 4 restricts
validity to. The fiches print the caveat when the conditions sit outside.
ISO 1999 Q. The fiches printed the library's fractile under ISO 1999's symbol,
inverting its meaning: in Formulae (4)/(5) Q is the percentage with worse
hearing, so the most-susceptible tenth is Q = 10 %, not the "Q = 0.9" the fiche
showed while citing 6.3.2. They now print ISO's Q as ISO defines it; the guides
note the difference from the fractile argument. Two further disclosures on the
same fiches: the age component H is evaluated from ISO 7029:2017 (the edition
ISO 1999 references undated in 6.2.2), which departs from the illustrative
Table A.3 selection by up to about 7 dB at the median, and the 2/3/4 kHz
average is the user's own choice, the Scope's NOTE 1 specifying no frequency
combination. The footer states a population scope rather than "the results
relate only to the tested specimen", which describes a laboratory specimen.
ISO 1999 Annex C is wired in as an independent oracle: its worked example (male
population aged 50, 90 dB for 30 years, 1/2/4 kHz at Q = 10 %) reproduces the
Table D.2 shifts it consumes (0, 9, 19 dB), the 13,3 dB left of the 4 kHz shift
after the Formula (1) compression (C.5) and the 31,1 dB combined threshold
(C.11). The composition it exercises is now exposed as combine_age_and_noise(),
which clause 6.2 wants available on its own: 6.2.3 recommends a database B
collected on a control population of the country under consideration, and this
applies Formula (1) to whichever database the user brings.
Verdict precision. The STI and SII fiches printed the requirement with one
decimal beside a measured value at full precision, so a verdict could contradict
itself: "STI = 0.50, required >= 0.5 -> FAIL" against a 0,52 requirement, and an
SII minimum of 0,75 printed as "0.8" (the committed example said so). Both now
print the requirement at the quantity's own precision. IEC 60268-16 Annex M
joins the conformance report as an end-to-end oracle: its printed MTF matrix,
speech spectrum and ambient noise give back its published MTI row and
STI = 0,76, exercising the whole A.5.3 to A.5.8 chain.
Noise-control prediction framing. The enclosure, silencer and HVAC fiches
compute design models but read as measurements: no prediction statement,
measurement headers, a footer scoped to a tested specimen, and committed
examples citing instrumentation that never existed ("Two-microphone
transfer-matrix bench"). They now follow the EN/ISO 12354 prediction exemplar
already in the repo, with a prediction-basis line, a statement naming what each
model does not represent, and a prediction footer. Their verdicts also decided
on the half-away-from-zero display rounding while printing Python's
round-half-to-even, so 0,25 printed 0.2 against a verdict taken on 0,3; both
now use the display rounding.
ISO/PAS 1996-3. With every onset rate at or below 10 dB/s the fiche withheld
the adjustment on the onset-rate gate (clauses 4.5/8) but justified it with "no
prominent impulse is present (governing P = 6.01 <= 5)", contradicting its own
boxed P. The note now states whichever gate governs. The assessment period is
read from the result instead of being hardcoded: 30 min is the Clause 5
*default*, now the default of the new assessment_period_min argument.
Smaller corrections:
- the HVAC elbow insertion loss took the wrong row of Bies Table 8.11 for a
W/lambda exactly on a bin edge; the rows read "a <= W/lambda < b", so an edge
opens its row (1 dB at 0,14, not 0 dB);
- the wind-turbine tonality fiche cited "subclauses 9.5.2-9.5.5" of
IEC 61400-11 for a chain running through 9.5.6 to 9.5.8 (Formulae 31-34);
- the ISO 1996-2 tonal fiche notes that Table J.1 keys on the mean audibility
of the J assessed spectra (ISO/PAS 20065 Clause 5.3.9), which a
single-spectrum assessment stands in for;
- the shared 10-90 % fractile band carried a hardcoded English legend in
Spanish figures, and the Spanish silencer and HVAC fiches printed the device
kind and duct-element label in English.
docs/ERRATA.md records two more source defects found on the way: ISO/PAS
1996-3:2022 Clause 5 swaps the 3.4/3.5 cross-references of the onset rate and
the level difference (its own units contradict the clause numbers printed with
them), and ISO 9613-2:1996 Table 2 prints 4,1 dB/km at 15 C / 80 % / 1 kHz
where ISO 9613-1 gives 4,1511, the neighbouring cells rounding correctly. The
library computes that coefficient from ISO 9613-1 directly, so it is unaffected.
Committed example fiches, the conformance report, the API reference and the
llms files are regenerated.
Correct the framing and the guards of the perception and noise-control fiches (#355)
A review pass over the psychoacoustics, hearing, speech, environmental and
noise-control fiches. The numerics held up; what did not were the statements
printed around them, plus one wrong table lookup.
ISO 1999 validated domain. nipts()/htlan() applied Formula (2) wherever they
were asked to: L_EX,8h = 130 dB over 60 years at the 0,99 fractile returned a
357,9 dB threshold shift with nothing marking it as an extrapolation. Since the
formula is quadratic in (L_EX,8h - L0) that number is meaningless, not merely
uncertain. Both now warn (NoiseInducedHearingLossWarning) outside the stated
ranges while still computing: durations outside 1-40 years (clause 6.3.1
validates Formula (2) over 10-40 and Formula (3) over 1-10), fractiles in the
tails clause 6.3.2 says "should not be estimated" (Q below 5 % or above 95 %),
and levels above the 100 dB of Annex D that the Scope's NOTE 4 restricts
validity to. The fiches print the caveat when the conditions sit outside.
ISO 1999 Q. The fiches printed the library's fractile under ISO 1999's symbol,
inverting its meaning: in Formulae (4)/(5) Q is the percentage with worse
hearing, so the most-susceptible tenth is Q = 10 %, not the "Q = 0.9" the fiche
showed while citing 6.3.2. They now print ISO's Q as ISO defines it; the guides
note the difference from the fractile argument. Two further disclosures on the
same fiches: the age component H is evaluated from ISO 7029:2017 (the edition
ISO 1999 references undated in 6.2.2), which departs from the illustrative
Table A.3 selection by up to about 7 dB at the median, and the 2/3/4 kHz
average is the user's own choice, the Scope's NOTE 1 specifying no frequency
combination. The footer states a population scope rather than "the results
relate only to the tested specimen", which describes a laboratory specimen.
ISO 1999 Annex C is wired in as an independent oracle: its worked example (male
population aged 50, 90 dB for 30 years, 1/2/4 kHz at Q = 10 %) reproduces the
Table D.2 shifts it consumes (0, 9, 19 dB), the 13,3 dB left of the 4 kHz shift
after the Formula (1) compression (C.5) and the 31,1 dB combined threshold
(C.11). The composition it exercises is now exposed as combine_age_and_noise(),
which clause 6.2 wants available on its own: 6.2.3 recommends a database B
collected on a control population of the country under consideration, and this
applies Formula (1) to whichever database the user brings.
Verdict precision. The STI and SII fiches printed the requirement with one
decimal beside a measured value at full precision, so a verdict could contradict
itself: "STI = 0.50, required >= 0.5 -> FAIL" against a 0,52 requirement, and an
SII minimum of 0,75 printed as "0.8" (the committed example said so). Both now
print the requirement at the quantity's own precision. IEC 60268-16 Annex M
joins the conformance report as an end-to-end oracle: its printed MTF matrix,
speech spectrum and ambient noise give back its published MTI row and
STI = 0,76, exercising the whole A.5.3 to A.5.8 chain.
Noise-control prediction framing. The enclosure, silencer and HVAC fiches
compute design models but read as measurements: no prediction statement,
measurement headers, a footer scoped to a tested specimen, and committed
examples citing instrumentation that never existed ("Two-microphone
transfer-matrix bench"). They now follow the EN/ISO 12354 prediction exemplar
already in the repo, with a prediction-basis line, a statement naming what each
model does not represent, and a prediction footer. Their verdicts also decided
on the half-away-from-zero display rounding while printing Python's
round-half-to-even, so 0,25 printed 0.2 against a verdict taken on 0,3; both
now use the display rounding.
ISO/PAS 1996-3. With every onset rate at or below 10 dB/s the fiche withheld
the adjustment on the onset-rate gate (clauses 4.5/8) but justified it with "no
prominent impulse is present (governing P = 6.01 <= 5)", contradicting its own
boxed P. The note now states whichever gate governs. The assessment period is
read from the result instead of being hardcoded: 30 min is the Clause 5
*default*, now the default of the new assessment_period_min argument.
Smaller corrections:
- the HVAC elbow insertion loss took the wrong row of Bies Table 8.11 for a
W/lambda exactly on a bin edge; the rows read "a <= W/lambda < b", so an edge
opens its row (1 dB at 0,14, not 0 dB);
- the wind-turbine tonality fiche cited "subclauses 9.5.2-9.5.5" of
IEC 61400-11 for a chain running through 9.5.6 to 9.5.8 (Formulae 31-34);
- the ISO 1996-2 tonal fiche notes that Table J.1 keys on the mean audibility
of the J assessed spectra (ISO/PAS 20065 Clause 5.3.9), which a
single-spectrum assessment stands in for;
- the shared 10-90 % fractile band carried a hardcoded English legend in
Spanish figures, and the Spanish silencer and HVAC fiches printed the device
kind and duct-element label in English.
docs/ERRATA.md records two more source defects found on the way: ISO/PAS
1996-3:2022 Clause 5 swaps the 3.4/3.5 cross-references of the onset rate and
the level difference (its own units contradict the clause numbers printed with
them), and ISO 9613-2:1996 Table 2 prints 4,1 dB/km at 15 C / 80 % / 1 kHz
where ISO 9613-1 gives 4,1511, the neighbouring cells rounding correctly. The
library computes that coefficient from ISO 9613-1 directly, so it is unaffected.
Committed example fiches, the conformance report, the API reference and the
llms files are regenerated.
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.
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.