• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aewallin / allantools / 6997134815

26 Nov 2023 05:57PM UTC coverage: 74.663% (-10.1%) from 84.742%
6997134815

push

github

aewallin
coverage workflow

1273 of 1705 relevant lines covered (74.66%)

2.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.61
/allantools/noise_kasdin.py
1
"""
2
Allantools Noise object
3

4
**Authors:** Julia Leute (julia.leute "at" gmail.com)
5
    Anders Wallin (anders.e.e.wallin "at" gmail.com)
6

7
Version history
8
---------------
9

10
**2019.07**
11
- Initial release, see also https://github.com/jleute/colorednoise
12

13
License
14
-------
15

16
This program is free software: you can redistribute it and/or modify
17
it under the terms of the GNU Lesser General Public License as published by
18
the Free Software Foundation, either version 3 of the License, or
19
(at your option) any later version.
20

21
This program is distributed in the hope that it will be useful,
22
but WITHOUT ANY WARRANTY; without even the implied warranty of
23
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
GNU Lesser General Public License for more details.
25

26
You should have received a copy of the GNU Lesser General Public License
27
along with this program.  If not, see <http://www.gnu.org/licenses/>.
28

29
"""
30

31
import numpy as np
2✔
32

33

34
class Noise(object):
2✔
35
    """ Generate discrete colored noise
36

37
    Python / Numpy implementation of [Kasdin1992]_
38
    Kasdin, N.J., Walter, T., "Discrete simulation of power law noise [for
39
    oscillator stability evaluation]," Frequency Control Symposium, 1992.
40
    46th., Proceedings of the 1992 IEEE, pp.274,283, 27-29 May 1992
41
    http://dx.doi.org/10.1109/FREQ.1992.270003
42

43
    Parameters
44
    ----------
45
    nr: integer
46
        length of generated time-series
47
        must be power of two
48
    qd: float
49
        discrete variance
50
    b: float
51
    
52
        +----+--------------------------------------------+
53
        | b  |  noise type                                |
54
        +====+============================================+
55
        | 0  | White Phase Modulation (WPM)               |
56
        +----+--------------------------------------------+
57
        | -1 | Flicker Phase Modulation (FPM)             |
58
        +----+--------------------------------------------+
59
        | -2 | White Frequency Modulation (WFM)           |
60
        +----+--------------------------------------------+
61
        | -3 | Flicker Frequency Modulation (FFM)         |
62
        +----+--------------------------------------------+
63
        | -4 | Random Walk Frequency Modulation (RWFM)    |
64
        +----+--------------------------------------------+
65

66
    Returns
67
    -------
68
    Noise()
69
        A Noise() instance
70
            
71
    :Example:
72
        ::
73

74
            import numpy as np
75
            noise = allantools.Noise(nr=2*8, qd=1.0e-20, b=-1)
76
            noise.generateNoise()
77
            print noise.time_series
78

79
    """
80

81
    def __init__(self, nr=2, qd=1, b=0):
3✔
82
        """ Initialize object with input data
83

84

85

86
        """
87
        self.nr = nr
3✔
88
        self.qd = qd
3✔
89
        self.b = b
3✔
90
        self.time_series = np.array([])
3✔
91

92
    def set_input(self, nr=2, qd=1, b=0):
3✔
93
        """ Set inputs after initialization
94

95
        Parameters
96
        ----------
97
        nr: integer
98
            length of generated time-series
99
            number must be power of two
100
        qd: float
101
            discrete variance
102
        b: float
103
            noise type
104
            
105
            +----+--------------------------------------------+
106
            | b  |  noise type                                |
107
            +====+============================================+
108
            | 0  | White Phase Modulation (WPM)               |
109
            +----+--------------------------------------------+
110
            | -1 | Flicker Phase Modulation (FPM)             |
111
            +----+--------------------------------------------+
112
            | -2 | White Frequency Modulation (WFM)           |
113
            +----+--------------------------------------------+
114
            | -3 | Flicker Frequency Modulation (FFM)         |
115
            +----+--------------------------------------------+
116
            | -4 | Random Walk Frequency Modulation (RWFM)    |
117
            +----+--------------------------------------------+
118
            
119
        """
