Tutorial - Generating Streams
The next step is to take sample_magnetometer
and repeatedly call this
function. In many cyberphysical systems and IoT applications, periodic sensing
is inherent. We want to compute the number of vehicles counted by the sensor
and update customers on the availability of space in the parking lot.
Previously, we mentioned how to take a Python function sample_magnetometer
and add it to our dataflow graph with the decorator @SQify
. However, @SQify
is the most basic function and requires tokens to come in to trigger its
computation, i.e. it needs a caller. We can simulate this caller periodically
by using a different decorator, @STREAMify
, which creates a special type of SQ.
@STREAMify
def sample_magnetometer(A, f, phi):
...
Here, the initial caller of sample_magnetometer
needs to provide more
information for a STREAMified function to work. As it periodically executes by
itself, the single call to start this operation needs to specify by which
clock it will use to retrigger itself, the period of time between the next
iteration, and the phase of the clock to synchronize to. The caller of this
function needs to provide this information through the keywords TTClock
,
TTPeriod
, and TTPhase
respectively.
Before we can update our @GRAPHify
function, we need to resolve our notion
of clocks in TTPython. As we are working with a distributed, time-sensitive,
and energy-constrained system, we have to be careful in our notion of time.
We have multiple clocks in our entire system due to each ensemble, and these
clocks have different levels of precision and synchronization. We designate
a clock to be the root clock, in which all other clocks will synchronize
to. This creates a clock hierarchy:
Insert picture of clock hierarchy
In TTPython, we have defaulted our master clock to use NIST’s clock, but
you can change this default (WIP). The master clock runs on the the
microsecond precision, so if we were to specify a period of 0.5 seconds,
we would assign TTPeriod=500000
.
For our parking lot example, we’ll have STREAMified SQ use the root clock.
@STREAMify
def sample_magnetometer(A, f, phi):
from math import cos, pi
return A - (A * cos(get_time() * 2 * f / pi + phi)))
def get_time():
...
@GRAPHify
def main(trigger):
A = 1
f = 0.25
phi = 0
with TTClock.root() as root_clock:
return sample_magnetometer(A, f, phi, TTClock=root_clock, TTPeriod=500000, TTPhase=0)
We reserve keywords that start with TT
as special keywords that are TTPython facing; that is,
sample_magnetometer
does not have access to these keywords. These help the TTPython
runtime to correctly setup the clock synchronization of the ensemble that hosts that particular
SQ.
This tutorial provides an example for generating streams of data to process and fuse with other streams (the topic of the next tutorial), and how stream generation is defined and expected to behave at runtime
Things to mention * STREAMify will cause a function to produce outputs periodically, creating a stream of data * The input tokens’ intersection dictates the period of time over which the stream will be generated * Meta kwargs like TTPeriod and TTClock are used to parameterize period, phase, clock domain, and data validity interval (explain what the last means) for the stream generation