<commit_before>"""
Module contains various generator functions:

`awgn`
   Generate additive White Gaussian noise
`gaussGrid`
   Gauss-Legendre quadrature grid and weights
`lebedev`
   Lebedev quadrature grid and weigths
`mf`
   Modal Radial Filter
`swg`
   Sampled Wave Generator, emulating discrete sampling
`wgc`
   Wave Generator, returns spatial Fourier coefficients
"""
import numpy as _np
from .sph import bn, bn_npf, sphankel, sph_harm, cart2sph, sph2cart
from .process import itc
from .utils import progress_bar

pi = _np.pi


def awgn(fftData, noiseLevel=80, printInfo=True):
    '''Adds White Gaussian Noise of approx. 16dB crest to a FFT block.

    Parameters
    ----------
    fftData : array of complex floats
       Input fftData block (e.g. from F/D/T or S/W/G)
    noiseLevel : int, optional
       Average noise Level in dB [Default: -80dB]
    printInfo : bool, optional
       Toggle print statements [Default: True]

    Returns
    -------
    noisyData : array of complex floats
       Output fftData block including white gaussian noise
    '''
    if printInfo:
        print('SOFiA A/W/G/N - Additive White Gaussian Noise Generator')

    dimFactor = 10**(noiseLevel / 20)
    fftData = _np.atleast_2d(fftData)
    channels = fftData.shape[0]
    NFFT = fftData.shape[1] * 2 - 2
    nNoise = _np.random.rand(channels, NFFT)
    nNoise = dimFactor * nNoise / _np.mean(_np.abs(nNoise))
    nNoiseSpectrum = _np.fft.rfft(nNoise, axis=1)

    return fftData + nNoiseSpectrum


def gaussGrid(AZnodes=10, ELnodes=5, plot=False):
    '''Compute Gauss-Legendre quadrature nodes and weigths in the SOFiA/VariSphear data format.

    Parameters
    ----------
    AZnodes, ELnodes : int, optional
       Number of azimutal / elevation nodes  [Default: 10 / 5]
    plot : bool, optional
        Show a globe plot of the selected grid [Default: False]

    Returns
    -------
    gridData : matrix of floats
       Gauss-Legendre quadrature positions and weigths
       ::
          [AZ_0, EL_0, W_0
               ...
          AZ_n, EL_n, W_n]
    Npoints : int
       Total number of nodes
    Nmax : int
       Highest stable grid order
    '''

    # Azimuth: Gauss
    AZ = _np.linspace(0, AZnodes - 1, AZnodes) * 2 * pi / AZnodes
    AZw = _np.ones(AZnodes) * 2 * pi / AZnodes

    # Elevation: Legendre
    EL, ELw = _np.polynomial.legendre.leggauss(ELnodes)
    EL = _np.arccos(EL)

    # Weights
    W = _np.outer(AZw, ELw) / 3
    W /= W.sum()

    # VariSphere order: AZ increasing, EL alternating
    gridData = _np.empty((ELnodes * AZnodes, 3))
    for k in range(0, AZnodes):
        curIDX = k * ELnodes
        gridData[curIDX:curIDX + ELnodes, 0] = AZ[k].repeat(ELnodes)
        gridData[curIDX:curIDX + ELnodes, 1] = EL[::-1 + k % 2 * 2]  # flip EL every second iteration
        gridData[curIDX:curIDX + ELnodes, 2] = W[k][::-1 + k % 2 * 2]  # flip W every second iteration

    return gridData


