Exemplo n.º 1
0
def _focused(omega, x0, n0, xs, ns, *, c=None):
    r"""Driving function for 2/3-dimensional WFS for a focused source.

    Parameters
    ----------
    omega : float
        Angular frequency of focused source.
    x0 : (N, 3) array_like
        Sequence of secondary source positions.
    n0 : (N, 3) array_like
        Sequence of normal vectors of secondary sources.
    xs : (3,) array_like
        Position of focused source.
    ns :  (3,) array_like
        Direction of focused source.
    c : float, optional
        Speed of sound.

    Returns
    -------
    d : (N,) numpy.ndarray
        Complex weights of secondary sources.
    selection : (N,) numpy.ndarray
        Boolean array containing ``True`` or ``False`` depending on
        whether the corresponding secondary source is "active" or not.
    secondary_source_function : callable
        A function that can be used to create the sound field of a
        single secondary source.  See `sfs.fd.synthesize()`.

    Notes
    -----
    .. math::

        D(\x_0,\w) = \i\wc \frac{\scalarprod{\x_0-\x_\text{s}}{\n_0}}
            {|\x_0-\x_\text{s}|^\frac{3}{2}}
            \e{\i\wc |\x_0-\x_\text{s}|}

    Examples
    --------
    .. plot::
        :context: close-figs

        d, selection, secondary_source = sfs.fd.wfs.focused_3d(
            omega, array.x, array.n, xs_focused, ns_focused)
        plot(d, selection, secondary_source)

    """
    x0 = _util.asarray_of_rows(x0)
    n0 = _util.asarray_of_rows(n0)
    xs = _util.asarray_1d(xs)
    k = _util.wavenumber(omega, c)
    ds = x0 - xs
    r = _np.linalg.norm(ds, axis=1)
    d = 1j * k * _inner1d(ds, n0) / r ** (3 / 2) * _np.exp(1j * k * r)
    selection = _util.source_selection_focused(ns, x0, xs)
    return d, selection, _secondary_source_point(omega, c)
Exemplo n.º 2
0
def _focused(omega, x0, n0, xs, ns, *, c=None):
    r"""Driving function for 2/3-dimensional WFS for a focused source.

    Parameters
    ----------
    omega : float
        Angular frequency of focused source.
    x0 : (N, 3) array_like
        Sequence of secondary source positions.
    n0 : (N, 3) array_like
        Sequence of normal vectors of secondary sources.
    xs : (3,) array_like
        Position of focused source.
    ns :  (3,) array_like
        Direction of focused source.
    c : float, optional
        Speed of sound.

    Returns
    -------
    d : (N,) numpy.ndarray
        Complex weights of secondary sources.
    selection : (N,) numpy.ndarray
        Boolean array containing ``True`` or ``False`` depending on
        whether the corresponding secondary source is "active" or not.
    secondary_source_function : callable
        A function that can be used to create the sound field of a
        single secondary source.  See `sfs.fd.synthesize()`.

    Notes
    -----
    .. math::

        D(\x_0,\w) = \i\wc \frac{\scalarprod{\x_0-\x_\text{s}}{\n_0}}
            {|\x_0-\x_\text{s}|^\frac{3}{2}}
            \e{\i\wc |\x_0-\x_\text{s}|}

    Examples
    --------
    .. plot::
        :context: close-figs

        d, selection, secondary_source = sfs.fd.wfs.focused_3d(
            omega, array.x, array.n, xs_focused, ns_focused)
        plot(d, selection, secondary_source)

    """
    x0 = _util.asarray_of_rows(x0)
    n0 = _util.asarray_of_rows(n0)
    xs = _util.asarray_1d(xs)
    k = _util.wavenumber(omega, c)
    ds = x0 - xs
    r = _np.linalg.norm(ds, axis=1)
    d = 1j * k * _inner1d(ds, n0) / r**(3 / 2) * _np.exp(1j * k * r)
    selection = _util.source_selection_focused(ns, x0, xs)
    return d, selection, _secondary_source_point(omega, c)
