コード例 #1
0
def test_time_window_interval_order_error():
    """ Test errors for incorrect order of values in interval."""
    sig = pyfar.Signal(np.ones(10), 2)
    with pytest.raises(ValueError, match='ascending'):
        dsp.time_window(sig, interval=[2, 1])
    with pytest.raises(ValueError, match='ascending'):
        dsp.time_window(sig, interval=[1, 2, 3, 0])
コード例 #2
0
def test_time_window_interval_four_values():
    """ Test time_window with four values given in interval."""
    sig = pyfar.Signal(np.ones(9), 1)
    sig_win = dsp.time_window(sig,
                              window='triang',
                              interval=[1, 3, 6, 7],
                              crop='none')
    time_win = np.array([[0, 0.25, 0.75, 1, 1, 1, 1, 0.5, 0]])
    npt.assert_allclose(sig_win.time, time_win)
    sig = pyfar.Signal(np.ones(10), 1)
    sig_win = dsp.time_window(sig,
                              window='triang',
                              interval=[1, 3, 6, 7],
                              crop='none')
    time_win = np.array([[0, 0.25, 0.75, 1, 1, 1, 1, 0.5, 0, 0]])
    npt.assert_allclose(sig_win.time, time_win)
コード例 #3
0
def test_time_window_interval_unit_error():
    """ Test errors for incorrect boundaries in combinations with unit."""
    sig = pyfar.Signal(np.ones(10), 2)
    with pytest.raises(ValueError, match='than signal'):
        dsp.time_window(sig, interval=[0, 11], unit='samples')
    with pytest.raises(ValueError, match='than signal'):
        dsp.time_window(sig, interval=[0, 6], unit='s')
コード例 #4
0
def test_time_window_right():
    """ Test window options right."""
    sig = pyfar.Signal(np.ones(7), 1)
    # Odd number of samples, crop='none'
    sig_win = dsp.time_window(sig,
                              window='triang',
                              interval=[2, 4],
                              shape='right',
                              crop='none')
    time_win = np.array([[1, 1, 1, 0.75, 0.25, 0, 0]])
    npt.assert_allclose(sig_win.time, time_win)
    # Even number of samples, crop='none'
    sig_win = dsp.time_window(sig,
                              window='triang',
                              interval=[2, 5],
                              shape='right',
                              crop='none')
    time_win = np.array([[1, 1, 1, 5 / 6, 3 / 6, 1 / 6, 0]])
    npt.assert_allclose(sig_win.time, time_win)
    # crop='end'
    sig_win = dsp.time_window(sig,
                              window='triang',
                              interval=[2, 5],
                              shape='right',
                              crop='end')
    time_win = np.array([[1, 1, 1, 5 / 6, 3 / 6, 1 / 6]])
    npt.assert_allclose(sig_win.time, time_win)
    # crop='window'
    sig_win = dsp.time_window(sig,
                              window='triang',
                              interval=[2, 5],
                              shape='right',
                              crop='window')
    time_win = np.array([[1, 1, 1, 5 / 6, 3 / 6, 1 / 6]])
    npt.assert_allclose(sig_win.time, time_win)
