<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 This code example is to animate the 2D Ambisonic panning functions interactively
Acoustic Holography and Holophony
Franz Zotter, 2016
This animation is about what discretizing the Ambisonic panning function on the circle does to the rE and rV measures.
Some function definitions and headers first.
Step1:  Let's take as an example $\mathrm{L}=7$ regularly spaced loudspeakers and the basic, rectangular weighting for panning on the horizon as an example for $5^\mathrm{th}$-order Ambisonic. As this is a $t=6$-design, it should already be able to perfectly control the $\boldsymbol{r}_\mathrm{V}$ vector.
Step2:  Taking $\mathrm{L}=12$, a $t=11$-design is already able to perfectly control the $\boldsymbol{r}_\mathrm{E}$ vector as well for $5^\mathrm{th}$ order basic-weighted panning.
Step3:  It also worlks for other weightings automatically. Let's for instance inspect the example for $\mathrm{max}-\boldsymbol{r}_\mathrm{E}$.
<ASSISTANT_TASK:>
Python Code:
import numpy as np
import scipy as sp
import math
from bokeh.plotting import figure, output_file, show
from bokeh.io import output_notebook
def inphase_weights(N):
    a=np.ones(N+1)
    for n in range(1,N+1):
        a[n]=(N-n+1)/(1.0*(N+n))*a[n-1]
    return a
def maxre_weights(N):
    m=np.arange(0,N+1)
    a=np.cos(np.pi/(2*(N+1))*m)
    return a
def basic_weights(N):
    a=np.ones(N+1)
    return a
def weighted_cosine_series(phi,a):
    N=a.size-1
    g=np.zeros(phi.size)
    amplitude=0;
    for m in range(0,N+1):
        g+=np.cos(m*phi)*a[m]*(2-(m==0))
        amplitude+=a[m]*(2-(m==0))
    return g/amplitude
def rvector(gls,phils):
    glsc=np.copy(gls)
    glsc=np.dot(np.diag(1/np.sum(glsc,1)),glsc)
    r=np.array([np.dot(glsc,np.cos(phils)),np.dot(glsc,np.sin(phils))])
    return r
def get_ambipan_loudspeaker_gains(N,phis,phils,weight_type):
    g=np.zeros(phils.size)
    if weight_type == 'in-phase':
        a=inphase_weights(N)
    elif weight_type == 'max-rE':
        a=maxre_weights(N)
    else:
        a=basic_weights(N)
    g=weighted_cosine_series(phils-phis,a)
    return g
Explanation: This code example is to animate the 2D Ambisonic panning functions interactively
Acoustic Holography and Holophony
Franz Zotter, 2016
This animation is about what discretizing the Ambisonic panning function on the circle does to the rE and rV measures.
Some function definitions and headers first.
End of explanation
L=7
Npt=200
phils=np.mod((2*np.pi*np.arange(0,L))/L+np.pi,2*np.pi)-np.pi
phis=np.linspace(-np.pi*0.99,np.pi,Npt)
gls=np.zeros((Npt,L))
for n in range(0,Npt):
    gls[n,:]=get_ambipan_loudspeaker_gains(5,phis[n],phils,'basic')
output_notebook()
p1 = figure(title="rE/rV directions of 5th order basic Ambi panning on 7 loudspeaeker",plot_width=600, plot_height=270, x_range=(-180,180), y_range=(-180.0,180.0))
p2 = figure(title="rE/rV widths of 5th order basic Ambi panning on 7 loudspeaeker",plot_width=600, plot_height=270, x_range=(-180,180),y_range=(-3,100))
rE=rvector(gls**2,phils)
dirE=np.arctan2(rE[1],rE[0])*180/np.pi;
lenE=np.sqrt(np.sum(rE**2,0))
sigmaE=2*np.arccos(lenE)*180/np.pi
p1.line(phis*180/np.pi, dirE, color="red",line_width=3,legend_label="rE basic")
p2.line(phis*180/np.pi, sigmaE, color="red",line_width=3,legend_label="rE basic")
rV=rvector(gls,phils)
dirV=np.arctan2(rV[1],rV[0])*180/np.pi;
lenV=np.sqrt(np.sum(rV**2,0))
sigmaV=2*np.arccos(lenV)*180/np.pi
p1.line(phis*180/np.pi, dirV, color="blue",line_width=3,legend_label="rV basic",line_dash=(6,2))
p2.line(phis*180/np.pi, sigmaV, color="blue",line_width=3,legend_label="rV basic",line_dash=(6,2))
p1.legend.background_fill_alpha = 0.5
p2.legend.background_fill_alpha = 0.5
show(p1)
show(p2)
Explanation: Let's take as an example $\mathrm{L}=7$ regularly spaced loudspeakers and the basic, rectangular weighting for panning on the horizon as an example for $5^\mathrm{th}$-order Ambisonic. As this is a $t=6$-design, it should already be able to perfectly control the $\boldsymbol{r}_\mathrm{V}$ vector.
End of explanation
L=12
Npt=200
phils=np.mod((2*np.pi*np.arange(0,L))/L+np.pi,2*np.pi)-np.pi
phis=np.linspace(-np.pi*0.99,np.pi,Npt)
gls=np.zeros((Npt,L))
for n in range(0,Npt):
    gls[n,:]=get_ambipan_loudspeaker_gains(5,phis[n],phils,'basic')
