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.

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.

def sample_magnetometer(A, f, phi):
    from math import cos, pi

    return A - (A * cos(get_time() * 2 * f / pi + phi)))

def get_time():

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