コード例 #5
0
def test_minimum_phase():
    # tests are separated since their reliability depends on the type of
    # filters. The homomorphic method works best for filters with odd numbers
    # of taps

    # method = 'hilbert'
    n_samples = 9
    filter_linphase = pyfar.Signal([0, 0, 0, 0, 1, 1, 0, 0, 0, 0], 44100)

    imp_minphase = pyfar.dsp.minimum_phase(filter_linphase,
                                           pad=False,
                                           method='hilbert',
                                           n_fft=2**18)

    ref = np.array([1, 1, 0, 0, 0], dtype=float)
    npt.assert_allclose(np.squeeze(imp_minphase.time),
                        ref,
                        rtol=1e-4,
                        atol=1e-4)

    # method = 'homomorphic'
    n_samples = 8
    imp_linphase = pyfar.signals.impulse(n_samples + 1,
                                         delay=int(n_samples / 2))

    ref = pyfar.signals.impulse(int(n_samples / 2) + 1)

    imp_minphase = pyfar.dsp.minimum_phase(imp_linphase,
                                           method='homomorphic',
                                           pad=False)
    npt.assert_allclose(imp_minphase.time, ref.time)

    # test pad length
    ref = pyfar.signals.impulse(n_samples + 1)
    imp_minphase = pyfar.dsp.minimum_phase(imp_linphase,
                                           method='homomorphic',
                                           pad=True)

    assert imp_minphase.n_samples == imp_linphase.n_samples
    npt.assert_allclose(imp_minphase.time, ref.time)

    # test error
    ref = pyfar.signals.impulse(n_samples + 1)
    imp_minphase, mag_error = pyfar.dsp.minimum_phase(
        imp_linphase, method='homomorphic', return_magnitude_ratio=True)

    npt.assert_allclose(np.squeeze(mag_error.freq),
                        np.ones(int(n_samples / 2 + 1), dtype=complex))

    # test multidim
    ref = pyfar.signals.impulse(n_samples + 1, amplitude=np.ones((2, 3)))
    imp_linphase = pyfar.signals.impulse(n_samples + 1,
                                         delay=int(n_samples / 2),
                                         amplitude=np.ones((2, 3)))
    imp_minphase = pyfar.dsp.minimum_phase(imp_linphase,
                                           method='homomorphic',
                                           pad=True)

    assert imp_minphase.n_samples == imp_linphase.n_samples
    npt.assert_allclose(imp_minphase.time, ref.time)
コード例 #6
0
def test_time_window_crop_interval():
    """ Test truncation of windowed signal to interval."""
    sig = pyfar.Signal(np.ones(10), 2)
    sig_win = dsp.time_window(sig,
                              interval=[1, 3],
                              shape='symmetric',
                              unit='samples',
                              crop='window')
    assert sig_win.n_samples == 3
    sig_win = dsp.time_window(sig,
                              interval=[0.5, 1.5],
                              shape='symmetric',
                              unit='s',
                              crop='window')
    assert sig_win.n_samples == 3
    sig_win = dsp.time_window(sig,
                              interval=[1, 3],
                              shape='left',
                              crop='window')
    assert sig_win.n_samples == 9
    sig_win = dsp.time_window(sig,
                              interval=[1, 3],
                              shape='right',
                              crop='window')
    assert sig_win.n_samples == 4
コード例 #7
0
def test_time_window_symmetric_zero():
    """ Test window option symmetric_zero."""
    sig = pyfar.Signal(np.ones(12), 2)
    sig_win = dsp.time_window(sig,
                              window='triang',
                              interval=[2, 4],
                              shape='symmetric_zero')
    time_win = np.array([[1, 1, 1, 0.75, 0.25, 0, 0, 0, 0.25, 0.75, 1, 1]])
    npt.assert_allclose(sig_win.time, time_win)
コード例 #8
0
def test_time_window_symmetric():
    """ Test window option symmetric."""
    sig = pyfar.Signal(np.ones(10), 2)
    sig_win = dsp.time_window(sig,
                              interval=[1, 5],
                              window='hann',
                              shape='symmetric',
                              crop='window')
    time_win = np.atleast_2d(sgn.windows.hann(5, sym=True))
    npt.assert_allclose(sig_win.time, time_win)
