From 0d74732106dda4ad3d9201ef055492be4a695e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M=2E=20Requena=20Plens?= Date: Tue, 4 Aug 2026 13:11:26 +0200 Subject: [PATCH 1/2] Put every guide on the map, and run the checks that ran for nobody The page the Start topic offers as the map of the library linked 100 of the 104 guides while stating 104 in the prose and again in its own meta description, in both languages. Two substantial features, the detailed per-band prediction and the marine-mammal exposure chain, were reachable only from the sidebar, and the page contradicted itself in the text a search engine quotes. The four rows are in, each in the section and the order the sidebar gives it. A check follows them, because the page is written by hand and nothing looked at it. It asserts that every guide of every domain topic is linked, that every count stated in figures is the number of guides there are, and that the count in words is the same number, since that is the half nobody remembers to update. It fails on both defects it exists for, verified by removing a row and by changing a figure. Three audits that existed and ran for nobody now run in CI: the colour tokens of both themes against the contrast floor, the landing page at ten viewport and font-size combinations, and the language suggestion bar. They share one preview, because starting three is three times the wait. One of them used to fail outright about one run in five, on a style rule that hides the dev toolbar: the execution context can be replaced between the navigation and that call, and the rejection took the whole run with it. The toolbar exists only under the dev server, so against a preview of the build the rule has nothing to hide and the call is allowed to fail. Puppeteer is a declared dev dependency now. Five checks were resting on it arriving as a transitive dependency of pa11y-ci, which under pnpm's layout is not even linked into site/node_modules: a pa11y-ci upgrade that dropped it would have taken all five with it, and the message would have been about a missing module. Two smaller things found while reading. The sound power from surface vibration guide was listed under the vibration topic while its URL, its module and its mirror all sit under devices: a reader on it got the vibration tree and a Vibration breadcrumb, and the devices tree showed five of the six guides in that folder. And the mirror had one absolute site path left, which GitHub resolves against its own domain, so the errata link 404s for anyone reading the docs folder rather than the site; the same file already does it right elsewhere. --- .github/workflows/docs.yml | 14 ++ docs/materials/absorbers/porous-absorbers.md | 2 +- scripts/api_taxonomy.py | 2 +- site/package.json | 21 +-- site/pnpm-lock.yaml | 3 + site/scripts/check-guides-index.mjs | 139 +++++++++++++++++++ site/scripts/check-home-headings.mjs | 9 +- site/scripts/check-lang-suggest.mjs | 9 +- site/scripts/check-page-chips.mjs | 9 +- site/scripts/check-sidebar.mjs | 9 +- site/scripts/shared/audit.mjs | 11 +- site/src/content/docs/es/start/guides.md | 17 +++ site/src/content/docs/start/guides.md | 17 +++ site/src/data/topics.mjs | 4 +- src/phonometry/_compat.py | 2 +- 15 files changed, 245 insertions(+), 23 deletions(-) create mode 100644 site/scripts/check-guides-index.mjs diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 6dd75a16f..e1f81509a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -92,6 +92,13 @@ jobs: # step and is worthless once it is not. run: pnpm run check:icons working-directory: site + - name: Check the guides index lists every guide + # The page the Start topic offers as the map of the library is written + # by hand, and it had drifted to 100 of 104 links while stating 104 in + # the prose and in its own meta description, in both languages. Cheap, + # so it runs before the build rather than after it. + run: pnpm run check:guides + working-directory: site - name: Check EN/ES translation parity run: node scripts/check-i18n-parity.mjs working-directory: site @@ -124,6 +131,13 @@ jobs: # all green. run: pnpm run check:sidebar working-directory: site + - name: Check the landing, the contrast and the language bar + # Three audits that existed and ran for nobody: the colour tokens of + # both themes against the contrast floor, the landing page's headings + # and grid at ten viewport and font-size combinations, and the + # behaviour of the language suggestion bar. They share one preview. + run: pnpm run check:visual + working-directory: site # Only the branch that publishes needs the tarball. A pull request has # already got what it came for once the checks above have run. - uses: actions/upload-pages-artifact@v5 diff --git a/docs/materials/absorbers/porous-absorbers.md b/docs/materials/absorbers/porous-absorbers.md index e91310b3f..e0541377d 100644 --- a/docs/materials/absorbers/porous-absorbers.md +++ b/docs/materials/absorbers/porous-absorbers.md @@ -425,7 +425,7 @@ $|\mu_a| > 40$ above 50 Hz, and $\mu_b$ falls from 1.0 at 50 Hz to 0.82 at even though the printed sentence says "the ratio modulus": the model gives $\mu_b(1500) = 0.811 + 0.473j$, whose real part is 1.1 % from the printed value while its modulus, 0.939, is 14.5 % away. That is recorded in -[Errata](/phonometry/reference/errata/), along with the parameter sweep that +[Errata](../../ERRATA.md), along with the parameter sweep that fails to bring $|\mu_b|$ anywhere near 0.82. The impedance peak of the book's thinner sample is printed as 860 Hz and comes out at 863.5 Hz under the $\mathrm{Im}(Z_s)$ peak rule. Everything else, including the whole diff --git a/scripts/api_taxonomy.py b/scripts/api_taxonomy.py index ea1d73abf..4d96b6928 100644 --- a/scripts/api_taxonomy.py +++ b/scripts/api_taxonomy.py @@ -416,7 +416,7 @@ def module_section(module: str) -> Section: def _parent_subpackage(module: str) -> str: - """``phonometry.signals.levels`` -> ``signal``; top level -> ``""``.""" + """``phonometry.signals.levels`` -> ``signals``; top level -> ``""``.""" parts = module.split(".") return parts[1] if len(parts) > 2 else "" diff --git a/site/package.json b/site/package.json index 5105ce5cc..daca53d40 100644 --- a/site/package.json +++ b/site/package.json @@ -4,20 +4,22 @@ "version": "1.0.0", "private": true, "scripts": { - "prebuild": "node scripts/stage-media.mjs && node scripts/copy-llms.mjs", - "postbuild": "node scripts/add-sitemap-lastmod.mjs && node scripts/fix-redirect-doctype.mjs && node scripts/optimize-images.mjs && node scripts/emit-page-markdown.mjs", + "build": "astro build", + "check:guides": "node scripts/check-guides-index.mjs", "check:i18n": "node scripts/check-i18n-parity.mjs", + "check:icons": "node scripts/icon-sheet.mjs --check", "check:math": "node scripts/check-math-render.mjs", "check:sidebar": "node scripts/with-preview.mjs -- node scripts/check-sidebar.mjs", + "check:visual": "node scripts/with-preview.mjs -- sh -c 'node scripts/check-contrast.mjs && node scripts/check-home-headings.mjs && node scripts/check-lang-suggest.mjs'", "dev": "astro dev", - "build": "astro build", - "identity:sync": "node scripts/sync-identity.mjs", - "preview": "astro preview", "html-validate": "html-validate 'dist/**/*.html'", - "pa11y": "node scripts/with-preview.mjs -- ./node_modules/.bin/pa11y-ci", - "lighthouse": "node scripts/lighthouse-audit.mjs", "icons:sheet": "node scripts/icon-sheet.mjs", - "check:icons": "node scripts/icon-sheet.mjs --check" + "identity:sync": "node scripts/sync-identity.mjs", + "lighthouse": "node scripts/lighthouse-audit.mjs", + "pa11y": "node scripts/with-preview.mjs -- ./node_modules/.bin/pa11y-ci", + "postbuild": "node scripts/add-sitemap-lastmod.mjs && node scripts/fix-redirect-doctype.mjs && node scripts/optimize-images.mjs && node scripts/emit-page-markdown.mjs", + "prebuild": "node scripts/stage-media.mjs && node scripts/copy-llms.mjs", + "preview": "astro preview" }, "dependencies": { "@astrojs/starlight": "^0.41.5", @@ -34,6 +36,7 @@ "devDependencies": { "html-validate": "^11.5.6", "lighthouse": "^13.4.1", - "pa11y-ci": "^4.1.1" + "pa11y-ci": "^4.1.1", + "puppeteer": "24.43.1" } } diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 6a4618841..8d73bc46f 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -48,6 +48,9 @@ importers: pa11y-ci: specifier: ^4.1.1 version: 4.1.1(supports-color@10.2.2)(typescript@5.9.3) + puppeteer: + specifier: 24.43.1 + version: 24.43.1(supports-color@10.2.2)(typescript@5.9.3) packages: diff --git a/site/scripts/check-guides-index.mjs b/site/scripts/check-guides-index.mjs new file mode 100644 index 000000000..151b4889d --- /dev/null +++ b/site/scripts/check-guides-index.mjs @@ -0,0 +1,139 @@ +// The guides index lists every guide, and says how many there are. +// +// start/guides.md is what the Start topic offers as the map of the library, and +// it is written by hand. Nothing checked it, so it drifted: it linked 100 of the +// 104 guides while stating 104 in the prose and again in the frontmatter +// description, in both languages. Two substantial features were reachable only +// from the sidebar, and the page contradicted itself in the text a search engine +// quotes. +// +// Both halves are checked here. The links, because a guide that is not on the +// map is as good as unwritten for a reader who starts there; and the count, +// because a number in prose is a claim like any other. The spelled-out form is +// checked too, since it is the half nobody remembers to update. +// +// Usage: node scripts/check-guides-index.mjs +import { readFileSync, readdirSync, statSync } from 'node:fs'; +import { dirname, join, relative } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { topics } from '../src/data/topics.mjs'; + +const here = dirname(fileURLToPath(import.meta.url)); +const CONTENT = join(here, '..', 'src', 'content', 'docs'); + +/** + * The topics whose pages are guides: every one that teaches, which is every + * topic except the three carrying no teaching prose. Derived rather than + * listed, for the same reason the byline derives it (src/components/PageTitle). + */ +const NOT_A_DOMAIN = new Set(['start', 'reference', 'api']); +const DOMAINS = topics.map((topic) => topic.id).filter((id) => id && !NOT_A_DOMAIN.has(id)); + +/** Every markdown file under `dir`, at any depth. */ +function walk(dir, out = []) { + for (const name of readdirSync(dir)) { + const full = join(dir, name); + if (statSync(full).isDirectory()) walk(full, out); + else if (/\.mdx?$/.test(name)) out.push(full); + } + return out; +} + +/** The guide routes of one locale, in tree order, without the index pages. */ +function guideRoutes(root) { + const routes = []; + for (const domain of DOMAINS) { + for (const file of walk(join(root, domain)).sort()) { + const route = relative(root, file).replace(/\.mdx?$/, ''); + if (!/(^|\/)index$/.test(route)) routes.push(route); + } + } + return routes; +} + +/** A count in words, in the two languages the site publishes. */ +function spell(n, lang) { + const table = { + en: { + units: ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', + 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', + 'eighteen', 'nineteen'], + tens: ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'], + }, + es: { + units: ['', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve', + 'diez', 'once', 'doce', 'trece', 'catorce', 'quince', 'dieciséis', 'diecisiete', + 'dieciocho', 'diecinueve'], + tens: ['', '', 'veinte', 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', + 'ochenta', 'noventa'], + }, + }[lang]; + const below100 = (v) => + v < 20 + ? table.units[v] + : table.tens[Math.floor(v / 10)] + + (v % 10 ? (lang === 'es' ? ' y ' : '-') + table.units[v % 10] : ''); + if (n < 100) return below100(n); + const rest = n % 100; + const hundreds = Math.floor(n / 100); + if (lang === 'es') { + const head = hundreds === 1 ? (rest ? 'ciento' : 'cien') : `${table.units[hundreds]}cientos`; + return rest ? `${head} ${below100(rest)}` : head; + } + // "a hundred and four" and "one hundred and four" are both the number; the + // page uses the first, and a rewrite to the second should not fail a check + // about arithmetic. + const head = hundreds === 1 ? '(?:a|one) hundred' : `${table.units[hundreds]} hundred`; + return rest ? `${head} and ${below100(rest)}` : head; +} + +const LOCALES = [ + { lang: 'en', root: CONTENT, word: 'guides' }, + { lang: 'es', root: join(CONTENT, 'es'), word: 'guías' }, +]; + +let failures = 0; +const fail = (message) => { + console.error(`FAIL ${message}`); + failures += 1; +}; +const ok = (message) => console.log(`ok ${message}`); + +for (const { lang, root, word } of LOCALES) { + const routes = guideRoutes(root); + const index = readFileSync(join(root, 'start', 'guides.md'), 'utf8'); + + const missing = routes.filter((route) => !index.includes(`${route}/`)); + if (missing.length > 0) { + fail(`${lang}: ${missing.length} guide(s) the index does not link: ${missing.join(', ')}`); + } else { + ok(`${lang}: the index links all ${routes.length} guides`); + } + + // Every number that introduces the word, in the prose and in the frontmatter + // description, has to be the number of guides there are. + const digits = [...index.matchAll(new RegExp(String.raw`(\d{2,4})\s+${word}`, 'g'))].map((m) => + Number(m[1]), + ); + const wrong = digits.filter((n) => n !== routes.length); + if (digits.length === 0) fail(`${lang}: the index states no count`); + else if (wrong.length > 0) { + fail(`${lang}: the index says ${[...new Set(wrong)].join(', ')} where there are ${routes.length}`); + } else { + ok(`${lang}: every count it states in figures says ${routes.length}`); + } + + // And the same number in words, which is the half nobody updates. + // `spell` returns a pattern rather than one spelling, since a number has more + // than one correct wording and the check is about the arithmetic. + const written = spell(routes.length, lang); + if (new RegExp(String.raw`\b${written}\s+${word}`, 'i').test(index)) { + ok(`${lang}: the count in words agrees with ${routes.length}`); + } else { + fail(`${lang}: the index does not say "${written} ${word}" anywhere, so its count in words has drifted from ${routes.length}`); + } +} + +console.log(`\n${failures} failing check(s).`); +process.exit(failures ? 1 : 0); diff --git a/site/scripts/check-home-headings.mjs b/site/scripts/check-home-headings.mjs index 8d5fb9cf3..5b4d9c4dc 100644 --- a/site/scripts/check-home-headings.mjs +++ b/site/scripts/check-home-headings.mjs @@ -176,7 +176,14 @@ for (const c of CASES) { } await page.setViewport({ width: c.width, height: c.height, deviceScaleFactor: SHOTS ? 2 : 1 }); await page.goto(`${BASE}${BASE_PATH}${c.path}`, { waitUntil: 'domcontentloaded', timeout: 90000 }); - await page.addStyleTag({ content: 'astro-dev-toolbar{display:none !important}' }); + // Only the dev server renders this toolbar, so against a preview of `dist` + // the rule matches nothing. The call itself is not free of consequence: the + // execution context can be replaced between the navigation and it, and the + // rejection failed a whole run once for a style rule that had nothing to + // hide. Losing it costs nothing, so it is allowed to fail. + await page + .addStyleTag({ content: 'astro-dev-toolbar{display:none !important}' }) + .catch(() => {}); await new Promise((r) => setTimeout(r, 500)); const { problems, root, wrapped } = await page.evaluate(audit); diff --git a/site/scripts/check-lang-suggest.mjs b/site/scripts/check-lang-suggest.mjs index 93b692723..54db035de 100644 --- a/site/scripts/check-lang-suggest.mjs +++ b/site/scripts/check-lang-suggest.mjs @@ -139,7 +139,14 @@ expect( await new Promise((r) => setTimeout(r, 400)); // The dev server parks its toolbar in the middle of the bottom edge, and // it hit-tests. It is not part of the site. - await page.addStyleTag({ content: 'astro-dev-toolbar{display:none !important}' }); + // Only the dev server renders this toolbar, so against a preview of `dist` + // the rule matches nothing. The call itself is not free of consequence: the + // execution context can be replaced between the navigation and it, and the + // rejection failed a whole run once for a style rule that had nothing to + // hide. Losing it costs nothing, so it is allowed to fail. + await page + .addStyleTag({ content: 'astro-dev-toolbar{display:none !important}' }) + .catch(() => {}); const hits = await page.evaluate(() => { // A regression that keeps the bar off screen is exactly what this check // is for, so report it as a failed hit test rather than throwing a raw diff --git a/site/scripts/check-page-chips.mjs b/site/scripts/check-page-chips.mjs index d691d072b..4a184a90f 100644 --- a/site/scripts/check-page-chips.mjs +++ b/site/scripts/check-page-chips.mjs @@ -113,7 +113,14 @@ async function landing({ path, viewport, theme, banner }) { banner ? (path.startsWith('/es/') ? ['en-US', 'en'] : ['es-ES', 'es']) : null, ); await page.goto(`${BASE}${BASE_PATH}${path}`, { waitUntil: 'networkidle0', timeout: 90000 }); - await page.addStyleTag({ content: 'astro-dev-toolbar{display:none !important}' }); + // Only the dev server renders this toolbar, so against a preview of `dist` + // the rule matches nothing. The call itself is not free of consequence: the + // execution context can be replaced between the navigation and it, and the + // rejection failed a whole run once for a style rule that had nothing to + // hide. Losing it costs nothing, so it is allowed to fail. + await page + .addStyleTag({ content: 'astro-dev-toolbar{display:none !important}' }) + .catch(() => {}); const out = await page.evaluate(async (wantBanner) => { const wait = (ms) => new Promise((r) => setTimeout(r, ms)); const bar = document.getElementById('lang-suggest'); diff --git a/site/scripts/check-sidebar.mjs b/site/scripts/check-sidebar.mjs index aec99c372..decf67deb 100644 --- a/site/scripts/check-sidebar.mjs +++ b/site/scripts/check-sidebar.mjs @@ -262,7 +262,14 @@ async function open(path, { phone = false } = {}) { const page = await context.newPage(); await page.setViewport(phone ? { width: 390, height: 844 } : { width: 1440, height: 900 }); await page.goto(`${BASE}${BASE_PATH}${path}`, { waitUntil: 'networkidle0', timeout: 90000 }); - await page.addStyleTag({ content: 'astro-dev-toolbar{display:none !important}' }); + // Only the dev server renders this toolbar, so against a preview of `dist` + // the rule matches nothing. The call itself is not free of consequence: the + // execution context can be replaced between the navigation and it, and the + // rejection failed a whole run once for a style rule that had nothing to + // hide. Losing it costs nothing, so it is allowed to fail. + await page + .addStyleTag({ content: 'astro-dev-toolbar{display:none !important}' }) + .catch(() => {}); if (phone) { await page.evaluate(() => document.querySelector('starlight-menu-button button')?.click()); await new Promise((r) => setTimeout(r, 300)); diff --git a/site/scripts/shared/audit.mjs b/site/scripts/shared/audit.mjs index 0fb77c5d6..015f50776 100644 --- a/site/scripts/shared/audit.mjs +++ b/site/scripts/shared/audit.mjs @@ -24,11 +24,12 @@ export const BASE_PATH = basePath; /** * The puppeteer package. * - * It is in the tree because pa11y-ci depends on it, not because we declare it, - * so under pnpm's default layout it is not linked into site/node_modules and a - * plain import does not find it. Normal resolution first (which is what works - * under `node-linker=hoisted`, npm and yarn), then the virtual store, then one - * actionable error. + * It is a declared dev dependency now. It used to be in the tree only because + * pa11y-ci depends on it, which under pnpm's default layout does not link it + * into site/node_modules at all: a plain import did not find it, and five + * checks rested on a package that a pa11y-ci upgrade could have taken away + * without a word. The store lookup below is kept because it costs nothing and + * still answers the case where the declared copy is not linked. */ export function loadPuppeteer() { const require = createRequire(import.meta.url); diff --git a/site/src/content/docs/es/start/guides.md b/site/src/content/docs/es/start/guides.md index f7f15f368..0f09478ee 100644 --- a/site/src/content/docs/es/start/guides.md +++ b/site/src/content/docs/es/start/guides.md @@ -318,6 +318,10 @@ ANSI/ASA S12.2 y ASTM E413/E1414. - [Transmisión por flancos en laboratorio (ISO 10848)](/phonometry/es/buildings/insulation/flanking-lab/): el índice de reducción de vibraciones de unión y las diferencias de niveles de flanco medidas en una instalación de ensayo. +- [Fuentes de impacto pesadas y blandas (ISO 16283-2)](/phonometry/es/buildings/insulation/heavy-impact-sources/): + la pelota de caucho y la máquina de neumático: el nivel de exposición a la + fuerza de impacto que las especifica, la comprobación de laboratorio que tienen + que pasar y el número único del anexo D de ISO 717-2. - [Índices globales de aislamiento (ISO 717)](/phonometry/es/buildings/insulation/insulation-ratings/): los motores de curva de referencia aéreo y de impactos con $C$, $C_{tr}$ y $C_I$, los términos de rango ampliado y la ficha de ISO 717. @@ -334,6 +338,10 @@ ANSI/ASA S12.2 y ASTM E413/E1414. - [Predicción del aislamiento acústico (EN 12354)](/phonometry/es/buildings/design/insulation-prediction/): el aislamiento in situ a ruido aéreo y de impactos entre recintos a partir de datos de elementos, con sus caminos de flancos. +- [Predicción detallada por bandas (ISO 12354)](/phonometry/es/buildings/design/detailed-prediction/): + la misma predicción banda a banda en vez de como un solo número: datos in situ + de elemento y de unión, el índice por flancos y el nivel de impactos, y la + contribución de cada camino a R'w y L'n,w. - [Predicción del aislamiento de paneles](/phonometry/es/buildings/design/panel-sound-insulation/): la ley de masa y la caída de coincidencia, las paredes dobles, rendijas y aberturas, la eficiencia de radiación de placas y las movilidades puntuales. @@ -467,6 +475,10 @@ IEC 61400-11. - [Ruido de rotorcraft: el método del hemisferio](/phonometry/es/aircraft/rotorcraft-noise/): el modelo de fuente en hemisferio con sus ajustes de propagación, la interpolación de condiciones de vuelo y los contornos de suceso único. +- [Emisión de la fuente ferroviaria CNOSSOS-EU](/phonometry/es/environment/sources/cnossos-rail-emission/): + la fuente ferroviaria del anexo II 2.3: rugosidad y filtro de contacto, ruido + de impacto, chirrido en curva, tracción y ruido aerodinámico, y las dos líneas + de fuente equivalentes. - [Ruido de aerogeneradores: potencia y audibilidad tonal](/phonometry/es/environment/sources/wind-turbine-noise/): el nivel de potencia acústica aparente referido al centro del rotor y la cadena de audibilidad tonal que decide si un tono se oye. @@ -491,6 +503,11 @@ ISO 17208-1/-2, ISO 18406 y JOMOPANS-ECHO. los solvers de modos normales, trazado de rayos y ecuación parabólica de la guía de ondas estratificada, y cómo elegir modelo de propagación. +- [Exposición a ruido de mamíferos marinos](/phonometry/es/underwater/marine-mammal-exposure/): + la cara auditiva de ese ruido: los audiogramas de grupo, las funciones de + ponderación reglamentarias con su versión de guía, y la exposición de una + campaña de hincado frente a los criterios de lesión. + ## [Fuentes y dispositivos](/phonometry/es/devices/) Potencia acústica, intensidad, declaraciones de emisión, electroacústica y diff --git a/site/src/content/docs/start/guides.md b/site/src/content/docs/start/guides.md index cfbd0838f..bcc9b2045 100644 --- a/site/src/content/docs/start/guides.md +++ b/site/src/content/docs/start/guides.md @@ -304,6 +304,10 @@ ISO 12999-1, ISO 10052, ANSI/ASA S12.2 and ASTM E413/E1414. - [Laboratory Flanking Transmission (ISO 10848)](/phonometry/buildings/insulation/flanking-lab/): the junction vibration reduction index and the flanking level differences measured on a test facility. +- [Heavy and Soft Impact Sources (ISO 16283-2)](/phonometry/buildings/insulation/heavy-impact-sources/): + the rubber ball and the bang machine: the impact force exposure level that + specifies them, the laboratory check they have to pass, and the single number + of ISO 717-2 Annex D. - [Insulation Ratings (ISO 717)](/phonometry/buildings/insulation/insulation-ratings/): the airborne and impact reference-curve engines with $C$, $C_{tr}$ and $C_I$, the enlarged-range terms and the ISO 717 fiche. @@ -319,6 +323,10 @@ ISO 12999-1, ISO 10052, ANSI/ASA S12.2 and ASTM E413/E1414. - [Predicting Sound Insulation (EN 12354)](/phonometry/buildings/design/insulation-prediction/): in-situ airborne and impact insulation between rooms from element data, with their flanking paths. +- [Detailed Per-Band Prediction (ISO 12354)](/phonometry/buildings/design/detailed-prediction/): + the same prediction band by band rather than as one number: in-situ element and + junction data, the flanking index and impact level, and the per-path + contributions behind R'w and L'n,w. - [Predicting Panel Sound Insulation](/phonometry/buildings/design/panel-sound-insulation/): the mass law and coincidence dip, double walls, slits and apertures, plate radiation efficiency and point mobilities. @@ -444,6 +452,10 @@ SAE ARP 5534, ECAC Doc 29/32 and IEC 61400-11. - [Rotorcraft noise: the hemisphere method](/phonometry/aircraft/rotorcraft-noise/): the hemisphere source model with its propagation adjustments, flight-condition interpolation, and the single-event contours. +- [CNOSSOS-EU railway source emission](/phonometry/environment/sources/cnossos-rail-emission/): + the rail source of Annex II 2.3: roughness and the contact filter, impact + noise, curve squeal, traction and aerodynamic noise, and the two equivalent + source lines. - [Wind-turbine noise: sound power and tonal audibility](/phonometry/environment/sources/wind-turbine-noise/): the apparent sound power level referred to the rotor centre, and the tonal audibility chain that decides whether a tone is audible. @@ -467,6 +479,11 @@ JOMOPANS-ECHO. the normal-mode, ray-tracing and parabolic-equation solvers of the stratified waveguide, and how to choose a propagation model. +- [Marine-mammal noise exposure](/phonometry/underwater/marine-mammal-exposure/): + the hearing side of that noise: the group audiograms, the regulatory weighting + functions with their guidance version, and the exposure of a pile-driving + campaign against the injury criteria. + ## [Sources and devices](/phonometry/devices/) Sound power, intensity, emission declarations, electroacoustics and programme diff --git a/site/src/data/topics.mjs b/site/src/data/topics.mjs index 863e790d5..52fba75af 100644 --- a/site/src/data/topics.mjs +++ b/site/src/data/topics.mjs @@ -275,8 +275,7 @@ export const topics = [ 'vibration/structural/mechanical-mobility', 'vibration/structural/junction-transmission', 'vibration/structural/transfer-stiffness', - 'devices/emission/vibration-sound-power', - ], + ], }, { label: 'Human vibration', @@ -383,6 +382,7 @@ export const topics = [ 'devices/emission/sound-power', 'devices/emission/sound-power-pressure', 'devices/emission/sound-power-reverberation', + 'devices/emission/vibration-sound-power', 'devices/emission/sound-power-intensity', ], }, diff --git a/src/phonometry/_compat.py b/src/phonometry/_compat.py index 10bc9beef..fd1dea253 100644 --- a/src/phonometry/_compat.py +++ b/src/phonometry/_compat.py @@ -46,7 +46,7 @@ #: Old module path -> relocated module path for the 4.0 taxonomy. The #: oversized ``metrology`` catch-all became three packages: the normalized #: frequency selectivity in ``filters``, the general signal analysis in -#: ``signal``, and the transverse metrology that gives the package its name. +#: ``signals``, and the transverse metrology that gives the package its name. _MOVED_4X: dict[str, str] = { "phonometry.metrology.core": "phonometry.filters.core", "phonometry.metrology.filter_design": "phonometry.filters.design", From 40ff23eebc0d4874330685f994e79b5b17d8ffbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M=2E=20Requena=20Plens?= Date: Tue, 4 Aug 2026 14:42:29 +0200 Subject: [PATCH 2/2] Answer the review of the guides map Both findings were real. The railway row had landed in the aircraft and wind block, because the insertion anchored on the wind-turbine row without checking which block that row sits in; it sits beside the road source now, in both languages. And the coverage check accepted a route appearing as plain text, which a code block or a comment would satisfy while giving the reader nothing to select; it requires the locale's link destination, and still fails when a row's link is emptied while its text remains. --- site/scripts/check-guides-index.mjs | 6 +++++- site/src/content/docs/es/start/guides.md | 8 ++++---- site/src/content/docs/start/guides.md | 8 ++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/site/scripts/check-guides-index.mjs b/site/scripts/check-guides-index.mjs index 151b4889d..805d299ba 100644 --- a/site/scripts/check-guides-index.mjs +++ b/site/scripts/check-guides-index.mjs @@ -104,7 +104,11 @@ for (const { lang, root, word } of LOCALES) { const routes = guideRoutes(root); const index = readFileSync(join(root, 'start', 'guides.md'), 'utf8'); - const missing = routes.filter((route) => !index.includes(`${route}/`)); + // The route has to appear as a link destination, not merely as text: a slug + // in a code block or a comment would satisfy a plain substring while giving + // the reader nothing to select. + const prefix = lang === 'es' ? '/phonometry/es/' : '/phonometry/'; + const missing = routes.filter((route) => !index.includes(`](${prefix}${route}/)`)); if (missing.length > 0) { fail(`${lang}: ${missing.length} guide(s) the index does not link: ${missing.join(', ')}`); } else { diff --git a/site/src/content/docs/es/start/guides.md b/site/src/content/docs/es/start/guides.md index 0f09478ee..5fcd2fff3 100644 --- a/site/src/content/docs/es/start/guides.md +++ b/site/src/content/docs/es/start/guides.md @@ -454,6 +454,10 @@ IEC 61400-11. la fuente viaria común europea del Anexo II 2.2: potencia acústica de rodadura y propulsión por categoría de vehículo y potencia direccional por metro de línea fuente. +- [Emisión de la fuente ferroviaria CNOSSOS-EU](/phonometry/es/environment/sources/cnossos-rail-emission/): + la fuente ferroviaria del anexo II 2.3: rugosidad y filtro de contacto, ruido + de impacto, chirrido en curva, tracción y ruido aerodinámico, y las dos líneas + de fuente equivalentes. - [Efecto suelo esférico y barreras avanzadas](/phonometry/es/environment/propagation/ground-barriers/): el coeficiente de reflexión de Weyl-Van der Pol sobre suelo de impedancia finita y la difracción en barreras desde la teoría ondulatoria. @@ -475,10 +479,6 @@ IEC 61400-11. - [Ruido de rotorcraft: el método del hemisferio](/phonometry/es/aircraft/rotorcraft-noise/): el modelo de fuente en hemisferio con sus ajustes de propagación, la interpolación de condiciones de vuelo y los contornos de suceso único. -- [Emisión de la fuente ferroviaria CNOSSOS-EU](/phonometry/es/environment/sources/cnossos-rail-emission/): - la fuente ferroviaria del anexo II 2.3: rugosidad y filtro de contacto, ruido - de impacto, chirrido en curva, tracción y ruido aerodinámico, y las dos líneas - de fuente equivalentes. - [Ruido de aerogeneradores: potencia y audibilidad tonal](/phonometry/es/environment/sources/wind-turbine-noise/): el nivel de potencia acústica aparente referido al centro del rotor y la cadena de audibilidad tonal que decide si un tono se oye. diff --git a/site/src/content/docs/start/guides.md b/site/src/content/docs/start/guides.md index bcc9b2045..a009e75e6 100644 --- a/site/src/content/docs/start/guides.md +++ b/site/src/content/docs/start/guides.md @@ -431,6 +431,10 @@ SAE ARP 5534, ECAC Doc 29/32 and IEC 61400-11. - [CNOSSOS-EU road traffic source emission](/phonometry/environment/sources/cnossos-road-emission/): the common EU road source of Annex II 2.2: rolling and propulsion sound power per vehicle category and the directional power per metre of source line. +- [CNOSSOS-EU railway source emission](/phonometry/environment/sources/cnossos-rail-emission/): + the rail source of Annex II 2.3: roughness and the contact filter, impact + noise, curve squeal, traction and aerodynamic noise, and the two equivalent + source lines. - [Spherical ground effect and advanced barriers](/phonometry/environment/propagation/ground-barriers/): the Weyl-Van der Pol reflection coefficient over finite-impedance ground, and wave-theoretic barrier diffraction. @@ -452,10 +456,6 @@ SAE ARP 5534, ECAC Doc 29/32 and IEC 61400-11. - [Rotorcraft noise: the hemisphere method](/phonometry/aircraft/rotorcraft-noise/): the hemisphere source model with its propagation adjustments, flight-condition interpolation, and the single-event contours. -- [CNOSSOS-EU railway source emission](/phonometry/environment/sources/cnossos-rail-emission/): - the rail source of Annex II 2.3: roughness and the contact filter, impact - noise, curve squeal, traction and aerodynamic noise, and the two equivalent - source lines. - [Wind-turbine noise: sound power and tonal audibility](/phonometry/environment/sources/wind-turbine-noise/): the apparent sound power level referred to the rotor centre, and the tonal audibility chain that decides whether a tone is audible.