"""
IEC 61260-1:2014
================
IEC 61260-1:2014 specifies performance requirements for analogue, sampled-data,
and digital implementations of band-pass filters.
Frequency functions
**************************************
.. autofunction:: acoustics.standards.iec_61260_1_2014.exact_center_frequency
.. autofunction:: acoustics.standards.iec_61260_1_2014.lower_frequency
.. autofunction:: acoustics.standards.iec_61260_1_2014.upper_frequency
.. autofunction:: acoustics.standards.iec_61260_1_2014.index_of_frequency
Nominal center frequencies
**************************
.. autoattribute:: acoustics.standards.iec_61260_1_2014.NOMINAL_OCTAVE_CENTER_FREQUENCIES
.. autoattribute:: acoustics.standards.iec_61260_1_2014.NOMINAL_THIRD_OCTAVE_CENTER_FREQUENCIES
.. autoattribute:: acoustics.standards.iec_61260_1_2014.REFERENCE_FREQUENCY
.. autoattribute:: acoustics.standards.iec_61260_1_2014.OCTAVE_FREQUENCY_RATIO
"""
import acoustics
import numpy as np
NOMINAL_OCTAVE_CENTER_FREQUENCIES = np.array([31.5, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0])
"""Nominal octave center frequencies.
"""
NOMINAL_THIRD_OCTAVE_CENTER_FREQUENCIES = np.array([
25.0, 31.5, 40.0, 50.0, 63.0, 80.0, 100.0, 125.0, 160.0, 200.0, 250.0, 315.0, 400.0, 500.0, 630.0, 800.0, 1000.0,
1250.0, 1600.0, 2000.0, 2500.0, 3150.0, 4000.0, 5000.0, 6300.0, 8000.0, 10000.0, 12500.0, 16000.0, 20000.0
])
"""Nominal third-octave center frequencies in the audio range.
"""
REFERENCE_FREQUENCY = 1000.0
"""Reference frequency.
"""
OCTAVE_FREQUENCY_RATIO = 10.0**(3.0 / 10.0)
"""Octave frequency ratio :math:`G`.
See equation 1.
"""
[docs]def exact_center_frequency(x, fraction=1, ref=REFERENCE_FREQUENCY, G=OCTAVE_FREQUENCY_RATIO):
"""
Center frequencies :math:`f_m` for band indices :math:`x`. See equation 2 and 3.
:param x: Band index :math:`x`.
:param ref: Reference center frequency :math:`f_r`.
:param fraction: Bandwidth designator :math`b`. For example, for 1/3-octave filter b=3.
:param G: Octave frequency ratio :math:`G`.
The center frequencies are given by
.. math:: f_m = f_r G^{x/b}
In case the bandwidth designator :math:`b` is an even number, the center frequencies are given by
.. math:: f_m = f_r G^{(2x+1)/2b}
See equation 2 and 3 of the standard.
"""
fraction = np.asarray(fraction)
uneven = (fraction % 2).astype('bool')
return ref * G**((2.0 * x + 1.0) / (2.0 * fraction)) * np.logical_not(uneven) + uneven * ref * G**(x / fraction)
[docs]def lower_frequency(center, fraction=1, G=OCTAVE_FREQUENCY_RATIO):
"""
Lower band-edge frequencies. See equation 4.
:param center: Center frequencies :math:`f_m`.
:param fraction: Bandwidth designator :math:`b`.
:param G: Octave frequency ratio :math:`G`.
The lower band-edge frequencies are given by
.. math:: f_1 = f_m G^{-1/2b}
See equation 4 of the standard.
"""
return center * G**(-1.0 / (2.0 * fraction))
[docs]def upper_frequency(center, fraction=1, G=OCTAVE_FREQUENCY_RATIO):
"""
Upper band-edge frequencies. See equation 5.
:param center: Center frequencies :math:`f_m`.
:param fraction: Bandwidth designator :math:`b`.
:param G: Octave frequency ratio :math:`G`.
The upper band-edge frequencies are given by
.. math:: f_2 = f_m G^{+1/2b}
See equation 5 of the standard.
"""
return center * G**(+1.0 / (2.0 * fraction))
[docs]def index_of_frequency(frequency, fraction=1, ref=REFERENCE_FREQUENCY, G=OCTAVE_FREQUENCY_RATIO):
"""Determine the band index `x` from a given frequency.
:param frequency: Frequencies :math:`f`.
:param fraction: Bandwidth designator :math:`b`.
:param ref: Reference frequency.
:param G: Octave frequency ratio :math:`G`.
The index of the center frequency is given by
.. math:: x = round{b \\frac{\log{f/f_{ref} }}{\log{G} }}
.. note:: This equation is not part of the standard. However, it follows from :func:`exact_center_frequency`.
"""
fraction = np.asarray(fraction)
uneven = (fraction % 2).astype('bool')
return (np.round((2.0 * fraction * np.log(frequency / ref) / np.log(G) - 1.0)) / 2.0 * np.logical_not(uneven) +
uneven * np.round(fraction * np.log(frequency / ref) / np.log(G)).astype('int16')).astype('int16')
def _nominal_center_frequency(center, fraction):
"""Nominal frequency according to standard.
:param center: Exact mid-frequency to be rounded.
:param fraction: Bandwidth designator or fraction.
"""
def _roundn(x, n):
return round(x, -int(np.floor(np.sign(x) * np.log10(abs(x)))) + n)
b = fraction
x = center
# Section E.1: 1/1-octaves
if b == 1:
n = index_of_frequency(x, b)
if -6 <= n < 5: # Correspond to indices when n=0 corresponds to 1000 Hz
return acoustics.standards.iec_61672_1_2013.NOMINAL_OCTAVE_CENTER_FREQUENCIES[n + 6]
elif n >= 5:
return 2.0 * _nominal_center_frequency(exact_center_frequency(n - 1, b), b) # WARNING: Unclear in standard!
else:
return 1. / 2.0 * _nominal_center_frequency(exact_center_frequency(n + 1, b),
b) # WARNING: Unclear in standard!
# Section E.2: 1/2-octaves
elif b == 2:
return _roundn(x, 2)
# Section E.1: 1/3-octaves
elif b == 3:
n = index_of_frequency(x, b)
if -20 <= n < 14: # Correspond to indices when n=0 corresponds to 1000 Hz
return acoustics.standards.iec_61672_1_2013.NOMINAL_THIRD_OCTAVE_CENTER_FREQUENCIES[n + 20]
elif n >= 14:
return 10. * _nominal_center_frequency(exact_center_frequency(n - 10, b),
b) # WARNING: Unclear in standard!
else:
return 1. / 10. * _nominal_center_frequency(exact_center_frequency(n + 10, b),
b) # WARNING: Unclear in standard!
# Section E3.3: 1/4 to 1/24-octaves, inclusive
elif 4 <= b <= 24:
msd = x // 10.0**np.floor(np.log10(x))
if msd < 5:
return _roundn(x, 2) # E3.2
else:
return _roundn(x, 1) # E3.3
# Section E3.5: > 1/24-octaves
elif b > 24:
raise NotImplementedError("b > 24 is not implemented")
else:
raise ValueError("Wrong value for b")
nominal_center_frequency = np.vectorize(_nominal_center_frequency)
"""Nominal center frequency.
:param center: Exact center frequency.
:param fraction: Band designator or fraction.
"""