120
        self.nr = nr
3✔
121
        self.qd = qd
3✔
122
        self.b = b
3✔
123

124
    def generateNoise(self):
3✔
125
        """ Generate noise time series based on input parameters
126

127
        Returns
128
        -------
129
        time_series: np.array
130
            Time series with colored noise.
131
            len(time_series) == nr
132

133
        """
134
        # Fill wfb array with white noise based on given discrete variance
135
        wfb = np.zeros(self.nr*2)
3✔
136
        wfb[:self.nr] = np.random.normal(0, np.sqrt(self.qd), self.nr)
3✔
137
        # Generate the hfb coefficients based on the noise type
138
        mhb = -self.b/2.0
3✔
139
        hfb = np.zeros(self.nr*2)
3✔
140
        hfb = np.zeros(self.nr*2)
3✔
141
        hfb[0] = 1.0
3✔
142
        indices = np.arange(self.nr-1)
3✔
143
        hfb[1:self.nr] = (mhb+indices)/(indices+1.0)
3✔
144
        hfb[:self.nr] = np.multiply.accumulate(hfb[:self.nr])
3✔
145
        # Perform discrete Fourier transform of wfb and hfb time series
146
        wfb_fft = np.fft.rfft(wfb)
3✔
147
        hfb_fft = np.fft.rfft(hfb)
3✔
148
        # Perform inverse Fourier transform of the product of wfb and hfb FFTs
149
        time_series = np.fft.irfft(wfb_fft*hfb_fft)[:self.nr]
3✔
150
        self.time_series = time_series
3✔
151

152
    def phase_psd_from_qd(self, tau0=1.0):
3✔
153
        """ return phase power spectral density coefficient :math:`g_b`
154
            for noise-type defined by (qd, b, tau0)
155
            where tau0 is the interval between data points
156

157
            Colored noise generated with (qd, b, tau0) parameters will
158
            show a phase power spectral density of
159
            :math:`S_x(f) = Phase_{PSD}(f) = g_b  f^b`
160

161
            [Kasdin1992]_ eqn (39)
162
        """
163
        return self.qd*2.0*pow(2.0*np.pi, self.b)*pow(tau0, self.b+1.0)
3✔
164

165
    def frequency_psd_from_qd(self, tau0=1.0):
3✔
166
        """ return frequency power spectral density coefficient :math:`h_a`
167
            for the noise type defined by (qd, b, tau0)
168

169
            Colored noise generated with (qd, b, tau0) parameters will
170
            show a frequency power spectral density of
171

172
            .. math::
173
            
174
                S_y(f) = Frequency_{PSD}(f) = h_a  f^a
175
            
176
            where the slope :math:`a` comes from the phase PSD slope :math:`b`:
177
            :math:`a = b + 2`
178

179
            [Kasdin1992]_ eqn (39)
180
        """
181
        a = self.b + 2.0
3✔
182
        return self.qd*2.0*pow(2.0*np.pi, a)*pow(tau0, a-1.0)
3✔
183

184
    def adev(self, tau0, tau):
3✔
185
        """ return predicted ADEV of noise-type at given tau
186

187
        """
188
        prefactor = self.adev_from_qd(tau0=tau0, tau=tau)
3✔
189
        c = self.c_avar()
3✔
190
        avar = pow(prefactor, 2)*pow(tau, c)
3✔
191
        return np.sqrt(avar)
3✔
192

193
    def mdev(self, tau0, tau):
3✔
194
        """ return predicted MDEV of noise-type at given tau
195

196
        """
197
        prefactor = self.mdev_from_qd(tau0=tau0, tau=tau)
3✔
198
        c = self.c_mvar()
3✔
199
        mvar = pow(prefactor, 2)*pow(tau, c)