Exemplo n.º 3
0
def plane_25d(x0, n0, n=[0, 1, 0], xref=[0, 0, 0], c=None):
    r"""Plane wave model by 2.5-dimensional WFS.

    Parameters
    ----------
    x0 : (N, 3) array_like
        Sequence of secondary source positions.
    n0 : (N, 3) array_like
        Sequence of secondary source orientations.
    n : (3,) array_like, optional
        Normal vector (propagation direction) of synthesized plane wave.
    xref : (3,) array_like, optional
        Reference position
    c : float, optional
        Speed of sound

    Returns
    -------
    delays : (N,) numpy.ndarray
        Delays of secondary sources in seconds.
    weights : (N,) numpy.ndarray
        Weights of secondary sources.
    selection : (N,) numpy.ndarray
        Boolean array containing ``True`` or ``False`` depending on
        whether the corresponding secondary source is "active" or not.
    secondary_source_function : callable
        A function that can be used to create the sound field of a
        single secondary source.  See `sfs.td.synthesize()`.

    Notes
    -----
    2.5D correction factor

    .. math::

        g_0 = \sqrt{2 \pi |x_\mathrm{ref} - x_0|}

    d using a plane wave as source model

    .. math::

        d_{2.5D}(x_0,t) = h(t)
        2 g_0 \scalarprod{n}{n_0}
        \dirac{t - \frac{1}{c} \scalarprod{n}{x_0}}

    with wfs(2.5D) prefilter h(t), which is not implemented yet.

    See :sfs:`d_wfs/#equation-td-wfs-plane-25d`

    Examples
    --------
    .. plot::
        :context: close-figs

        delays, weights, selection, secondary_source = \
            sfs.td.wfs.plane_25d(array.x, array.n, npw)
        d = sfs.td.wfs.driving_signals(delays, weights, signal)
        plot(d, selection, secondary_source)

    """
    if c is None:
        c = _default.c
    x0 = _util.asarray_of_rows(x0)
    n0 = _util.asarray_of_rows(n0)
    n = _util.normalize_vector(n)
    xref = _util.asarray_1d(xref)
    g0 = _np.sqrt(2 * _np.pi * _np.linalg.norm(xref - x0, axis=1))
    delays = _inner1d(n, x0) / c
    weights = 2 * g0 * _inner1d(n, n0)
    selection = _util.source_selection_plane(n0, n)
    return delays, weights, selection, _secondary_source_point(c)
Exemplo n.º 4
0
def focused_25d(x0, n0, xs, ns, xref=[0, 0, 0], c=None):
    r"""Point source by 2.5-dimensional WFS.

    Parameters
    ----------
    x0 : (N, 3) array_like
        Sequence of secondary source positions.
    n0 : (N, 3) array_like
        Sequence of secondary source orientations.
    xs : (3,) array_like
        Virtual source position.
    ns : (3,) array_like
        Normal vector (propagation direction) of focused source.
        This is used for secondary source selection,
        see `sfs.util.source_selection_focused()`.
    xref : (3,) array_like, optional
        Reference position
    c : float, optional
        Speed of sound

    Returns
    -------
    delays : (N,) numpy.ndarray
        Delays of secondary sources in seconds.
    weights: (N,) numpy.ndarray
        Weights of secondary sources.
    selection : (N,) numpy.ndarray
        Boolean array containing ``True`` or ``False`` depending on
        whether the corresponding secondary source is "active" or not.
    secondary_source_function : callable
        A function that can be used to create the sound field of a
        single secondary source.  See `sfs.td.synthesize()`.

    Notes
    -----
    2.5D correction factor

    .. math::

         g_0 = \sqrt{\frac{|x_\mathrm{ref} - x_0|}
         {|x_0-x_s| + |x_\mathrm{ref}-x_0|}}


    d using a point source as source model

    .. math::

         d_{2.5D}(x_0,t) = h(t)
         \frac{g_0  \scalarprod{(x_0 - x_s)}{n_0}}
         {|x_0 - x_s|^{3/2}}
         \dirac{t + \frac{|x_0 - x_s|}{c}}

    with wfs(2.5D) prefilter h(t), which is not implemented yet.

    See :sfs:`d_wfs/#equation-td-wfs-focused-25d`

    Examples
    --------
    .. plot::
        :context: close-figs

        delays, weights, selection, secondary_source = \
            sfs.td.wfs.focused_25d(array.x, array.n, xf, nf)
        d = sfs.td.wfs.driving_signals(delays, weights, signal)
        plot(d, selection, secondary_source, t=tf)

    """
    if c is None:
        c = _default.c
    x0 = _util.asarray_of_rows(x0)
    n0 = _util.asarray_of_rows(n0)
    xs = _util.asarray_1d(xs)
    xref = _util.asarray_1d(xref)
    ds = x0 - xs
    r = _np.linalg.norm(ds, axis=1)
    g0 = _np.sqrt(_np.linalg.norm(xref - x0, axis=1)
                  / (_np.linalg.norm(xref - x0, axis=1) + r))
    delays = -r/c
    weights = g0 * _inner1d(ds, n0) / (2 * _np.pi * r**(3/2))
    selection = _util.source_selection_focused(ns, x0, xs)
    return delays, weights, selection, _secondary_source_point(c)
