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()