Source code for mosqito.utils.am_noise_generator
import numpy as np
[docs]
def am_noise_generator(xmod, spl_level, print_m=False):
""" Amplitude-modulated braodband noise generation
This function creates an amplitude-modulated (AM) signal with Gaussian
broadband (noise) carrier and arbitrary modulating signal 'xmod'.
The AM signal length is the same as the length of 'xmod'.
The signal level is adjusted to 'spl_level' in dB.
Parameters
----------
xmod: array
Modulating signal, dim(N).
spl_level: float
Sound Pressure Level [dB ref 20 uPa RMS] of the modulated signal.
print_m: bool, optional
If True, calculated modulation index is printed.
If False, the modulation index is printed only in case of overmodulation (m>1)
Default is False.
Returns
-------
y_am: numpy.array
Amplitude-modulated noise signal in Pascals, dim(N).
m: float
Modulation index
Warning
-------
spl_level must be provided in dB, ref=2e-5 Pa.
See Also
--------
.am_sine_generator : Amplitude modulation with sine wave carrier
.fm_sine_generator : Frequency modulation with sine wave carrier
.sine_wave_generator : Sine wave generation
Notes
-----
The modulation index 'm' will be equal to the peak value of the modulating
signal 'mod_signal'. Its value can be printed by setting the optional flag
'print_m' to True.
For 'm' = 0.5, the carrier amplitude varies by 50% above and below its
unmodulated level. For 'm' = 1.0, it varies by 100%. With 100% modulation
the wave amplitude sometimes reaches zero, and this represents full
modulation. Increasing the modulating signal beyond that point is known as
overmodulation.
Examples
--------
.. plot::
:include-source:
>>> from mosqito.utils import am_noise_generator
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> fs = 48000
>>> duration = 1
>>> t = np.linspace(0, duration, int(duration*fs))
>>> dB = 60
>>> fm = 4
>>> xmod = np.sin(2*np.pi*t*fm)
>>> y_am, MI = am_noise_generator(xmod, dB, True)
>>> fig, plots = plt.subplots(2, 1)
>>> plots[0].set_title('Amplitude-modulated broadband noise')
>>> plots[0].plot(t, xmod, 'C0', label='Modulating signal')
>>> plots[0].legend(loc='upper right')
>>> plots[0].grid()
>>> plots[0].set_ylabel('Amplitude')
>>> plots[0].set_xlim([0, duration])
>>> plots[1].plot(t, y_am, '#69c3c5', label='AM signal')
>>> plots[1].legend(loc='upper right')
>>> plots[1].grid()
>>> plots[1].set_ylabel('Amplitude')
>>> plots[1].set_xlim([0, duration])
>>> plots[1].set_xlabel('Time [s]')
>>> fig.set_tight_layout('tight')
"""
# signal length in samples
Nt = xmod.shape[0]
# create vector of zero-mean, unitary std dev random samples
rng = np.random.default_rng()
xc = rng.standard_normal(Nt)
# AM signal
y_am = (1 + xmod)*xc
# AM modulation index
m = np.max(np.abs(xmod))
if print_m:
print(f"AM Modulation index = {m}")
if m > 1:
print("Warning ['am_noise_generator']: modulation index m > 1\n\tSignal is overmodulated!")
# normalise broadband signal energy to 'spl_level' [dB SPL ref 20 uPa]
p_ref = 20e-6
A_rms = p_ref * 10**(spl_level/20)
y_am *= A_rms/np.std(y_am)
return y_am, m