LASP 1.0
Library for Acoustic Signal Processing
Loading...
Searching...
No Matches
lasp_imptube.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3"""!
4Author: J.A. de Jong - ASCEE
5
6Description: Two-microphone impedance tube methods
7"""
8__all__ = ['TwoMicImpedanceTube']
9# from lrftubes import Air
10from .lasp_measurement import Measurement
11from numpy import pi, sqrt, exp
12import numpy as np
13from scipy.interpolate import UnivariateSpline
14# from lrftubes import PrsDuct
15from functools import lru_cache
16
18 def __init__(self, mnormal: Measurement,
19 mswitched: Measurement,
20 s: float,
21 d1: float,
22 d2: float,
23 fl: float = None,
24 fu: float = None,
25 periodic_method=False,
26 # mat= Air(),
27 D_imptube = 50e-3,
28 thermoviscous = True,
29 **kwargs):
30
31 """
32 Initialize two-microphone impedance tube methods
33
34 Args:
35 mnormal: Measurement in normal configuration
36 mswitched: Measurement in normal configuration
37 s: Microphone distance
38 fl: Lower evaluation frequency
39 fu: Lower evaluation frequency
40
41 kwargs: tuple with extra arguments, of which:
42 N: Period length of periodic excitation *obligatory*
43 chan0: Measurement channel index of mic 0
44 chan1: Measurement channel index of mic 1
45
46 """
47 self.mnormal = mnormal
48 self.mswitched = mswitched
49
50 self.mat = mat
51 self.s = s
52 self.d1 = d1
53 self.d2 = d2
54
55 self.fl = fl
56 if fl is None:
57 ksmin = 0.1*pi
58 kmin = ksmin/s
59 self.fl = kmin*mat.c0/2/pi
60
61 self.fu = fu
62 if fu is None:
63 ksmax = 0.8*pi
64 kmax = ksmax/s
65 self.fu = kmax*mat.c0/2/pi
66
67 self.thermoviscous = thermoviscous
68 self.D_imptube = D_imptube
69
70 self.periodic_method = periodic_method
71 self.channels = [kwargs.pop('chan0', 0), kwargs.pop('chan1', 1)]
72 # Compute calibration correction
73 if periodic_method:
74 self.N = kwargs.pop('N')
75 freq, C1 = mnormal.periodicCPS(self.N, channels=self.channels)
76 freq, C2 = mswitched.periodicCPS(self.N, channels=self.channels)
77 else:
78 self.nfft = kwargs.pop('nfft', 16000)
79 freq, C1 = mnormal.CPS(nfft=self.nfft, channels=self.channels)
80 freq, C2 = mswitched.CPS(nfft=self.nfft, channels=self.channels)
81
82 # Store this, as it is often used for just a single sample.
83 self.C1 = C1
84 self.freq = freq
85
86 self.il = np.where(self.freq<= self.fl)[0][-1]
87 self.ul = np.where(self.freq > self.fu)[0][0]
88
89 # Calibration correction factor
90 # self.K = 0*self.freq + 1.0
91 K = sqrt(C2[:,0,1]*C1[:,0,0]/(C2[:,1,1]*C1[:,1,0]))
92 # self.K = UnivariateSpline(self.freq, K.real)(self.freq) +\
93 # 1j*UnivariateSpline(self.freq, K.imag)(self.freq)
94 self.K = K
95
96 def cut_to_limits(self, ar):
97 return ar[self.il:self.ul]
98
99 def getFreq(self):
100 """
101 Array of frequencies, cut to limits of validity
102 """
103 return self.cut_to_limits(self.freq)
104
105 @lru_cache
106 def G_AB(self, meas):
107 if meas is self.mnormal:
108 C = self.C1
109 freq = self.freq
110 else:
111 if self.periodic_method:
112 freq, C = meas.periodicCPS(self.N, self.channels)
113 else:
114 freq, C = meas.CPS(nfft=self.nfft, channels=self.channels)
115
116 # Microphone transfer function
117 G_AB = self.K*C[:,1,0]/C[:,0,0]
118 return self.getFreq(), self.cut_to_limits(G_AB)
119
120 def k(self, freq):
121 """
122 Wave number, or thermoviscous wave number
123 """
124 if self.thermoviscous:
125 D = self.D_imptube
126 S = pi/4*D**2
127 d = PrsDuct(0, S=S, rh=D/4, cs='circ')
128 d.mat = self.mat
129 omg = 2*pi*freq
130 G, Z = d.GammaZc(omg)
131 return G
132 else:
133 return 2*pi*freq/self.mat.c0
134
135
136 def R(self, meas):
137 freq, G_AB = self.G_AB(meas)
138 s = self.s
139 k = self.k(freq)
140 d1 = self.d1
141 RpA = (G_AB - exp(-1j*k*s))/(exp(1j*k*s)-G_AB)
142
143 R = RpA*exp(2*1j*k*(s+d1))
144
145 return freq, R
146
147 def alpha(self, meas):
148 """
149 Acoustic absorption coefficient
150 """
151 freq, R = self.R(meas)
152 return freq, 1 - np.abs(R)**2
153
154 def z(self, meas):
155 """
156 Acoustic impedance at the position of the sample, in front of the sample
157 """
158 freq, R = self.R(meas)
159 return freq, self.mat.z0*(1+R)/(1-R)
160
161 def zs(self, meas):
162 """
163 Sample impedance jump, assuming a cavity behind the sample with
164 thickness d2
165 """
166 freq, R = self.R(meas)
167 z0 = self.mat.z0
168 k = 2*pi*freq/self.mat.c0
169 d2 = self.d2
170
171 zs = 2*z0*(1-R*exp(2*1j*k*d2))/((R-1)*(exp(2*d2*k*1j)-1))
172 return freq, zs
173
z(self, meas)
Acoustic impedance at the position of the sample, in front of the sample.
alpha(self, meas)
Acoustic absorption coefficient.
__init__(self, Measurement mnormal, Measurement mswitched, float s, float d1, float d2, float fl=None, float fu=None, periodic_method=False, D_imptube=50e-3, thermoviscous=True, **kwargs)
Initialize two-microphone impedance tube methods.
k(self, freq)
Wave number, or thermoviscous wave number.
zs(self, meas)
Sample impedance jump, assuming a cavity behind the sample with thickness d2.
getFreq(self)
Array of frequencies, cut to limits of validity.