コード例 #9
0
ファイル: deterministic.py プロジェクト: pyfar-seminar/pyfar
def impulse(n_samples, delay=0, amplitude=1, sampling_rate=44100):
    """
    Generate a single or multi channel impulse signal, also known as the
    Dirac delta function.

    .. math::
        s(n) =
        \\begin{cases}
        \\text{amplitude},  & \\text{if $n$ = delay}\\\\
        0, & \\text{else}
        \\end{cases}


    Parameters
    ----------
    n_samples : int
        Length of the impulse in samples
    delay : double, array like, optional
        Delay in samples. The default is ``0``.
    amplitude : double, optional
        The peak amplitude of the impulse. The default is ``1``.
    sampling_rate : int, optional
        The sampling rate in Hz. The default is ``44100``.

    Returns
    -------
    signal : Signal
        The impulse signal. The Signal is in the time domain and has the
        ``none`` FFT normalization (see
        :py:func:`~pyfar.dsp.fft.normalization`). The delay and amplitude
        are written to `comment`.

    Notes
    -----
    The parameters `delay` and `amplitude` must be scalars and/or array likes
    of the same shape.
    """
    # check and match the cshape
    cshape = _get_common_shape(delay, amplitude)
    delay, amplitude = _match_shape(cshape, delay, amplitude)

    # generate the impulse
    n_samples = int(n_samples)
    impulse = np.zeros(cshape + (n_samples, ), dtype=np.double)
    for idx in np.ndindex(cshape):
        impulse[idx + (delay[idx], )] = amplitude[idx]

    # save to Signal
    nl = "\n"  # required as variable because f-strings cannot contain "\"
    comment = (f"Impulse signal (delay = {str(delay).replace(nl, ',')} "
               f"samples, amplitude = {str(amplitude).replace(nl, ',')})")

    signal = pyfar.Signal(impulse, sampling_rate, comment=comment)

    return signal
コード例 #10
0
def test_time_window_multichannel():
    """ Test time_window of multichannel signal."""
    time = np.array([[[1, 1, 1, 1], [2, 2, 2, 2]], [[3, 3, 3, 3], [4, 4, 4,
                                                                   4]]])
    sig = pyfar.Signal(time, 1)
    sig_win = dsp.time_window(sig,
                              window='triang',
                              interval=[1, 2],
                              shape='symmetric',
                              crop='window')
    time_win = np.array([[[0.5, 0.5], [1, 1]], [[1.5, 1.5], [2, 2]]])
    npt.assert_allclose(sig_win.time, time_win)
コード例 #11
0
def test_zero_phase():
    """Test zero phase generation."""
    # generate test signal and zero phase version
    signal = pf.Signal([0, 0, 0, 2], 44100)
    signal_zero = dsp.zero_phase(signal)
    # assert type and id
    assert isinstance(signal_zero, pf.Signal)
    assert id(signal) != id(signal_zero)
    # assert freq data
    assert np.any(np.abs(np.imag(signal.freq)) > 1e-15)
    assert np.all(np.abs(np.imag(signal_zero.freq)) == 0)
    # assert time data
    npt.assert_allclose(signal_zero.time, np.atleast_2d([2, 0, 0, 0]))
コード例 #12
0
def test_time_window_crop_end():
    """ Test crop option 'end'."""
    sig = pyfar.Signal(np.ones(10), 2)
    sig_win = dsp.time_window(sig,
                              interval=[1, 3],
                              shape='symmetric',
                              unit='samples',
                              crop='end')
    assert sig_win.n_samples == 4
    sig_win = dsp.time_window(sig,
                              interval=[0.5, 1.5],
                              shape='symmetric',
                              unit='s',
                              crop='end')
    assert sig_win.n_samples == 4
    sig_win = dsp.time_window(sig, interval=[1, 3], shape='left', crop='end')
    assert sig_win.n_samples == 10
    sig_win = dsp.time_window(sig, interval=[1, 3], shape='right', crop='end')
    assert sig_win.n_samples == 4
コード例 #13
0
def test_time_window_input():
    """Test errors when calling with incorrect parameters."""
    sig = pyfar.Signal(np.ones(5), 2)
    with pytest.raises(TypeError, match='signal'):
        dsp.time_window([1., 2.], interval=(0, 4))
    with pytest.raises(ValueError, match='shape'):
        dsp.time_window(sig, interval=(0, 4), shape='top')
    with pytest.raises(TypeError, match='crop'):
        dsp.time_window(sig, interval=(0, 4), crop='t')
    with pytest.raises(ValueError, match='unit'):
        dsp.time_window(sig, interval=[0, 1], unit='kg')
    with pytest.raises(TypeError, match='interval'):
        dsp.time_window(sig, interval=1)
    with pytest.raises(ValueError, match='contain'):
        dsp.time_window(sig, interval=[1, 2, 3])
    with pytest.raises(ValueError, match='longer'):
        dsp.time_window(sig, interval=[1, 11])
    with pytest.raises(ValueError):
        dsp.time_window(sig, interval=['a', 'b'])
