//  [python]
def ssl_SHmethod(MicrophoneArray, Theta_l, Phi_l, method, sphere_config, plot_method):
    """
    This script is stored some classical SSL algorithms for source localization
    :param MicrophoneArray:  The Microphone array properties
    :param Theta_l:  The elevation of the source
    :param Phi_l:  The azimuth of the source
    :param method:  The chosen algorithm
    :param sphere_config: The configuration of sphere
    :param plot_method: The method of plotting (2D or 3D)
    :return:  A figure
    """
    radius = MicrophoneArray._radius
    c = 343       # Velocity of sound
    num_mic = MicrophoneArray._numelements
    ka = 4.496   # k = 2* np.pi * f / c      # if f > 2607, N_order = 4
    N = 4
    Mic_Theta = MicrophoneArray._thetas
    Mic_R = MicrophoneArray._radius
    Mic_Phi = MicrophoneArray._phis

    # Calculate bn(ka)
    if sphere_config == "rigid":
        bn = np.zeros(51, dtype=complex)
        for n in range(51):
            jn_ka = s.spherical_jn(n, ka)
            jn_ka_der = s.spherical_jn(n, ka, derivative=True)
            yn_ka = s.spherical_yn(n, ka)
            yn_ka_der = s.spherical_yn(n, ka, derivative=True)

            # Compute the second kind of Hankel function and its derivative
            hn2_ka = jn_ka - 1j * yn_ka
            hn2_ka_der = jn_ka_der - 1j * yn_ka_der

            bn[n] = 4 * np.pi * (1j) ** n * (jn_ka - jn_ka_der / hn2_ka_der * hn2_ka)

    if sphere_config == "open":
        bn = np.zeros(51, dtype=complex)
        for n in range(51):
            bn[n] = 4 * np.pi * (1j) ** n * s.spherical_jn(n, ka)
    # bn = calc_bn(ka, 51)
    # Calculate microphone pressures for a single frequency
    p = np.zeros((num_mic, 1), dtype=complex)
    for m in range(num_mic):
        for n in range(51):
            Bn = np.eye(2 * n + 1, dtype=complex) * bn[n]
            p[m] += SphHarmonic(n, Theta_l, Phi_l).conj().T @ Bn @ SphHarmonic(n, Mic_Theta[m], Mic_Phi[m])

    # Add noise
    p = addNoise(p, 20)

    # Calculate spherical harmonics
    Y_nm = np.zeros(((N + 1) ** 2, num_mic), dtype=complex)
    for num in range(num_mic):
        for n in range(N + 1):
            Y_nm[n ** 2:(n + 1) ** 2, num] = SphHarmonic(n, Mic_Theta[num], Mic_Phi[num])

    # Transform signal from frequency domain to spherical harmonic domain
    p_nm = 4 * np.pi / num_mic * Y_nm.conj() @ p

    theta = np.arange(0, np.pi, 1 / 180 * np.pi)
    phi = np.arange(0, 2 * np.pi, 1 / 180 * np.pi)
    if method == "PWD":
        Out = np.zeros((len(theta), len(phi)), dtype=complex)
        B_n = np.zeros(((N + 1) ** 2), dtype=complex)
        D_nm = np.zeros(((N + 1) ** 2, 1), dtype=complex)
        for num1 in range(len(theta)):
            for num2 in range(len(phi)):
                for n in range(N + 1):
                    B_n[n ** 2:(n + 1) ** 2] = 1 / bn[n]
                    D_nm[n ** 2:(n + 1) ** 2, 0] = SphHarmonic(n, theta[num1], phi[num2])
                Out[num1, num2] = D_nm.T @ np.diag(B_n) @ p_nm

        # Convert to dB and clip values below -22 dB
        out = 20 * np.log10(np.abs(Out))
        out = out - np.max(out)
        x, y = np.where(out == np.max(out))
        out = np.clip(out, -22, None)
        source_pos = np.array([x, y])

    if method == 'SHMVDR':
        resolution = 1
        sphCOV = np.dot(p_nm, p_nm.T.conj())
        lambda_reg = 1e-2  #  Regularization factors,
        sphCOV += lambda_reg * np.eye(sphCOV.shape[0])
        # print(sphCOV.shape)
        out = []
        P_mvdr = np.zeros((len(theta), len(phi)), dtype=complex)  # MUSIC specturm
        for num1 in range(len(theta)):
            for num2 in range(len(phi)):
                U_nm = np.zeros(((N + 1) ** 2), dtype=complex)
                for n in range(N + 1):
                    U_nm[n ** 2:(n + 1) ** 2] = bn[n] * SphHarmonic(n, theta[num1], phi[num2]).conj()
                U_nm = U_nm[:, np.newaxis]
                invA_b = np.linalg.solve(sphCOV, U_nm)
                b_invA_b = np.dot(U_nm.T.conj(), invA_b)
                w_mvdr = invA_b / b_invA_b
                P_mvdr[num1, num2] = w_mvdr.T.conj() @ sphCOV @ w_mvdr

        # Convert to dB and clip values below -22 dB
        out = 10 * np.log10(np.abs(P_mvdr))
        out = out - np.max(out)
        x, y = np.where(out == np.max(out))
        source_pos = np.array([x,y])

    if method == "DAS":
        Out = np.zeros((len(theta), len(phi)), dtype=complex)
        B_n = np.zeros(((N + 1) ** 2), dtype=complex)
        D_nm = np.zeros(((N + 1) ** 2, 1), dtype=complex)
        for num1 in range(len(theta)):
            for num2 in range(len(phi)):
                for n in range(N + 1):
                    D_nm[n ** 2:(n + 1) ** 2, 0] = bn[n] * np.conj(SphHarmonic(n, theta[num1], phi[num2]))
                Out[num1, num2] = D_nm.T.conj() @ p_nm

        # Convert to dB and clip values below -22 dB
        out = 20 * np.log10(np.abs(Out))
        out = out - np.max(out)
        x, y = np.where(out == np.max(out))
        out = np.clip(out, -17, None)
        source_pos = np.array([x, y])

    if method == "MUSIC":
        P_music = np.zeros((len(theta), len(phi)), dtype=complex)  # MUSIC spectrogram
        Bn = np.zeros(((N + 1) ** 2, 1), dtype=complex)
        for n in range(N + 1):
            Bn[n ** 2:(n + 1) ** 2] = bn[n]

        L = 1  # The number of sound source
        a_nm = p_nm / Bn
        S = np.dot(a_nm, a_nm.T.conj())

        # Calculate the eigen value and eigen vector
        D, V = np.linalg.eigh(S)

        # Sort the eigenvalue and get the index
        I = np.argsort(D)
        Y = np.diag(D)[I]

        # Calculate the noise subspace
        E = V[:, I[:-L]]
        # Setup output grid

        for num1 in range(len(theta)):
            for num2 in range(len(phi)):
                y_nm = np.zeros(((N + 1) ** 2), dtype=complex)
                for n in range(N + 1):
                    y_nm[n ** 2:(n + 1) ** 2] = SphHarmonic(n, theta[num1], phi[num2]).T
                P_music[num1, num2] = 1 / (y_nm @ E @ E.T.conj() @ y_nm.T.conj())

        # Convert to dB and clip values below -22 dB
        out = 10 * np.log10(np.abs(P_music))
        out = out - np.max(out)
        x, y = np.where(out == np.max(out))
        source_pos = np.array([x, y])

    if method == "SHMLE":

        Out = np.zeros((len(theta), len(phi)), dtype=complex)

        for num1 in range(len(theta)):
            for num2 in range(len(phi)):
                D_nm = np.zeros(((N + 1) ** 2), dtype=complex)
                for n in range(N + 1):
                    D_nm[n ** 2:(n + 1) ** 2] = bn[n] * SphHarmonic(n, theta[num1], phi[num2]).conj()
                D_nm = D_nm[:, np.newaxis]
                Out[num1, num2] = np.linalg.norm(p_nm - D_nm @ np.linalg.pinv(D_nm) @ p_nm)

        # Convert to dB and clip values below -22 dB
        out = -20 * np.log10(np.abs(Out))
        out = out - np.max(out)
        x, y = np.where(out == np.max(out))
        source_pos = np.array([x,y])

    print("source_pos:", source_pos)
    plot_SSL_results(out, [Theta_l], [Phi_l], plot_method, method,"unit",sphere_config, vmin_value=None, vmax_value=None, source_est=source_pos.T)
    return out, source_pos