Exemplo n.º 5
0
def point_25d_legacy(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None):
    r"""Driving function for 2.5-dimensional WFS for a virtual point source.

    .. versionadded:: 0.5
        `point_25d()` was renamed to `point_25d_legacy()` (and a new
        function with the name `point_25d()` was introduced). See notes for
        further details.

    Parameters
    ----------
    omega : float
        Angular frequency of point source.
    x0 : (N, 3) array_like
        Sequence of secondary source positions.
    n0 : (N, 3) array_like
        Sequence of normal vectors of secondary sources.
    xs : (3,) array_like
        Position of virtual point source.
    xref : (3,) array_like, optional
        Reference point for synthesized sound field.
    c : float, optional
        Speed of sound.
    omalias: float, optional
        Angular frequency where spatial aliasing becomes prominent.

    Returns
    -------
    d : (N,) numpy.ndarray
        Complex weights of secondary sources.
    selection : (N,) numpy.ndarray
        Boolean array containing ``True`` or ``False`` depending on
        whether the corresponding secondary source is "active" or not.
    secondary_source_function : callable
        A function that can be used to create the sound field of a
        single secondary source.  See `sfs.fd.synthesize()`.

    Notes
    -----
    `point_25d_legacy()` derives 2.5D WFS from the 2D
    Neumann-Rayleigh integral (i.e. the approach by Rabenstein & Spors), cf.
    :cite:`Spors2008`.

    .. math::

        D(\x_0,\w) = \sqrt{\i\wc |\x_\text{ref}-\x_0|}
            \frac{\scalarprod{\x_0-\x_\text{s}}{\n_0}}
            {|\x_0-\x_\text{s}|^\frac{3}{2}}
            \e{-\i\wc |\x_0-\x_\text{s}|}

    The theoretical link of `point_25d()` and `point_25d_legacy()` was
    introduced as *unified WFS framework* in :cite:`Firtha2017`.
    Also cf. Eq. (2.145)-(2.147) :cite:`Schultz2016`.

    Examples
    --------
    .. plot::
        :context: close-figs

        d, selection, secondary_source = sfs.fd.wfs.point_25d_legacy(
            omega, array.x, array.n, xs)
        normalize_gain = np.linalg.norm(xs)
        plot(normalize_gain * d, selection, secondary_source)

    """
    x0 = _util.asarray_of_rows(x0)
    n0 = _util.asarray_of_rows(n0)
    xs = _util.asarray_1d(xs)
    xref = _util.asarray_1d(xref)
    k = _util.wavenumber(omega, c)
    ds = x0 - xs
    r = _np.linalg.norm(ds, axis=1)
    d = (
        preeq_25d(omega, omalias, c) *
        _np.sqrt(_np.linalg.norm(xref - x0)) * _inner1d(ds, n0) /
        r ** (3 / 2) * _np.exp(-1j * k * r))
    selection = _util.source_selection_point(n0, x0, xs)
    return d, selection, _secondary_source_point(omega, c)
