|
a |
|
b/arm_model/delay.py |
|
|
1 |
#!/usr/bin/env python |
|
|
2 |
|
|
|
3 |
import numpy as np |
|
|
4 |
import pylab as pl |
|
|
5 |
import unittest |
|
|
6 |
from logger import Logger |
|
|
7 |
from scipy.interpolate import CubicSpline |
|
|
8 |
|
|
|
9 |
|
|
|
10 |
class Delay: |
|
|
11 |
"""Implements a signal delay. |
|
|
12 |
|
|
|
13 |
We assume that values prior to the delay have a default value (y(t < t_c - |
|
|
14 |
d) = v). Moreover we define a memory variable that is 10 x delay and |
|
|
15 |
restricts the size of the buffer. |
|
|
16 |
|
|
|
17 |
""" |
|
|
18 |
|
|
|
19 |
def __init__(self, delay, default_value): |
|
|
20 |
""" |
|
|
21 |
1D Delay |
|
|
22 |
|
|
|
23 |
Parameters |
|
|
24 |
---------- |
|
|
25 |
delay: the delay of this component |
|
|
26 |
default_value: the default value of the delay |
|
|
27 |
|
|
|
28 |
""" |
|
|
29 |
self.logger = Logger('Delay') |
|
|
30 |
self.t = [] |
|
|
31 |
self.y = [] |
|
|
32 |
self.delay = delay |
|
|
33 |
self.memory = 1000.0 * delay |
|
|
34 |
self.default_value = default_value |
|
|
35 |
self.assert_add = False |
|
|
36 |
|
|
|
37 |
def add(self, t, y): |
|
|
38 |
"""Append the delay buffer with the current value of the signal. |
|
|
39 |
|
|
|
40 |
Restrict the buffer to contain values coresponding to: |
|
|
41 |
|
|
|
42 |
[current time - memory (K x delay)], K~1000 |
|
|
43 |
|
|
|
44 |
Parameters |
|
|
45 |
---------- |
|
|
46 |
|
|
|
47 |
t: time |
|
|
48 |
y: value |
|
|
49 |
|
|
|
50 |
""" |
|
|
51 |
# ensure that time is in the range [t - memory, t] |
|
|
52 |
time = np.array(self.t) |
|
|
53 |
values = np.array(self.y) |
|
|
54 |
mask = (np.array(time) < t) & (np.array(time) > t - self.memory) |
|
|
55 |
self.t = time[mask].tolist() |
|
|
56 |
self.y = values[mask].tolist() |
|
|
57 |
|
|
|
58 |
# append container |
|
|
59 |
self.t.append(t) |
|
|
60 |
self.y.append(y) |
|
|
61 |
|
|
|
62 |
self.assert_add = True |
|
|
63 |
|
|
|
64 |
def get_delayed(self): |
|
|
65 |
"""Get a delaied version of the signal (CubicSpline). Ensure to call add(t, y) |
|
|
66 |
before getting a delayed value. |
|
|
67 |
|
|
|
68 |
Returns |
|
|
69 |
------- |
|
|
70 |
|
|
|
71 |
a delayed version of the signal y |
|
|
72 |
|
|
|
73 |
""" |
|
|
74 |
assert self.assert_add == True, 'Should call add(t, y) before get_delayed()' |
|
|
75 |
|
|
|
76 |
t = self.t |
|
|
77 |
y = self.y |
|
|
78 |
d = self.delay |
|
|
79 |
|
|
|
80 |
# # 2 (this can cause problem during numerical integration) |
|
|
81 |
# if len(t) == 2 and t[-1] - d >= 0: |
|
|
82 |
# return y[0] + (y[1] - y[0]) / (t[1] - t[0]) * (d - t[0]) |
|
|
83 |
|
|
|
84 |
# < 3 |
|
|
85 |
if len(t) < 3 or t[-1] - d < 0: |
|
|
86 |
return self.default_value |
|
|
87 |
|
|
|
88 |
# 3+ |
|
|
89 |
cs = CubicSpline(np.array(t), np.array(y)) |
|
|
90 |
|
|
|
91 |
self.assert_add = False |
|
|
92 |
|
|
|
93 |
return cs(t[-1] - d) |
|
|
94 |
|
|
|
95 |
|
|
|
96 |
class DelayArray: |
|
|
97 |
""" |
|
|
98 |
Implements a N-D signal delay. |
|
|
99 |
|
|
|
100 |
We assume that values prior to the delay have a default value (y(t < t_c - |
|
|
101 |
d) = v). Moreover we define a memory variable that is 10 x delay and |
|
|
102 |
restricts the size of the buffer. |
|
|
103 |
|
|
|
104 |
""" |
|
|
105 |
|
|
|
106 |
def __init__(self, n, delay, default_value): |
|
|
107 |
""" |
|
|
108 |
N-D Delay |
|
|
109 |
|
|
|
110 |
Parameters |
|
|
111 |
---------- |
|
|
112 |
delay: n x 1 array of delays |
|
|
113 |
default_value: n x 1 array of default values |
|
|
114 |
|
|
|
115 |
""" |
|
|
116 |
self.n = n |
|
|
117 |
self.delay_array = [Delay(delay[i], default_value[i]) |
|
|
118 |
for i in range(n)] |
|
|
119 |
|
|
|
120 |
def add(self, t, y): |
|
|
121 |
"""Append the delay buffer with the current value of the signal. |
|
|
122 |
|
|
|
123 |
Restrict the buffer to contain values coresponding to: |
|
|
124 |
|
|
|
125 |
[current time - memory (10.0 x delay)] |
|
|
126 |
|
|
|
127 |
Parameters |
|
|
128 |
---------- |
|
|
129 |
t: time |
|
|
130 |
y: n x 1 array of values |
|
|
131 |
|
|
|
132 |
""" |
|
|
133 |
|
|
|
134 |
n = self.n |
|
|
135 |
assert len(y) == n, 'Dimensions mismatch in y' |
|
|
136 |
|
|
|
137 |
[self.delay_array[i].add(t, y[i]) for i in range(n)] |
|
|
138 |
|
|
|
139 |
def get_delayed(self): |
|
|
140 |
"""Get a delaied version of the signal (CubicSpline). Ensure to call add(t, y) |
|
|
141 |
before getting a delayed value. |
|
|
142 |
|
|
|
143 |
Returns |
|
|
144 |
------- |
|
|
145 |
a delayed version of the signal y |
|
|
146 |
|
|
|
147 |
""" |
|
|
148 |
return [self.delay_array[i].get_delayed() for i in range(self.n)] |
|
|
149 |
|
|
|
150 |
|
|
|
151 |
class TestDelay(unittest.TestCase): |
|
|
152 |
|
|
|
153 |
def test_delay(self): |
|
|
154 |
d = np.pi / 2 |
|
|
155 |
delay = Delay(d, 0.2) |
|
|
156 |
t = np.linspace(0, 2.5 * np.pi, num=100, endpoint=True) |
|
|
157 |
y = [] |
|
|
158 |
yd = [] |
|
|
159 |
for i in t: |
|
|
160 |
y.append(np.sin(i) + 0.1 * np.cos(7 * i)) |
|
|
161 |
delay.add(i, y[-1]) |
|
|
162 |
yd.append(delay.get_delayed()) |
|
|
163 |
|
|
|
164 |
# plot |
|
|
165 |
pl.figure() |
|
|
166 |
pl.plot(t, y, 'r', t, yd, 'b') |
|
|
167 |
pl.title('Delay = ' + str(d)) |
|
|
168 |
pl.xlabel('$t \; (s)$') |
|
|
169 |
pl.ylabel('$y(t)$') |
|
|
170 |
pl.legend(['$y(t)$', '$y(t-d)$']) |
|
|
171 |
|
|
|
172 |
def test_delay_array(self): |
|
|
173 |
n = 2 |
|
|
174 |
delay = [np.pi / 2, np.pi / 4] |
|
|
175 |
default_value = [0.1, 0.2] |
|
|
176 |
delay_array = DelayArray(2, delay, default_value) |
|
|
177 |
t = np.linspace(0, 2.5 * np.pi, num=100, endpoint=True) |
|
|
178 |
y = [] |
|
|
179 |
yd = [] |
|
|
180 |
for i in t: |
|
|
181 |
y1 = np.sin(i) + 0.1 * np.cos(7 * i) |
|
|
182 |
y2 = np.sin(i) - 0.1 * np.cos(7 * i) |
|
|
183 |
y.append([y1, y2]) |
|
|
184 |
delay_array.add(i, y[-1]) |
|
|
185 |
yd.append(delay_array.get_delayed()) |
|
|
186 |
|
|
|
187 |
# plot |
|
|
188 |
pl.figure() |
|
|
189 |
pl.plot(t, np.array(y), 'r', t, np.array(yd), 'b') |
|
|
190 |
pl.title('Delay = ' + str(delay)) |
|
|
191 |
pl.xlabel('$t \; (s)$') |
|
|
192 |
pl.ylabel('$y(t)$') |
|
|
193 |
pl.legend(['$y(t)$', '$y(t-d)$']) |
|
|
194 |
|
|
|
195 |
|
|
|
196 |
if __name__ == '__main__': |
|
|
197 |
unittest.main() |