@@ -229,3 +229,171 @@ def repeat_n_m(v):
229229 """
230230 krlist = [np .tile (v , (2 * i + 1 , 1 )).T for i , v in enumerate (v .T .tolist ())]
231231 return np .squeeze (np .concatenate (krlist , axis = - 1 ))
232+
233+
234+ def circular_pw (N , k , r , setup ):
235+ r"""Radial coefficients for a plane wave.
236+
237+ Computes the radial component of the circular harmonics expansion of a
238+ plane wave impinging on a circular array.
239+
240+ .. math::
241+
242+ \mathring{P}_n(k) = i^{-n} b_n(kr)
243+
244+ Parameters
245+ ----------
246+ N : int
247+ Maximum order.
248+ k : (M,) array_like
249+ Wavenumber.
250+ r : float
251+ Radius of microphone array.
252+ setup : {'open', 'card', 'rigid'}
253+ Array configuration (open, cardioids, rigid).
254+
255+ Returns
256+ -------
257+ bn : (M, N+1) numpy.ndarray
258+ Radial weights for all orders up to N and the given wavenumbers.
259+ """
260+ kr = util .asarray_1d (k * r )
261+ n = np .arange (N + 1 )
262+
263+ bn = circ_radial_weights (N , kr , setup )
264+ for i , x in enumerate (kr ):
265+ bn [i , :] = bn [i , :] * (1j )** (n )
266+ return bn
267+
268+
269+ def circular_ls (N , k , r , rs , setup ):
270+ r"""Radial coefficients for a line source.
271+
272+ Computes the radial component of the circular harmonics expansion of a
273+ line source impinging on a circular array.
274+
275+ .. math::
276+
277+ \mathring{P}_n(k) = \frac{-i}{4} H_n^{(2)}(k r_s) b_n(kr)
278+
279+ Parameters
280+ ----------
281+ N : int
282+ Maximum order.
283+ k : (M,) array_like
284+ Wavenumber.
285+ r : float
286+ Radius of microphone array.
287+ rs : float
288+ Distance of source.
289+ setup : {'open', 'card', 'rigid'}
290+ Array configuration (open, cardioids, rigid).
291+
292+ Returns
293+ -------
294+ bn : (M, N+1) numpy.ndarray
295+ Radial weights for all orders up to N and the given wavenumbers.
296+ """
297+ k = util .asarray_1d (k )
298+ krs = k * rs
299+ n = np .arange (N + 1 )
300+
301+ bn = circ_radial_weights (N , k * r , setup )
302+ for i , x in enumerate (krs ):
303+ Hn = special .hankel2 (n , x )
304+ bn [i , :] = bn [i , :] * - 1j / 4 * Hn
305+ return bn
306+
307+
308+ def circ_radial_weights (N , kr , setup ):
309+ r"""Radial weighing functions.
310+
311+ Computes the radial weighting functions for diferent array types
312+
313+ For instance for an rigid array
314+
315+ .. math::
316+
317+ b_n(kr) = J_n(kr) - \frac{J_n^\prime(kr)}{H_n^{(2)\prime}(kr)}H_n^{(2)}(kr)
318+
319+ Parameters
320+ ----------
321+ N : int
322+ Maximum order.
323+ kr : (M,) array_like
324+ Wavenumber * radius.
325+ setup : {'open', 'card', 'rigid'}
326+ Array configuration (open, cardioids, rigid).
327+
328+ Returns
329+ -------
330+ bn : (M, N+1) numpy.ndarray
331+ Radial weights for all orders up to N and the given wavenumbers.
332+
333+ """
334+ n = np .arange (N + 1 )
335+ Bns = np .zeros ((len (kr ), N + 1 ), dtype = complex )
336+ for i , x in enumerate (kr ):
337+ Jn = special .jv (n , x )
338+ if setup == 'open' :
339+ bn = Jn
340+ elif setup == 'card' :
341+ bn = Jn - 1j * special .jvp (n , x , n = 1 )
342+ elif setup == 'rigid' :
343+ Jnd = special .jvp (n , x , n = 1 )
344+ Hn = special .hankel2 (n , x )
345+ Hnd = special .h2vp (n , x )
346+ bn = Jn - Jnd / Hnd * Hn
347+ else :
348+ raise ValueError ('setup must be either: open, card or rigid' )
349+ Bns [i , :] = bn
350+ return np .squeeze (Bns )
351+
352+
353+ def circ_diagonal_mode_mat (bk ):
354+ """Diagonal matrix of radial coefficients for all modes/wavenumbers.
355+
356+ Parameters
357+ ----------
358+ bk : (M, N+1) numpy.ndarray
359+ Vector containing values for all wavenumbers :math:`M` and modes up to
360+ order :math:`N`
361+
362+ Returns
363+ -------
364+ Bk : (M, 2*N+1, 2*N+1) numpy.ndarray
365+ Multidimensional array containing diagnonal matrices with input
366+ vector on main diagonal.
367+ """
368+ bk = mirror_vec (bk )
369+ if len (bk .shape ) == 1 :
370+ bk = bk [np .newaxis , :]
371+ K , N = bk .shape
372+ Bk = np .zeros ([K , N , N ], dtype = complex )
373+ for k in range (K ):
374+ Bk [k , :, :] = np .diag (bk [k , :])
375+ return np .squeeze (Bk )
376+
377+
378+ def mirror_vec (v ):
379+ """Mirror elements in a vector.
380+
381+ Returns a vector of length *2*len(v)-1* with symmetric elements.
382+ The first *len(v)* elements are the same as *v* and the last *len(v)-1*
383+ elements are *v[:0:-1]*. The function can be used to order the circular
384+ harmonic coefficients. If *v* is a matrix, it is treated as a stack of
385+ vectors residing in the last index and broadcast accordingly.
386+
387+ Parameters
388+ ----------
389+ v : (, N+1) numpy.ndarray
390+ Input vector of stack of input vectors
391+
392+ Returns
393+ -------
394+ : (, 2*N+1) numpy.ndarray
395+ Vector of stack of vectors containing mirrored elements
396+ """
397+ if len (v .shape ) == 1 :
398+ v = v [np .newaxis , :]
399+ return np .concatenate ((v , v [:, :0 :- 1 ]), axis = 1 )
0 commit comments