output_notebook()
p3 = figure(title="rE/rV directions of 5th order basic Ambi panning on 12 loudspeaeker",plot_width=600, plot_height=270, x_range=(-180,180), y_range=(-180.0,180.0))
p4 = figure(title="rE/rV widths of 5th order basic Ambi panning on 12 loudspeaeker",plot_width=600, plot_height=270, x_range=(-180,180),y_range=(-3,100))
rE=rvector(gls**2,phils)
dirE=np.arctan2(rE[1],rE[0])*180/np.pi;
lenE=np.sqrt(np.sum(rE**2,0))
sigmaE=2*np.arccos(lenE)*180/np.pi
p3.line(phis*180/np.pi, dirE, color="red",line_width=3,legend_label="rE basic")
p4.line(phis*180/np.pi, sigmaE, color="red",line_width=3,legend_label="rE basic")
rV=rvector(gls,phils)
dirV=np.arctan2(rV[1],rV[0])*180/np.pi;
lenV=np.sqrt(np.sum(rV**2,0))
sigmaV=2*np.arccos(lenV)*180/np.pi
p3.line(phis*180/np.pi, dirV, color="blue",line_width=3,legend_label="rV basic",line_dash=(6,2))
p4.line(phis*180/np.pi, sigmaV, color="blue",line_width=3,legend_label="rV basic",line_dash=(6,2))
p3.legend.background_fill_alpha = 0.5
p4.legend.background_fill_alpha = 0.5
show(p3)
show(p4)
Explanation: Taking $\mathrm{L}=12$, a $t=11$-design is already able to perfectly control the $\boldsymbol{r}_\mathrm{E}$ vector as well for $5^\mathrm{th}$ order basic-weighted panning.
End of explanation
L=12
Npt=200
phils=np.mod((2*np.pi*np.arange(0,L))/L+np.pi,2*np.pi)-np.pi
phis=np.linspace(-np.pi*0.99,np.pi,Npt)
gls=np.zeros((Npt,L))
for n in range(0,Npt):
    gls[n,:]=get_ambipan_loudspeaker_gains(5,phis[n],phils,'max-rE')
output_notebook()
p5 = figure(title="rE/rV directions of 5th order max-rE Ambi panning on 12 loudspeaeker",plot_width=600, plot_height=270, x_range=(-180,180), y_range=(-180.0,180.0))
p6 = figure(title="rE/rV widths of 5th order max-rE Ambi panning on 12 loudspeaeker",plot_width=600, plot_height=270, x_range=(-180,180),y_range=(-3,100))
rE=rvector(gls**2,phils)
dirE=np.arctan2(rE[1],rE[0])*180/np.pi;
lenE=np.sqrt(np.sum(rE**2,0))
sigmaE=2*np.arccos(lenE)*180/np.pi
p5.line(phis*180/np.pi, dirE, color="red",line_width=3,legend_label="rE basic")
p6.line(phis*180/np.pi, sigmaE, color="red",line_width=3,legend_label="rE basic")
rV=rvector(gls,phils)
dirV=np.arctan2(rV[1],rV[0])*180/np.pi;
lenV=np.sqrt(np.sum(rV**2,0))
sigmaV=2*np.arccos(lenV)*180/np.pi
p5.line(phis*180/np.pi, dirV, color="blue",line_width=3,legend_label="rV basic",line_dash=(6,2))
p6.line(phis*180/np.pi, sigmaV, color="blue",line_width=3,legend_label="rV basic",line_dash=(6,2))
p5.legend.background_fill_alpha = 0.5
p6.legend.background_fill_alpha = 0.5
show(p5)
show(p6)
Explanation: It also worlks for other weightings automatically. Let's for instance inspect the example for $\mathrm{max}-\boldsymbol{r}_\mathrm{E}$.
End of explanation
<END_TASK>