Actualizar de la 3.3 a la 4.0
La 4.0 rompe duro y no lleva capa de compatibilidad. Casi nada se ha borrado: de los 885 nombres que la raíz exportaba en la 3.3.0, diez no existen ya en ningún sitio y el resto simplemente vive en una dirección con nombre en vez de en una plana. Así que el trabajo es sobre todo un barrido de imports, y esta página es sobre todo un mapa.
Con las dos primeras secciones arreglas los imports. La tercera léela aunque tengas prisa, porque en tres sitios el arreglo evidente es el equivocado, y uno de esos tres cambia un número sin levantar nada.
La regla única: los imports bajan un nivel
Sección titulada «La regla única: los imports bajan un nivel»En la 3.3.0 la raíz reexportaba la biblioteca entera, así que from phonometry import laeq funcionaba para casi cualquier nombre. Hoy la raíz exporta
veinticinco cosas: los veintiún subpaquetes, las tres clases que no pertenecen a
ningún dominio concreto (Signal, ReportMetadata, PhonometryWarning) y
__version__.
import phonometry
print(len(phonometry.__all__)) # 25print("laeq" in phonometry.__all__) # False
from phonometry.signals import laeq # esta es la grafía nuevaMedido sobre la superficie entera, 872 de aquellos 885 nombres de la raíz los
publica hoy exactamente un subpaquete, ninguno dos, y ninguno necesita una ruta
más profunda que phonometry.<subpaquete>. Otros tres siguen en la raíz, donde
siempre estuvieron. Así que el cambio no tiene ambigüedad: busca el subpaquete e
importa de él.
La forma de atributo no se retiró nunca, que es el cambio más pequeño posible para el código que tiraba de la raíz:
from phonometry import filters
_, _, _, labels = filters.nominal_frequencies(1)print(labels)Encontrar la casa nueva de un nombre
Sección titulada «Encontrar la casa nueva de un nombre»En vez de buscar cada uno en la referencia, pregúntaselo al paquete instalado:
import importlibimport pkgutil
import phonometry
def whereis(name): """Cada módulo público que publica `name`, del menos profundo al que más.""" found = ["phonometry"] if name in phonometry.__all__ else [] for attribute in phonometry.__all__: package = getattr(phonometry, attribute) if not hasattr(package, "__path__"): continue modules = [package.__name__] modules += [m.name for m in pkgutil.walk_packages(package.__path__, package.__name__ + ".")] for module_name in modules: if any(part.startswith("_") for part in module_name.split(".")): continue try: module = importlib.import_module(module_name) except ImportError: continue if name in getattr(module, "__all__", ()): found.append(module_name) return sorted(found, key=lambda path: path.count("."))
print(whereis("laeq")[0]) # phonometry.signalsprint(whereis("reverberation_time")[0]) # phonometry.roomprint(whereis("sensitivity")[0]) # phonometry.metrologyprint(whereis("Signal")[0]) # phonometryRecorre el árbol en unos dos segundos. Cuando devuelve varias rutas, la menos
profunda es el import canónico: un subpaquete reexporta lo que publican sus
propios módulos, y esa ruta corta es la que usan las guías y los tests. Los
cuatro nombres que la raíz sigue publicando por su cuenta, Signal,
PhonometryWarning, ReportMetadata y __version__, vuelven como
phonometry a secas. Una lista vacía significa que el nombre está en la
última sección de esta página.
Módulos que se partieron en vez de mudarse
Sección titulada «Módulos que se partieron en vez de mudarse»Cinco de los treinta módulos que cambiaron de ruta no se mudaron a una
dirección nueva. Se dividieron, y quien siga sólo la primera mitad de la
partición se lleva un ImportError por el resto. Merece la pena revisarlos uno
a uno, y el paquete privado de renderizado va con ellos porque le pasó lo
mismo:
| Módulo en 3.3.0 | Dónde está hoy su contenido |
|---|---|
phonometry.impedance_tube | materials.absorbers.impedance_tube (función de transferencia de dos micrófonos), materials.absorbers.four_microphone (TransferMatrix, transfer_matrix_one_load, wave_decomposition, face_quantities, air_layer_transfer_matrix), materials.absorbers.standing_wave y fluids |
phonometry.scattering_diffusion | materials.diffusers.reverberation_room_scattering para la parte de cámara reverberante (ScatteringResult, ScatteringUncertainty, BASE_PLATE_BANDS y el resto), materials.diffusers.scattering_diffusion para los nueve nombres de campo libre |
phonometry.materials.porous_absorber | materials.absorbers.porous para los modelos de material en masa, materials.absorbers.layered para toda la API multicapa (layered_absorber, AirLayer, MembraneLayer, PerforatedPlateLayer, MicroperforatedPlateLayer, LayeredAbsorberResult) |
phonometry.metrology.spectra | signals.spectra, más signals.multitaper (multitaper_psd) y signals.windows (window_metrics) |
phonometry.compliance | filters.compliance (FilterComplianceResult, class_limits, verify_filter_class), filters.weighting_compliance (verify_weighting_class, weighting_class_limits), aircraft.measurement_system (verify_aircraft_noise_system) |
phonometry._plotting | phonometry._plot.<dominio>: los 82 renderizadores se repartieron por dominio, así que plot_airborne_insulation está en _plot.building y plot_age_threshold en _plot.hearing. Privado en ambos casos, y la vía soportada es el .plot() del resultado |
Los otros veinticinco se mudaron enteros, y el resolutor de arriba los encuentra sin leer esta tabla. Cada uno de los tres primeros era alcanzable por dos rutas en la 3.3.0, la plana y la de dentro de su paquete, y las dos han desaparecido.
Tres sitios donde el arreglo evidente es el equivocado
Sección titulada «Tres sitios donde el arreglo evidente es el equivocado»Todo lo demás de esta página se anuncia en cuanto lo ejecutas, y el arreglo es el que parece. Estos tres no son así.
Los auxiliares del tubo de impedancia tomaban kelvin
Sección titulada «Los auxiliares del tubo de impedancia tomaban kelvin»air_density_iso y speed_of_sound_iso documentaban y consumían su
temperatura en kelvin. Sus sucesores toman grados Celsius, como el resto de
la biblioteca. El nombre cambió, así que el import falla y te enteras, pero el
problema es el argumento que arrastras: 293,15 es un número de aspecto
legítimo en cualquiera de las dos unidades.
from phonometry.materials import speed_of_sound_iso10534
print(round(float(speed_of_sound_iso10534(temperature_c=20.0)), 5)) # 343.28784print(round(float(speed_of_sound_iso10534(temperature_c=293.15)), 5)) # 477.13003343,29 m/s frente a 477,13 m/s: un 39 % de error, sin excepción ninguna, y cada coeficiente de absorción calculado a partir de ahí sale mal en una cantidad de aspecto plausible. Convierte el valor, no te limites a renombrar la palabra clave.
| 3.3.0 | 4.0 |
|---|---|
impedance_tube.air_density_iso(T_kelvin, p_kPa) | materials.air_density_iso10534(temperature_c=T_kelvin - 273.15, atmospheric_pressure_kpa=p_kPa) |
impedance_tube.speed_of_sound_iso(T_kelvin) | materials.speed_of_sound_iso10534(temperature_c=T_kelvin - 273.15) |
Con la conversión aplicada, los dos devuelven exactamente lo que devolvían en la 3.3.0, bit a bit.
scattering_diffusion.speed_of_sound tomaba Celsius, y su sustituto responde distinto
Sección titulada «scattering_diffusion.speed_of_sound tomaba Celsius, y su sustituto responde distinto»El auxiliar de ISO 17497-1 que se llamaba igual tomaba grados Celsius, así que
ahí no hay conversión que hacer. La trampa es el sustituto. fluids.air() usa
un modelo del medio más completo que aquella fórmula de una línea de la
ISO 17497-1, y a 20 °C responde 343,99 m/s donde el auxiliar viejo respondía
343,20 m/s. Es poco, y no es nada despreciable si estás reproduciendo una
medición hecha con el número antiguo. El apartado 8 de la ISO 17497-1 imprime
la fórmula si necesitas ese apartado exacto.
El campo tl de un resultado submarino es pl
Sección titulada «El campo tl de un resultado submarino es pl»transmission_loss pasó a propagation_loss, cosa que el import detecta, y
con él se renombró el campo de dentro del resultado. Ese no levanta nada: un
PropagationLossResult no tiene .tl, así que el AttributeError aparece
allí donde lo leas, que puede quedar lejos de la llamada.
from phonometry.underwater.propagation.closed_form import propagation_loss
result = propagation_loss([1000.0], 1000.0)print(round(float(result.pl[0]), 5)) # 60.06013, el número que guardaba .tlEl mismo renombrado llega a underwater.sonar_equation, cuyo parámetro
transmission_loss es ahora propagation_loss.
El entorno se metió en una dataclase
Sección titulada «El entorno se metió en una dataclase»Cinco puntos de entrada ya no toman las condiciones ambientales como números sueltos. Ahí no basta con renombrar la palabra clave, porque el parámetro ha desaparecido del todo:
| 3.3.0 | 4.0 |
|---|---|
airport_noise.event_level(..., temperature=15.0, pressure=101.325) | event_level(..., atmosphere=AerodromeAtmosphere(temperature_c=15.0, atmospheric_pressure_kpa=101.325)) |
airport_noise.noise_contour(...) | lo mismo, con AerodromeAtmosphere |
rotorcraft_noise.rotorcraft_event_level(..., temperature=15.0, relative_humidity=70.0) | rotorcraft_event_level(..., atmosphere=RotorcraftAtmosphere(temperature_c=15.0, relative_humidity_percent=70.0)) |
rotorcraft_noise.rotorcraft_noise_contour(...) | lo mismo, con RotorcraftAtmosphere |
outdoor_propagation.predicted_receiver_level(..., humidity=70.0) | predicted_receiver_level(..., atmosphere=AtmosphericConditions(relative_humidity_percent=70.0)) |
Las sustituciones punto a punto del rotorcraft fueron por el mismo camino:
bank_angle y path_angle son ahora
RotorcraftTrackState(bank_angle_deg=..., path_angle_deg=...).
from phonometry.aircraft.airport_noise import AerodromeAtmosphere
atmosphere = AerodromeAtmosphere(temperature_c=15.0, atmospheric_pressure_kpa=101.325)print(atmosphere.temperature_c, atmosphere.atmospheric_pressure_kpa)Un parámetro que nombra una magnitud lleva su unidad
Sección titulada «Un parámetro que nombra una magnitud lleva su unidad»En todo lo demás, las condiciones ambientales conservaron su sitio y ganaron un sufijo. La regla es que un parámetro público que nombra una magnitud dimensional termina en la unidad que teclea quien llama, porque el número es donde se pierde la unidad y ninguna guardia puede cazarlo por magnitud: 101,325 y 101325 son las dos presiones legítimas en esta biblioteca.
| 3.3.0 | 4.0 |
|---|---|
temperature= | temperature_c= |
pressure=, static_pressure=, barometric_pressure=, ambient_pressure=, atmospheric_pressure= | atmospheric_pressure_kpa=, o _pa= donde el apartado esté escrito en pascales |
humidity=, relative_humidity= | relative_humidity_percent= |
diameter= y sus trece compuestos | diameter_m= |
angle=, angles=, bank_angle=, path_angle=, grazing_angle=, launch_angles= | _deg= o _rad=, según lo que lea la función |
runway_gradient= | runway_gradient_ratio= |
sound_speed_gradient= | sound_speed_gradient_per_s= |
Un caso merece mención propia porque era una trampa y no una incomodidad.
critical_angle era un campo en radianes en un resultado y un parámetro en
grados en las dos funciones que estaban una pantalla más abajo, o sea la misma
palabra para dos cosas separadas por un factor 57. Ahora es
critical_angle_rad en el resultado y critical_angle_deg en los parámetros.
transmission_loss= pasó a propagation_loss= sólo en los resultados de sónar
y de propagación numérica submarina. ReactiveSilencerResult y
SoundReductionResult siguen llamando al suyo transmission_loss=, y con
razón: es como lo llaman las normas que hay detrás.
Las funciones de la EN 29052-1 de materials siguen la misma regla, con la
unidad de cada magnitud al final de su nombre:
| 3.3.0 | 4.0 |
|---|---|
apparent_dynamic_stiffness(resonant_frequency=, total_mass_per_area=) | resonant_frequency_hz=, total_mass_per_area_kg_m2= |
enclosed_gas_stiffness(thickness=) | thickness_m= |
installed_dynamic_stiffness(apparent_stiffness, airflow_resistivity, gas_stiffness=) | apparent_stiffness_n_m3, airflow_resistivity_kpa_s_m2= sólo por nombre, gas_stiffness_n_m3= |
natural_frequency(dynamic_stiffness=, mass_per_area=) | dynamic_stiffness_n_m3=, mass_per_area_kg_m2= |
floating_floor_resonance(resonant_frequency=, total_mass_per_area=, floor_mass_per_area=, airflow_resistivity=, thickness=) | resonant_frequency_hz=, total_mass_per_area_kg_m2=, floor_mass_per_area_kg_m2=, airflow_resistivity_kpa_s_m2=, thickness_m= |
La resistividad al flujo es la que se gana la palabra clave. La EN 29052-1 pone
sus umbrales en kPa·s/m², y todas las demás resistividades al flujo de la
biblioteca van en Pa·s/m², una unidad mil veces menor, así que una cifra en
Pa·s/m² pasada por posición caía sin aviso en la rama equivocada del
apartado 8.2; ahora se escribe por su nombre. Y por debajo de 100 kPa·s/m²,
installed_dynamic_stiffness ya no toma como cero un término de gas ausente:
la 3.3.0 devolvía con installed_dynamic_stiffness(20e6, 50.0),
la Fórmula 6 sin su segundo término, y la 4.0 lanza un error y pide
.
Las condiciones y las banderas se escriben por su nombre
Sección titulada «Las condiciones y las banderas se escriben por su nombre»Dos reglas dejaron 37 firmas keyword-only. Una condición o una opción que lleva
valor por defecto se escribe por su nombre, y toda bandera booleana pública es
keyword-only. Las dos existen porque el sitio donde se pierde el significado es
la llamada: air_attenuation(freqs, 20.0, 50.0, 101.325) son una temperatura,
una humedad y una presión en un orden que no recuerda nadie, y
bank.filter(x, True) no dice cuál de cinco banderas se ha activado.
El fallo es ruidoso, que es de lo que se trata:
import numpy as np
from phonometry.signals import leq
x = np.random.default_rng(1).standard_normal(4800) * 0.01
try: leq(x, 48000)except TypeError as error: print(error) # leq() takes 1 positional argument but 2 were givenEsa llamada merece una segunda mirada. leq nunca tuvo fs, porque una media
energética no lo necesita, así que en la 3.3.0 el 48000 se ligaba en silencio a
calibration_factor y la respuesta salía 93,6 dB alta sin una queja. Ahora es
un TypeError, que es lo que siempre debió ser.
print(round(float(leq(x, dbfs=True)), 3))Una grafía por concepto
Sección titulada «Una grafía por concepto»Los nombres públicos se habían ido repartiendo entre varias grafías de la
misma magnitud, y la 4.0 se queda con la que ya usaba la mayoría. La regla de
las magnitudes que se cuentan en plural es la que seguía casi toda la API: un
campo o un parámetro que guarda un array es frequencies, times,
distances o levels, y uno que guarda un solo valor va en singular. Un
argumento vectorizado que admite un número o un array conserva su nombre.
| 3.3.0 o 4.0.0rc1 | 4.0 |
|---|---|
sound_speed (válvulas de control, medición de silenciadores, regímenes de Weston, ruido ambiente, FluidSeabed, SoundSpeedProfile, ShipSourceLevelResult) | speed_of_sound |
c= y rho= en sound_intensity, c= en las dos conversiones de desajuste de fase, phase_mismatch(), absorption_coefficient_uncertainty y monopole_source_level | speed_of_sound=, density= |
speed_of_sound_m_s= en thickness_critical_frequency_product | speed_of_sound= |
sample_rate en el SignalSource de FDTD, STOIResult e impact_force_exposure_level | fs |
freq= en linkwitz_riley y adaptation_term_kc | frequency= y frequencies= |
.frequency en los resultados que llevan un eje de frecuencias: IntensityResult, FieldIndicators, IntensityInstrumentComplianceResult, RoomAcousticsResult, los cinco resultados de auditorio, ImpedanceTubeResult, PorousMediumResult, LayeredAbsorberResult, DiffuseFieldAbsorptionResult, BiotWavesResult, SlitResonatorAbsorberResult, MetadiffuserResult, TransferMatrix, los dos resultados de ruido de válvulas, AircraftBandAttenuation, AmbientNoiseResult, ShipTrafficSpectrum | .frequencies |
frequency= con un array: las funciones de transmisión por flancos, internal_spectrum, pipe_transmission_loss, expander_noise, transfer_matrix_one_load, transfer_matrix_two_load, TransferMatrix.plot | frequencies= |
.time en ZwickerLoudness, EcmaLoudness, MooreGlasbergTimeVaryingLoudness, los resultados de tonalidad, aspereza y fuerza de fluctuación de ECMA-418-2, DecayCurve, QuasiPeakResult | .times |
.level en DecayCurve, NpdLevelResult, NoiseContourResult, RotorcraftNoiseContourResult, LowFrequencyResult, DuctPathStage, LateLateralResult, TransferStiffnessResult | .levels |
.distance en NpdLevelResult y RotorcraftEventResult | .distances |
level=, measured_level=, operational_level= en sti_from_impulse_response, stipa, sti_adjusted_for_levels y STIResult.adjusted_for_levels | levels=, measured_levels=, operational_levels= |
.passed en AircraftSystemComplianceResult, QuasiPeakDynamicsResult, HeavyImpactSourceCheck, RigidMassCalibrationResult | .passes, como en todos los demás verificadores |
MicrophoneNoise(weighting="CCIR") | MicrophoneNoise(weighting="468"), la grafía que usan weighting_filter y weighted_thd para la curva UIT-R BS.468-4 |
times, levels = decay_curve(ir, fs) sigue desempaquetando la curva de
caída, en el mismo orden. Los solvers FDTD conservan c y rho, que nombran
los mapas en los que están escritas sus ecuaciones.
Todo .plot() toma primero los ejes
Sección titulada «Todo .plot() toma primero los ejes»El .plot() de cada resultado se lee ahora plot(ax=None, *, ...): primero
los ejes, y todo lo demás por su nombre, language incluido. Tres no empezaban
por los ejes en la 3.3.0, así que plot(ax) les entregaba los ejes al
parámetro equivocado:
| 3.3.0 | 4.0 |
|---|---|
LoudspeakerCharacteristics.plot("impedance", ax) | .plot(ax, quantity="impedance") |
MicrophoneCharacteristics.plot("noise", ax) | .plot(ax, quantity="noise") |
TransferMatrix.plot(f, rho_c, ax) | .plot(ax, frequencies=f, characteristic_impedance=rho_c) |
Seis resultados posteriores a la 3.3.0 tomaban language por posición en la
4.0.0rc1: las cuatro valoraciones de protectores auditivos y los dos resultados
de intensimetría en baja frecuencia. plot(ax, "es") sobre ellos es ahora
plot(ax, language="es"), como en todos los demás.
Los veredictos son objetos de resultado, no diccionarios
Sección titulada «Los veredictos son objetos de resultado, no diccionarios»Las cinco funciones verify_* devolvían dict[str, Any] y ahora devuelven una
dataclase congelada, como cualquier otro resultado de la biblioteca. Dos
envoltorios públicos que existían sólo para empaquetar ese diccionario se
fueron con el cambio, ya que el verificador devuelve ahora el objeto él mismo.
| 3.3.0 | 4.0 |
|---|---|
verify_filter_class(bank)["overall_class"] | verify_filter_class(bank).overall_class |
verify_weighting_class(wf)["overall_class"] | verify_weighting_class(wf).overall_class |
verify_intensity_class(...)["bands"] | verify_intensity_class(...).bands |
verify_quasi_peak_dynamics()["passed"] | verify_quasi_peak_dynamics().passes |
verify_aircraft_noise_system(...)["passed"] | verify_aircraft_noise_system(...).passes |
filter_class_compliance(bank) | verify_filter_class(bank), que devuelve el mismo FilterComplianceResult |
from phonometry.filters import OctaveFilterBank, verify_filter_class
verdict = verify_filter_class(OctaveFilterBank(48000, fraction=1, limits=[125, 4000]))print(verdict.overall_class, len(verdict.bands))verify_running_rms_decay es posterior a la 3.3.0, y en la 4.0.0rc1 devolvía
un bool desnudo, que tiraba el intervalo impreso y el tiempo medido que había
juzgado. Ahora devuelve un RunningRmsDecayVerification que conserva los dos:
el veredicto es .passes, junto a .measured_time_s, .printed_time_s,
.tolerance_s y el intervalo como .lower_time_s y .upper_time_s. El objeto
se niega a leerse como valor de verdad, así que un
if verify_running_rms_decay(...): escrito contra la versión candidata levanta
TypeError en vez de dar por buenos todos los vibrómetros.
El banco de filtros devuelve un resultado, no una tupla
Sección titulada «El banco de filtros devuelve un resultado, no una tupla»octave_filter() y OctaveFilterBank.filter() devolvían dos elementos
normalmente y tres cuando sigbands=True pedía las señales por banda. La
longitud de la respuesta dependía de una palabra clave, que es la razón por la
que ese par necesitaba doce declaraciones @overload para poder tiparse, y por
la que _, _, bands = bank.filter(...) era una línea en la que había que contar
comas.
Ahora devuelven un OctaveFilterResult con campos con nombre, como cualquier
otro cálculo de la biblioteca.
| 3.3.0 | 4.0 |
|---|---|
spl, freq = octave_filter(x, fs) | r = octave_filter(x, fs), y luego r.levels y r.frequencies |
spl, _ = octave_filter(x, fs) | octave_filter(x, fs).levels |
_, freq = octave_filter(x, fs) | octave_filter(x, fs).frequencies |
_, _, bands = bank.filter(x, sigbands=True) | bank.filter(x, sigbands=True).require_bands() |
import numpy as npfrom phonometry import filters
result = filters.octave_filter(np.zeros(8000) + 1e-6, 8000, fraction=3)print(len(result.frequencies), result.bands)# 25 Nonebands es None salvo que la llamada haya pedido sigbands=True, y levels
es None en una llamada hecha con calculate_level=False. El código que
necesita cualquiera de los dos puede decirlo con require_bands() o
require_levels(), que devuelven el valor o fallan nombrando el argumento que
faltaba, en vez de entregar un None que revienta unas líneas más adelante.
El resultado también se dibuja solo, así que el espectro de bandas es una llamada:
ax = result.plot()Las formas de onda dentro de los resultados vuelven como el Signal del que salieron
Sección titulada «Las formas de onda dentro de los resultados vuelven como el Signal del que salieron»Una transformación a la que se le entrega un Signal devuelve un Signal
desde que se escribió el contrato, pero cinco objetos de resultado todavía
guardaban su forma de onda como un array desnudo junto a un fs suelto. Ahora
conservan el tipo con el que llegó la entrada:
| Campo | Es un Signal cuando la entrada lo era |
|---|---|
envelope(...).signal | a la frecuencia de muestreo de la entrada |
time_synchronous_average(...).period_waveform | a la frecuencia de muestreo de la entrada |
resample_signal(...).signal | a fs_new, que entonces tiene que ser un número entero de hercios |
align_impulse_responses(...).aligned, .reference | cada uno si su propia entrada lo era |
underwater.pile_strike_metrics(...).pressure | a la frecuencia de muestreo de la entrada |
Un array desnudo sigue devolviendo un array desnudo, así que el código que
pasa arrays no lo nota. El código que pasa un Signal y hace aritmética con
uno de estos campos pasa por np.asarray, como con cualquier Signal, y una
entrada calibrada vuelve con calibration_factor=1.0, así que el campo
alimenta a la función siguiente en pascales sin que el factor se aplique otra
vez.
Un flujo por bloques entrega bloques Signal
Sección titulada «Un flujo por bloques entrega bloques Signal»io.read_blocks es posterior a la 3.3.0, y en la 4.0.0rc1 entregaba arrays
float64 desnudos. Ahora entrega un Signal por bloque, con la frecuencia de
muestreo, la calibración, las etiquetas de canal y la procedencia que da
io.read, y toma el mismo calibration_factor= y lee el mismo sidecar. Para
un bucle escrito contra la versión candidata cambian dos cosas. Un bloque de
un archivo calibrado llega a los filtros y a las funciones de nivel en
pascales, así que un calibration_factor aplicado a mano encima cuenta dos
veces. Y la aritmética sobre un bloque, o sobre lo que un filtro devuelve para
él, pasa por np.asarray, como con cualquier Signal.
Las tablas publicadas no admiten escritura
Sección titulada «Las tablas publicadas no admiten escritura»Toda tabla que la biblioteca publica a nivel de módulo es de sólo lectura en
la 4.0. En la 3.3.0 las tablas publicadas eran diccionarios corrientes y los
arrays publicados admitían aritmética sobre el propio array, así que una
línea como REFERENCE_CURVE[500] = 0.9 o BAND_IMPORTANCE *= 2 cambiaba el
número para todo lo que se ejecutara después en el mismo proceso, sin que
nada lo avisara. Los diccionarios son ahora types.MappingProxyType, también
los que van anidados dentro de otra tabla, y los arrays tienen desactivada la
bandera writeable. Leer no cambia; escribir lanza TypeError en una tabla y
ValueError en un array.
El código que retocaba una tabla publicada tiene que copiarla antes:
from phonometry.materials.absorbers import REFERENCE_CURVEfrom phonometry.speech.sii import BAND_IMPORTANCE
curve = dict(REFERENCE_CURVE)curve[4000] = 1.0weights = BAND_IMPORTANCE.copy()weights[0] = 0.0print(REFERENCE_CURVE[4000], curve[4000], weights[0], BAND_IMPORTANCE[0])# 0.9 1.0 0.0 0.0083Una tabla que lleva tablas dentro necesita copiar cada nivel, como en
{area: dict(rows) for area, rows in GUIDE_VALUES.items()}. La misma copia
hace falta antes de serializar una tabla con pickle, copiarla en profundidad o
escribirla como JSON, porque un mapeo de sólo lectura no admite ninguna de las
tres cosas.
Un resultado guarda una copia propia de los arrays que recibe
Sección titulada «Un resultado guarda una copia propia de los arrays que recibe»En la 3.3.0 y en la 4.0.0rc1 un resultado guardaba el mismo array que se le
pasaba siempre que ese array ya era del tipo que lee la función, float64
para niveles y frecuencias, porque en ese caso np.asarray devuelve su
argumento sin tocarlo. Cambiar después el array de quien llamaba,
levels[0] = 0.0 o frequencies *= 2, cambiaba un resultado ya calculado y
quizá ya impreso. En la 4.0 todo array que una función o un método de la
biblioteca guarda de los argumentos que recibe es una copia propia, de sólo
lectura como las tablas publicadas, y el array de quien llama queda como
estaba. Lo mismo vale para los objetos que guardan arrays para entregarlos
después: la rejilla de frecuencias de una noise_control.SilencerChain, los
ejes y las trayectorias con los que se construye una aircraft.AnpDatabase
y los mapas del medio de simulation.FDTD2D y simulation.ElasticFDTD2D,
cuyos c, rho, c_p y c_s siguen siendo ahora aquellos con los que se
calculó el paso de tiempo. Una Signal también guarda una copia propia de
sus muestras, que sigue siendo escribible para procesarla en su sitio, como
siempre lo fueron las muestras de un fichero leído con io.read. Un registro
de resultado construido a mano, llamando directamente a su clase, hace la
misma copia: todo registro público que puede guardar un array copia los
arrays que recibe al construirse, lo construya quien lo construya, también
dentro de una tupla, una lista o cualquier diccionario. Un registro guardado
dentro de otro se guarda entero y responde de sus propios arrays, y una
Signal no es una excepción: los siete resultados que devuelven una forma de
onda como Signal (filters.OctaveFilterResult, signals.EnvelopeResult,
signals.ResampledSignalResult, signals.AlignedImpulseResponseResult,
signals.SynchronousAverageResult, environment.DirectSoundSubtraction y
underwater.PileStrikeResult) guardan una propia cuando los construye la
biblioteca, cuyas muestras siguen siendo escribibles como las de cualquier
Signal, y guardan la de quien llama cuando se construyen a mano con ella.
Todos los demás arrays que guarda un resultado rechazan las escrituras en su
sitio, tanto los que calculó como los que recibió, cuando en la 3.3.0 la
mayoría de los calculados las aceptaban. Escribir en uno lanza ahora
ValueError, así que el código que retocaba uno en su sitio tiene que
copiarlo antes:
import numpy as np
from phonometry import io, room
levels = np.array([60.0, 58.0, 55.0, 52.0, 50.0, 48.0, 45.0, 42.0, 40.0, 38.0])nc = room.noise_criterion(levels)levels[0] = 0.0samples = np.zeros(4)signal = io.Signal(samples, fs=48_000)samples[0] = 1.0measured = np.array([1.0, 0.5, 0.25, 0.125])response = room.ImpulseResponseResult(ir=measured, fs=48_000, method="farina")measured[0] = 0.0edited = nc.levels.copy()edited[0] = 62.0print(nc.levels[0], nc.levels.flags.writeable, response.ir[0], response.ir.flags.writeable)print(signal.data[0, 0], signal.data.flags.writeable)# 60.0 False 1.0 False# 0.0 TrueUna fila de catálogo dice lo que su fuente afirma de cada celda
Sección titulada «Una fila de catálogo dice lo que su fuente afirma de cada celda»Los catálogos publicados de materiales son nuevos en la 4.0: ni la 3.3.0 ni
la 4.0.0rc1 los tenían. Cambiaron de forma una vez de camino a la versión
final, y el código escrito contra la rama principal entretanto necesita los
cambios de abajo. Todas las filas de todos los catálogos comparten ahora una
base, io.CatalogueRow (con io.BandedRow para una fila que imprime un valor
por banda), y las mismas tres preguntas tienen la misma respuesta en
cualquiera de ellas.
| Antes | 4.0 |
|---|---|
SolidMaterial.estimated, OrthotropicWood.estimated | row.basis, que asigna "estimated" a cada campo con la nota al pie |
SolidMaterial.is_estimate(field), OrthotropicWood.is_estimated(field) | row.basis_of(field) == "estimated" |
row.is_derived(field) en un valor que la página da en una unidad que la fila no guarda (los °F y psi de la Tabla 14.1 de Ver & Beranek, los sabines de la Tabla 7.1 de Long) | field in row.converted; row.converted[field] es la cifra de la página y su unidad, como ("3e5", "psi") |
row.is_derived(field) en un valor que la página da por referencia a otra de sus filas: una celda en blanco bajo un bloque (Tabla 8.7 de Ver & Beranek, Tabla 30 de ASHRAE) o una descripción que dice «Parecido al anterior» (capítulo 32 de Harris) | field in row.carried; row.carried[field] nombra la fila |
OrthotropicWood(...) y PlateauMaterial(...) con sus propios campos por posición | todos los campos por nombre, como en cualquier otra fila |
from phonometry._internal.catalogue import CatalogueRow | from phonometry.io import CatalogueRow |
is_derived responde ahora sólo por lo que calcula la biblioteca y podría
volver a calcular con las celdas de la propia fila, como una velocidad de
barra obtenida de un módulo y una densidad. Un valor que la página da en otra
unidad sigue siendo el número de la página, y también lo es uno que da por
referencia a otra de sus filas, así que ninguno de los dos es ya derivado. basis_of responde con la entrada del propio campo, si no
con la de la fila, y si no con una cadena vacía, que quiere decir que la
fuente no dice cómo se obtuvo el número; las cinco cosas que puede afirmar una
fuente están en io.CATALOGUE_BASES.
from phonometry import solids
board = solids.PUBLISHED_SOLIDS["hopkins-2007-table-a2/plasterboard_natural_gypsum"]ear = solids.damping_named("EAR C-1002")[0]print(board.basis_of("poisson_ratio"))# estimatedprint(ear.converted["youngs_modulus_max_pa"], ear.is_derived("youngs_modulus_max_pa"))# ('3e5', 'psi') FalseLas capas elásticas pasaron a ser filas de catálogo en el mismo cambio. Una
ResilientLayer es una io.CatalogueRow como cualquier otra fila, con la
clave y la atribución de las demás:
| Antes | 4.0 |
|---|---|
PUBLISHED_RESILIENT_LAYERS["mineral_wool_rock_60_30"], resilient_layer("mineral_wool_rock_60_30") | "hopkins-2007-table-a3/mineral_wool_rock_60_30", la clave "<tabla>/<fila>" de todos los catálogos |
layer.attributed_to, una cadena | layer.attributed_to["row"], un mapeo como en todas las filas |
ResilientLayer(...) con la rigidez, la densidad y el espesor obligatorios | todas las magnitudes opcionales, y apparent_dynamic_stiffness_n_m3 junto a dynamic_stiffness_n_m3 para una fuente que sólo da la aparente |
layer.natural_frequency(mass_per_area=...) | layer.natural_frequency(mass_per_area_kg_m2=...); una capa que sólo guarda toma además airflow_resistivity_pa_s_m2= y gas_stiffness_n_m3=, y sin el primero lanza io.CatalogueError |
La Tabla A3 de Hopkins imprime , la rigidez de la capa instalada tal como
la define la lista de símbolos del propio libro, así que sus quince filas
siguen guardando dynamic_stiffness_n_m3 y devuelven las frecuencias
naturales que devolvían. Cuatro de ellas guardan una densidad que la página
imprime una sola vez para su bloque y deja en blanco en su propia línea, y
layer.carried["density_kg_m3"] nombra ahora la fila que la imprime; la
densidad es la misma.
from phonometry import materials
rebond = materials.resilient_layer("hopkins-2007-table-a3/rebond_foam_64_20")print(rebond.attributed_to["row"], round(rebond.natural_frequency(mass_per_area_kg_m2=120.0), 1))# Hopkins and Hall (2006) 43.6Una fila de catálogo se comprueba al construirse
Sección titulada «Una fila de catálogo se comprueba al construirse»Toda fila de catálogo se comprueba en su constructor, la construya la
biblioteca a partir de una tabla empaquetada o quien llama a mano, y una celda
que nada de lo que viene después podría leer lanza io.CatalogueError,
nombrando la fila y el campo. El código escrito entretanto contra la rama
principal que contara con que se aceptara algo de lo siguiente tiene que
cambiar:
| Antes se aceptaba | 4.0 |
|---|---|
NaN, un infinito, True o un texto como "0,97" en un campo numérico | CatalogueError; una celda que la página deja vacía es None |
una fracción en un campo entero (year), 1 en un campo booleano (has_section_drawing), un número en un campo de texto | CatalogueError |
un name o un source vacíos, o un matiz cuyo texto está vacío, como misprinted={"porosity": ""} | CatalogueError; why_missing respondía "" para esa celda, lo que escondía el matiz |
un matiz con la clave de un campo que la clase no tiene, como en ranges={"no_such_field": ...}, o un matiz sobre un número (ranges, approximate, unquantified, …) con la clave de un campo de texto | CatalogueError, que nombra el matiz y la clave |
bounded_above o bounded_below sobre un campo sin intervalo; un intervalo, o uno de los intervalos de las lecturas de reported, con un extremo que no es finito o con el extremo bajo por encima del alto; una lista de reported sin nada dentro; una uncertainty negativa | CatalogueError |
un valor junto a misprinted, unquantified, not_derivable o reported en el mismo campo; una entrada de converted o carried en un campo que no guarda nada | CatalogueError: los cuatro primeros dicen que no hay valor que servir, y los dos últimos no tienen nada que describir |
un valor negativo en un campo cuyo nombre acaba en _kg_m3, _kg_m2, _kg, _kg_mol, _m_s, _pa, _pa_s_m2, _pa_s_m, _n_m3, _mm, _um, _m, _m2, _m_hz o _per_cm (una densidad, una masa por unidad de superficie, una masa, una masa molar, una velocidad, una presión o un módulo, una resistividad al flujo, una resistencia al flujo específica, una rigidez por unidad de superficie, una longitud, un área, un producto espesor por frecuencia, una cuenta por centímetro); una porosidad fuera de 0 a 1; shot_content_percent, binder_content_percent o adhered_area_percent fuera de 0 a 100 | CatalogueError; una temperatura en grados Celsius, una tasa de decaimiento por metro y un nivel en decibelios siguen pudiendo ser negativos |
approximate=[...] guardado como la lista que se le dio, un mapeo guardado como el diccionario | un frozenset, y todo mapeo congelado hasta el fondo, con sus pares en tuplas |
un campo de una subclase anotado con un tipo para el que el contrato no tiene comprobación, un float o un int a secas incluidos | TypeError la primera vez que se construye la clase; un campo es float | None (Optional[float] es lo mismo), int | None, bool, str, frozenset[str] o un Mapping[str, ...] de ellos, porque cualquier celda de una fila puede faltar |
PorousMaterial.frame_constants() sobre una fila sin structural_loss_factor, leída como un esqueleto que no disipa nada | ValueError, que dice qué tenía la página en esa celda |
GroundSurface.porosity con valores de 26,9 a 58,1 en seis filas de la Tabla 6.7 de Cox y D’Antonio, que la página imprime en tanto por ciento en una columna de fracciones que no dice su unidad | None, con misprinted["porosity"] citando la cifra que imprime la página; why_missing("porosity") y printed("porosity") lo dicen |
Todas las demás filas empaquetadas cumplían ya el contrato, así que esas seis porosidades son las únicas celdas publicadas que cambian, y todas las filas porosas publicadas con los dos módulos imprimen su factor de pérdidas.
from phonometry import io, materials
try: materials.PorousMaterial(name="Panel core", source="a datasheet", porosity=97.0)except io.CatalogueError as error: print(error)# 'Panel core': porosity is 97.0, and a porosity is a fraction from 0 to 1
carpet = materials.Carpet( name="Loop pile", source="a datasheet", pile_height_mm=6.0, approximate=["pile_height_mm"])print(carpet.approximate)# frozenset({'pile_height_mm'})La clase de un banco de filtros lee todos los requisitos de IEC 61260-1
Sección titulada «La clase de un banco de filtros lee todos los requisitos de IEC 61260-1»verify_filter_class calificaba solo la máscara de la Tabla 1. Para la edición
de 2014 califica ahora también la desviación del ancho de banda efectivo del
5.12 y la suma de las señales de salida del 5.16, calculadas como las calcula
IEC 61260-2:2016, y la class de una banda y la overall_class del banco son
la clase más estricta que se cumple en los tres. Para la edición de 1995
califica la respuesta integrada del filtro del 4.5.3 y la suma de las señales
de salida del 4.9 junto a la Tabla 1, como las ensaya el capítulo 5 de esa
edición. En las dos ediciones cada banda se califica a la frecuencia de
entrada del banco hasta su mitad, así que una banda diezmada se califica
sobre las imágenes que su diezmado pliega sobre ella, y el diezmador del banco
las deja a más de 125 dB por debajo.
| Antes | 4.0 |
|---|---|
| cada banda de un banco diezmado se diezmaba hasta que su frecuencia de Nyquist de proceso era 1,25 veces su borde superior de banda | se diezma sólo hasta dejarla en dieciséis veces ese borde, así que la transformación bilineal ya no deforma la banda: el nivel de una banda se mueve hasta 0,07 dB con ruido blanco, sobre todo en las bandas más bajas, y el banco de octavas, que sumaría sus salidas hasta +0,94 dB y leería clase 2 en el 5.16, es clase 1 en todos los requisitos |
la class de una banda leída de margin_class{c}_db | class sobre todos los requisitos; margin_class{c}_db sigue siendo el margen de la Tabla 1, junto a bandwidth_margin_class{c}_db y summation_margin_class{c}_db, que vale None en las dos bandas de los extremos |
| un solo requisito por el que preguntar | requirements, requirement_class(name) y binding_margin_db(name, cls) leen cada uno por separado, y plot(requirement="summation") lo dibuja |
un veredicto con edition="1995" solo sobre la Tabla 1 | la Tabla 1, "effective_bandwidth" (la respuesta integrada del filtro del 4.5.3) y "summation" (4.9, recorrida de la frecuencia central más baja a la más alta, así que las bandas de los extremos también la llevan), las dos con la S que el 5.3.3 sube en pasos de 12 hasta que la respuesta integrada se estabiliza, que el points_per_bandwidth del resultado indica (36 para el banco de octavas por defecto) |
una banda diezmada calificada hasta la frecuencia de Nyquist de su frecuencia diezmada, con checked_to_omega parado ahí | calificada a la frecuencia de entrada hasta su mitad, imágenes incluidas; checked_to_omega es la mitad de la frecuencia de entrada entre la frecuencia central, y range_limited marca una máscara de la Tabla 1 que sigue más allá |
el filtro antialiasing que scipy.signal.resample_poly diseña por defecto, que dejaba las imágenes del banco de octavas a 8, 16 y 32 kHz 68,9 dB por debajo, sin llegar a los 70 dB de la clase 1 | un filtro de la misma longitud dimensionado para una banda atenuada de 120 dB, con sus imágenes al menos 125,4 dB por debajo; el nivel de una banda se mueve menos de 0,01 dB |
Los bancos de octavas y de tercios de octava siguen siendo clase 1.
from phonometry import filters
octave = filters.verify_filter_class( filters.OctaveFilterBank(48000, fraction=1, limits=[125, 4000]))print(octave.overall_class, octave.requirement_class("relative_attenuation"))# 1 1Un veredicto lee de la norma el límite que esta imprime
Sección titulada «Un veredicto lee de la norma el límite que esta imprime»Un veredicto llevaba antes como campo del constructor el límite con el que se
le juzgaba. En 3.3.0 lo hacían tres: ToneAssessment.criterion_db,
ExposureAssessment.action_value y limit_value, y
RigidMassCalibrationResult.tolerance. Otros seis, posteriores a 3.3.0, lo
hacían en 4.0.0rc1, y el resto de la tabla llegó después de 4.0.0rc1.
Cualquier código que construyera uno a mano, o que lo reescribiera con
dataclasses.replace, podía juzgar una medición contra cualquier número y
seguir poniendo el nombre de la norma encima del resultado. En 4.0 todo límite
que imprime una norma es una propiedad de solo lectura, leída de los campos
que la seleccionan: las bandas, la condición, la fuente, la clase, la sala. Un
veredicto que se guardaba junto a su límite se lee también de los campos
medidos, o se contrasta con ellos al construir el resultado, de modo que no
puede contradecir el límite. Las funciones que devuelven estos veredictos
reciben los mismos argumentos y dan las mismas respuestas, y cada nombre de la
tabla sigue leyendo el mismo valor; solo cambia el código que construye un
veredicto directamente o que sustituye uno de estos campos. La última columna
indica en qué versión apareció cada veredicto.
| Veredicto | Ya no son campos del constructor | Campos que conserva o gana | Desde |
|---|---|---|---|
building.PositionSpreadCheck | limit_db, stage, spread_db, action, next_positions, corner_standard_deviation_db | levels_db, del que se leen todos | después de 4.0.0rc1 |
building.HeavyImpactSourceCheck | frequencies, nominal, tolerance, deviation, within_tolerance, passes | source, measured | 4.0.0rc1 |
noise_control.BandFlatnessCheck | limit_db, satisfied | octave_centres_hz, spread_db | después de 4.0.0rc1 |
noise_control.SourcePositionCheck | required_positions, satisfied, exceeds_maximum | positions_used, max_octave_spread_db | después de 4.0.0rc1 |
noise_control.TestEnvironmentApplicability | environmental_correction_limit_db, background_margin_limit_db, required_area_ratio, applicable | base_standard, actual_area_ratio, mean_absorption_coefficient | después de 4.0.0rc1 |
environment.ReflectionGridCheck | nominal_m, deviations_m, within | check, path_differences_m | después de 4.0.0rc1 |
emission.HardWalledRoomCheck | limit_db, minimum_volume_m3, box_dimension_limit_m, minimum_microphone_distance_m | las bandas, la sala y la caja de referencia | después de 4.0.0rc1 |
emission.ReferenceSoundSourceVerdict | todos los *_limit_db, adjacent_step_db, core_range_db, extended_range_db, frequency_range_met, not_judged | las bandas, los niveles y las cifras medidas | después de 4.0.0rc1 |
emission.ReferenceSourceDriftResult, emission.SpecialRoomSuitabilityCheck | limit_db | las bandas | después de 4.0.0rc1 |
emission.SpecialRoomReverberationCheck | lower_limit, upper_limit | las bandas | después de 4.0.0rc1 |
emission.IntensityInstrumentComplianceResult | limit_class1, limit_class2, spacing_offset_db | device, spacing; las filas por banda se leen de ellos, véase la sección siguiente | 4.0.0rc1 |
emission.InverseSquareLawResult, emission.SourceDirectionalityResult | tolerance_db | lo selecciona room; los radios cualificados deben ser los que alcanzan los niveles | después de 4.0.0rc1 |
hearing.AmbientNoiseCheck | limits_db, table_bands_hz | gana earphone_attenuation_db y allowed_threshold_shift_db | después de 4.0.0rc1 |
hearing.FixtureIsolationCheck | required_db | las bandas | después de 4.0.0rc1 |
hearing.ReatSoundFieldCheck | allowable_variation_db | gana free_field_rejection_db | después de 4.0.0rc1 |
hearing.DiffuseSoundFieldCheck | allowable_variation_db | gana front_to_random_index_db | después de 4.0.0rc1 |
hearing.FreeSoundFieldCheck | axis_offset_m, inverse_distance_difference_db | los seleccionan field y loudspeaker_distance_m | después de 4.0.0rc1 |
electroacoustics.ProgrammeSignalCheck | offset_db, relative_levels_db, tolerance_plus_db, tolerance_minus_db | frequencies_hz, band_levels_db | después de 4.0.0rc1 |
electroacoustics.NeckLoopVerification | limits | lo selecciona neck_loop_type | después de 4.0.0rc1 |
psychoacoustics.ToneAssessment | criterion_db, prominent | gana method, "tone_to_noise_ratio" o "prominence_ratio" | 3.3.0 |
underwater.WeightedExposureResult | criteria | gana impulsive | 4.0.0rc1 |
vibration.ExposureAssessment | action_value, limit_value, exceeds_action, exceeds_limit, zone | value, kind, metric | 3.3.0 |
vibration.PhaseVerification, vibration.WeightingVerification | tolerance_deg (fase), within_tolerance | la ponderación y las frecuencias | 4.0.0rc1 |
vibration.RunningRmsDecayVerification | printed_time_s, tolerance_s | method, integration_time_s, measured_time_s | después de 4.0.0rc1 |
vibration.SignalBurstVerification | printed, deviation_percent, tolerance_percent, within_tolerance | la fila de la tabla, las columnas y measured | 4.0.0rc1 |
vibration.VibrationMeterVerification | lower_percent, upper_percent, within_tolerance | gana measured_response | después de 4.0.0rc1 |
vibration.AssessmentVelocity | guide_value_mm_s | lo selecciona building_class | después de 4.0.0rc1 |
vibration.MountingCheck | acceptable, frequency_limit_hz, peak_acceleration_limit_m_s2, device | gana peak_acceleration_m_s2 y upper_frequency_hz | después de 4.0.0rc1 |
vibration.DamageAssessment | guideline_mm_s | gana massive_structure, solo por nombre | 4.0.0rc1 |
vibration.RigidMassCalibrationResult | expected, deviation, within_tolerance, passes, tolerance | frequencies, measured, mass, quantity | 3.3.0 |
vibration.LevelDifferenceCheck | limit_db | lo selecciona condition | después de 4.0.0rc1 |
vibration.OutputMassCheck | mass_limit_kg | gana output_apparent_mass_kg, la fuerza de salida medida entre la aceleración de salida | después de 4.0.0rc1 |
También cambian dos llamadas. rigid_mass_calibration_check ya no recibe
tolerance: el apartado 7.5.2 de ISO 7626-2:2015 dice que el bloque «shall
agree within ±5 %», así que una aceptación interna más estricta se lee de
.deviation. Y HeavyImpactSourceCheck y RigidMassCalibrationResult, cuyo
passes era un campo almacenado, se niegan a leerse como valor de verdad
igual que los demás veredictos: if check: lanza TypeError y el veredicto
es .passes.
import dataclasses
from phonometry import vibration
check = vibration.check_blocked_output([100.0, 200.0], [90.0, 95.0], [60.0, 70.0])print(check.limit_db, check.passes)# 20.0 Truetry: dataclasses.replace(check, limit_db=10.0)except TypeError as error: print(type(error).__name__)# TypeErrorcalibration = vibration.rigid_mass_calibration_check([0.104, 0.093], [50.0, 500.0], 10.0)print(calibration.tolerance, calibration.within_tolerance.tolist())# 0.05 [True, False]Los límites que una norma deja en manos del usuario siguen siendo campos: los
límites de rugosidad y de tasa de decaimiento «por defecto» de ISO 3095, la
tolerancia que ISO/DIS 16032 7.6 no imprime para «aproximadamente 30 s», el
periodo de curado que fija la especificación del producto según ISO 10140-1
G.4, el desplazamiento de umbral que ISO 8253-1 deja aceptar al examinador, y
los límites de especificación, de caso de ensayo y de fila de límites que se
pasan a las comprobaciones de conformidad, de ISO 17534-1 y del
RD 1367/2007. Los registros de requisitos de ISO 8297 y de IEC 60118-4,
PlantRequirement y LoopRequirement, también conservan como campo el límite
de cada fila: un mismo tipo de registro reúne requisitos cuyos límites mezclan
cifras impresas con la geometría de la planta, la intensidad de campo
especificada o el ruido medido con el bucle apagado.
Un veredicto se lee de los valores que guarda
Sección titulada «Un veredicto se lee de los valores que guarda»La sección anterior sacó de los constructores los límites impresos. Las
banderas del veredicto se quedaron dentro: passes, complies, qualified,
todos los *_met y *_ok, los avisos y la clase de cada banda de un
veredicto de filtro, de ponderación o de instrumento de intensidad. La función
que construía el resultado las calculaba y las guardaba junto a las lecturas
de las que salían, así que un resultado construido a mano, o reescrito con
dataclasses.replace, podía guardar lecturas que no cumplen y una bandera que
dice que sí. En 4.0 todas son propiedades de solo lectura, leídas de los
valores que guarda el resultado y del límite que imprime la norma. El
resultado conserva aquello de lo que se lee su veredicto: las lecturas, los
márgenes, las posiciones, las cifras medidas. Una cifra que solo alimentaba el
veredicto, como el nivel redondeado de un periodo del RD 1367/2007 o la
fracción de gotas de lluvia dentro de su tolerancia, también es una propiedad.
Las funciones que devuelven estos resultados reciben los mismos argumentos y
dan los mismos veredictos, y cada nombre de la tabla sigue leyendo el mismo
valor; solo cambia el código que construye uno de estos resultados
directamente o que sustituye uno de los campos de la lista. «Solo por nombre»
marca un campo que solo se pasa por nombre.
| Resultado | Ya no son campos del constructor | Se lee de | Desde |
|---|---|---|---|
building.JointTestElementCheck | length_ok, width_ok, passes | joint_length_m, joint_width_mm, window_or_door_gap | después de 4.0.0rc1 |
building.GapWidthCheck | gap_width_mm, spread_mm, enough_positions, uniform, passes | readings_mm | después de 4.0.0rc1 |
building.JointGapSeriesCheck | nominal_measured, minimum_measured, working_range_measured, passes | gap_widths_mm, nominal_gap_mm, minimum_gap_mm | después de 4.0.0rc1 |
building.LiningCuringCheck | cured, lag_within_third, passes | curing_time_days, time_lag_days, required_curing_days | después de 4.0.0rc1 |
building.RainGeneratorVerification | rate_deviation_mm_h, rate_ok, drop_share, drops_ok, velocity_share, velocities_ok, passes | rain_type, rainfall_rate_mm_h, drop_diameters_mm, fall_velocities_m_s | después de 4.0.0rc1 |
building.ServiceEquipmentPositionCheck | separation_m, surface_distance_m, source_distance_m, heights_m, corner_height_m, corner_wall_distances_m y los seis *_ok | la sala, la esquina y las posiciones | después de 4.0.0rc1 |
building.DbHrCheck | reported, margin, complies | requirement, value | 4.0.0rc1 |
building.LowFrequencyIntensityResult, building.LowFrequencyElementResult | qualified | surface_pressure_intensity_indicator, frente al límite de 6.4.2 que selecciona absorbing_specimen_surface | 4.0.0rc1 |
electroacoustics.BackgroundNoiseAssessment | reference_signal_to_noise_ratio_db, category, report_required | noise_levels_db, noise_is_tonal | después de 4.0.0rc1 |
electroacoustics.FeedbackStabilityResult | is_stable | loop_gain, stability_margin | 4.0.0rc1 |
emission.FreeFieldCheck | room, frequencies_hz, band_radius_m, maximum_qualified_radius_m, todos los *_met, full_frequency_range, conforming_range_hz, conforming_radius_m, not_judged | los argumentos de check_free_field: gana source_directionality, reflecting_plane_absorption_coefficient, reflecting_plane_margin_m, paths_in_working_area, speed_of_sound | después de 4.0.0rc1 |
emission.IntensityInstrumentComplianceResult | bands, overall_class, range_limited | frequencies, residual_index, device, spacing | 4.0.0rc1 |
emission.ReferenceSourceCalibration | sound_power_level_db, intensity_bands, intensity_agreement, room_qualified | gana intensity_sound_power_level_db y room_qualification | después de 4.0.0rc1 |
emission.HardWalledSoundPowerResult | background_requirement_met, upper_bound | gana background_margin_db, background_margin_ref_db | después de 4.0.0rc1 |
emission.InSituSoundPowerResult | background_requirement_met, upper_bound, grade, sigma_r0, sigma_tot, expanded_uncertainty | gana background_margin_db, background_margin_ref_db, los menores de todas las posiciones, y excess_levels_db, directivity_range_db, los dos indicadores con los que la tabla 2 asigna el grado | 4.0.0rc1 |
emission.SpecialRoomSoundPowerResult | background_requirement_met, background_requirement_met_a | gana background_margin_db, background_margin_ref_db, background_margin_a_db | después de 4.0.0rc1 |
emission.EmissionPressureResult | upper_bound | gana background_margin_db, solo por nombre | 4.0.0rc1 |
emission.PrecisionCriteria | de criterion_1 a criterion_5, qualified | gana las lecturas: indicators, scan_intensity_level_1, scan_intensity_level_2, frequencies, repeatability_limit_db, pressure_residual_index_db, field_nonuniformity_1, field_nonuniformity_2 | 3.3.0 |
emission.DiscretePointIntensityResult | todo salvo los indicadores y el grado: partial_power, sound_power, sound_power_level, achieved_grade, los dos criterios, las cifras con ponderación A y el resto | gana normal_intensity, segment_areas_m2, pressure_residual_index_db, band_type | 4.0.0rc1 |
emission.TurbineTestEnvironmentCheck | correction_ok, wind_ok, windscreen_advised | environmental_correction_db, ratio, wind_speed_m_s | después de 4.0.0rc1 |
environment.ImpulseOnset | level_difference, prominence, qualifies | level_start, level_end, onset_rate | 3.3.0 |
environment.ImpulseProminenceResult | per_impulse, qualifies, prominence, adjustment | onset_rates, level_differences, assessment_period_min | 3.3.0 |
environment.ResidualCorrectionResult | reliable | margin | 3.3.0 |
environment.PeriodAssessment | reported_level, reported_long_term, max_phase_level, phase_pass, daily_pass, long_term_pass | phases, los dos niveles y limit; gana new_activity, solo por nombre | 4.0.0rc1 |
environment.ModulationPeriod | modulation_depths_db, fundamental_frequencies_hz, valid_blocks, rated, rating_db, mean_modulation_frequency_hz, mode_modulation_frequency_hz | blocks | después de 4.0.0rc1 |
environment.SoundRelevantTurbines | relevant, total_level_db, relevant_level_db | predicted_levels_db | después de 4.0.0rc1 |
environment.WindShearProfile | typical | shear_exponent | después de 4.0.0rc1 |
environment.WindTurbineTonalityResult | is_audible | tonal_audibility, has_identified_tone, que también es de solo lectura (sección siguiente) | 3.3.0 |
filters.FilterComplianceResult | bands, overall_class, range_limited | gana band_margins, las filas sin su clase | 3.3.0 |
filters.WeightingComplianceResult | bands, overall_class, range_limited | gana band_margins, las filas sin su clase | 4.0.0rc1 |
hearing.AscendingThresholdResult | threshold_db, determined, series_exhausted | ascent_levels_db, shortened | después de 4.0.0rc1 |
hearing.TaskContribution | spread_advisory | n_samples, sample_range_db | 3.3.0 |
hearing.ExposureResult | sampling_advisory | gana sample_range_db, n_workers, sample_duration_hours, solo por nombre | 3.3.0 |
hearing.AttenuationDifferenceResult | difference_db, criterion_db, significant | las dos medias y sus incertidumbres expandidas | después de 4.0.0rc1 |
materials.CeilingSpecimenCheck | area_error_m2, deflection_ok, substructure_ok, fixture_ok, supports_ok, humidity_ok, ce_marking_depth, satisfied | gana las cifras medidas: deflection_mm, substructure_width_mm, substructure_height_mm, fixture_density_kg_m2, support_width_mm, support_height_mm, support_centre_distance_m, lowest_relative_humidity_percent | después de 4.0.0rc1 |
metrology.TrendTestResult | trend_free, bounds | statistic, frente a la región que dan values, method, alpha y median | 3.3.0 |
metrology.StationarityTestResult | stationary, bounds | count, frente a la región que dan segment_values, method y alpha | 3.3.0 |
metrology.CouplerCheck | ratio_recommended, approximation_valid, full_solution_advised, conditions_valid | gana heat_conduction_method, temperature_c, static_pressure_pa, relative_humidity_percent, in_air, solo por nombre | después de 4.0.0rc1 |
metrology.FreeFieldArrangementCheck | distances_ok, support_ok, annex_a_range, attenuation_accuracy | gana air_attenuation | después de 4.0.0rc1 |
noise_control.CabinUncertainty | ratio_satisfied, stateable, stated_band_range_hz, increased_uncertainty_band_range_hz, excess_standard_deviation_db | method, volume_ratio | después de 4.0.0rc1 |
room.RoomAcousticsResult | edt_valid, t20_valid, t30_valid | dynamic_range y los tiempos de caída | 3.3.0 |
room.BackgroundMarginCheck | needs_correction, unusable, satisfied | margins_db | después de 4.0.0rc1 |
room.DetailVerdict | satisfied, room_ok, fittings_ok, sources_ok | gana room_detail, fitting_detail, source_detail | después de 4.0.0rc1 |
underwater.WeightedExposureResult | exceeds_injury, exceeds_tts | sel_margin, peak_margin, tts_margin, tts_peak_margin | 4.0.0rc1 |
vibration.PeopleAssessment | complies, criterion, within_uncertainty | kb_fmax, kb_ftr, guide, source; gana rare_short_events, solo por nombre | después de 4.0.0rc1 |
vibration.RailwayChange | complies, kb_fmax_met, kb_ftr_met, kb_fmax_increase_percent, kb_ftr_increase_percent | gana kb_fmax_before, kb_fmax_after, kb_ftr_before, kb_ftr_after | después de 4.0.0rc1 |
vibration.TransferStiffnessResult | valid | gana transmissibility, el |T| del método indirecto | 3.3.0 |
vibration.DrivingPointStiffnessResult | adequate | gana blocked_output_difference_db, unwanted_input_difference_db | después de 4.0.0rc1 |
aircraft.AircraftSystemComplianceResult | passes, checks | gana directional, frequency_response, linearity, resolution, los argumentos de verify_aircraft_noise_system | 4.0.0rc1 |
broadcast.QuasiPeakDynamicsResult | passes, worst_margin_db, worst_deviation_db, stimuli | gana readings_percent, las once lecturas en el orden de las tablas 2 y 3 | 4.0.0rc1 |
building.FloorCoveringImprovementResult | limited | gana background_margin_db, el menor margen de todas las posiciones y de los dos forjados | 3.1.0 |
building.VibrationReductionResult | single_number, bracketed | frequencies, k_ij; gana modal_overlap, band_type | 3.1.0 |
building.ServiceEquipmentBackgroundResult | difference_db, correction_db, corrected_db, regime | measured_db, background_db | después de 4.0.0rc1 |
building.ServiceEquipmentResult | standardizable | frequencies_hz, band | después de 4.0.0rc1 |
emission.SoundPowerIntensityResult | partial_power_level, sound_power, sound_power_level, negative_band, negative_partial_power_index, dynamic_capability_index, achieved_grade, a_weighting_omitted_bands, sound_power_level_a | partial_power y los indicadores; gana pressure_residual_index_db, band_type, repeatability_limit_db | 3.1.0 |
emission.PrecisionIntensityResult | sound_power, sound_power_level, sound_power_level_normalized, not_applicable_band, sound_power_level_a | partial_power; gana temperature_c, barometric_pressure_pa, solo por nombre | 3.1.0 |
emission.InDuctSoundPowerResult | information_only_band | frequencies, flow_velocity | 4.0.0rc1 |
emission.SourceLocationPlan | spectral_character, a_weighted_spectral_character | standard, standard_deviation_db, a_weighted_standard_deviation_db | después de 4.0.0rc1 |
environment.ImpulsiveSoundResult | prominence, adjustment, category, adjusted_laeq | onsets, laeq | 3.3.0 |
environment.ModulationBlock | status | prominence; gana excluded, solo por nombre | después de 4.0.0rc1 |
environment.ReferenceTrackCheck | conditions | la rugosidad, las tasas de decaimiento y los límites; gana curve_radius_m, track_gradient_ratio, solo por nombre | después de 4.0.0rc1 |
FilterComplianceResult y WeightingComplianceResult guardan sus filas por
banda como band_margins: las filas que guardaba bands, sin la clave
"class". Una fila que declara una clase se rechaza, y la propiedad bands
añade a cada fila la clase que ganan sus márgenes, de modo que
result.bands[i]["class"] se lee como antes.
IntensityInstrumentComplianceResult ya no recibe filas por banda: guarda las
frecuencias y el índice presión-intensidad residual medido, y lee de ellos,
del dispositivo y de la separación de los micrófonos las filas, la clase y
range_limited.
Las filas de las que se lee un veredicto son de solo lectura. band_margins y
between_nominals se copian en diccionarios de solo lectura al construir el
resultado, de modo que escribir en una lanza TypeError y escribir después en
los diccionarios con los que se construyó no mueve la clase; para cambiar una
fila, cópiela con dict(row), porque copy.deepcopy no puede copiar un
diccionario de solo lectura. Las filas de AircraftSystemComplianceResult.checks,
QuasiPeakDynamicsResult.stimuli y ReferenceTrackCheck.conditions se
construyen, cada vez que se leen, a partir de lo que guarda el resultado y de
las tablas de la norma.
import dataclasses
from phonometry import building, filters
gap = building.check_gap_width([5.0, 5.1, 5.2, 5.1])print(gap.passes, round(gap.spread_mm, 1))# True 0.2try: dataclasses.replace(gap, passes=False)except TypeError as error: print(type(error).__name__)# TypeErrorwider = dataclasses.replace(gap, readings_mm=(5.0, 5.1, 5.6, 5.1))print(wider.uniform, wider.passes)# False Falsebank = filters.OctaveFilterBank(fs=48000, fraction=1, order=6, limits=[125, 4000])result = filters.verify_filter_class(bank)print(result.overall_class, result.bands[0]["class"], "class" in result.band_margins[0])# 1 1 FalseUna bandera sigue siendo un campo cuando no es un veredicto contra un límite y
nada de lo que guarda el resultado la decide: una opción que fija quien llama,
como small_room o noise_is_tonal; una rama que eligió quien llama con lo
que pasó, como InverseSquareLawResult.origin_fitted; un hecho de una entrada
que el resultado no guarda, como TimeWeightedEnvelope.calibrated; una
propiedad del fichero leído; si un resolvedor iterativo convergió; y una
columna de una fila de una tabla publicada. CriticalCouplingResult.converged
conserva el desenlace de su resolución, que el resultado no puede repetir,
pero ya no puede declarar la convergencia junto a una absorción que no supera
0,999, la tercera condición de la bandera. Un régimen que el resultado podría
leer de sus propios valores es un veredicto, y la sección siguiente enumera los
que se han movido. PlantRequirement y LoopRequirement no cambian: sus
filas conservan cada límite como campo por la razón que da la sección
anterior, y PlantRequirement.holds sigue junto a él. TrackCondition
conserva su holds como fila del informe, pero ReferenceTrackCheck
construye sus condiciones a partir de la vía que guarda, así que ningún
veredicto lee una construida en otro sitio. También se quedan los checks de
sí o no de SoundLevelMeterPeriodicRequirement: son lo que vio quien ensaya,
una sobrecarga indicada o un indicador enclavado, no un límite juzgado.
Un resultado lee su régimen de los valores que guarda
Sección titulada «Un resultado lee su régimen de los valores que guarda»Algunos resultados guardaban un régimen o una clasificación junto a los valores de los que se leía: el régimen de una válvula, si un martillo rebota, si el terreno apantalla un trayecto, si se encontró un tono, si un promediado síncrono tuvo que interpolar. La función comparaba valores que el resultado guarda, o podría guardar, con una constante o con un umbral que imprime su fuente, y guardaba la respuesta, así que un resultado construido a mano podía guardar la rigidez de un martillo en régimen subcrítico y decir que es sobrecrítico. En 4.0 cada una es una propiedad de solo lectura, leída de los valores que conserva el resultado, y un resultado que no conservaba lo que lee la prueba ahora lo conserva, con la unidad en el nombre. «Solo por nombre» marca un campo que solo se pasa por nombre.
Un valor justo en un límite cae donde lo pone la fuente. Hopkins imprime el caso sobrecrítico como K m ≥ 4 Zdp², así que el amortiguamiento crítico es sobrecrítico, e IEC TS 61400-11-2 pide un total «al menos 3 dB» por encima del fondo para la resta logarítmica, así que una diferencia de 3 dB la recibe. IEC 60534-8-4 no zanja su umbral de cavitación en un solo sentido: la prueba de 5.1 no deja el umbral mismo de ningún lado, la región de la Ecuación (9) y la Ecuación (19b) lo ponen con los puntos con cavitación, y 4.1, la Ecuación (18b) y 6.3 con los turbulentos. La biblioteca lo lee como turbulento, donde la Ecuación (9) devuelve cero y las dos ramas se encuentran sin escalón.
Las funciones reciben los mismos argumentos y devuelven los mismos números.
Tiene que cambiar el código que construye uno de estos resultados directamente
o que sustituye uno de los campos de los que se lee su régimen, y también el que
lee band_type de vibration_reduction_index (véase debajo de la tabla).
| Resultado | Ya no es campo del constructor | Se lee de | Desde |
|---|---|---|---|
building.TappingForceResult | over_critical, K m ≥ 4 Zdp² (Hopkins, ecuación 3.95) | contact_stiffness, impedance; gana mass_kg, solo por nombre | 4.0.0rc1 |
noise_control.HydrodynamicValveNoise | regime, con cavitación cuando Δp supera x_Fzp1 (p1 - pv) (IEC 60534-8-4, 5.1) | differential, corrected_ratio; gana inlet_pressure_pa, vapour_pressure_pa | 4.0.0rc1 |
noise_control.AerodynamicValveNoise | regime, los cinco intervalos de IEC 60534-8-3, 5.2 | pressure_ratio, boundaries; gana specific_heat_ratio, pressure_recovery, efficiency_correction, solo por nombre | 4.0.0rc1 |
aircraft.TerrainScreeningResult | screened, terreno estrictamente por encima de la línea de visión | source, receiver, distances, heights | 3.3.0 |
hearing.AutomaticThresholdResult | is_peak, retained (ISO 8253-1, 6.3.5 a)) | reversal_levels_db | después de 4.0.0rc1 |
hearing.SweepThresholdResult | is_peak | reversal_levels_db | después de 4.0.0rc1 |
metrology.WaveMotionCorrection | interpolated | frequencies_hz, speed_of_sound_ratio | después de 4.0.0rc1 |
noise_control.CabinInsulationResult | apparent (ISO 11957, 3.6) | method | después de 4.0.0rc1 |
environment.WindTurbineTonalityResult | has_identified_tone (IEC 61400-11, 9.5.2 a 9.5.4) | levels, frequencies; gana candidate_frequency_hz, solo por nombre | 3.3.0 |
building.LabJointInsulationResult | regime (ISO 10140-1, J.1) | r_s_measured_db, r_s_max_db; gana limit_at_maximum, solo por nombre | después de 4.0.0rc1 |
environment.TurbineSoundLevels | regimes (IEC TS 61400-11-2, 11.7) | total_levels_db, background_levels_db | después de 4.0.0rc1 |
underwater.WestonPropagationResult | regime | range_m, boundaries | 4.0.0rc1 |
signals.SynchronousAverageResult | interpolated, f_s T no es un número entero de muestras | fs, period_s | 4.0.0rc1 |
building.VibrationReductionResult.band_type sigue siendo un campo, como el
conjunto de bandas que declara quien llama: octave_bands() declara
"octave", porque una sola banda de octava no se distingue de una de tercio
de octava por su espaciado. vibration_reduction_index ya no lo rellena con
el conjunto de bandas que leía de las frecuencias; lo deja en None, y el
resultado lee el espaciado cada vez que calcula la media del anexo A, que da la
misma media que antes.
Los números que elige un régimen quedan atados a él, así que un resultado construido a mano ya no puede llevar un régimen junto a los valores de otro. Al construirse, cada uno de estos resultados rechaza:
TappingForceResult: una frecuencia de corte, una frecuencia límite, un espectro de fuerza o una potencia inyectada que no sean los que dan K, Zdp y m en su régimen, y unos límites de las ecuaciones (3.99) y (3.100) que no estén a un factor 2 el uno del otro.HydrodynamicValveNoise: una presión de vapor igual o superior a la presión de entrada; un x_F o un x_Fzp1 que no sean los que dan la Ecuación (1) o la (3c) a partir de los campos de al lado; un punto en la evaporación o más allá, x_F = 1; campos de cavitación en un punto turbulento, o ninguno en uno con cavitación.AerodynamicValveNoise: unos límites que no sean los de su gas y su guarnición, y un número de Mach o una eficiencia acústica de otro régimen.TerrainScreeningResult: una fuente o un receptor que no sean un punto finito, una fuente en el receptor o más allá, una sección que no vaya de uno al otro, y una diferencia de camino o unas aristas de difracción que no sean las de la goma elástica sobre esa sección.AutomaticThresholdResultySweepThresholdResult: inversiones que no se alternan, un trazado que no conserva ningún pico o ningún valle, y una media, un umbral o un umbral continuo que no sean los que dan las inversiones.WaveMotionCorrection: una frecuencia más allá de la última fila de la tabla C.3, y una corrección que la tabla no da en su frecuencia.CabinInsulationResult: un aislamiento ponderado A con cualquier método que no sea el del ruido real.WindTurbineTonalityResult: una candidata que no sea una línea del espectro a 20 Hz o más, y una cadena de las fórmulas 30 a 34 que no sea la que da el espectro en torno a ella.LabJointInsulationResult: un índice corregido por otra regla de J.1.TurbineSoundLevels: una diferencia de niveles que no sea el total menos el fondo, un nivel del aerogenerador por otra regla de 11.7, y una incertidumbre en un intervalo cuyo nivel no se puede determinar.WestonPropagationResult: una pérdida compuesta que no sea la ley del régimen vigente.SynchronousAverageResult: una rejilla de periodo de más o menos muestras queround(fs * period_s).
import dataclasses
import numpy as np
from phonometry import building
bands = np.array([100.0, 200.0, 400.0, 800.0])force = building.tapping_force_spectrum(bands, 1.0e6, 3.0e4)print(force.over_critical)# Falseprint(building.tapping_force_spectrum(bands, 1.0e6, 100.0).over_critical)# Truetry: dataclasses.replace(force, impedance=100.0)except ValueError as error: print("cut_off_frequency" in str(error))# TrueLos diez nombres que ya no están
Sección titulada «Los diez nombres que ya no están»Todo lo demás se movió. Estos diez no existen en ningún módulo público:
| Ya no está | Qué escribir en su lugar |
|---|---|
octavefilter, getansifrequencies, normalizedfreq | filters.octave_filter, filters.nominal_frequencies, filters.normalized_frequencies. Eran las grafías originales de PyOctaveBand, renombradas en la 3.1 y conservadas como alias hasta la 4.0 |
calculate_sensitivity | metrology.sensitivity, el mismo renombrado de la 3.1 |
filter_class_compliance | filters.verify_filter_class |
air_density_iso, speed_of_sound_iso | materials.air_density_iso10534, materials.speed_of_sound_iso10534, y lee el aviso de los kelvin de más arriba |
speed_of_sound | fluids.air(temperature_c=...).speed_of_sound para el medio. Usa un modelo más completo que aquella fórmula de una línea de la ISO 17497-1, así que el número se mueve: 343,99 m/s frente a 343,20 m/s a 20 °C |
depth_to_pressure | fluids.depth_to_gauge_pressure_mpa(depth_m=...), que devuelve el mismo número, o fluids.depth_to_absolute_pressure_pa para la absoluta. Las dos son keyword-only y las dos toman un latitude_deg opcional |
TransmissionLossResult | PropagationLossResult, con .tl renombrado a .pl |
Lo que no ha cambiado
Sección titulada «Lo que no ha cambiado»Los números. El informe de conformidad son las mismas 995 comprobaciones contra las mismas normas con los mismos valores esperados que tenía antes de la reorganización, y de cada renombrado de esta página se comprobó que devuelve lo que devolvía su predecesor. Si un resultado se mueve después de que termines de actualizar, eso es un defecto y merece la pena avisar.
Un veredicto es la excepción, y la sección sobre las clases de filtro de más
arriba explica cómo: verify_filter_class califica el ancho de banda efectivo
del 5.12 y la suma de salidas del 5.16 además de la máscara de la Tabla 1 para
la edición de 2014, y la respuesta integrada del filtro del 4.5.3 y la suma del
4.9 con edition="1995", así que la clase de un banco es la más estricta que
cumple en los tres. Cada banda se califica a la frecuencia de entrada del banco
hasta la mitad de ella, así que los márgenes de la Tabla 1 de una banda
diezmada recogen las imágenes que su diezmado pliega sobre ella.
Los bancos de filtros diezmados son la otra: cada banda conserva ahora su frecuencia de Nyquist de proceso en dieciséis veces su borde superior y diezma con un filtro antialiasing dimensionado para una banda atenuada de 120 dB, así que el nivel de una banda se mueve hasta 0,07 dB con ruido blanco, como dice la misma sección.
Lo que dibuja cada .plot() está intacto, y también lo que imprime cada
.report() salvo una etiqueta: la ficha del micrófono escribe dB(468) donde
escribía dB(CCIR). El contrato de Signal es el que era, ahora cumplido en dos
sitios más, los bloques de read_blocks y las formas de onda dentro de cinco
resultados. Cada fórmula está intacta también. La 4.0 recolocó los muebles y
puso la unidad en la etiqueta; salvo esas dos, no recalculó nada.