コード例 #14
0
ファイル: test_utils.py プロジェクト: richarddeffner/pyfar
def test_copy(sphericalvoronoi, timedata, frequencydata):
    """ Test copy method used by several classes."""
    obj_list = [
        pyfar.Signal(1000, 44100),
        pyfar.Orientations(),
        pyfar.Coordinates(),
        pyfar.dsp.Filter(), sphericalvoronoi, timedata, frequencydata
    ]

    for obj in obj_list:
        # Create copy
        obj_copy = obj.copy()
        # Check class
        assert isinstance(obj_copy, obj.__class__)
        # Check ID
        assert id(obj) != id(obj_copy)
        # Check attributes
        assert len(obj.__dict__) == len(obj_copy.__dict__)
        # Check if copied objects are equal
        assert obj_copy == obj
コード例 #15
0
ファイル: deterministic.py プロジェクト: pyfar-seminar/pyfar
def _time_domain_sweep(n_samples, frequency_range, n_fade_out, amplitude,
                       sampling_rate, sweep_type, sweep_rate=None):

    # check input
    if np.atleast_1d(frequency_range).size != 2:
        raise ValueError(
            "frequency_range must be an array like with to elements.")
    if frequency_range[1] > sampling_rate/2:
        raise ValueError(
            "Upper frequency limit is larger than half the sampling rate.")
    if frequency_range[0] == 0 and sweep_type == "exponential":
        raise ValueError("The exponential sweep can not start at 0 Hz.")

    # generate sweep
    if sweep_type == "linear":
        sweep = _linear_sweep(
            n_samples, frequency_range, amplitude, sampling_rate)
    elif sweep_type == 'exponential':
        sweep = _exponential_sweep(
            n_samples, frequency_range, amplitude, sweep_rate, sampling_rate)

    # fade out
    n_fade_out = int(n_fade_out)
    if n_fade_out > 0:
        # check must be done here because n_samples might not be defined if
        # using the sweep_rate for exponential sweeps
        if sweep.size < n_fade_out:
            raise ValueError("The sweep must be longer than n_fade_out.")

        sweep[-n_fade_out:] *= np.cos(np.linspace(0, np.pi/2, n_fade_out))**2

    # save to signal
    comment = (f"{sweep_type} sweep between {frequency_range[0]} "
               f"and {frequency_range[1]} Hz "
               f"with {n_fade_out} samples squared cosine fade-out.")
    signal = pyfar.Signal(
        sweep, sampling_rate, fft_norm="none", comment=comment)

    return signal
