LASP 1.0
Library for Acoustic Signal Processing
Loading...
Searching...
No Matches
soundpressureweighting.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: Filter design for frequency weighting curves (i.e. A and C
7weighting)
8"""
9from .fir_design import freqResponse, arbitrary_fir_design
10from scipy.signal import bilinear_zpk, zpk2sos
11import numpy as np
12
13__all__ = ['SPLFilterDesigner']
14
15
17 fr = 1000.
18 fL = 10**1.5
19 fH = 10**3.9
20
21
22 fLsq = fL**2
23 fHsq = fH**2
24 frsq = fr**2
25 fA = 10**2.45
26 D = np.sqrt(.5)
27
28 b = (1/(1-D))*(frsq+fLsq*fHsq/frsq-D*(fLsq+fHsq))
29 c = fLsq*fHsq
30 f2 = (3-np.sqrt(5.))/2*fA
31 f3 = (3+np.sqrt(5.))/2*fA
32 f1 = np.sqrt((-b-np.sqrt(b**2-4*c))/2)
33 f4 = np.sqrt((-b+np.sqrt(b**2-4*c))/2)
34 f4sq = f4**2
35
36 def __init__(self, fs):
37 """Initialize a filter bank designer.
38
39 Args:
40 fs: Sampling frequency [Hz]
41 """
42 self.fs = fs
43
44 def _A_uncor(self, f):
45 """
46 Computes the uncorrected frequency response of the A-filter
47
48 Args:
49 f: Frequency (array, float)
50
51 Returns:
52 Linear filter transfer function
53 """
54 fsq = f**2
55 num = self.f4sq*fsq**2
56 denom1 = (fsq+self.f1**2)
57 denom2 = np.sqrt((fsq+self.f2**2)*(fsq+self.f3**2))*(fsq+self.f4sq)
58
59 return (num/(denom1*denom2))
60
61
62 def A(self, f):
63 """
64 Computes the linear A-weighting freqency response. Hence, to obtain
65 A-weighted values, the *amplitude* need to be multiplied with this value.
66 Hence, to correct dB levels, the value of 20*log(A) needs to be added to
67 the level
68
69 Args:
70 f: Frequency array to compute values for
71 Returns:
72 A(f) for each frequency
73 """
74 Auncor = self._A_uncor(f)
75 A1000 = self._A_uncor(self.fr)
76 return Auncor/A1000
77
78
79 def _C_uncor(self, f):
80 """
81 Computes the uncorrected frequency response of the C-filter
82 """
83 fsq = f**2
84 num = self.f4sq*fsq
85 denom1 = (fsq+self.f1**2)
86 denom2 = (fsq+self.f4**2)
87 return num/(denom1*denom2)
88
89
90 def C(self, f):
91 """
92 Computes the linear A-weighting freqency response
93 """
94 Cuncor = self._C_uncor(f)
95 C1000 = self._C_uncor(self.fr)
96 return Cuncor/C1000
97
98
99 def A_fir_design(self):
100
101 fs = self.fs
102
103 assert int(fs) == 48000
104 freq_design = np.linspace(0, 17e3, 3000)
105 freq_design[-1] = fs/2
106 amp_design = self.A(freq_design)
107 amp_design[-1] = 0.
108
109 L = 2048 # Filter order
110 fir = arbitrary_fir_design(fs, L, freq_design, amp_design,
111 window='rectangular')
112 return fir
113
114
115 def C_fir_design(self):
116 fs = self.fs
117 assert int(fs) == 48000
118 fs = 48000.
119 freq_design = np.linspace(0, 17e3, 3000)
120 freq_design[-1] = fs/2
121 amp_design = C(freq_design)
122 amp_design[-1] = 0.
123
124 L = 2048 # Filter order
125 fir = arbitrary_fir_design(fs, L, freq_design, amp_design,
126 window='rectangular')
127 return fir
128
129 def C_Sos_design(self):
130 """
131 Create filter coefficients of the C-weighting filter. Uses the bilinear
132 transform to convert the analog filter to a digital one.
133
134 Returns:
135 Sos: Second order sections
136 """
137 fs = self.fs
138 p1 = 2*np.pi*self.f1
139 p4 = 2*np.pi*self.f4
140 zeros_analog = [0,0]
141 poles_analog = [-p1, -p1, -p4, -p4]
142 k_analog = p4**2/self._C_uncor(self.fr)
143
144 z, p, k = bilinear_zpk(zeros_analog, poles_analog, k_analog, fs)
145 sos = zpk2sos(z, p, k)
146 return sos
147
148 def A_Sos_design(self):
149 """
150 Create filter coefficients of the A-weighting filter. Uses the bilinear
151 transform to convert the analog filter to a digital one.
152
153 Returns:
154 Sos: Second order sections
155 """
156 fs = self.fs
157 p1 = 2*np.pi*self.f1
158 p2 = 2*np.pi*self.f2
159 p3 = 2*np.pi*self.f3
160 p4 = 2*np.pi*self.f4
161 zeros_analog = [0,0,0,0]
162 poles_analog = [-p1,-p1,-p2,-p3,-p4,-p4]
163 k_analog = p4**2/self._A_uncor(self.fr)
164
165 z, p, k = bilinear_zpk(zeros_analog, poles_analog, k_analog, fs)
166 sos = zpk2sos(z, p, k)
167 return sos
168
169
171 from asceefig.plot import Figure
172
173 fs = 48000.
174 freq_design = np.linspace(0, 17e3, 3000)
175 freq_design[-1] = fs/2
176 amp_design = A(freq_design)
177 amp_design[-1] = 0.
178 firs = []
179
180 # firs.append(arbitrary_fir_design(fs,L,freq_design,amp_design,window='hamming'))
181 # firs.append(arbitrary_fir_design(fs,L,freq_design,amp_design,window='hann'))
182 firs.append(A_fir_design())
183 # from scipy.signal import iirdesign
184 # b,a = iirdesign()
185 freq_check = np.logspace(0, np.log10(fs/2), 5000)
186 f = Figure()
187
188 f.semilogx(freq_check, 20*np.log10(A(freq_check)))
189 for fir in firs:
190 H = freqResponse(fs, freq_check, fir)
191 f.plot(freq_check, 20*np.log10(np.abs(H)))
192
193 f.fig.get_axes()[0].set_ylim(-75, 3)
194
195
197 from asceefig.plot import Figure
198
199 fs = 48000.
200 freq_design = np.linspace(0, 17e3, 3000)
201 freq_design[-1] = fs/2
202 amp_design = C(freq_design)
203 amp_design[-1] = 0.
204 firs = []
205
206 # firs.append(arbitrary_fir_design(fs,L,freq_design,amp_design,window='hamming'))
207 # firs.append(arbitrary_fir_design(fs,L,freq_design,amp_design,window='hann'))
208 firs.append(C_fir_design())
209 # from scipy.signal import iirdesign
210 # b,a = iirdesign()
211 freq_check = np.logspace(0, np.log10(fs/2), 5000)
212 f = Figure()
213
214 f.semilogx(freq_check, 20*np.log10(C(freq_check)))
215 for fir in firs:
216 H = freqResponse(fs, freq_check, fir)
217 f.plot(freq_check, 20*np.log10(np.abs(H)))
218
219 f.fig.get_axes()[0].set_ylim(-30, 1)
220
C(self, f)
Computes the linear A-weighting freqency response.
A_Sos_design(self)
Create filter coefficients of the A-weighting filter.
_A_uncor(self, f)
Computes the uncorrected frequency response of the A-filter.
_C_uncor(self, f)
Computes the uncorrected frequency response of the C-filter.
A(self, f)
Computes the linear A-weighting freqency response.
__init__(self, fs)
Initialize a filter bank designer.
C_Sos_design(self)
Create filter coefficients of the C-weighting filter.