3✔
200
        return np.sqrt(mvar)
3✔
201

202
    def c_avar(self):
3✔
203
        """ return tau exponent "c" for noise type.
204
            AVAR = prefactor * h_a * tau^c
205
        """
206
        if self.b == -4:
3✔
207
            return 1.0
3✔
208
        elif self.b == -3:
3✔
209
            return 0.0
3✔
210
        elif self.b == -2:
3✔
211
            return -1.0
3✔
212
        elif self.b == -1:
3✔
213
            return -2.0
3✔
214
        elif self.b == 0:
3✔
215
            return -2.0
3✔
216

217
    def c_mvar(self):
3✔
218
        """ return tau exponent "c" for noise type.
219
            MVAR = prefactor * h_a * tau^c
220
        """
221
        if self.b == -4:
3✔
222
            return 1.0
3✔
223
        elif self.b == -3:
3✔
224
            return 0.0
3✔
225
        elif self.b == -2:
3✔
226
            return -1.0
3✔
227
        elif self.b == -1:
3✔
228
            return -2.0
3✔
229
        elif self.b == 0:
3✔
230
            return -3.0
3✔
231

232
    def adev_from_qd(self, tau0=1.0, tau=1.0):
3✔
233
        """ prefactor for Allan deviation for noise type defined by (qd, b, tau0)
234

235
        Colored noise generated with (qd, b, tau0) parameters will
236
        show an Allan variance of:
237

238
        .. math::
239
        
240
            AVAR = prefactor \\cdot h_a \\cdot \\tau^c
241

242
        where :math:`a = b + 2` is the slope of the frequency PSD.
243
        and :math:`h_a` is the frequency PSD prefactor :math:`S_y(f) = h_a  f^a`
244

245
        The relation between a, b, c is:
246
        
247
        +---+---+---------+----------+
248
        | a | b | c(AVAR) | c(MVAR)  |
249
        +===+===+=========+==========+
250
        |-2 |-4 |  1      | 1        |
251
        +---+---+---------+----------+
252
        |-1 |-3 |  0      | 0        |
253
        +---+---+---------+----------+
254
        | 0 |-2 | -1      | -1       |
255
        +---+---+---------+----------+
256
        |+1 |-1 | -2      | -2       |
257
        +---+---+---------+----------+
258
        |+2 | 0 | -2      | -3       |
259
        +---+---+---------+----------+
260

261
        Coefficients from [Dawkins2007]_.
262
        
263
        Vernotte2015 Table I
264

265
        """
266
        #g_b = self.phase_psd_from_qd(tau0)
267
        h_b = self.frequency_psd_from_qd(tau0)
3✔
268
        f_h = 0.5/tau0
3✔
269
        if self.b == 0: # WPM
3✔
270
            coeff = 3.0*f_h / (4.0*pow(np.pi, 2))  # E, White PM, tau^-1
3✔
271
        elif self.b == -1:
3✔
272
            # D, Flicker PM, tau^-1
273
            gamma = 0.57721566490153286060651209 # https://en.wikipedia.org/wiki/Euler%27s_constant
3✔
274
            coeff = (3*gamma-np.log(2)+3*np.log(2.0*np.pi*f_h*tau))/(4.0*pow(np.pi, 2))
3✔
275
        elif self.b == -2: # # C, white FM,  1/sqrt(tau)
3✔
276
            coeff = 0.5  
3✔
277
        elif self.b == -3:
3✔
278
            coeff = 2*np.log(2)  # B, flicker FM,  constant ADEV
3✔
279
        elif self.b == -4:
3✔
280
            coeff = 2.0*pow(np.pi, 2)/3.0  # A, RW FM, sqrt(tau)
3✔
281

282
        #return np.sqrt(coeff*g_b*pow(2.0*np.pi, 2))
283
        return np.sqrt(coeff*h_b)
3✔
284

285
    def mdev_from_qd(self, tau0=1.0, tau=1.0):