コード例 #16
0
ファイル: stochastic.py プロジェクト: pyfar-seminar/pyfar
def pulsed_noise(n_pulse,
                 n_pause,
                 n_fade=90,
                 repetitions=5,
                 rms=1,
                 spectrum="pink",
                 frozen=True,
                 sampling_rate=44100,
                 seed=None):
    """
    Generate single channel normally distributed pulsed white or pink noise.

    The pink noise is generated by applying a ``sqrt(1/f)`` filter to the
    spectrum.

    Parameters
    ----------
    n_pulse : int
        The length of the pulses in samples
    n_pause : int
        The length of the pauses between the pulses in samples.
    n_fade : int, optional
        The length of the squared sine/cosine fade-in and fade outs in samples.
        The default is ``90``, which equals approximately 2 ms at sampling
        rates of 44.1 and 48 kHz.
    repetitions : int, optional
        Specifies the number of noise pulses. The default is ``5``.
    rms : double, array like, optional
        The RMS amplitude of the white signal. The default is ``1``.
    spectrum: string, optional
        The noise spectrum, which can be ``pink`` or ``white``. The default is
        ``pink``.
    frozen : boolean, optional
        If ``True``, all noise pulses are identical. If ``False`` each noise
        pulse is a separate stochastic process. The default is ``True``.
    sampling_rate : int, optional
        The sampling rate in Hz. The default is ``44100``.
    seed : int, None, optional
        The seed for the random generator. Pass a seed to obtain identical
        results for multiple calls. The default is ``None``, which will yield
        different results with every call.

    Returns
    -------
    signal : Signal
        The noise signal. The Signal is in the time domain and has the ``rms``
        FFT normalization (see :py:func:`~pyfar.dsp.fft.normalization`).
        `comment` contains information about the selected parameters.
    """

    if n_pulse < 2 * n_fade:
        raise ValueError(
            "n_fade too large. It must be smaller than n_pulse/2.")

    # get the noise sample
    n_pulse = int(n_pulse)
    repetitions = int(repetitions)
    n_samples = n_pulse if frozen else n_pulse * repetitions

    p_noise = noise(n_samples, spectrum, rms, sampling_rate, seed).time
    p_noise = np.tile(p_noise, (repetitions, 1)) if frozen else \
        p_noise.reshape((repetitions, n_pulse))

    # fade the noise
    if n_fade > 0:
        n_fade = int(n_fade)
        fade = np.sin(np.linspace(0, np.pi / 2, n_fade))**2
        p_noise[..., 0:n_fade] *= fade
        p_noise[..., -n_fade:] *= fade[::-1]

    # add the pause
    p_noise = np.concatenate((p_noise, np.zeros((repetitions, int(n_pause)))),
                             -1)

    # reshape to single channel signal and discard final pause
    p_noise = p_noise.reshape((1, -1))[..., :-int(n_pause)]

    # save to Signal
    frozen_str = "frozen" if frozen else ""
    comment = (f"{frozen_str} {spectrum} pulsed noise signal (rms = {rms}, "
               f"{repetitions} repetitions, {n_pulse} samples pulse duration, "
               f"{n_pause} samples pauses, and {n_fade} samples fades.")

    signal = pyfar.Signal(p_noise,
                          sampling_rate,
                          fft_norm="rms",
                          comment=comment)

    return signal
コード例 #17
0
ファイル: stochastic.py プロジェクト: pyfar-seminar/pyfar
def noise(n_samples, spectrum="white", rms=1, sampling_rate=44100, seed=None):
    """
    Generate single or multi channel normally distributed white or pink noise.

    The pink noise is generated by applying a ``sqrt(1/f)`` filter to the
    spectrum.

    Parameters
    ----------
    n_samples : int
        The length of the signal in samples
    spectrum : str, optional
        ``white`` to generate noise with constant energy across frequency.
        ``pink`` to generate noise with constant energy across filters with
        constant relative bandwith. The default is ``white``.
    rms : double, array like, optional
        The route mean square (RMS) value of the noise signal. A multi channel
        noise signal is generated if an array of RMS values is passed.
        The default is ``1``.
    sampling_rate : int, optional
        The sampling rate in Hz. The default is ``44100``.
    seed : int, None, optional
        The seed for the random generator. Pass a seed to obtain identical
        results for multiple calls. The default is ``None``, which will yield
        different results with every call.

    Returns
    -------
    signal : Signal
        The noise signal. The signal is in the time domain and has the ``rms``
        FFT normalization (see :py:func:`~pyfar.dsp.fft.normalization`). The
        type of the spectrum (``white``, ``pink``) and the RMS amplitude are
        written to `comment`.
    """

    # generate the noise
    rms = np.atleast_1d(rms)
    n_samples = int(n_samples)
    cshape = np.atleast_1d(rms).shape
    rng = np.random.default_rng(seed)
    noise = rng.standard_normal(np.prod(cshape + (n_samples, )))
    noise = noise.reshape(cshape + (n_samples, ))

    if spectrum == "pink":
        # apply 1/f filter in the frequency domain
        noise = fft.rfft(noise, n_samples, sampling_rate, 'none')
        noise /= np.sqrt(np.arange(1, noise.shape[-1] + 1))
        noise = fft.irfft(noise, n_samples, sampling_rate, 'none')
    elif spectrum != "white":
        raise ValueError(
            f"spectrum is '{spectrum}' but must be 'white' or 'pink'")

    # level the noise
    rms_current = np.atleast_1d(np.sqrt(np.mean(noise**2, axis=-1)))
    for idx in np.ndindex(rms.shape):
        noise[idx] = noise[idx] / rms_current[idx] * rms[idx]

    # save to Signal
    nl = "\n"  # required as variable because f-strings cannot contain "\"
    comment = f"{spectrum} noise signal (rms = {str(rms).replace(nl, ',')})"

    signal = pyfar.Signal(noise,
                          sampling_rate,
                          fft_norm="rms",
                          comment=comment)

    return signal
