55=====================================
66This example demonstrates the process of creating an actionable sensor, i.e., a sensor that has an
77actionable property. This includes creation of :class:`~.Action` and :class:`~.ActionGenerator`
8- that will handle how this property can evolves over time, and how to interface with the sensor via
8+ that will handle how this property evolves over time, and how to interface with the sensor via
99given actions.
1010"""
1111
2323# Action
2424# ------
2525# The logic of this `direction` switching will be handled by our custom :class:`~.Action`.
26- # The class' ` act` method contains the calculations needed to take in the property's current value
27- # and return its new value after a particular amount of time has elapsed.
26+ # The class' :meth:`~.Action. act` method contains the calculations needed to take in the
27+ # property's current value and return its new value after a particular amount of time has elapsed.
2828#
2929# This class inherits 3 important properties:
30- # * `generator` details which :class:`~.ActionGenerator` the action was created by. This will be
31- # discussed in detail later on.
32- # * `end_time` specifies when the action should be completed by. For example, we might want the
33- # sensor to switch from North to West in 5 seconds. So the end time would be 5 seconds from the
34- # current time (the "current time" is a value stored by the sensor itself, and gets updated each
35- # time it is called to action, which is a process discussed in detail later on). We will model the
36- # action behaviour so that the direction does not change value until the end time is reached. I.E
37- # we only switch to West exactly when 5 seconds have elapsed, and not before.
38- # * `target_value` indicates what the new value of the property should be once the action is
39- # complete (end time is reached).
30+ #
31+ # * :attr:`~.Action.generator` details which :class:`~.ActionGenerator` the action was created by.
32+ # This will be discussed in detail later on.
33+ # * :attr:`~.Action.end_time` specifies when the action should be completed by. For example, we
34+ # might want the sensor to switch from North to West in 5 seconds. So the end time would be 5
35+ # seconds from the current time (the "current time" is a value stored by the sensor itself, and
36+ # gets updated each time it is called to action, which is a process discussed in detail later
37+ # on). We will model the action behaviour so that the direction does not change value until the
38+ # end time is reached. I.E we only switch to West exactly when 5 seconds have elapsed, and not
39+ # before.
40+ # * :attr:`~.Action.target_value` indicates what the new value of the property should be once the
41+ # action is complete (end time is reached).
42+ #
4043
4144from stonesoup .sensor .action import Action
4245
@@ -45,7 +48,7 @@ class ChangeDirectionAction(Action):
4548 """Simply changes the direction that the sensor is looking in when the action `end_time` is
4649 reached."""
4750 def act (self , current_time , timestamp , init_value ):
48- """Only change to target direction once `end_time` is reached. Otherwise keep same value.
51+ """Only change to target direction once `end_time` is reached. Otherwise, keep same value.
4952
5053 Parameters
5154 ----------
@@ -69,16 +72,17 @@ def act(self, current_time, timestamp, init_value):
6972# %%
7073# It is within the :class:`~.Action` where you can detail more complicated modifications to the
7174# attribute. For example, the :class:`~.ChangeDwellAction` is an action for use with the
72- # `dwell_centre` property of the :class:`~.RadarRotatingBearingRange` sensor (or any other model
73- # with similar dwell dynamics). It contains the logic of calculating the angle turned by the dwell
74- # centre in the given time delta, given a constant rpm.
75+ # :attr:`~.RadarRotatingBearingRange.dwell_centre` property of the
76+ # :class:`~.RadarRotatingBearingRange` sensor (or any other model with similar dwell dynamics). It
77+ # contains the logic of calculating the angle turned by the dwell centre in the given time delta,
78+ # given a constant rpm.
7579
7680
7781# %%
7882# Action Generator
7983# ----------------
8084# Now that we have the logic of how the `direction` can change over time, we need to detail what
81- # the potential changes can be for a given time frame. A :class:`~.ActionGenerator` type handles
85+ # the potential changes can be for a given time frame. An :class:`~.ActionGenerator` type handles
8286# the details of these potential property values.
8387# In the more complicated dwell centre example above, this might be in determining what potential
8488# new bearings are achievable in 5 seconds given a specific rpm.
@@ -87,28 +91,32 @@ def act(self, current_time, timestamp, init_value):
8791# for every direction.
8892#
8993# 5 important properties are inherited from this class:
90- # * `owner` specifies the sensor (or other :class:`Actionable`) that the corresponding property
91- # belongs to. I.E. which sensor we will be querying.
92- # * `attribute` is the string-valued name of the property to be modified.
93- # * `start_time` is the time at which the sensor is queried and `end_time` the time at which it is
94- # queried to. E.g. "from now (9 o'clock), what can you do by 10 o'clock?"
95- # * `current_value` details what the current value of the property is. In our case this would be
96- # what the current direction the sensor is looking in is.
94+ #
95+ # * :attr:`~.ActionGenerator.owner` specifies the sensor (or other :class:`~.Actionable`) that the
96+ # corresponding property belongs to. I.E. which sensor we will be querying.
97+ # * :attr:`~.ActionGenerator.attribute` is the string-valued name of the property to be modified.
98+ # * :attr:`~.ActionGenerator.start_time` is the time at which the sensor is queried and
99+ # :attr:`~.ActionGenerator.end_time` the time at which it is queried to. E.g. "from now
100+ # (9 o'clock), what can you do by 10 o'clock?"
101+ # * :attr:`~.ActionGenerator.current_value` details what the current value of the property is. In
102+ # our case this would be what the current direction the sensor is looking in is.
97103#
98104# By inheriting this class, we are required to define several things:
99- # * the `default_action` property determines what the behaviour of the property should be, given
100- # no actions have been passed to it (or that it has no actions to perform at the given time). For
101- # our `direction` example, we'll simply say that the direction won't change. So the default action
102- # should be one of our `ChangeDirectionAction` types with a target value equal to the current value
103- # of the direction. For the dwell centre example discussed above, this might be reverting to a
104- # default, anti-clockwise rotation at the given rpm. The default action's end-time should last
105- # until the end of the query (i.e. until `end_time`).
106- # * the `__iter__` method defines how we calculate the potential actions for the sensor in the
107- # given time frame. We should be able to loop through this generator object and get out a
108- # `ChangeDirectionAction` for every potential new direction.
109- # * we should also define the `__contains__` method for this generator. This way, for a given
110- # `ChangeDirectionAction` or particular direction, we can say whether this is possible by simply
111- # asking "is this IN my generator?"
105+ #
106+ # * the :attr:`~.ActionGenerator.default_action` property determines what the behaviour of the
107+ # property should be, given no actions have been passed to it (or that it has no actions to
108+ # perform at the given time). For our `direction` example, we'll simply say that the direction
109+ # won't change. So the default action should be one of our :class:`ChangeDirectionAction`
110+ # types with a target value equal to the current value of the direction. For the dwell centre
111+ # example discussed above, this might be reverting to a default, anti-clockwise rotation at the
112+ # given rpm. The default action's end-time should last until the end of the query (i.e. until
113+ # :attr:`~.ActionGenerator.end_time`).
114+ # * the :meth:`__iter__` method defines how we calculate the potential actions for the sensor in
115+ # the given time frame. We should be able to loop through this generator object and get out a
116+ # :class:`ChangeDirectionAction` for every potential new direction.
117+ # * we should also define the :meth:`__contains__` method for this generator.
118+ # This way, for a given :class:`ChangeDirectionAction` or particular direction, we can say
119+ # whether this is possible by simply asking "is this IN my generator?"
112120
113121from stonesoup .sensor .action import ActionGenerator
114122from stonesoup .base import Property
@@ -129,14 +137,14 @@ def default_action(self):
129137 target_value = self .current_value )
130138
131139 def __contains__ (self , item ):
132- """Can switch to any direction in any time frame (as long as it is sensible. eg . we
140+ """Can switch to any direction in any time frame (as long as it is sensible. e.g . we
133141 shouldn't expect to be able to look in the direction "up" or "weast")."""
134142 if isinstance (item , ChangeDirectionAction ):
135143 item = item .target_value # grab the target value of the action to check against
136144
137145 potential_directions = self .owner .potential_directions
138146
139- return item in potential_directions # if its a potential direction, then it is possible
147+ return item in potential_directions # if it's a potential direction, then it is possible
140148
141149 def __iter__ (self ):
142150 """
@@ -155,33 +163,36 @@ def __iter__(self):
155163# the logic needed for dealing with being actioned (i.e. receiving :class:`~.Action` sets in some
156164# manner and applying them to their corresponding properties).
157165# To flag a particular property as something which should be actioned, simply define it as an
158- # :class`~.ActionableProperty` and provide a :class:`ActionGenerator` so that it is clear how it
166+ # :class: `~.ActionableProperty` and provide a :class:`~. ActionGenerator` so that it is clear how it
159167# should be modified over time.
160168#
161169# An :class:`~.Actionable` keeps track of its "schedule" via a dictionary, keyed by the actionable
162170# properties' names, where values are the most recent actions attributed to those properties. In
163- # the instance where a key has corresponding value/action that has been completed, the `act` method
164- # will handle the removal of this action from the schedule dictionary. An important point to take
165- # here is that only a single action can be scheduled per property.
171+ # the instance where a key has corresponding value/action that has been completed, the
172+ # :meth:`~.Actionable.act` method will handle the removal of this action from the schedule
173+ # dictionary. An important point to take here is that only a single action can be scheduled per
174+ # property.
166175#
167176# There are 3 important methods inherited by :class:`~.Actionable`:
168- # * `actions` will return a set of action generators (one for each actionable property of the
169- # sensor). The method requires a timestamp to be passed in such that the generators have an end
170- # time to calculate their possibilities for.
171- # * `add_actions` takes a sequence of provided actions and adds them as values to their
172- # corresponding actionable properties in the sensor's schedule dictionary. This will overwrite any
173- # pre-existing actions for those properties.
174- # * `act` handles the actual calling to each of the scheduled actions for every actionable
175- # property. A timestamp is required so that the sensor knows what time to act until. For every
176- # actionable property the sensor has, this method calls the property's corresponding scheduled
177- # action, if it has one, up until the timestamp has been reached, and replaces the property's value
178- # with the new calculated one. The method also handles situations where the given timestamp
179- # overruns the end time of scheduled actions, whereby it will revert to calling the property's
180- # corresponding generator's default action for the rest of the time needed to reach the timestamp.
181177#
182- # An :class:`~.Actionable` type requires a method to `validate_timestamp` in order to keep track
183- # of what it should consider as "now". In the case of :class:`~.sensor` types, this is done by
184- # taking the sensor's corresponding movement controller's timestamp.
178+ # * :meth:`~.Actionable.actions` will return a set of action generators (one for each actionable
179+ # property of the sensor). The method requires a timestamp to be passed in such that the
180+ # generators have an end time to calculate their possibilities for.
181+ # * :meth:`~.Actionable.add_actions` takes a sequence of provided actions and adds them as values
182+ # to their corresponding actionable properties in the sensor's schedule dictionary. This will
183+ # overwrite any pre-existing actions for those properties.
184+ # * :meth:`~.Actionable.act` handles the actual calling to each of the scheduled actions for every
185+ # actionable property. A timestamp is required so that the sensor knows what time to act until.
186+ # For every actionable property the sensor has, this method calls the property's corresponding
187+ # scheduled action, if it has one, up until the timestamp has been reached, and replaces the
188+ # property's value with the new calculated one. The method also handles situations where the
189+ # given timestamp overruns the end time of scheduled actions, whereby it will revert to calling
190+ # the property's corresponding generator's default action for the rest of the time needed to
191+ # reach the timestamp.
192+ #
193+ # An :class:`~.Actionable` type requires a method to :meth:`~.Actionable.validate_timestamp` in
194+ # order to keep track of what it should consider as "now". In the case of :class:`~.Sensor` types,
195+ # this is done by taking the sensor's corresponding movement controller's timestamp.
185196#
186197# There will be some logic in calculating whether a target falls within the sensor's FoV at a given
187198# time, but the important point to take from this model is the creation of
0 commit comments