Measure the noise of railway rolling stock by ISO 3095, on a reference track judged by EN 15610 and EN 15461 (#909)
Three new modules under environment.sources carry the ISO 3095 type test of a railway vehicle: the pass-by level and transit exposure level of a pressure record, the stationary, constant-speed, starting and braking results from their runs, the Annex A, C, D, E and G steps, and a single verdict on the reference track from the rail roughness of EN 15610 and the decay rates of EN 15461. Every result has .plot(), a new guide in English and Spanish walks a pass-by and a reference track through, the conformance report pins the limits of Figures 2 and 3, the positions of Figure 10 and the budget of Table G.2, and four printed defects of ISO 3095 Table G.1 and the EN 15610 listing are in the errata register.
Publish the two track-descriptor digits a caller could receive but not name (#688)
The CNOSSOS-EU track descriptor has six digits, and `phonometry.environment`
published four of them. `TrackMeasure` (digit 4: rail damper, low barrier,
absorber plate, embedded rail) and `RailJoints` (digit 5) sit in
`cnossos_rail.py` beside the other four and were left out of every `__all__` on
the way up, so `from phonometry.environment import TrackMeasure` raised
`ImportError` while its four siblings imported.
That is not cosmetic. `TrackDescriptor` declares both as fields and
`from_code("BMSDSH")` hands back `TrackMeasure.RAIL_DAMPER`, so a caller held an
enum member it had no supported way to name, and could not build a descriptor
for a damped or jointed track without reaching into a private module.
The round-trip test had the same hole, asserting digits 1, 2, 3 and 6 and
skipping the two it could not name either. It asserts all six now, and a second
case carries digits 4 and 5 away from their defaults.
The gap survived because nothing looked for it. The reference table documents
both enums, and its gate asks only that every published name have a row, never
that every row name something a reader can reach; those two rows were the only
entries in the whole table pointing at nothing. So the guard here closes the
class, not the case: a new architecture test walks the fields of every published
dataclass and fails on any whose type is defined in the tree and published by no
package. Checked the way this repo checks a guard, by reverting the exports and
watching it name exactly these two.
Every verdict reads its pass and fail flags, grades and classes from the values it holds (#949)
A result no longer stores a verdict beside the readings it came from. Sixty-two result classes used to keep the flag, grade or class their function had worked out (`passes`, `complies`, `qualified`, the `*_met` and `*_ok` flags, the advisories, the per-band classes of the IEC 61260-1, IEC 61672-1 and IEC 61043 verdicts, the ISO 9614-2 grade, the ISO/PAS 1996-3 category, the IEC TS 61400-11-2 block status and the rest), so a result built by hand or rewritten with `dataclasses.replace` could hold readings that fail and a flag that says they pass. Each of them is now a read-only property, read from the values the result keeps and the limit the standard prints, and so is any figure that only fed it, such as the rounded level of an RD 1367/2007 period or the acceptance region of a trend test.
Measure what a road surface adds to traffic noise, by the ISO 11819-1 statistical pass-by (#867)
* Measure what a road surface adds to traffic noise, by the ISO 11819-1 statistical pass-by
The regression of each vehicle category's maximum level on the logarithm of
speed, the vehicle sound level at the Table 1 reference speed, the index of
9.5, the difference from a reference surface and the average of Annex D,
with the 7.3 counts and the 9.3 speed window judged and warned about, the
Table 2 random errors published and each line's own confidence interval.
Levels are carried unrounded and rounded once, to one decimal, only where
they are reported.
* The Annex E report of ISO 11819-1 is a conformance row from pass-bys to its difference
Seven rows: the two printed tables, the vehicle sound levels read off
pass-bys whose lines are the printed ones, the index of the levels as the
report prints them, the corrected index, the difference from the 77,3 dB
reference surface and the Annex D average. Two defects of the printed page
are registered: the speed spreads of Annex E do not fit the regression
beside them, and the one table of Annex D is numbered after Annex E.
* A reference surface given as a numpy number is read as an index
Any real number now counts as the reference index, and only a mapping as the
three vehicle sound levels of a reference surface; a numpy integer used to
be taken for a mapping and refused.
* A guide to measuring what a road surface adds to traffic noise
The statistical pass-by of ISO 11819-1 in English and Spanish, with its
docs mirror: the vehicle and road speed categories, how the measurement
goes, the vehicle sound level and its speed window, the index, the
reference surface of clause 10 and Annex D, what the standard leaves to the
tester about temperature, how precise the result is, and the rounding the
Annex E example uses against the one the clause asks for. Two figures draw a
site's three clouds with their lines and one category with its window, and
the page is registered in the sources overview, the guides index and the
changelog.
* The Table 2 test says what the arithmetic reproduces and what it does not
Two standard errors of 100 cars give the printed 0,3 dB; for 40 heavy
vehicles they give 0,62 dB where 0,7 dB is printed, and the test no longer
offers an explanation the clause does not give.
* The pass-by index adds the vehicle sound levels as 9.2 reports them, so Annex E gives 79,9 dB end to end
9.5 defines the levels of its index as the vehicle sound levels "according
to 9.2", which rounds every level to one decimal, so statistical_pass_by now
indexes the three levels rounded once, half up, and does the same for
temperature-corrected levels and for a reference given as levels. Run from
pass-bys on the regression lines Annex E prints, the index is 79,946 dB and
reports as the printed 79,9 dB, where the unrounded levels gave 80,0 dB; that
unrounded index stays on the result as full_precision_index_db, and the
corrected levels as reported_corrected_vehicle_sound_levels_db.
The Annex E conformance row now reads the index the library returns instead
of recomputing it. New tests pin the 9.3 window on pass-bys placed with a
known mean and spread of lg v, the 7.3 counts on their boundaries, and other
weighting factors through every index of the result. The 9.3 window is drawn
as an opaque wash that stays visible on a light page, the figures label the
reported levels, and a pass-by is a "paso" in the Spanish figures as in the
guide.
* The pass-by guide draws the site, follows 9.2 and 9.5 to the index, and is linked from the environment overview
A diagram shows the microphone 7,5 m from the measuring lane and 1,2 m high,
the second position for a narrow shoulder, and the 30 m of level, straight
road each side of it. The rounding section now says what the clauses settle:
the index adds the levels of 9.2 to one decimal, the Annex E example does the
same, and its printed lines alone could not have told the two chains apart.
Annex D is described as the seven dense bituminous surfaces it prints, 10.1
as the clause that bounds the texture, the corrected report items as the
optional ones clause 13 lists, and the Spanish page quotes the standard
between angle quotes. The environment overview, the sources overview and the
guides index now list the page.
* The Annex E residual spreads agree with the regression within the rounding of what is printed beside them
The erratum on the Annex E speed spreads cited the residual row as matching
the printed slope, correlation and level spread outright. It matches within
the rounding of the printed r and s_L, as the speed row is judged, and the
entry now gives those ranges in both languages.
* The Annex E speed-spread erratum argues from the coefficient of variation
The claim that any conversion scaling with the mean speed and growing with the
spread of lg v keeps the dual-axle vehicles the widest did not follow, since
those vehicles have the lower mean and the wider spread. The entry now uses the
ratio of the standard deviation to the mean, which depends on the spread of
lg v alone, and gives its values to first order and for a log-normal speed.
* Regenerate the conformance counts and the llms files on the current main
Measure the noise of railway rolling stock by ISO 3095, on a reference track judged by EN 15610 and EN 15461 (#909)
Three new modules under environment.sources carry the ISO 3095 type test of a railway vehicle: the pass-by level and transit exposure level of a pressure record, the stationary, constant-speed, starting and braking results from their runs, the Annex A, C, D, E and G steps, and a single verdict on the reference track from the rail roughness of EN 15610 and the decay rates of EN 15461. Every result has .plot(), a new guide in English and Spanish walks a pass-by and a reference track through, the conformance report pins the limits of Figures 2 and 3, the positions of Figure 10 and the budget of Table G.2, and four printed defects of ISO 3095 Table G.1 and the EN 15610 listing are in the errata register.
Read the quasi-peak value method, not an r.m.s. through its network (#647)
* Read the quasi-peak value method, not an r.m.s. through its network
ITU-R BS.468-4 clause 3 reserves dBqps for the quasi-peak value method,
and NOTE 1 says an r.m.s. meter reading the same network gives figures
that are not directly comparable. The network was already exposed and the
curve landed in #645; the detector itself was missing.
Clause 2 prints no time constant, so the three are solved for against the
eleven reference readings of Tables 2 and 3 rather than quoted. How well
eleven windows pin three constants is measured and stated with the
procedure that produced it, because the answer depends on the question:
with the other two frozen, charge holds from 1.014 to 1.894 ms, discharge
from 233 to 367 ms and the reading device from 96 to 200 ms, so the
reading device is what the tables pin down least.
Four ballistics that all pass all eleven windows still read 2.61 dB apart
on a click and 0.03 dB apart on a steady tone. That is a property of the
standard, so it is documented as a lower bound on any reading here.
Four misprints found on the way are recorded in docs/ERRATA.md, none of
which changes what this library computes. IEC 60268-1:1985 Figure A1
prints the last shunt capacitor as 41.47 nF where BS.468-4 Figure 1a
prints 31.47 nF, and the annex contradicts itself two pages earlier:
against Table AI the printed value breaks that table's own tolerance
column at seven frequencies.
MicrophoneNoise(weighting="CCIR") stays metadata and weighted_thd stays
an r.m.s. through the network; both are now documented as such.
* Key the quasi-peak caches by the ballistics, and remeasure what they identify
The published statement about how far each of the three time constants can
move before one of the eleven windows fails could not be measured. The chain
caches the clause 2.6 calibration factor and the steady 5 kHz reference on the
sample rate alone, and both run through the detector, so moving the constants
left the caches answering with a reference computed for a different
instrument. The symptom was that the same value passed or failed depending on
what order the probes ran in.
It was correct while nothing could change the constants, and wrong for a
module whose documentation publishes a claim about exactly that. The
ballistics now travel through the chain as an argument and into both cache
keys, and verify_quasi_peak_dynamics takes them too, so the claim is
re-derivable rather than a number to be believed.
Remeasured on that footing, all eleven windows hold from 1.02 to 1.90 ms of
charge (1.88), 230 to 370 ms of discharge (1.61) and 96 to 200 ms of reading
device (2.09), each edge a value that conforms rather than one rounded past
the boundary. The earlier figures were taken through the contaminated path and
were wrong in both directions; one published edge did not even conform.
Two things follow that were not said before. The ranges are marginal and not a
box: the fastest charge with the shortest indicator misses by 1.11 dB. And the
spread between conforming instruments is larger than reported, because the
first measurement read the needle before it had finished rising. Sampling the
region and keeping the 243 sets that meet all eleven windows gives 4.61 dB on
a transient, more than the widest window the tables draw, against 0.00 dB on a
steady tone, which is the calibration doing what it promises.
Four tests hold the range from both sides, so it can be neither widened nor
quietly narrowed, and one asks in the order that used to give a wrong answer.
* Build the fixture before the block that expects it to raise
Ten pytest.raises blocks across nine files built their argument inside the
block. If the construction itself raised, the test would pass for the wrong
reason and the call it exists to check would never run. Sonar flags this as
S5778 but only on new code, so eight of the ten would never have been
reported: the two it did report were in a file that happens to be new.
Also removes a redefinition in _plot/signals.py, where _TIME_LABEL and
_LAG_LABEL are each assigned twice at module level. Both pairs carry the same
string so nothing is wrong today, but editing the first has no effect, which
is the kind of thing that is discovered the hard way. A sweep of the rest of
src finds no other case.
* Say on every page that this documentation is ahead of the release
The site documents the tree, which is ahead of what is on PyPI: pages here
describe modules, entry points and names that 3.3.0 does not have, so a reader
who installs the published version and follows a page gets an ImportError and
no reason for it. A standing banner says so.
Starlight's banner is per page, set in frontmatter, and its config-level form
takes one string with no way to vary it by locale. Half this site is Spanish,
so the component picks the text from the route's language. A page that sets
its own banner still wins: that one is about the page, this one is about the
site.
* Say the verdict in words, show how much of each tolerance is spent
The conformance tables marked every row with a tick or a cross, which can only
say two things. There are four verdicts: a check can also be by design, as the
Chebyshev and Bessel architectures are against a mask they cannot meet, or not
applicable. Those two had nowhere to go and were carried in a display string
where nothing could count them. A word says all four, reads the same in a
plain-text diff and needs no colour.
The Markdown report also never showed the number that matters most for a
metrology library: how much of a clause's published tolerance the deviation
actually consumes. The artefact has carried it since the report became
structured data, the site page has shown it, and the report itself did not.
It is there now on 469 of the 566 rows, with the dash on the other 97
explained rather than left to be guessed: those clauses state no two-sided
tolerance, so there is no budget to spend.
The same column appears twice in the PR comment and was explained under the
second table, which the reader reaches after meeting the column under the
first. The note moves to the head of the section.
Two responsive defects, both measured in a browser at a 390 px viewport rather
than reasoned about:
- The domain filter on the conformance page is sized by its longest option,
"2D FDTD wave simulation (Attenborough & Van Renterghem 2021, Ch. 4)", which
asks for 552 px inside a 374 px row. A flex item's automatic minimum size is
its content, so it refused to shrink and took the document to 582 px. With
min-width: 0 it is 390.
- Inline KaTeX had no scroll box, only display math did, and KaTeX never breaks
a formula. One acceleration formula on the errata page is 1011 px wide and
carried that document to 1067 px.
Wide tables were not part of it: they already scroll inside their own
containers, and only looked guilty because getBoundingClientRect reports an
element's full width whether or not its container clips it.
* Draw the verdicts and the count, instead of spelling them in emoji
The report says Pass, Fail, By design and n/a in words now, which fixed the
meaning an emoji could never carry, but left the tables with nothing to scan.
This adds the pictures back as SVG the repository owns and generates: four
verdict marks and a summary banner under .github/badges, redrawn by
`make conformance` from docs/conformance.json so the count on the banner is
always the tree's own. Nothing points at them yet; the documents come next.
A mark is one file with no dark twin, and that is a measured decision rather
than a shortcut. Each fill clears 3:1 on GitHub light, GitHub dark, GitHub dim
and the documentation site's ground alike, so the same file is right in every
theme, the way a shields.io badge has been for a decade. Pairing them instead
would need a <picture> element on every row, which costs 163 kB on the 566-row
table against 9.6 kB for reference-style images of a single-file mark. The
banner is the opposite case, once per page, where the pair costs 278
characters and buys a card that sits properly on its page, so it ships as a
light and a dark variant on the _dark naming the README and ThemeImage.astro
already pair on.
Shape carries the meaning alongside colour: a filled disc, a hexagon, a
rounded square and a hollow ring stay apart from each other in greyscale and
to a reader who cannot separate the hues. Every file states its verdict in a
<title> as well, because an image of a tick is not a verdict to anyone who
cannot see it.
The blue moved off Primer's #0969da, which measures 2.89:1 on GitHub Dim, onto
#1f6feb at 3.24:1. The whole contrast table is measured by the tests rather
than asserted in a comment.
Glyphs are outlines of the committed fonts, as the brand assets already are,
so the shared conversion moved into scripts/svg_text.py and generate_brand.py
now calls it; the brand output is byte for byte what it was. The banner is
drawn ten units to the pixel so every coordinate is a whole number, which
leaves no float to be formatted two ways on two machines. Two runs, three hash
seeds, a different timezone and a Turkish locale all write identical bytes.
Also fixes an assertion left pointing at a tick that no longer renders.
* Say why inline math has no scroll box, having measured what one costs
The commit before this gave inline KaTeX the scroll box display math already
had, on the reasoning that a formula wider than the column should scroll rather
than push the page sideways. The reasoning is right and the fix is not
available: overflow does nothing on an inline box, so the box has to become
inline-block, and that changes how every formula in the corpus takes part in
line layout.
Measured over the 991 inline formulas on the four most formula-heavy pages,
990 of them move. Their x and their width are untouched, but each box grows
from 22 px to 24 px tall, which lifts the line it sits on, and down a long page
the shift accumulates to 3645 px. Every variant gives the same 24 px, because
the growth is inline-block itself and not the overflow: vertical-align baseline,
a negative vertical-align, and dropping the overflow entirely all do. Only
overflow: clip keeps the document height, and it clips the formula instead of
scrolling it, which is worse than the defect.
Two formulas on one page do not justify taxing the typography of the 990 that
are already the right width, so the rule is gone and the comment says why, with
the numbers, so the next reader does not re-derive it. A formula wide enough to
need a scroll box gets fixed where it is written, as display math.
* Draw the verdict beside the word, wherever the report is read
The indicators the last commit generated were sitting in .github/badges
with nothing pointing at them. Now the three places that publish a
conformance verdict cite them, and each does it the way its own renderer
allows.
docs/CONFORMANCE.md gets a mark in front of every Status cell and in the
filter-class showcase, as reference-style images with one definition per
mark at the foot of the document. Measured on the real 566-row table, the
three ways of writing a per-row indicator cost +10.6 kB by reference,
+58.0 kB inline, and +162.8 kB for a per-theme <picture> on every row; the
mark is one file that clears 3:1 on every ground, so the last option buys
nothing. The head of the document gets the summary banner as the <picture>
pair the README already uses, and a legend that lists the marks the page
actually draws. Every mark keeps the word beside it: the word is the
verdict, the picture illustrates it, and the word is what survives a raw
view, a plain-text diff and an image that never arrives.
The pull-request comment gets the banner pinned to the head SHA, so it
reports the branch under review rather than main, and loses the last of the
emoji: the tick, the cross and the warning sign on its own test-status line
are now marks and words.
The site inlines the same four marks as an SVG sprite read off the
committed files at build time, which costs no request and lets the Spanish
page say "Por diseño" where the English one says "By design". It also fixes
a live defect there: the status cell asked whether the verdict was "pass"
and drew everything else as a red cross announced as "Fail", so a by-design
row was published as a failure, and the "failing rows only" filter listed it
as one. Both now know all four verdicts.
The README carries the banner under the paragraph that makes the
conformance claim, linked to the report.
Two measurements changed a decision. Starlight's own green is 1.53:1 on the
light card, so the site bar uses the marks' measured fills instead. And the
green and the red are within 1.02:1 of each other, so a failing bar reads as
a full one in greyscale; both bars now leave a sliver of track between the
segments.
* Lead every conformance row with its verdict, in words
On the documentation site the verdict was the eighth and last cell of a table
wider than the content well, drawn as a mark with the word hidden from sight,
and the box around that table never scrolled. A table whose columns cannot fit
is laid out at its min-content width anyway, but Chromium reports its border
box as the containing block's width, so the scroll container saw scrollWidth
equal to clientWidth, offered no scrollbar and clipped everything past its
right edge. Measured at 1280 px, the status cell ended 106 px outside the box
and scrollLeft stayed pinned at 0; at 360 px it ended 863 px outside. A pixel
census of untouched screenshots found exactly one verdict mark painted in the
viewport at twelve of the fourteen width and language combinations, and that
one was the mark in the section heading. Below 1440 px no data row carried a
verdict a sighted reader could reach, while the Markdown mirror printed the
word in every row.
min-width: min-content gives the container something to scroll, the verdict
moves to the first column, and its word is drawn rather than hidden. The mark
needed display: inline-block as well, because Starlight's content reset makes
every svg a block and had been placing the mark on a line above the word it
belongs to in every cell that draws one; and the column after the verdict
carries the gutter, because a nowrap verdict runs past its own cell where the
table is squeezed and padding on the overflowing cell cannot hold a gap open.
The mark stays aria-hidden, so a screen reader still hears the verdict exactly
once.
Measured afterwards at 360, 390, 768, 1024, 1280, 1366 and 1440 px in both
languages: the status cell is inside the container at all fourteen, the
container reports real overflow of 28 to 867 px and scrolls to the last
column, the page body never scrolls sideways, the verdict and its mark share a
line with at least 13 px to the citation beside them, and the marks painted in
the viewport go from one to one per visible row.
The palette's "clears 3:1 on every ground" was measured against five grounds,
four of which were not grounds. GitHub stripes even table rows, so half of the
report's rows sit on a stripe rather than a canvas, and this site overrides
Starlight's page colour, so the #17181c that three comments named is not a
ground anywhere here. Against the nine real grounds, eight clear 3:1 and
GitHub's dimmed table stripe does not: the four fills measure 2.75 to 2.82
there. They stay as they are, because no colour can do better. Clearing 3:1 on
#2d333b needs a relative luminance of at least 0.1972 while a white glyph
reading at 4.5:1 on the fill allows at most 0.1833, so the two windows do not
overlap; the glyph wins, because it is the part of the mark that is read and
every surface prints the verdict in words in the same cell. The test now
measures all nine grounds, pins the one exception, and proves the arithmetic
behind it.
What a picture pair follows was stated backwards in one direction and
correctly in the other, so both were measured across all four crossings of
desktop theme and GitHub theme. On github.com the pair follows the GitHub
theme: GitHub upgrades it into a themed-picture element that rewrites the
source's media to "not all" or to a pair of prefers-color-scheme queries.
Rendered without that JavaScript, as the /markdown API output under GitHub's
own stylesheet is, the media stays as authored and the desktop decides, so two
of the four crossings serve the wrong twin. The docstring now says both, and
says why the choice is safe either way: the banner paints its own card, whose
ink reads at 14.84:1 light and 15.91:1 dark, and which separates from a
mismatched page at 17.3:1 and 17.8:1.
The committed report drops its summary banner. That file is regenerated from
the tree it sits in and gated byte for byte, so it claims to be that tree's
report, while an image can only be cited at a fixed ref; on any branch that
adds or removes a check, a picture of main's count sat directly above a
sentence carrying the branch's. The sentence carries every number the picture
did. In its place the report gains the ten checks closest to their published
limit, which the pull-request comment and the site page have both ranked since
they were written and the published report never showed.
Two smaller corrections. The pull-request comment no longer reports a missing
test artefact as "0 tests, 0 failures (all green)". And the report's legend and
the renderer's docstring no longer say the mark sits beside its word in
Markdown: measured under GitHub's own table stylesheet, the Status column
settles at about 53.5 px of content box against the 54 px a mark plus its word
needs, so the word wraps under the mark in 563 of 566 cells at 1012 px and 493
at 1400, and a non-breaking space does not change it.
* Make the measurements in the comments the ones this tree produces
Five sentences quoted figures the tree does not give, and the branch
contradicted itself on two of them.
The scroll comment said the container reports 106 px of overflow at 1280 px.
It reports 116. The 106 is what it reported before the gutter rule sixty lines
below, which the same comment says grows the table by 10 px, so the file
disagreed with itself by exactly the amount it documented.
The byte figures were taken against a baseline that had neither the banner nor
the legend, and came out 0.6 kB high: +10.6, +58.0 and +162.8 where the
committed report gives +10.0, +57.3 and +162.2. Two other places in the same
branch already quoted the right ones.
The wrap counts came from one harness and do not survive another, which is
fair enough because the count depends on the width the harness gives the
container: 566 of 566 in one and 563 in the other. The wrapping is the
finding, so the sentence states that and stops pretending to a tally.
The line-box note said the cell is no wider. True in English and false in
Spanish, where the longer word costs 4.8 px. Both twins are peers, so the
sentence now says which is which.
And the report and the PR comment do not share two functions; they build their
markup separately and meet at asset_url, which is the one that matters,
because a path that is wrong there is wrong on both.