Exemplo n.º 6
0
def point_25d(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None):
    r"""Driving function for 2.5-dimensional WFS of a virtual point source.

    .. versionchanged:: 0.5
        see notes, old handling of `point_25d()` is now `point_25d_legacy()`

    Parameters
    ----------
    omega : float
        Angular frequency of point source.
    x0 : (N, 3) array_like
        Sequence of secondary source positions.
    n0 : (N, 3) array_like
        Sequence of normal vectors of secondary sources.
    xs : (3,) array_like
        Position of virtual point source.
    xref : (3,) array_like, optional
        Reference point xref or contour xref(x0) for amplitude correct
        synthesis.
    c : float, optional
        Speed of sound in m/s.
    omalias: float, optional
        Angular frequency where spatial aliasing becomes prominent.

    Returns
    -------
    d : (N,) numpy.ndarray
        Complex weights of secondary sources.
    selection : (N,) numpy.ndarray
        Boolean array containing ``True`` or ``False`` depending on
        whether the corresponding secondary source is "active" or not.
    secondary_source_function : callable
        A function that can be used to create the sound field of a
        single secondary source.  See `sfs.fd.synthesize()`.

    Notes
    -----
    `point_25d()` derives 2.5D WFS from the 3D
    Neumann-Rayleigh integral (i.e. the TU Delft approach).
    The eq. (3.10), (3.11) in :cite:`Start1997`, equivalent to
    Eq. (2.137) in :cite:`Schultz2016`

    .. math::

        D(\x_0,\w) = \sqrt{8 \pi \, \i\wc}
            \sqrt{\frac{|\x_\text{ref}-\x_0| \cdot
            |\x_0-\x_\text{s}|}{|\x_\text{ref}-\x_0| + |\x_0-\x_\text{s}|}}
            \scalarprod{\frac{\x_0-\x_\text{s}}{|\x_0-\x_\text{s}|}}{\n_0}
            \frac{\e{-\i\wc |\x_0-\x_\text{s}|}}{4\pi\,|\x_0-\x_\text{s}|}

    is implemented.
    The theoretical link of `point_25d()` and `point_25d_legacy()` was
    introduced as *unified WFS framework* in :cite:`Firtha2017`.

    Examples
    --------
    .. plot::
        :context: close-figs

        d, selection, secondary_source = sfs.fd.wfs.point_25d(
            omega, array.x, array.n, xs)
        normalize_gain = 4 * np.pi * np.linalg.norm(xs)
        plot(normalize_gain * d, selection, secondary_source)

    """
    x0 = _util.asarray_of_rows(x0)
    n0 = _util.asarray_of_rows(n0)
    xs = _util.asarray_1d(xs)
    xref = _util.asarray_1d(xref)
    k = _util.wavenumber(omega, c)

    ds = x0 - xs
    dr = xref - x0
    s = _np.linalg.norm(ds, axis=1)
    r = _np.linalg.norm(dr, axis=1)

    d = (
        preeq_25d(omega, omalias, c) *
        _np.sqrt(8 * _np.pi) *
        _np.sqrt((r * s) / (r + s)) *
        _inner1d(n0, ds) / s *
        _np.exp(-1j * k * s) / (4 * _np.pi * s))
    selection = _util.source_selection_point(n0, x0, xs)
    return d, selection, _secondary_source_point(omega, c)
