Tutorial - Generating Streams ============================= .. _tutorial-streamify: 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. .. code-block:: python @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. .. code-block:: python @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