def lebedev(degree, plot=False, printInfo=True):
    '''Compute Lebedev quadrature nodes and weigths.

    Parameters
    ----------
    Degree : int
       Lebedev Degree. Currently available: 6, 14, 26, 38, 50, 74, 86, 110, 146, 170, 194
    plot : bool, optional
       Plot selected Lebedev grid [Default: False]

    Returns
    -------
    gridData : array_like
       Lebedev quadrature positions and weigths: [AZ, EL, W]
    Nmax : int
       Highest stable grid order
    '''
    from sofia import lebedev

    if printInfo:
        print('SOFiA Lebedev Grid')

    deg_avail = _np.array([6, 14, 26, 38, 50, 74, 86, 110, 146, 170, 194])

    if degree not in deg_avail:
        raise ValueError('WARNING: Invalid quadrature degree', degree, '[deg] supplied. Choose one of the following:\n', deg_avail)

    leb = lebedev.genGrid(degree)
    theta, phi, _ = cart2sph(leb.x, leb.y, leb.z)
    theta = theta % (2 * pi)
    gridData = _np.array([theta, phi + pi / 2, leb.w]).T
    gridData = gridData[gridData[:, 1].argsort()]  # ugly double sorting that keeps rows together
    gridData = gridData[gridData[:, 0].argsort()]

    # TODO: turnover
    Nmax = _np.floor(_np.sqrt(degree / 1.3) - 1)

    if plot:
        import matplotlib.pyplot as plt
        from mpl_toolkits.mplot3d import Axes3D

        fig = plt.figure()
        ax = fig.gca(projection='3d')
        ax.set_zlim(-1.01, 1.01)

        # Create sphere
        u = _np.linspace(0, 2 * pi, 20)
        v = _np.linspace(0, pi, 20)

        X = _np.outer(_np.cos(u), _np.sin(v))
        Y = _np.outer(_np.sin(u), _np.sin(v))
        Z = _np.outer(_np.ones(_np.size(u)), _np.cos(v))

        ax.plot_surface(X, Y, Z, rstride=1, cstride=1, linewidth=1,
                        color='gray', alpha=0.6, antialiased=True)

        # Scatter points
        ax.scatter(leb.x, leb.y, leb.z, color='black', s=50)

        # Finish up
        plt.title('Lebedev configuration with ' + str(degree) + ' degrees')
        ax.set_xlabel('X')
        ax.set_ylabel('Y')
        ax.set_zlabel('Z')
        plt.show()

    return gridData, Nmax


def mf(N, kr, ac, amp_maxdB=0, plc=0, fadeover=0, printInfo=True):
    """Generate modal radial filters

    Parameters
    ----------
    N : int
       Maximum Order
    kr : array_like
       Vector or Matrix of kr values
       ::
          First Row  (M=1) N: kr values microphone radius
          Second Row (M=2) N: kr values sphere/microphone radius
          [kr_mic;kr_sphere] for rigid/dual sphere configurations
          ! If only one kr-vector is given using a rigid/dual sphere
          Configuration: kr_sphere = kr_mic
    ac : int {0, 1, 2, 3, 4}
       Array configuration<commit_msg>remove old dep and internal lebedev<commit_after>"""
Module contains various generator functions:

`awgn`
   Generate additive White Gaussian noise
`gaussGrid`
   Gauss-Legendre quadrature grid and weights
`lebedev`
   Lebedev quadrature grid and weigths
`mf`
   Modal Radial Filter
`swg`
   Sampled Wave Generator, emulating discrete sampling
`wgc`
   Wave Generator, returns spatial Fourier coefficients