Exemplo n.º 7
0
def point_25d_legacy(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None):
    r"""Driving function for 2.5-dimensional WFS for a virtual point source.

    .. versionadded:: 0.5
        `point_25d()` was renamed to `point_25d_legacy()` (and a new
        function with the name `point_25d()` was introduced). See notes for
        further details.

    Parameters
    ----------
    omega : float
        Angular frequency of point source.
    x0 : (N, 3) array_like
        Sequence of secondary source positions.
    n0 : (N, 3) array_like
        Sequence of normal vectors of secondary sources.
    xs : (3,) array_like
        Position of virtual point source.
    xref : (3,) array_like, optional
        Reference point for synthesized sound field.
    c : float, optional
        Speed of sound.
    omalias: float, optional
        Angular frequency where spatial aliasing becomes prominent.

    Returns
    -------
    d : (N,) numpy.ndarray
        Complex weights of secondary sources.
    selection : (N,) numpy.ndarray
        Boolean array containing ``True`` or ``False`` depending on
        whether the corresponding secondary source is "active" or not.
    secondary_source_function : callable
        A function that can be used to create the sound field of a
        single secondary source.  See `sfs.fd.synthesize()`.

    Notes
    -----
    `point_25d_legacy()` derives 2.5D WFS from the 2D
    Neumann-Rayleigh integral (i.e. the approach by Rabenstein & Spors), cf.
    :cite:`Spors2008`.

    .. math::

        D(\x_0,\w) = \sqrt{\i\wc |\x_\text{ref}-\x_0|}
            \frac{\scalarprod{\x_0-\x_\text{s}}{\n_0}}
            {|\x_0-\x_\text{s}|^\frac{3}{2}}
            \e{-\i\wc |\x_0-\x_\text{s}|}

    The theoretical link of `point_25d()` and `point_25d_legacy()` was
    introduced as *unified WFS framework* in :cite:`Firtha2017`.
    Also cf. Eq. (2.145)-(2.147) :cite:`Schultz2016`.

    Examples
    --------
    .. plot::
        :context: close-figs

        d, selection, secondary_source = sfs.fd.wfs.point_25d_legacy(
            omega, array.x, array.n, xs)
        normalize_gain = np.linalg.norm(xs)
        plot(normalize_gain * d, selection, secondary_source)

    """
    x0 = _util.asarray_of_rows(x0)
    n0 = _util.asarray_of_rows(n0)
    xs = _util.asarray_1d(xs)
    xref = _util.asarray_1d(xref)
    k = _util.wavenumber(omega, c)
    ds = x0 - xs
    r = _np.linalg.norm(ds, axis=1)
    d = (preeq_25d(omega, omalias, c) * _np.sqrt(_np.linalg.norm(xref - x0)) *
         _inner1d(ds, n0) / r**(3 / 2) * _np.exp(-1j * k * r))
    selection = _util.source_selection_point(n0, x0, xs)
    return d, selection, _secondary_source_point(omega, c)
Exemplo n.º 8
0
def point_25d(omega, x0, n0, xs, xref=[0, 0, 0], c=None, omalias=None):
    r"""Driving function for 2.5-dimensional WFS of a virtual point source.

    .. versionchanged:: 0.5
        see notes, old handling of `point_25d()` is now `point_25d_legacy()`

    Parameters
    ----------
    omega : float
        Angular frequency of point source.
    x0 : (N, 3) array_like
        Sequence of secondary source positions.
    n0 : (N, 3) array_like
        Sequence of normal vectors of secondary sources.
    xs : (3,) array_like
        Position of virtual point source.
    xref : (3,) array_like, optional
        Reference point xref or contour xref(x0) for amplitude correct
        synthesis.
    c : float, optional
        Speed of sound in m/s.
    omalias: float, optional
        Angular frequency where spatial aliasing becomes prominent.

    Returns
    -------
    d : (N,) numpy.ndarray
        Complex weights of secondary sources.
    selection : (N,) numpy.ndarray
        Boolean array containing ``True`` or ``False`` depending on
        whether the corresponding secondary source is "active" or not.
    secondary_source_function : callable
        A function that can be used to create the sound field of a
        single secondary source.  See `sfs.fd.synthesize()`.

    Notes
    -----
    `point_25d()` derives 2.5D WFS from the 3D
    Neumann-Rayleigh integral (i.e. the TU Delft approach).
    The eq. (3.10), (3.11) in :cite:`Start1997`, equivalent to
    Eq. (2.137) in :cite:`Schultz2016`

    .. math::

        D(\x_0,\w) = \sqrt{8 \pi \, \i\wc}
            \sqrt{\frac{|\x_\text{ref}-\x_0| \cdot
            |\x_0-\x_\text{s}|}{|\x_\text{ref}-\x_0| + |\x_0-\x_\text{s}|}}
            \scalarprod{\frac{\x_0-\x_\text{s}}{|\x_0-\x_\text{s}|}}{\n_0}
            \frac{\e{-\i\wc |\x_0-\x_\text{s}|}}{4\pi\,|\x_0-\x_\text{s}|}

    is implemented.
    The theoretical link of `point_25d()` and `point_25d_legacy()` was
    introduced as *unified WFS framework* in :cite:`Firtha2017`.

    Examples
    --------
    .. plot::
        :context: close-figs

        d, selection, secondary_source = sfs.fd.wfs.point_25d(
            omega, array.x, array.n, xs)
        normalize_gain = 4 * np.pi * np.linalg.norm(xs)
        plot(normalize_gain * d, selection, secondary_source)

    """
    x0 = _util.asarray_of_rows(x0)
    n0 = _util.asarray_of_rows(n0)
    xs = _util.asarray_1d(xs)
    xref = _util.asarray_1d(xref)
    k = _util.wavenumber(omega, c)

    ds = x0 - xs
    dr = xref - x0
    s = _np.linalg.norm(ds, axis=1)
    r = _np.linalg.norm(dr, axis=1)

    d = (preeq_25d(omega, omalias, c) * _np.sqrt(8 * _np.pi) * _np.sqrt(
        (r * s) / (r + s)) * _inner1d(n0, ds) / s * _np.exp(-1j * k * s) /
         (4 * _np.pi * s))
    selection = _util.source_selection_point(n0, x0, xs)
    return d, selection, _secondary_source_point(omega, c)
