Coverage for src/gncpy/dynamics/basic/coordinated_turn_known.py: 82%
33 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-13 06:15 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-13 06:15 +0000
1import numpy as np
2from .linear_dynamics_base import LinearDynamicsBase
5class CoordinatedTurnKnown(LinearDynamicsBase):
6 """Implements the linear coordinated turn with known turn rate model.
8 This is a slight variation from normal :class:`.LinearDynamicsBase` classes
9 because it does not allow for control models, instead it has the input
10 matrix coded directly. It also has the turn angle included in the state
11 to help with debugging and coding but is not strictly required by the
12 dynamics.
14 Notes
15 -----
16 See :cite:`Li2000_SurveyofManeuveringTargetTrackingDynamicModels` and
17 :cite:`Blackman1999_DesignandAnalysisofModernTrackingSystems` for details.
19 Attributes
20 ----------
21 turn_rate : float
22 Turn rate in rad/s
23 """
25 state_names = ("x pos", "y pos", "x vel", "y vel", "turn angle")
27 def __init__(self, turn_rate=5 * np.pi / 180, **kwargs):
28 super().__init__(**kwargs)
29 self.turn_rate = turn_rate
30 self._control_model = None
32 @property
33 def control_model(self):
34 return self._control_model
36 @control_model.setter
37 def control_model(self, model):
38 self._control_model = model
40 def get_state_mat(self, timestep, dt):
41 """Returns the discrete time state matrix.
43 Parameters
44 ----------
45 timestep : float
46 timestep.
47 dt : float
48 time difference
50 Returns
51 -------
52 N x N numpy array
53 state matrix.
54 """
55 # avoid division by 0
56 if abs(self.turn_rate) < 1e-8:
57 return np.array(
58 [
59 [1, 0, dt, 0, 0],
60 [0, 1, 0, dt, 0],
61 [0, 0, 1, 0, 0],
62 [0, 0, 0, 1, 0],
63 [0, 0, 0, 0, 1],
64 ]
65 )
66 ta = self.turn_rate * dt
67 s_ta = np.sin(ta)
68 c_ta = np.cos(ta)
69 return np.array(
70 [
71 [1, 0, s_ta / self.turn_rate, -(1 - c_ta) / self.turn_rate, 0],
72 [0, 1, (1 - c_ta) / self.turn_rate, s_ta / self.turn_rate, 0],
73 [0, 0, c_ta, -s_ta, 0],
74 [0, 0, s_ta, c_ta, 0],
75 [0, 0, 0, 0, 1],
76 ]
77 )
79 def get_input_mat(self, timestep, *args):
80 """Gets the input matrix.
82 This enforces the no control model requirement of these dynamics by
83 forcing the input matrix to be zeros.
85 Parameters
86 ----------
87 timestep : float
88 Current timestep.
89 *args : tuple
90 additional arguments, not used.
92 Returns
93 -------
94 N x 1 numpy array
95 input matrix.
96 """
97 return np.zeros((5, 1))
99 def propagate_state(self, timestep, state, u=None, state_args=None, ctrl_args=None):
100 """Propagates the state forward in time.
102 Parameters
103 ----------
104 timestep : float
105 Current timestep.
106 state : N x 1 numpy array
107 current state.
108 u : 1 x 1 numpy array, optional
109 Control input, should not be needed with this model. The default is
110 None.
111 state_args : tuple, optional
112 Additional arguments needed to get the state matrix. The default is
113 None.
114 ctrl_args : tuple, optional
115 Additional arguments needed to get the input matrix, not needed.
116 The default is None.
118 Raises
119 ------
120 RuntimeError
121 If state_args is None.
123 Returns
124 -------
125 N x 1 numpy array
126 Next state.
127 """
128 if state_args is None:
129 raise RuntimeError("state_args must be (dt, )")
130 if ctrl_args is None:
131 ctrl_args = ()
132 F = self.get_state_mat(
133 timestep,
134 *state_args,
135 )
136 if u is None:
137 return F @ state + np.array(
138 [0, 0, 0, 0, state_args[0] * self.turn_rate]
139 ).reshape((5, 1))
140 G = self.get_input_mat(timestep, *ctrl_args)
141 return (
142 F @ state
143 + G @ u
144 + np.array([0, 0, 0, 0, state_args[0] * self.turn_rate]).reshape((5, 1))
145 )