xref: /btstack/3rd-party/lc3-google/test/attdet.py (revision 4930cef6e21e6da2d7571b9259c7f0fb8bed3d01)
1*4930cef6SMatthias Ringwald#
2*4930cef6SMatthias Ringwald# Copyright 2022 Google LLC
3*4930cef6SMatthias Ringwald#
4*4930cef6SMatthias Ringwald# Licensed under the Apache License, Version 2.0 (the "License");
5*4930cef6SMatthias Ringwald# you may not use this file except in compliance with the License.
6*4930cef6SMatthias Ringwald# You may obtain a copy of the License at
7*4930cef6SMatthias Ringwald#
8*4930cef6SMatthias Ringwald#     http://www.apache.org/licenses/LICENSE-2.0
9*4930cef6SMatthias Ringwald#
10*4930cef6SMatthias Ringwald# Unless required by applicable law or agreed to in writing, software
11*4930cef6SMatthias Ringwald# distributed under the License is distributed on an "AS IS" BASIS,
12*4930cef6SMatthias Ringwald# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*4930cef6SMatthias Ringwald# See the License for the specific language governing permissions and
14*4930cef6SMatthias Ringwald# limitations under the License.
15*4930cef6SMatthias Ringwald#
16*4930cef6SMatthias Ringwald
17*4930cef6SMatthias Ringwaldimport numpy as np
18*4930cef6SMatthias Ringwald
19*4930cef6SMatthias Ringwaldimport build.lc3 as lc3
20*4930cef6SMatthias Ringwaldimport tables as T, appendix_c as C
21*4930cef6SMatthias Ringwald
22*4930cef6SMatthias Ringwald
23*4930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
24*4930cef6SMatthias Ringwald
25*4930cef6SMatthias Ringwaldclass AttackDetector:
26*4930cef6SMatthias Ringwald
27*4930cef6SMatthias Ringwald    def __init__(self, dt, sr):
28*4930cef6SMatthias Ringwald
29*4930cef6SMatthias Ringwald        self.dt = dt
30*4930cef6SMatthias Ringwald        self.sr = sr
31*4930cef6SMatthias Ringwald        self.ms = T.DT_MS[dt]
32*4930cef6SMatthias Ringwald
33*4930cef6SMatthias Ringwald        self.xn1 = 0
34*4930cef6SMatthias Ringwald        self.xn2 = 0
35*4930cef6SMatthias Ringwald        self.en1 = 0
36*4930cef6SMatthias Ringwald        self.an1 = 0
37*4930cef6SMatthias Ringwald        self.p_att = 0
38*4930cef6SMatthias Ringwald
39*4930cef6SMatthias Ringwald    def is_enabled(self, nbytes):
40*4930cef6SMatthias Ringwald
41*4930cef6SMatthias Ringwald        c1 = self.dt == T.DT_10M and \
42*4930cef6SMatthias Ringwald             self.sr == T.SRATE_32K and nbytes > 80
43*4930cef6SMatthias Ringwald
44*4930cef6SMatthias Ringwald        c2 = self.dt == T.DT_10M and \
45*4930cef6SMatthias Ringwald             self.sr >= T.SRATE_48K and nbytes >= 100
46*4930cef6SMatthias Ringwald
47*4930cef6SMatthias Ringwald        c3 = self.dt == T.DT_7M5 and \
48*4930cef6SMatthias Ringwald             self.sr == T.SRATE_32K and nbytes >= 61 and nbytes < 150
49*4930cef6SMatthias Ringwald
50*4930cef6SMatthias Ringwald        c4 = self.dt == T.DT_7M5 and \
51*4930cef6SMatthias Ringwald             self.sr >= T.SRATE_48K and nbytes >= 75 and nbytes < 150
52*4930cef6SMatthias Ringwald
53*4930cef6SMatthias Ringwald        return c1 or c2 or c3 or c4
54*4930cef6SMatthias Ringwald
55*4930cef6SMatthias Ringwald    def run(self, nbytes, x):
56*4930cef6SMatthias Ringwald
57*4930cef6SMatthias Ringwald        ### 3.3.6.2 Downsampling and filtering input
58*4930cef6SMatthias Ringwald
59*4930cef6SMatthias Ringwald        mf = int(16 * self.ms)
60*4930cef6SMatthias Ringwald
61*4930cef6SMatthias Ringwald        r = len(x) // mf
62*4930cef6SMatthias Ringwald        x_att = np.array([ np.sum(x[i*r:(i+1)*r]) for i in range(mf) ])
63*4930cef6SMatthias Ringwald
64*4930cef6SMatthias Ringwald        x_hp = np.empty(mf)
65*4930cef6SMatthias Ringwald        x_hp[0 ] = 0.375 * x_att[0 ] - 0.5 * self.xn1    + 0.125 * self.xn2
66*4930cef6SMatthias Ringwald        x_hp[1 ] = 0.375 * x_att[1 ] - 0.5 * x_att[0   ] + 0.125 * self.xn1
67*4930cef6SMatthias Ringwald        x_hp[2:] = 0.375 * x_att[2:] - 0.5 * x_att[1:-1] + 0.125 * x_att[0:-2]
68*4930cef6SMatthias Ringwald        self.xn2 = x_att[-2]
69*4930cef6SMatthias Ringwald        self.xn1 = x_att[-1]
70*4930cef6SMatthias Ringwald
71*4930cef6SMatthias Ringwald        ### 3.3.6.3 Energy calculation
72*4930cef6SMatthias Ringwald
73*4930cef6SMatthias Ringwald        nb = int(self.ms / 2.5)
74*4930cef6SMatthias Ringwald
75*4930cef6SMatthias Ringwald        e_att = np.array([ np.sum(np.square(x_hp[40*i:40*(i+1)]))
76*4930cef6SMatthias Ringwald                           for i in range(nb) ])
77*4930cef6SMatthias Ringwald
78*4930cef6SMatthias Ringwald        a_att = np.empty(nb)
79*4930cef6SMatthias Ringwald        a_att[0] = np.maximum(0.25 * self.an1, self.en1)
80*4930cef6SMatthias Ringwald        for i in range(1,nb):
81*4930cef6SMatthias Ringwald            a_att[i] = np.maximum(0.25 * a_att[i-1], e_att[i-1])
82*4930cef6SMatthias Ringwald        self.en1 = e_att[-1]
83*4930cef6SMatthias Ringwald        self.an1 = a_att[-1]
84*4930cef6SMatthias Ringwald
85*4930cef6SMatthias Ringwald        ### 3.3.6.4 Attack Detection
86*4930cef6SMatthias Ringwald
87*4930cef6SMatthias Ringwald        p_att = -1
88*4930cef6SMatthias Ringwald        flags = [ (e_att[i] > 8.5 * a_att[i]) for i in range(nb) ]
89*4930cef6SMatthias Ringwald
90*4930cef6SMatthias Ringwald        for (i, f) in enumerate(flags):
91*4930cef6SMatthias Ringwald            if f: p_att = i
92*4930cef6SMatthias Ringwald
93*4930cef6SMatthias Ringwald        f_att = p_att >= 0 or self.p_att - 1 >= nb // 2
94*4930cef6SMatthias Ringwald        self.p_att = 1 + p_att
95*4930cef6SMatthias Ringwald
96*4930cef6SMatthias Ringwald        return self.is_enabled(nbytes) and f_att
97*4930cef6SMatthias Ringwald
98*4930cef6SMatthias Ringwald
99*4930cef6SMatthias Ringwalddef initial_state():
100*4930cef6SMatthias Ringwald    return { 'en1': 0.0, 'an1': 0.0, 'p_att': 0 }
101*4930cef6SMatthias Ringwald
102*4930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
103*4930cef6SMatthias Ringwald
104*4930cef6SMatthias Ringwalddef check_enabling(rng, dt):
105*4930cef6SMatthias Ringwald
106*4930cef6SMatthias Ringwald    ok = True
107*4930cef6SMatthias Ringwald
108*4930cef6SMatthias Ringwald    for sr in range(T.SRATE_16K, T.NUM_SRATE):
109*4930cef6SMatthias Ringwald
110*4930cef6SMatthias Ringwald        attdet = AttackDetector(dt, sr)
111*4930cef6SMatthias Ringwald
112*4930cef6SMatthias Ringwald        for nbytes in [ 61, 61-1, 75-1, 75, 80, 80+1, 100-1, 100, 150-1, 150 ]:
113*4930cef6SMatthias Ringwald
114*4930cef6SMatthias Ringwald            f_att = lc3.attdet_run(dt, sr, nbytes,
115*4930cef6SMatthias Ringwald                initial_state(), 2 * rng.random(T.NS[dt][sr]+6) - 1)
116*4930cef6SMatthias Ringwald
117*4930cef6SMatthias Ringwald            ok = ok and f_att == attdet.is_enabled(nbytes)
118*4930cef6SMatthias Ringwald
119*4930cef6SMatthias Ringwald    return ok
120*4930cef6SMatthias Ringwald
121*4930cef6SMatthias Ringwalddef check_unit(rng, dt, sr):
122*4930cef6SMatthias Ringwald
123*4930cef6SMatthias Ringwald    ns = T.NS[dt][sr]
124*4930cef6SMatthias Ringwald    ok = True
125*4930cef6SMatthias Ringwald
126*4930cef6SMatthias Ringwald    attdet = AttackDetector(dt, sr)
127*4930cef6SMatthias Ringwald
128*4930cef6SMatthias Ringwald    state_c = initial_state()
129*4930cef6SMatthias Ringwald    x_c = np.zeros(ns+6)
130*4930cef6SMatthias Ringwald
131*4930cef6SMatthias Ringwald    for run in range(100):
132*4930cef6SMatthias Ringwald
133*4930cef6SMatthias Ringwald        ### Generate noise, and an attack at random point
134*4930cef6SMatthias Ringwald
135*4930cef6SMatthias Ringwald        x = ((2 * rng.random(ns)) - 1) * (2 ** 8 - 1)
136*4930cef6SMatthias Ringwald        x[(ns * rng.random()).astype(int)] *= 2 ** 7
137*4930cef6SMatthias Ringwald
138*4930cef6SMatthias Ringwald        ### Check Implementation
139*4930cef6SMatthias Ringwald
140*4930cef6SMatthias Ringwald        f_att = attdet.run(100, x)
141*4930cef6SMatthias Ringwald
142*4930cef6SMatthias Ringwald        x_c = np.append(x_c[-6:], x)
143*4930cef6SMatthias Ringwald        f_att_c = lc3.attdet_run(dt, sr, 100, state_c, x_c)
144*4930cef6SMatthias Ringwald
145*4930cef6SMatthias Ringwald        ok = ok and f_att_c == f_att
146*4930cef6SMatthias Ringwald        ok = ok and np.amax(np.abs(1 - state_c['en1']/attdet.en1)) < 2
147*4930cef6SMatthias Ringwald        ok = ok and np.amax(np.abs(1 - state_c['an1']/attdet.an1)) < 2
148*4930cef6SMatthias Ringwald        ok = ok and state_c['p_att'] == attdet.p_att
149*4930cef6SMatthias Ringwald
150*4930cef6SMatthias Ringwald    return ok
151*4930cef6SMatthias Ringwald
152*4930cef6SMatthias Ringwalddef check_appendix_c(dt):
153*4930cef6SMatthias Ringwald
154*4930cef6SMatthias Ringwald    sr = T.SRATE_48K
155*4930cef6SMatthias Ringwald
156*4930cef6SMatthias Ringwald    state = initial_state()
157*4930cef6SMatthias Ringwald
158*4930cef6SMatthias Ringwald    x = np.append(np.zeros(6), C.X_PCM_ATT[dt][0])
159*4930cef6SMatthias Ringwald    f_att = lc3.attdet_run(dt, sr, C.NBYTES_ATT[dt], state, x)
160*4930cef6SMatthias Ringwald    ok = f_att == C.F_ATT[dt][0]
161*4930cef6SMatthias Ringwald
162*4930cef6SMatthias Ringwald    x = np.append(x[-6:], C.X_PCM_ATT[dt][1])
163*4930cef6SMatthias Ringwald    f_att = lc3.attdet_run(dt, sr, C.NBYTES_ATT[dt], state, x)
164*4930cef6SMatthias Ringwald    ok = f_att == C.F_ATT[dt][1]
165*4930cef6SMatthias Ringwald
166*4930cef6SMatthias Ringwald    return ok
167*4930cef6SMatthias Ringwald
168*4930cef6SMatthias Ringwalddef check():
169*4930cef6SMatthias Ringwald
170*4930cef6SMatthias Ringwald    rng = np.random.default_rng(1234)
171*4930cef6SMatthias Ringwald    ok = True
172*4930cef6SMatthias Ringwald
173*4930cef6SMatthias Ringwald    for dt in range(T.NUM_DT):
174*4930cef6SMatthias Ringwald        ok and check_enabling(rng, dt)
175*4930cef6SMatthias Ringwald
176*4930cef6SMatthias Ringwald    for dt in range(T.NUM_DT):
177*4930cef6SMatthias Ringwald        for sr in range(T.SRATE_32K, T.NUM_SRATE):
178*4930cef6SMatthias Ringwald            ok = ok and check_unit(rng, dt, sr)
179*4930cef6SMatthias Ringwald
180*4930cef6SMatthias Ringwald    for dt in range(T.NUM_DT):
181*4930cef6SMatthias Ringwald        ok = ok and check_appendix_c(dt)
182*4930cef6SMatthias Ringwald
183*4930cef6SMatthias Ringwald    return ok
184*4930cef6SMatthias Ringwald
185*4930cef6SMatthias Ringwald### ------------------------------------------------------------------------ ###
186