Exemplo n.º 9
0
def point_25d_legacy(x0, n0, xs, xref=[0, 0, 0], c=None):
    r"""Driving function for 2.5-dimensional WFS of a virtual point source.

    .. versionadded:: 0.61
        `point_25d()` was renamed to `point_25d_legacy()` (and a new
        function with the name `point_25d()` was introduced). See notes below
        for further details.

    Parameters
    ----------
    x0 : (N, 3) array_like
        Sequence of secondary source positions.
    n0 : (N, 3) array_like
        Sequence of secondary source orientations.
    xs : (3,) array_like
        Virtual source position.
    xref : (3,) array_like, optional
        Reference position
    c : float, optional
        Speed of sound

    Returns
    -------
    delays : (N,) numpy.ndarray
        Delays of secondary sources in seconds.
    weights: (N,) numpy.ndarray
        Weights of secondary sources.
    selection : (N,) numpy.ndarray
        Boolean array containing ``True`` or ``False`` depending on
        whether the corresponding secondary source is "active" or not.
    secondary_source_function : callable
        A function that can be used to create the sound field of a
        single secondary source.  See `sfs.td.synthesize()`.

    Notes
    -----
    2.5D correction factor

    .. math::

         g_0 = \sqrt{2 \pi |x_\mathrm{ref} - x_0|}


    d using a point source as source model

    .. math::

         d_{2.5D}(x_0,t) =
         \frac{g_0  \scalarprod{(x_0 - x_s)}{n_0}}
         {2\pi |x_0 - x_s|^{3/2}}
         \dirac{t - \frac{|x_0 - x_s|}{c}}  \ast_t h(t)

    with wfs(2.5D) prefilter h(t), which is not implemented yet.

    See :sfs:`d_wfs/#equation-td-wfs-point-25d`

    `point_25d_legacy()` derives 2.5D WFS from the 2D
    Neumann-Rayleigh integral (i.e. the approach by Rabenstein & Spors), cf.
    :cite:`Spors2008`.
    The theoretical link of `point_25d()` and `point_25d_legacy()` was
    introduced as *unified WFS framework* in :cite:`Firtha2017`.

    Examples
    --------
    .. plot::
        :context: close-figs

        delays, weights, selection, secondary_source = \
            sfs.td.wfs.point_25d(array.x, array.n, xs)
        d = sfs.td.wfs.driving_signals(delays, weights, signal)
        plot(d, selection, secondary_source, t=ts)

    """
    if c is None:
        c = _default.c
    x0 = _util.asarray_of_rows(x0)
    n0 = _util.asarray_of_rows(n0)
    xs = _util.asarray_1d(xs)
    xref = _util.asarray_1d(xref)
    g0 = _np.sqrt(2 * _np.pi * _np.linalg.norm(xref - x0, axis=1))
    ds = x0 - xs
    r = _np.linalg.norm(ds, axis=1)
    delays = r / c
    weights = g0 * _inner1d(ds, n0) / (2 * _np.pi * r**(3 / 2))
    selection = _util.source_selection_point(n0, x0, xs)
    return delays, weights, selection, _secondary_source_point(c)