コード例 #18
0
def test_time_window_default():
    """ Test time_window function with default values."""
    sig = pyfar.Signal(np.ones(10), 2)
    sig_win = dsp.time_window(sig, interval=(0, sig.n_samples - 1))
    time_win = np.atleast_2d(sgn.windows.hann(10, sym=True))
    npt.assert_allclose(sig_win.time, time_win)
コード例 #19
0
def test_time_window_interval_types():
    sig = pyfar.Signal(np.ones(10), 2)
    dsp.time_window(sig, interval=(1, 2))
    dsp.time_window(sig, interval=[1, 2])
    dsp.time_window(sig, interval=(1, 2, 3, 4))
    dsp.time_window(sig, interval=[1, 2, 3, 4])
コード例 #20
0
def test_time_window_crop_none():
    """ Test crop option 'none'."""
    sig = pyfar.Signal(np.ones(10), 2)
    sig_win = dsp.time_window(sig, interval=[1, 3], crop='none')
    assert sig_win.n_samples == 10
コード例 #21
0
ファイル: deterministic.py プロジェクト: pyfar-seminar/pyfar
def sine(frequency, n_samples, amplitude=1, phase=0, sampling_rate=44100,
         full_period=False):
    """Generate a single or multi channel sine signal.

    Parameters
    ----------
    frequency : double, array like
        Frequency of the sine in Hz (0 <= `frequency` <= `sampling_rate`/2).
    n_samples : int
        Length of the signal in samples.
    amplitude : double, array like, optional
        The amplitude. The default is ``1``.
    phase : double, array like, optional
        The phase in radians. The default is ``0``.
    sampling_rate : int, optional
        The sampling rate in Hz. The default is ``44100``.
    full_period : boolean, optional
        Make sure that the returned signal contains an integer number of
        periods resulting in a periodic signal. This is done by adjusting the
        frequency of the sine. The default is ``False``.

    Returns
    -------
    signal : Signal
        The sine signal. The Signal is in the time domain and has the ``rms``
        FFT normalization (see :py:func:`~pyfar.dsp.fft.normalization`).
        The exact frequency, amplitude and phase are written to `comment`.

    Notes
    -----
    The parameters `frequency`, `amplitude`, and `phase` must must be scalars
    and/or array likes of the same shape.
    """

    # check and match the cshape
    cshape = _get_common_shape(frequency, amplitude, phase)
    frequency, amplitude, phase = _match_shape(
        cshape, frequency, amplitude, phase)

    if np.any(frequency < 0) or np.any(frequency > sampling_rate/2):
        raise ValueError(
            f"The frequency must be between 0 and {sampling_rate/2} Hz")

    # generate the sine signal
    n_samples = int(n_samples)
    times = np.arange(n_samples) / sampling_rate
    sine = np.zeros(cshape + (n_samples, ))
    for idx in np.ndindex(cshape):
        if full_period:
            # nearest number of full periods
            num_periods = np.round(
                n_samples / sampling_rate * frequency[idx])
            # corresponding frequency
            frequency[idx] = num_periods * sampling_rate / n_samples

        sine[idx] = amplitude[idx] * \
            np.sin(2 * np.pi * frequency[idx] * times + phase[idx])

    # save to Signal
    nl = "\n"  # required as variable because f-strings cannot contain "\"
    comment = (f"Sine signal (f = {str(frequency).replace(nl, ',')} Hz, "
               f"amplitude = {str(amplitude).replace(nl, ',')}, "
               f"phase = {str(phase).replace(nl, ',')} rad)")

    signal = pyfar.Signal(
        sine, sampling_rate, fft_norm="rms", comment=comment)

    return signal