1
1
using MeasureIR
2
2
using Unitful: s, kHz
3
3
using SampledSignals: SampleBuf, samplerate
4
- # using Suppressor
4
+ using Suppressor
5
5
using DSP
6
6
using Test
7
7
8
+ """
9
+ Simulate running the given stimulus through a real system, including a linear
10
+ IR, nonlinear distortion, noise, and clock skew.
11
+
12
+ If `dist` is given, it should be a multiplier that scales the stimulus before
13
+ passing into `tanh`. The distorted signal is scaled to have the same energy as
14
+ the original. A multiplier of 1 gives pretty mild distortion, and it goes up
15
+ from there.
16
+
17
+ Returns the simulated response, as well as the IR used.
18
+ """
19
+ function simIR (stim; ir= nothing , dist= nothing , noisepow= - Inf , skew= 1.0 )
20
+ if dist != = nothing
21
+ stimenergy = sum (x-> x^ 2 , stim)
22
+ stim .= tanh .(stim)
23
+ distenergy = sum (x-> x^ 2 , stim)
24
+
25
+ stim .*= sqrt (sigenergy/ distenergy)
26
+ end
27
+ if ir === nothing
28
+ ir = [zeros (100 ); 1 ; randn .() .* exp .(range (- 1.5 , - 8 , length= 7500 ))]
29
+ end
30
+ resp = conv (stim, ir)
31
+ if noisepow > - Inf
32
+ resp .+ = db2amp (noisepow) .* randn .()
33
+ end
34
+ if skew != 1.0
35
+ resp = resample (resp, skew)
36
+ end
37
+
38
+ resp, ir
39
+ end
40
+
41
+ """
42
+ Generates a broadband random signal of length `N` that's periodic with period
43
+ `P`. `P` does not need to be an integer.
44
+ """
45
+ function randperiodic (N, P)
46
+ f = 1 / P # fundamental freq
47
+ # we add eps() here so we don't get hit by floating-point error when f
48
+ # divides evenly into 1
49
+ H = Int (fld (0.5 + eps (), f)) # number of harmonics we can fit below nyquist
50
+ sum (cos .(2 π.* (h.* f.* (0 : N- 1 ). + rand ()))./ H for h in 1 : H)
51
+ end
52
+
8
53
function testmeasure (measfunc)
9
54
meas = measfunc (8192 )
10
55
stim = stimulus (meas)
@@ -103,10 +148,10 @@ end
103
148
meas = golay (L)
104
149
ir = randn (L÷ 2 ) .* exp .(linspace (0 ,- 8 , L÷ 2 ))
105
150
stim = stimulus (meas)
106
- out = @color_output false @ capture_err analyze (meas, conv (stim, ir))
151
+ out = @capture_err analyze (meas, conv (stim, ir))
107
152
@test out == " "
108
153
ir = randn (3 L÷ 2 ) .* exp .(linspace (0 ,- 8 , 3 L÷ 2 ))
109
- out = @color_output false @ capture_err analyze (meas, conv (stim, ir))
154
+ out = @capture_err analyze (meas, conv (stim, ir))
110
155
@test out == string (" Warning: nonsilent samples past end of analysis " ,
111
156
" window. Check your test signal is long enough for " ,
112
157
" the response\n " )
@@ -117,17 +162,17 @@ end
117
162
# L = 4096
118
163
# meas = expsweep(L)
119
164
# stim = stimulus(meas)
120
- # out = @color_output false @ capture_err analyze(meas, stim)
165
+ # out = @capture_err analyze(meas, stim)
121
166
# @test out == ""
122
- # out = @color_output false @ capture_err analyze(meas, tanh.(stim*2))
167
+ # out = @capture_err analyze(meas, tanh.(stim*2))
123
168
# @test out == "Warning: Energy in noncausal IR is above noise floor. Check for nonlinearity\n"
124
169
#
125
170
# # now try with some noise and a more realistic IR
126
171
# testir = randn(512) .* exp.(linspace(0,-8, 512)) / 2
127
172
# resp = conv(stim, testir)[1:length(stim)]
128
- # out = @color_output false @ capture_err analyze(meas, stim.+randn.().*0.1)
173
+ # out = @capture_err analyze(meas, stim.+randn.().*0.1)
129
174
# @test out == ""
130
- # out = @color_output false @ capture_err analyze(meas, tanh.(stim*2).+randn.().*0.1)
175
+ # out = @capture_err analyze(meas, tanh.(stim*2).+randn.().*0.1)
131
176
# @test out == "Warning: Energy in noncausal IR is above noise floor. Check for nonlinearity\n"
132
177
# end
133
178
@@ -189,34 +234,46 @@ end
189
234
# plt = plot(welch_pgram(sig[(1:10000) .+ 50000], 256).power)
190
235
@testset " findpilot" begin
191
236
sr = 48 kHz
237
+ dur = 1 s
192
238
@testset " f=$f " for f in 0.99 kHz: 0.005 kHz: 1.01 kHz
193
239
sig = [zeros (50000 ); pilot (dur, f, samplerate= sr); zeros (48000 * 10 )]
194
240
sig .+ = 8 .* randn .() # SNR of -21.1dB - not bad!
195
- onset, offset = findpilot (sig, 1 kHz, 1 s; samplerate= sr)
196
- # make sure we don't over-shoot
197
- @test offset- onset <= 48000
198
- # undershooting by a bit is OK
199
- @test offset- onset > 48000 * 0.7
200
- @test onset >= 50000
201
- @test onset <= 50000 + 48000 * 0.3
241
+ onset = findpilot (sig, 1 kHz, 1 s; samplerate= sr)
242
+ @test isapprox (onset, 50001 , rtol= 0.1 )
202
243
end
203
244
end
204
245
205
- # TODO : for some reason this is failing when skew == 1
206
246
@testset " pilotsync" begin
207
- L = 5 s
247
+ dur = 5 s
208
248
sr = 48 kHz
209
249
f = 1 kHz
250
+ L = 5 * 48000
210
251
@testset " skew=$skew " for skew in 0.99 : 0.005 : 1.01
211
- p = pilot (L , f; samplerate= sr)
252
+ p = pilot (dur , f; samplerate= sr)
212
253
sig = [zeros (50000 );
213
254
isapprox (skew, 1 ) ? p : resample (p, skew);
214
255
zeros (48000 * 15 )]
215
256
sig .+ = randn .() ./ sqrt (2 ) # SNR of 0dB
216
- onset, offset = findpilot (sig, f, L ; samplerate= sr)
217
- @test isapprox (pilotsync (sig[onset : offset ], f; samplerate= sr),
257
+ onset = findpilot (sig, f, dur ; samplerate= sr)
258
+ @test isapprox (pilotsync (sig[( 0 : L - 1 ) .+ onset ], f; samplerate= sr),
218
259
1 / skew, rtol= 5e-7 )
219
260
end
220
261
end
221
262
263
+ @testset " getperiod" begin
264
+ N = 65535
265
+ meas = mls (N, 20 )
266
+
267
+ # test at a variety of skews in ppm
268
+ skews = 1 .+ (- 400 : 80 : 400 ) ./ 1e6
269
+
270
+ @testset " skew=$skew " for skew in skews
271
+ resp, ir = simIR (stimulus (meas), skew= skew, noisepow= - 8.2 + 20 ) # -20dB SNR
272
+ period = getperiod (resp, N)
273
+ err = (period- skew* N)/ period
274
+ @show skew, err
275
+ @test isapprox (period, skew* N, rtol= 1e-7 )
276
+ end
277
+ end
278
+
222
279
end # @testset MeasureIR
0 commit comments