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