"""
import numpy as _np
from .sph import bn, bn_npf, sphankel, sph_harm, cart2sph, sph2cart
from .process import itc
from .utils import progress_bar

pi = _np.pi


def awgn(fftData, noiseLevel=80, printInfo=True):
    '''Adds White Gaussian Noise of approx. 16dB crest to a FFT block.

    Parameters
    ----------
    fftData : array of complex floats
       Input fftData block (e.g. from F/D/T or S/W/G)
    noiseLevel : int, optional
       Average noise Level in dB [Default: -80dB]
    printInfo : bool, optional
       Toggle print statements [Default: True]

    Returns
    -------
    noisyData : array of complex floats
       Output fftData block including white gaussian noise
    '''
    if printInfo:
        print('SOFiA A/W/G/N - Additive White Gaussian Noise Generator')

    dimFactor = 10**(noiseLevel / 20)
    fftData = _np.atleast_2d(fftData)
    channels = fftData.shape[0]
    NFFT = fftData.shape[1] * 2 - 2
    nNoise = _np.random.rand(channels, NFFT)
    nNoise = dimFactor * nNoise / _np.mean(_np.abs(nNoise))
    nNoiseSpectrum = _np.fft.rfft(nNoise, axis=1)

    return fftData + nNoiseSpectrum


def gaussGrid(AZnodes=10, ELnodes=5, plot=False):
    '''Compute Gauss-Legendre quadrature nodes and weigths in the SOFiA/VariSphear data format.

    Parameters
    ----------
    AZnodes, ELnodes : int, optional
       Number of azimutal / elevation nodes  [Default: 10 / 5]
    plot : bool, optional
        Show a globe plot of the selected grid [Default: False]

    Returns
    -------
    gridData : matrix of floats
       Gauss-Legendre quadrature positions and weigths
       ::
          [AZ_0, EL_0, W_0
               ...
          AZ_n, EL_n, W_n]
    Npoints : int
       Total number of nodes
    Nmax : int
       Highest stable grid order
    '''

    # Azimuth: Gauss
    AZ = _np.linspace(0, AZnodes - 1, AZnodes) * 2 * pi / AZnodes
    AZw = _np.ones(AZnodes) * 2 * pi / AZnodes

    # Elevation: Legendre
    EL, ELw = _np.polynomial.legendre.leggauss(ELnodes)
    EL = _np.arccos(EL)

    # Weights
    W = _np.outer(AZw, ELw) / 3
    W /= W.sum()

    # VariSphere order: AZ increasing, EL alternating
    gridData = _np.empty((ELnodes * AZnodes, 3))
    for k in range(0, AZnodes):
        curIDX = k * ELnodes
        gridData[curIDX:curIDX + ELnodes, 0] = AZ[k].repeat(ELnodes)
        gridData[curIDX:curIDX + ELnodes, 1] = EL[::-1 + k % 2 * 2]  # flip EL every second iteration
        gridData[curIDX:curIDX + ELnodes, 2] = W[k][::-1 + k % 2 * 2]  # flip W every second iteration

    return gridData


def lebedev(degree, printInfo=True):
    '''Compute Lebedev quadrature nodes and weigths.

    Parameters
    ----------
    Degree : int
       Lebedev Degree. Currently available: 6, 14, 26, 38, 50, 74, 86, 110, 146, 170, 194
    plot : bool, optional
       Plot selected Lebedev grid [Default: False]

    Returns
    -------
    gridData : array_like
       Lebedev quadrature positions and weigths: [AZ, EL, W]
    Nmax : int
       Highest stable grid order

    '''
    from . import lebedev

    if printInfo:
        print('SOFiA Lebedev Grid')

    deg_avail = _np.array([6, 14, 26, 38, 50, 74, 86, 110, 146, 170, 194])

    if degree not in deg_avail:
        raise ValueError('WARNING: Invalid quadrature degree', degree, '[deg] supplied. Choose one of the following:\n', deg_avail)

    leb = lebedev.genGrid(degree)
    theta, phi, _ = cart2sph(leb.x, leb.y, leb.z)
    theta = theta % (2 * pi)
    gridData = _np.array([theta, phi + pi / 2, leb.w]).T
    gridData = gridData[gridData[:, 1].argsort()]  # ugly double sorting that keeps rows together
    gridData = gridData[gridData[:, 0].argsort()]

    # TODO: turnover
    Nmax = _np.floor(_np.sqrt(degree / 1.3) - 1)

    return gridData, Nmax


def mf(N, kr, ac, amp_maxdB=0, plc=0, fadeover=0, printInfo=True):
    """Generate modal radial filters

    Parameters
    ----------
    N : int
       Maximum Order
    kr : array_like
       Vector or Matrix of kr values
       ::
          First Row  (M=1) N: kr values microphone radius
          Second Row (M=2) N: kr values sphere/microphone radius
          [kr_mic;kr_sphere] for rigid/dual sphere configurations
          ! If only one kr-vector is given using a rigid/dual sphere
          Configuration: kr_sphere = kr_mic
    ac : int {0, 1, 2, 3, 4}
       Array configuration
{'fraction_non_alphanumeric': 0.11262602016322612, 'fraction_numerical': 0.023523763802208355, 'mean_word_length': 2.443305785123967, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3598343', 'n_tokens_mistral': 3845, 'n_tokens_neox': 3506, 'n_words': 1077}