Exemplo n.º 10
0
def point_25d(x0, n0, xs, xref=[0, 0, 0], c=None):
    r"""Driving function for 2.5-dimensional WFS of a virtual point source.

    .. versionchanged:: 0.61
        see notes, old handling of `point_25d()` is now `point_25d_legacy()`

    Parameters
    ----------
    x0 : (N, 3) array_like
        Sequence of secondary source positions.
    n0 : (N, 3) array_like
        Sequence of secondary source orientations.
    xs : (3,) array_like
        Virtual source position.
    xref : (N, 3) array_like or (3,) array_like
        Reference curve of correct amplitude xref(x0)
    c : float, optional
        Speed of sound

    Returns
    -------
    delays : (N,) numpy.ndarray
        Delays of secondary sources in seconds.
    weights: (N,) numpy.ndarray
        Weights of secondary sources.
    selection : (N,) numpy.ndarray
        Boolean array containing ``True`` or ``False`` depending on
        whether the corresponding secondary source is "active" or not.
    secondary_source_function : callable
        A function that can be used to create the sound field of a
        single secondary source.  See `sfs.td.synthesize()`.

    Notes
    -----

    Eq. (2.138) in :cite:`Schultz2016`:

    .. math::

         d_{2.5D}(x_0, x_{ref}, t) =
         \sqrt{8\pi}
         \frac{\scalarprod{(x_0 - x_s)}{n_0}}{|x_0 - x_s|}
         \sqrt{\frac{|x_0 - x_s||x_0 - x_{ref}|}{|x_0 - x_s|+|x_0 - x_{ref}|}}
         \cdot
         \frac{\dirac{t - \frac{|x_0 - x_s|}{c}}}{4\pi |x_0 - x_s|} \ast_t h(t)

    .. math::

         h(t) = F^{-1}(\sqrt{\frac{j \omega}{c}})

    with wfs(2.5D) prefilter h(t), which is not implemented yet.

    `point_25d()` derives WFS from 3D to 2.5D via the stationary phase
    approximation approach (i.e. the Delft approach).
    The theoretical link of `point_25d()` and `point_25d_legacy()` was
    introduced as *unified WFS framework* in :cite:`Firtha2017`.

    Examples
    --------
    .. plot::
        :context: close-figs

        delays, weights, selection, secondary_source = \
            sfs.td.wfs.point_25d(array.x, array.n, xs)
        d = sfs.td.wfs.driving_signals(delays, weights, signal)
        plot(d, selection, secondary_source, t=ts)

    """
    if c is None:
        c = _default.c
    x0 = _util.asarray_of_rows(x0)
    n0 = _util.asarray_of_rows(n0)
    xs = _util.asarray_1d(xs)
    xref = _util.asarray_of_rows(xref)

    x0xs = x0 - xs
    x0xref = x0 - xref
    x0xs_n = _np.linalg.norm(x0xs, axis=1)
    x0xref_n = _np.linalg.norm(x0xref, axis=1)

    g0 = 1 / (_np.sqrt(2 * _np.pi) * x0xs_n**2)
    g0 *= _np.sqrt((x0xs_n * x0xref_n) / (x0xs_n + x0xref_n))

    delays = x0xs_n / c
    weights = g0 * _inner1d(x0xs, n0)
    selection = _util.source_selection_point(n0, x0, xs)
    return delays, weights, selection, _secondary_source_point(c)