pathsim 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pathsim/__init__.py +3 -0
- pathsim/blocks/__init__.py +14 -0
- pathsim/blocks/_block.py +209 -0
- pathsim/blocks/adder.py +30 -0
- pathsim/blocks/amplifier.py +34 -0
- pathsim/blocks/delay.py +70 -0
- pathsim/blocks/differentiator.py +70 -0
- pathsim/blocks/function.py +82 -0
- pathsim/blocks/integrator.py +66 -0
- pathsim/blocks/lti.py +155 -0
- pathsim/blocks/multiplier.py +30 -0
- pathsim/blocks/ode.py +86 -0
- pathsim/blocks/rf/__init__.py +4 -0
- pathsim/blocks/rf/filters.py +169 -0
- pathsim/blocks/rf/noise.py +218 -0
- pathsim/blocks/rf/sources.py +163 -0
- pathsim/blocks/rf/wienerhammerstein.py +338 -0
- pathsim/blocks/rng.py +57 -0
- pathsim/blocks/scope.py +224 -0
- pathsim/blocks/sources.py +71 -0
- pathsim/blocks/spectrum.py +316 -0
- pathsim/connection.py +112 -0
- pathsim/simulation.py +652 -0
- pathsim/solvers/__init__.py +25 -0
- pathsim/solvers/_solver.py +403 -0
- pathsim/solvers/bdf.py +240 -0
- pathsim/solvers/dirk2.py +101 -0
- pathsim/solvers/dirk3.py +86 -0
- pathsim/solvers/esdirk32.py +131 -0
- pathsim/solvers/esdirk4.py +99 -0
- pathsim/solvers/esdirk43.py +139 -0
- pathsim/solvers/esdirk54.py +141 -0
- pathsim/solvers/esdirk85.py +200 -0
- pathsim/solvers/euler.py +81 -0
- pathsim/solvers/rk4.py +61 -0
- pathsim/solvers/rkbs32.py +101 -0
- pathsim/solvers/rkck54.py +108 -0
- pathsim/solvers/rkdp54.py +111 -0
- pathsim/solvers/rkdp87.py +116 -0
- pathsim/solvers/rkf45.py +102 -0
- pathsim/solvers/rkf78.py +111 -0
- pathsim/solvers/rkv65.py +103 -0
- pathsim/solvers/ssprk22.py +62 -0
- pathsim/solvers/ssprk33.py +65 -0
- pathsim/solvers/ssprk34.py +74 -0
- pathsim/subsystem.py +267 -0
- pathsim/utils/__init__.py +0 -0
- pathsim/utils/adaptivebuffer.py +87 -0
- pathsim/utils/anderson.py +180 -0
- pathsim/utils/funcs.py +205 -0
- pathsim/utils/gilbert.py +110 -0
- pathsim/utils/progresstracker.py +90 -0
- pathsim/utils/realtimeplotter.py +230 -0
- pathsim/utils/statespacerealizations.py +116 -0
- pathsim/utils/waveforms.py +36 -0
- pathsim-0.2.0.dist-info/LICENSE.txt +21 -0
- pathsim-0.2.0.dist-info/METADATA +149 -0
- pathsim-0.2.0.dist-info/RECORD +109 -0
- pathsim-0.2.0.dist-info/WHEEL +5 -0
- pathsim-0.2.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/blocks/__init__.py +0 -0
- tests/blocks/test_adder.py +85 -0
- tests/blocks/test_amplifier.py +66 -0
- tests/blocks/test_block.py +138 -0
- tests/blocks/test_delay.py +122 -0
- tests/blocks/test_differentiator.py +102 -0
- tests/blocks/test_function.py +165 -0
- tests/blocks/test_integrator.py +92 -0
- tests/blocks/test_lti.py +162 -0
- tests/blocks/test_multiplier.py +87 -0
- tests/blocks/test_ode.py +125 -0
- tests/blocks/test_rng.py +109 -0
- tests/blocks/test_scope.py +196 -0
- tests/blocks/test_sources.py +119 -0
- tests/blocks/test_spectrum.py +119 -0
- tests/solvers/__init__.py +0 -0
- tests/solvers/test_bdf.py +364 -0
- tests/solvers/test_dirk2.py +138 -0
- tests/solvers/test_dirk3.py +137 -0
- tests/solvers/test_esdirk32.py +158 -0
- tests/solvers/test_esdirk4.py +138 -0
- tests/solvers/test_esdirk43.py +158 -0
- tests/solvers/test_esdirk54.py +160 -0
- tests/solvers/test_esdirk85.py +157 -0
- tests/solvers/test_euler.py +223 -0
- tests/solvers/test_rk4.py +138 -0
- tests/solvers/test_rkbs32.py +159 -0
- tests/solvers/test_rkck54.py +157 -0
- tests/solvers/test_rkdp54.py +159 -0
- tests/solvers/test_rkdp87.py +157 -0
- tests/solvers/test_rkf45.py +159 -0
- tests/solvers/test_rkf78.py +160 -0
- tests/solvers/test_rkv65.py +160 -0
- tests/solvers/test_solver.py +119 -0
- tests/solvers/test_ssprk22.py +136 -0
- tests/solvers/test_ssprk33.py +136 -0
- tests/solvers/test_ssprk34.py +136 -0
- tests/test_connection.py +176 -0
- tests/test_simulation.py +271 -0
- tests/test_subsystem.py +182 -0
- tests/utils/__init__.py +0 -0
- tests/utils/test_adaptivebuffer.py +111 -0
- tests/utils/test_anderson.py +142 -0
- tests/utils/test_funcs.py +143 -0
- tests/utils/test_gilbert.py +108 -0
- tests/utils/test_progresstracker.py +144 -0
- tests/utils/test_realtimeplotter.py +122 -0
- tests/utils/test_statespacerealizations.py +107 -0
tests/blocks/test_lti.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## TESTS FOR
|
|
4
|
+
## 'blocks.lti.py'
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
import unittest
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from pathsim.blocks.lti import StateSpace, TransferFunction
|
|
16
|
+
|
|
17
|
+
#base solver for testing
|
|
18
|
+
from pathsim.solvers._solver import Solver
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# TESTS ================================================================================
|
|
22
|
+
|
|
23
|
+
class TestStateSpace(unittest.TestCase):
|
|
24
|
+
"""
|
|
25
|
+
Test the implementation of the 'StateSpace' block class
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def test_init(self):
|
|
29
|
+
|
|
30
|
+
#test default initialization
|
|
31
|
+
S = StateSpace()
|
|
32
|
+
self.assertEqual(S.initial_value, 0.0)
|
|
33
|
+
self.assertEqual(S.engine, None)
|
|
34
|
+
self.assertEqual(S.inputs, {0:0.0})
|
|
35
|
+
self.assertEqual(S.outputs, {0:0.0})
|
|
36
|
+
|
|
37
|
+
#test special initialization (siso)
|
|
38
|
+
S = StateSpace(A=np.eye(2),
|
|
39
|
+
B=np.ones(2),
|
|
40
|
+
C=np.ones(2),
|
|
41
|
+
D=1,
|
|
42
|
+
initial_value=None)
|
|
43
|
+
self.assertTrue(np.all(S.initial_value == np.zeros(2)))
|
|
44
|
+
self.assertEqual(S.inputs, {0:0.0})
|
|
45
|
+
self.assertEqual(S.outputs, {0:0.0})
|
|
46
|
+
|
|
47
|
+
#test special initialization (mimo)
|
|
48
|
+
S = StateSpace(A=np.eye(2),
|
|
49
|
+
B=np.ones((2, 2)),
|
|
50
|
+
C=np.ones((2, 2)),
|
|
51
|
+
D=np.ones((2, 2)),
|
|
52
|
+
initial_value=np.ones(2))
|
|
53
|
+
self.assertTrue(np.all(S.initial_value == np.ones(2)))
|
|
54
|
+
self.assertEqual(S.inputs, {0:0.0, 1:0.0})
|
|
55
|
+
self.assertEqual(S.outputs, {0:0.0, 1:0.0})
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_len(self):
|
|
59
|
+
|
|
60
|
+
#no direct passthrough (siso)
|
|
61
|
+
S = StateSpace(D=0)
|
|
62
|
+
self.assertEqual(len(S), 0)
|
|
63
|
+
|
|
64
|
+
#no direct passthrough (mimo)
|
|
65
|
+
S = StateSpace(D=np.zeros(2))
|
|
66
|
+
self.assertEqual(len(S), 0)
|
|
67
|
+
|
|
68
|
+
#direct passthrough (siso)
|
|
69
|
+
S = StateSpace(D=3)
|
|
70
|
+
self.assertEqual(len(S), 1)
|
|
71
|
+
|
|
72
|
+
#direct passthrough (mimo)
|
|
73
|
+
S = StateSpace(D=np.array([0, 5]))
|
|
74
|
+
self.assertEqual(len(S), 1)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_str(self):
|
|
78
|
+
|
|
79
|
+
S = StateSpace()
|
|
80
|
+
|
|
81
|
+
#test default str method
|
|
82
|
+
self.assertEqual(str(S), "StateSpace")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_set_solver(self):
|
|
86
|
+
|
|
87
|
+
S = StateSpace(initial_value=1.0)
|
|
88
|
+
|
|
89
|
+
#test that no solver is initialized
|
|
90
|
+
self.assertEqual(S.engine, None)
|
|
91
|
+
|
|
92
|
+
S.set_solver(Solver, tolerance_lte=1e-6)
|
|
93
|
+
|
|
94
|
+
#test that solver is now available
|
|
95
|
+
self.assertTrue(isinstance(S.engine, Solver))
|
|
96
|
+
|
|
97
|
+
#test that solver parametes have been set
|
|
98
|
+
self.assertEqual(S.engine.tolerance_lte, 1e-6)
|
|
99
|
+
self.assertEqual(S.engine.initial_value, 1.0)
|
|
100
|
+
|
|
101
|
+
#test that jacobian has been generated correctly
|
|
102
|
+
self.assertEqual(S.engine.jac(0, 0, 0), S.A)
|
|
103
|
+
|
|
104
|
+
S.set_solver(Solver, tolerance_lte=1e-3)
|
|
105
|
+
|
|
106
|
+
#test that solver tolerance is changed
|
|
107
|
+
self.assertEqual(S.engine.tolerance_lte, 1e-3)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_update(self):
|
|
111
|
+
|
|
112
|
+
S = StateSpace(initial_value=1.1)
|
|
113
|
+
S.set_solver(Solver)
|
|
114
|
+
|
|
115
|
+
#test if output is zero
|
|
116
|
+
self.assertEqual(S.get(0), 0.0)
|
|
117
|
+
|
|
118
|
+
S.set(0, 3.3)
|
|
119
|
+
err = S.update(0)
|
|
120
|
+
|
|
121
|
+
#test if error is correctly 0
|
|
122
|
+
self.assertGreater(err, 0.0)
|
|
123
|
+
|
|
124
|
+
#test if engine state is calculated correctly
|
|
125
|
+
self.assertAlmostEqual(S.get(0), 2.2, 8)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class TtestTransferFunction(unittest.TestCase):
|
|
129
|
+
"""
|
|
130
|
+
Test the implementation of the 'TransferFunction' block class
|
|
131
|
+
|
|
132
|
+
inherits most methods from the 'StateSpace' block, so only
|
|
133
|
+
testing ot the initialization is required
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
def test_init(self):
|
|
137
|
+
|
|
138
|
+
#test default initialization
|
|
139
|
+
with self.assertRaises(ValueError):
|
|
140
|
+
T = TransferFunction()
|
|
141
|
+
|
|
142
|
+
#test specific initialization (siso)
|
|
143
|
+
T = TransferFunction(Poles=2, Residues=0.5, Const=5.5)
|
|
144
|
+
self.assertEqual(T.A, 2)
|
|
145
|
+
self.assertEqual(T.B, 1)
|
|
146
|
+
self.assertEqual(T.C, 0.5)
|
|
147
|
+
self.assertEqual(T.D, 5.5)
|
|
148
|
+
|
|
149
|
+
#test specific initialization (mimo)
|
|
150
|
+
T = TransferFunction(Poles=np.array([1, 2]),
|
|
151
|
+
Residues=2*np.ones((2, 2)),
|
|
152
|
+
Const=np.ones(2))
|
|
153
|
+
self.assertTrue(np.all(T.A == np.diag(np.array([1, 2]))))
|
|
154
|
+
self.assertTrue(np.all(T.B == np.ones(2)))
|
|
155
|
+
self.assertTrue(np.all(T.C == 2*np.ones(2)))
|
|
156
|
+
self.assertTrue(np.all(T.D == np.ones(2)))
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# RUN TESTS LOCALLY ====================================================================
|
|
160
|
+
|
|
161
|
+
if __name__ == '__main__':
|
|
162
|
+
unittest.main(verbosity=2)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## TESTS FOR
|
|
4
|
+
## 'blocks.multiplier.py'
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
import unittest
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from pathsim.blocks.multiplier import Multiplier
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# TESTS ================================================================================
|
|
19
|
+
|
|
20
|
+
class TestMultiplier(unittest.TestCase):
|
|
21
|
+
"""
|
|
22
|
+
Test the implementation of the 'Multiplier' block class
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def test_str(self):
|
|
26
|
+
|
|
27
|
+
M = Multiplier()
|
|
28
|
+
|
|
29
|
+
#test default str method
|
|
30
|
+
self.assertEqual(str(M), "Multiplier")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_update_single(self):
|
|
34
|
+
|
|
35
|
+
M = Multiplier()
|
|
36
|
+
|
|
37
|
+
#set block inputs
|
|
38
|
+
M.set(0, 1)
|
|
39
|
+
|
|
40
|
+
#update block
|
|
41
|
+
err = M.update(None)
|
|
42
|
+
|
|
43
|
+
#test if update was correct
|
|
44
|
+
self.assertEqual(M.get(0), 1)
|
|
45
|
+
|
|
46
|
+
#test if error was computed correctly
|
|
47
|
+
self.assertGreater(err, 0)
|
|
48
|
+
|
|
49
|
+
#update block again
|
|
50
|
+
err = M.update(None)
|
|
51
|
+
|
|
52
|
+
#test error, now should be 0
|
|
53
|
+
self.assertEqual(err, 0)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_update_multi(self):
|
|
57
|
+
|
|
58
|
+
M = Multiplier()
|
|
59
|
+
|
|
60
|
+
#set block inputs
|
|
61
|
+
M.set(0, 1)
|
|
62
|
+
M.set(1, 2.0)
|
|
63
|
+
M.set(2, 3.1)
|
|
64
|
+
|
|
65
|
+
#update block
|
|
66
|
+
err = M.update(None)
|
|
67
|
+
|
|
68
|
+
#test if update was correct
|
|
69
|
+
self.assertEqual(M.get(0), 6.2)
|
|
70
|
+
|
|
71
|
+
#test if error was computed correctly
|
|
72
|
+
self.assertGreater(err, 0)
|
|
73
|
+
|
|
74
|
+
#update block again
|
|
75
|
+
err = M.update(None)
|
|
76
|
+
|
|
77
|
+
#test error, now should be 0
|
|
78
|
+
self.assertEqual(err, 0)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# RUN TESTS LOCALLY ====================================================================
|
|
85
|
+
|
|
86
|
+
if __name__ == '__main__':
|
|
87
|
+
unittest.main(verbosity=2)
|
tests/blocks/test_ode.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## TESTS FOR
|
|
4
|
+
## 'blocks.ode.py'
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
import unittest
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from pathsim.blocks.ode import ODE
|
|
16
|
+
|
|
17
|
+
#base solver for testing
|
|
18
|
+
from pathsim.solvers._solver import Solver
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# TESTS ================================================================================
|
|
22
|
+
|
|
23
|
+
class TestODE(unittest.TestCase):
|
|
24
|
+
"""
|
|
25
|
+
Test the implementation of the 'ODE' block class
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def test_init(self):
|
|
29
|
+
|
|
30
|
+
#test default initialization
|
|
31
|
+
D = ODE()
|
|
32
|
+
|
|
33
|
+
self.assertEqual(D.engine, None)
|
|
34
|
+
self.assertEqual(D.jac, None)
|
|
35
|
+
self.assertEqual(D.initial_value, 0.0)
|
|
36
|
+
|
|
37
|
+
#test special initialization
|
|
38
|
+
def f(x, u, t):
|
|
39
|
+
return -x**2
|
|
40
|
+
def j(x, u, t):
|
|
41
|
+
return -2*x
|
|
42
|
+
|
|
43
|
+
D = ODE(func=f, initial_value=1.0, jac=j)
|
|
44
|
+
|
|
45
|
+
#test that ode function is correctly assigned
|
|
46
|
+
self.assertEqual(D.func(1, 0, 0), f(1, 0, 0))
|
|
47
|
+
self.assertEqual(D.func(2, 0, 0), f(2, 0, 0))
|
|
48
|
+
self.assertEqual(D.func(3, 0, 0), f(3, 0, 0))
|
|
49
|
+
|
|
50
|
+
#test that ode jacobian is correctly assigned
|
|
51
|
+
self.assertEqual(D.jac(1, 0, 0), j(1, 0, 0))
|
|
52
|
+
self.assertEqual(D.jac(2, 0, 0), j(2, 0, 0))
|
|
53
|
+
self.assertEqual(D.jac(3, 0, 0), j(3, 0, 0))
|
|
54
|
+
|
|
55
|
+
self.assertEqual(D.initial_value, 1.0)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_len(self):
|
|
59
|
+
|
|
60
|
+
D = ODE()
|
|
61
|
+
|
|
62
|
+
#has direct passthrough
|
|
63
|
+
self.assertEqual(len(D), 0)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_str(self):
|
|
67
|
+
|
|
68
|
+
D = ODE()
|
|
69
|
+
|
|
70
|
+
#test default str method
|
|
71
|
+
self.assertEqual(str(D), "ODE")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_set_solver(self):
|
|
75
|
+
|
|
76
|
+
def f(x, u, t):
|
|
77
|
+
return -x**2
|
|
78
|
+
def j(x, u, t):
|
|
79
|
+
return -2*x
|
|
80
|
+
|
|
81
|
+
D = ODE(func=f, initial_value=1.0, jac=j)
|
|
82
|
+
|
|
83
|
+
#test that no solver is initialized
|
|
84
|
+
self.assertEqual(D.engine, None)
|
|
85
|
+
|
|
86
|
+
D.set_solver(Solver, tolerance_lte=1e-6)
|
|
87
|
+
|
|
88
|
+
#test that solver is now available
|
|
89
|
+
self.assertTrue(isinstance(D.engine, Solver))
|
|
90
|
+
self.assertEqual(D.engine.tolerance_lte, 1e-6)
|
|
91
|
+
|
|
92
|
+
#test that solver function is correctly assigned
|
|
93
|
+
self.assertEqual(D.engine.func(1, 0, 0), f(1, 0, 0))
|
|
94
|
+
self.assertEqual(D.engine.func(2, 0, 0), f(2, 0, 0))
|
|
95
|
+
self.assertEqual(D.engine.func(3, 0, 0), f(3, 0, 0))
|
|
96
|
+
|
|
97
|
+
#test that solver jacobian is correctly assigned
|
|
98
|
+
self.assertEqual(D.engine.jac(1, 0, 0), j(1, 0, 0))
|
|
99
|
+
self.assertEqual(D.engine.jac(2, 0, 0), j(2, 0, 0))
|
|
100
|
+
self.assertEqual(D.engine.jac(3, 0, 0), j(3, 0, 0))
|
|
101
|
+
|
|
102
|
+
D.set_solver(Solver, tolerance_lte=1e-3)
|
|
103
|
+
|
|
104
|
+
#test that solver tolerance is changed
|
|
105
|
+
self.assertEqual(D.engine.tolerance_lte, 1e-3)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_update(self):
|
|
109
|
+
|
|
110
|
+
D = ODE()
|
|
111
|
+
D.set_solver(Solver)
|
|
112
|
+
|
|
113
|
+
err = D.update(0)
|
|
114
|
+
|
|
115
|
+
#test if error is correctly 0
|
|
116
|
+
self.assertEqual(err, 0.0)
|
|
117
|
+
|
|
118
|
+
#test if engine state is retrieved correctly
|
|
119
|
+
self.assertEqual(D.get(0), 0.0)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# RUN TESTS LOCALLY ====================================================================
|
|
123
|
+
|
|
124
|
+
if __name__ == '__main__':
|
|
125
|
+
unittest.main(verbosity=2)
|
tests/blocks/test_rng.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## TESTS FOR
|
|
4
|
+
## 'blocks.rng.py'
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
import unittest
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from pathsim.blocks.rng import RNG
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# TESTS ================================================================================
|
|
19
|
+
|
|
20
|
+
class TestRNG(unittest.TestCase):
|
|
21
|
+
"""
|
|
22
|
+
Test the implementation of the 'RNG' block class
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def test_init(self):
|
|
26
|
+
|
|
27
|
+
R = RNG()
|
|
28
|
+
|
|
29
|
+
self.assertEqual(R.sampling_rate, None)
|
|
30
|
+
|
|
31
|
+
R = RNG(sampling_rate=1)
|
|
32
|
+
|
|
33
|
+
self.assertEqual(R.n_samples, 0)
|
|
34
|
+
self.assertEqual(R.sampling_rate, 1)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_len(self):
|
|
38
|
+
|
|
39
|
+
R = RNG()
|
|
40
|
+
|
|
41
|
+
#no passthrough
|
|
42
|
+
self.assertEqual(len(R), 0)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_str(self):
|
|
46
|
+
|
|
47
|
+
R = RNG()
|
|
48
|
+
|
|
49
|
+
#test default str method
|
|
50
|
+
self.assertEqual(str(R), "RNG")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_reset(self):
|
|
54
|
+
|
|
55
|
+
R = RNG()
|
|
56
|
+
|
|
57
|
+
for t in range(10):
|
|
58
|
+
R.sample(t)
|
|
59
|
+
|
|
60
|
+
R.reset()
|
|
61
|
+
|
|
62
|
+
#test if reset worked
|
|
63
|
+
self.assertEqual(R.n_samples, 0)
|
|
64
|
+
self.assertEqual(R.get(0), 0.0)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_sample(self):
|
|
68
|
+
|
|
69
|
+
#first test default 'sampling_rate=None'
|
|
70
|
+
R = RNG()
|
|
71
|
+
|
|
72
|
+
for t in range(10):
|
|
73
|
+
|
|
74
|
+
#test sample counter
|
|
75
|
+
self.assertEqual(R.n_samples, t)
|
|
76
|
+
|
|
77
|
+
old = R.get(0)
|
|
78
|
+
|
|
79
|
+
R.sample(t)
|
|
80
|
+
|
|
81
|
+
#test if new random value is sampled
|
|
82
|
+
self.assertNotEqual(old, R.get(0))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
#next test finite sampling rate (samples every two seconds)
|
|
86
|
+
R = RNG(sampling_rate=0.5)
|
|
87
|
+
|
|
88
|
+
for t in range(10):
|
|
89
|
+
|
|
90
|
+
#test sample counter
|
|
91
|
+
self.assertEqual(R.n_samples, t//2)
|
|
92
|
+
|
|
93
|
+
old = R.get(0)
|
|
94
|
+
|
|
95
|
+
R.sample(t)
|
|
96
|
+
|
|
97
|
+
if t%2 == 0:
|
|
98
|
+
#test if value remains the same is sampled
|
|
99
|
+
self.assertEqual(old, R.get(0))
|
|
100
|
+
|
|
101
|
+
else:
|
|
102
|
+
#test if new random value is sampled
|
|
103
|
+
self.assertNotEqual(old, R.get(0))
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# RUN TESTS LOCALLY ====================================================================
|
|
107
|
+
|
|
108
|
+
if __name__ == '__main__':
|
|
109
|
+
unittest.main(verbosity=2)
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
########################################################################################
|
|
2
|
+
##
|
|
3
|
+
## TESTS FOR
|
|
4
|
+
## 'blocks.scope.py'
|
|
5
|
+
##
|
|
6
|
+
## Milan Rother 2024
|
|
7
|
+
##
|
|
8
|
+
########################################################################################
|
|
9
|
+
|
|
10
|
+
# IMPORTS ==============================================================================
|
|
11
|
+
|
|
12
|
+
import unittest
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from pathsim.blocks.scope import Scope, RealtimeScope
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# TESTS ================================================================================
|
|
19
|
+
|
|
20
|
+
class TestScope(unittest.TestCase):
|
|
21
|
+
"""
|
|
22
|
+
Test the implementation of the 'Scope' block class
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def test_init(self):
|
|
26
|
+
|
|
27
|
+
#test default initialization
|
|
28
|
+
S = Scope()
|
|
29
|
+
|
|
30
|
+
self.assertEqual(S.sampling_rate, None)
|
|
31
|
+
self.assertEqual(S.t_wait, 0.0)
|
|
32
|
+
self.assertEqual(S.labels, [])
|
|
33
|
+
self.assertEqual(S.recording, {})
|
|
34
|
+
|
|
35
|
+
#test specific initialization
|
|
36
|
+
S = Scope(sampling_rate=1, t_wait=1.0, labels=["1", "2"])
|
|
37
|
+
|
|
38
|
+
self.assertEqual(S.sampling_rate, 1)
|
|
39
|
+
self.assertEqual(S.t_wait, 1.0)
|
|
40
|
+
self.assertEqual(S.labels, ["1", "2"])
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_len(self):
|
|
44
|
+
|
|
45
|
+
S = Scope()
|
|
46
|
+
|
|
47
|
+
#no passthrough
|
|
48
|
+
self.assertEqual(len(S), 0)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_str(self):
|
|
52
|
+
|
|
53
|
+
S = Scope()
|
|
54
|
+
|
|
55
|
+
#test default str method
|
|
56
|
+
self.assertEqual(str(S), "Scope")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_reset(self):
|
|
60
|
+
|
|
61
|
+
S = Scope()
|
|
62
|
+
|
|
63
|
+
for t in range(10):
|
|
64
|
+
|
|
65
|
+
S.set(0, t)
|
|
66
|
+
S.sample(t)
|
|
67
|
+
|
|
68
|
+
#test that we have some recording
|
|
69
|
+
self.assertGreater(len(S.recording), 0)
|
|
70
|
+
|
|
71
|
+
S.reset()
|
|
72
|
+
|
|
73
|
+
#test if reset was successful
|
|
74
|
+
self.assertEqual(S.recording, {})
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_sample(self):
|
|
78
|
+
|
|
79
|
+
#single input default initialization
|
|
80
|
+
S = Scope()
|
|
81
|
+
|
|
82
|
+
for t in range(10):
|
|
83
|
+
|
|
84
|
+
S.set(0, t)
|
|
85
|
+
S.sample(t)
|
|
86
|
+
|
|
87
|
+
#test most recent recording
|
|
88
|
+
self.assertEqual(S.recording[t], t)
|
|
89
|
+
|
|
90
|
+
#multi input default initialization
|
|
91
|
+
S = Scope()
|
|
92
|
+
|
|
93
|
+
for t in range(10):
|
|
94
|
+
|
|
95
|
+
S.set(0, t)
|
|
96
|
+
S.set(1, 2*t)
|
|
97
|
+
S.set(2, 3*t)
|
|
98
|
+
S.sample(t)
|
|
99
|
+
|
|
100
|
+
#test most recent recording
|
|
101
|
+
self.assertTrue(np.all(np.equal(S.recording[t], [t, 2*t, 3*t])))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_read(self):
|
|
105
|
+
|
|
106
|
+
_time = np.arange(10)
|
|
107
|
+
|
|
108
|
+
#single input default initialization
|
|
109
|
+
S = Scope()
|
|
110
|
+
|
|
111
|
+
for t in _time:
|
|
112
|
+
|
|
113
|
+
S.set(0, t)
|
|
114
|
+
S.sample(t)
|
|
115
|
+
|
|
116
|
+
time, result = S.read()
|
|
117
|
+
|
|
118
|
+
#test if time was recorded correctly
|
|
119
|
+
self.assertTrue(np.all(np.equal(time, _time)))
|
|
120
|
+
|
|
121
|
+
#test if input was recorded correctly
|
|
122
|
+
self.assertTrue(np.all(np.equal(result, _time)))
|
|
123
|
+
|
|
124
|
+
#multi input default initialization
|
|
125
|
+
S = Scope()
|
|
126
|
+
|
|
127
|
+
for t in _time:
|
|
128
|
+
|
|
129
|
+
S.set(0, t)
|
|
130
|
+
S.set(1, 2*t)
|
|
131
|
+
S.set(2, 3*t)
|
|
132
|
+
S.sample(t)
|
|
133
|
+
|
|
134
|
+
time, result = S.read()
|
|
135
|
+
|
|
136
|
+
#test if time was recorded correctly
|
|
137
|
+
self.assertTrue(np.all(np.equal(time, _time)))
|
|
138
|
+
|
|
139
|
+
#test if multi input was recorded correctly
|
|
140
|
+
self.assertTrue(np.all(np.equal(result, [_time, 2*_time, 3*_time])))
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_sampling_rate(self):
|
|
144
|
+
|
|
145
|
+
_time = np.arange(10)
|
|
146
|
+
|
|
147
|
+
#single input special sampling rate
|
|
148
|
+
S = Scope(sampling_rate=0.5)
|
|
149
|
+
|
|
150
|
+
for t in _time:
|
|
151
|
+
|
|
152
|
+
S.set(0, t)
|
|
153
|
+
S.sample(t)
|
|
154
|
+
|
|
155
|
+
time, result = S.read()
|
|
156
|
+
|
|
157
|
+
#test if time was recorded correctly
|
|
158
|
+
self.assertTrue(np.all(np.equal(time, _time[1::2])))
|
|
159
|
+
|
|
160
|
+
#test if input was recorded correctly
|
|
161
|
+
self.assertTrue(np.all(np.equal(result, _time[1::2])))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def test_t_wait(self):
|
|
165
|
+
|
|
166
|
+
_time = np.arange(10)
|
|
167
|
+
|
|
168
|
+
#single input special t_wait
|
|
169
|
+
S = Scope(t_wait=5)
|
|
170
|
+
|
|
171
|
+
for t in _time:
|
|
172
|
+
|
|
173
|
+
S.set(0, t)
|
|
174
|
+
S.sample(t)
|
|
175
|
+
|
|
176
|
+
time, result = S.read()
|
|
177
|
+
|
|
178
|
+
#test if time was recorded correctly
|
|
179
|
+
self.assertTrue(np.all(np.equal(time, _time[5:])))
|
|
180
|
+
|
|
181
|
+
#test if input was recorded correctly
|
|
182
|
+
self.assertTrue(np.all(np.equal(result, _time[5:])))
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class TestRealtimeScope(unittest.TestCase):
|
|
186
|
+
"""
|
|
187
|
+
Test the implementation of the 'RealtimeScope' block class
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
pass #no tests implemented yet, since it just inherits from 'Scope' and is not critical
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# RUN TESTS LOCALLY ====================================================================
|
|
194
|
+
|
|
195
|
+
if __name__ == '__main__':
|
|
196
|
+
unittest.main(verbosity=2)
|