Skip to content

Commit c95be03

Browse files
committed
Updates to Create_Actionable_Sensor.py example
1 parent f5e4479 commit c95be03

File tree

1 file changed

+71
-60
lines changed

1 file changed

+71
-60
lines changed

docs/examples/Creating_Actionable_Sensor.py

Lines changed: 71 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
=====================================
66
This example demonstrates the process of creating an actionable sensor, i.e., a sensor that has an
77
actionable 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
99
given actions.
1010
"""
1111

@@ -23,20 +23,23 @@
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

4144
from 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

113121
from stonesoup.sensor.action import ActionGenerator
114122
from 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

Comments
 (0)