3✔
286
        # FIXME: tau is unused here - can we remove it?
287
        """ prefactor for Modified Allan deviation for noise
×
288
            type defined by (qd, b, tau0)
×
289

290
            Colored noise generated with (qd, b, tau0) parameters will
×
291
            show an Modified Allan variance of:
×
292
            
293
            .. math::
×
294
            
295
                MVAR = prefactor \\cdot h_a \\cdot \\tau^c
×
296

297
            where :math:`a = b + 2` is the slope of the frequency PSD.
×
298
            and :math:`h_a` is the frequency PSD prefactor :math:`S_y(f) = h_a  f^a`
×
299

300
        """
×
301
        g_b = self.phase_psd_from_qd(tau0)
3✔
302
        h_b = self.frequency_psd_from_qd(tau0)
3✔
303
        # f_h = 0.5/tau0 #unused!?
304
        if self.b == 0:
3✔
305
            coeff = 3.0/(8.0*pow(np.pi, 2))  # E, White PM, tau^-{3/2}
3✔
306
        elif self.b == -1:
3✔
307
            # D, Flicker PM, tau^-1
308
            coeff = (24.0*np.log(2)-9.0*np.log(3))/8.0/pow(np.pi, 2)
3✔
309
        elif self.b == -2:
3✔
310
            # C, white FM,  1/sqrt(tau)
311
            coeff = 0.25
3✔
312
        elif self.b == -3:
3✔
313
            # B, flicker FM,  constant MDEV
314
            #coeff = 2.0*np.log(3.0*pow(3.0, 11.0/16.0)/4.0)
315
            coeff = (27.0*np.log(3)-32.0*np.log(2))/8.0/pow(np.pi, 2)  # Vernotte Table I
3✔
316
            coeff = (27.0/20.0)*np.log(2) # Benkler2015 Table 1
3✔
317
        elif self.b == -4:
3✔
318
            # A, RW FM, sqrt(tau)
319
            coeff = 11.0/20.0*pow(np.pi, 2)
3✔
320

321
        return np.sqrt(coeff*h_b)
3✔
322

323
    def pdev_from_qd(self, tau0=1.0, tau=1.0):
3✔
324
        # FIXME: tau is unused here - can we remove it?
325
        """ prefactor for Parabolic Allan deviation for noise
×
326
            type defined by (qd, b, tau0)
×
327

328
            Colored noise generated with (qd, b, tau0) parameters will
×
329
            show an Parabolic Allan variance of:
×
330
            
331
            .. math::
×
332
            
333
                PVAR = prefactor \\cdot h_a \\cdot \\tau^c
×
334

335
            where :math:`a = b + 2` is the slope of the frequency PSD.
×
336
            and :math:`h_a` is the frequency PSD prefactor :math:`S_y(f) = h_a  f^a`
×
337

338
        """
×
339
        g_b = self.phase_psd_from_qd(tau0)
×
340
        # f_h = 0.5/tau0 #unused!?
341
        if self.b == 0: # WPM, tau^(-3/2)
×
342
            coeff = 3.0/(2.0*pow(np.pi, 2))  # Vernotte Table I
×
343
        elif self.b == -1:
×
344
            # D, Flicker PM, tau^-1
345
            coeff = (3.0*np.log(16)-1.0)/2.0/pow(np.pi, 2)
×
346
        elif self.b == -2: # C, white FM,  1/sqrt(tau)
×
347
            coeff = 3.0/5.0
×
348
        elif self.b == -3:
×
349
            # B, flicker FM,  constant xDEV
350
            coeff = 2.0*(7.0-np.log(16.0) )/5.0
×
351
        elif self.b == -4:
×
352
            # A, RW FM, sqrt(tau)
353
            coeff = 26.0/35.0*pow(np.pi, 2)
×
354

355
        return np.sqrt(coeff*g_b*pow(2.0*np.pi, 2))
×
356

357

358
# end of file noise_kasdin.py
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc