This repository has been archived by the owner on May 31, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 13
/
signals.py
157 lines (142 loc) · 6.01 KB
/
signals.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# -*- coding: utf-8 -*-
"""
This module implements the signals class and its derivatives Signals are
dynamic quantities with associated uncertainties. A signal has to be defined
together with a time axis.
IMPORTANT NOTE: This module is experimental!
"""
import numpy as np
import scipy.signal as dsp
from matplotlib.pyplot import figure, plot, fill_between, xlabel, ylabel, legend
from PyDynamic.misc.testsignals import rect
from PyDynamic.uncertainty.propagate_MonteCarlo import MC
from PyDynamic.uncertainty.propagate_filter import FIRuncFilter
__all__ = ["Signal"]
class Signal:
"""The base class which defines the interfaces and default behaviour."""
unit_time = ""
unit_values = ""
name = ""
def __init__(self, time, values, Ts=None, Fs=None, uncertainty=None):
if len(values.shape) > 1:
raise NotImplementedError("Multivariate signals are not "
"implemented yet.")
assert (len(time) == len(values))
self.time = time
self.values = values
# set sampling interval and frequency
if (Ts is None) and (Fs is None):
self.Ts = np.unique(np.diff(self.time)).mean()
self.Fs = 1 / self.Ts
elif isinstance(Ts, float):
self.Ts = Ts
if Fs is None:
self.Fs = 1 / Ts
else:
assert (np.abs(
Fs * self.Ts - 1) < 1e-5), "Sampling interval and " \
"sampling frequency are " \
"inconsistent."
# set initial uncertainty
if isinstance(uncertainty, float):
self.uncertainty = np.ones_like(values) * uncertainty
elif isinstance(uncertainty, np.ndarray):
uncertainty = uncertainty.squeeze()
if len(uncertainty.shape) == 1:
assert (len(uncertainty) == len(time))
else:
assert (uncertainty.shape[0] == uncertainty.shape[1])
self.uncertainty = uncertainty
else:
self.uncertainty = np.zeros_like(values)
self.set_labels()
def set_labels(self, unit_time=None, unit_values=None, name_values=None):
if isinstance(unit_time, str):
self.unit_time = unit_time
else:
self.unit_time = "s"
if isinstance(unit_values, str):
self.unit_values = unit_values
else:
self.unit_values = "a.u."
if isinstance(name_values, str):
self.name = name_values
else:
self.name = "signal"
def plot(self, fignr=1, figsize=(10, 8)):
figure(fignr, figsize=figsize)
plot(self.time, self.values, label=self.name)
fill_between(self.time, self.values - self.uncertainty,
self.values + self.uncertainty, color="gray", alpha=0.2)
xlabel("time / %s" % self.unit_time)
ylabel("%s / %s" % (self.name, self.unit_values))
legend(loc="best")
def plot_uncertainty(self, fignr=2, **kwargs):
figure(fignr, **kwargs)
plot(self.time, self.uncertainty,
label="uncertainty associated with %s" % self.name)
xlabel("time / %s" % self.unit_time)
ylabel("uncertainty / %s" % self.unit_values)
legend(loc="best")
def apply_filter(self, b, a=1, filter_uncertainty=None,
MonteCarloRuns=None):
"""Apply digital filter (b, a) to the signal values
Apply digital filter (b, a) to the signal values and propagate the
uncertainty associated with the signal.
Parameters
----------
b: np.ndarray
filter numerator coefficients
a: np.ndarray
filter denominator coefficients, use a=1 for FIR-type filter
filter_uncertainty: np.ndarray
covariance matrix associated with filter coefficients,
Uab=None if no uncertainty associated with filter
MonteCarloRuns: int
number of Monte Carlo runs, if `None` then GUM linearization
will be used
Returns
-------
no return variables
"""
if isinstance(a, list):
a = np.array(a)
if not (isinstance(a, np.ndarray)): # FIR type filter
if len(self.uncertainty.shape) == 1:
if not isinstance(MonteCarloRuns, int):
self.values, self.uncertainty = \
FIRuncFilter(self.values, self.uncertainty, b,
Utheta=filter_uncertainty)
else:
self.values, self.uncertainty = \
MC(self.values, self.uncertainty, b, a,
filter_uncertainty, runs=MonteCarloRuns)
else:
if not isinstance(MonteCarloRuns, int):
MonteCarloRuns = 10000
self.values, self.uncertainty = \
MC(self.values, self.uncertainty, b, a, filter_uncertainty,
runs=MonteCarloRuns)
else: # IIR-type filter
if not isinstance(MonteCarloRuns, int):
MonteCarloRuns = 10000
self.values, self.uncertainty = \
MC(self.values, self.uncertainty, b, a, filter_uncertainty,
runs=MonteCarloRuns)
if __name__ == "__main__":
N = 1024
delta_t = 0.01
t = np.arange(0, N * delta_t, delta_t)
x = rect(t, delta_t * N // 4, delta_t * N // 4 * 3)
ux = 0.02
signal = Signal(t, x, Ts=delta_t, uncertainty=ux)
b = dsp.firls(15,
[0, 0.2 * signal.Fs / 2, 0.25 * signal.Fs / 2, signal.Fs / 2],
[1, 1, 0, 0], nyq=signal.Fs / 2)
Ub = np.diag(b * 1e-1)
signal.apply_filter(b, filter_uncertainty=Ub)
signal.plot_uncertainty()
bl, al = dsp.bessel(4, 0.2)
Ul = np.diag(np.r_[al[1:] * 1e-3, bl * 1e-2] ** 2)
signal.apply_filter(bl, al, filter_uncertainty=Ul)
signal.plot_uncertainty(fignr=3)