10 Signal Scripts

Signal scripts allow the users to analyze and interpret signals in many ways. Combine signals using mathematical operations, generate references, implement protocol parsers, extract statistical informations or search for conflicts automatically.

This article is based on impulse 1.8.

How to create a signal script ?

Signal scripts can be created either using the template menus Add->Scripting - xxx or by changing an existing configuration item into a script production, using the configuration dialogs Production combo box.

Configuration of the production output

Scripts are executed on the fly, as soon as the signal data is required for further processing, and re-executed when settings or input signal have been changed. Before executing a script, the system needs to know the source signals, the type and the domain of the signal. All these informations need to be entered into the plot configuration dialog. If you leave the entry fields empty, impulse tries to extract the information from the sources. The fields will display this information in light gray <e.g. Derived(Float)>. 

Primary Source
The primary source is the first source signal of the production.
Additional (Sources)
If more than 1 source signal is required, add them into this table. Instead of using the table you may drag and drop (with a closed dialog) signals onto the plot item.
Process type
You may select between discrete and continuous process type.
Signal type
Select the output signal type of your script (Float, String, Logic, Integer, ..).
Signal descriptor
The signal descriptor describes the signal type in more details (e.g the bit width of a logic vector (default<bits=16>)). See below for more details. But in most case, you will use the standard settings (default<>) - Press CTRL-Space to view content proposals.
Domain Class/Base
The domain base is just required if your output signal has a different domain than the source signals. If not, just don't touch these settings.
Domain Range
Use this field to set the domain range, so the minimum and maximum value of the domain (e.g. 0 .. 1000 Hz)

How to get started with scripting ?

Have a look at the script examples. Select a script that is close to your requirements, than copy script and settings into the dialog. Most script exampels are also available at the template menu.

Script Examples

Use the build-in script editor

You may use the built-in script editor instead of the text field in the dialog. Click on "Edit in js editor" to open a new eclipse editor. As soon as you save the content, its signals will be updated and changes get visible in a parallel impulse viewer. If there are errors in the script, log messages will be send to the console view.

The script itself

Intro

Impulse uses JavaScript as its default language (other languages might be added in future). Rhino interpreter is used up to Java 6 and above the Nashorn interpreter that uses the byte code engine of java and improves performance a lot. The available scripts are compatible for both interpreters.

The typical goal of a script is to read the content of source signals and create one output signal. A simple script looks like this:

// input: an array of all input signals
// in0: primary input of type ISamplePointer,IReadableSamples
// in1..: additional inputs of type ISamplePointer,IReadableSamples
// out: output signal of type IFloatSamplesWriter
// console: console output of type MessageConsoleStream
// iter: iterator of type ISamplesIterator
// progress: progess control of type  IScriptProgress

progress.cont();
while ( iter.hasNext()) {
    var current <:Long:> = iter.next(out);
    out.write(current, false, input[0].floatValue() +  input[1].floatValue());
}

SamplesIterator instance tier allows to iterate over events of multiple source signals. 'iter.next()' returns the current domain value (e.g current time). All input variable can be accessed as signal pointers: For each event found, the iterator set the pointers to the current position, e.g. floatValue() returns the float value at that position.

Variable out represents a samples writer of the configured type (e.g. IFloatSamplesWriter). You create the new signal by writing consecutively new values at given domain positions (write(domainValue,conflict,value).

The IScriptProgress instance progress let you control the execution of your script.
The line progress.cont() in the above example handles the execution of the script when the signal is extended (when you use a port, and data will be continuously added). If new data has been read, the script will be called again. With the above line, the iterator item will be pointed to the position of the previous end. If the line is not included, iter is set to the first source sample, and the script has to run over the complete signal again.

If there is just one input and data shall not be extended, you can simply iterate in the following way. Instead of floatValue(), you use floatValueAt(eventIdx).

for (var i=0;i<in0.getCount();i++){ 
	real[i] = in0.floatValueAt(i);
}

in0,in1,...,inN and the input array

Lookin into the script example and templates, you will find input definition like "in0" and "in1"  and also the variable "input". The first ones (in0,in1,..) allow you to reference inputs configured in the plot dialog (you will find on the left side of the plot dialog). "in0" refers to the primary input, in1 (and in2,..)refere to the additional inputs. The variable "input" combines all configured inputs into one array (this may include in0, if a primary input is configured, or not).

Reading the input

All input references combine the interfaces ISamplesPointer and IReadableSamples. The IReadableSamples interface defines the basic reader interface. It has methods like:

  • getCount()
  • valueAt(int idx)
  • intValueAt(int idx)

So you can get the total number of available samples and use one of the accessor methods to get the value at a given index.

The ISamplesPointer inteface allows to modify an index and read the value at the current index. You find functions like:

  • setIndex(int idx)
  • goPrev()
  • goNext()
  • val()
  • intValue();

If an input has a an additional dimension (struct members or arrays) you might read the value object and use the member accessors:

  • structAt(n).intValueOf("max")
  • intValueOf("max")

If you use a SamplesIterator, all pointers are updated by the iterator automatically. There is no need to use 'goNext()','goPrev()',... implicitly.


while ( iter.hasNext()) {
   .... in0.intValue() ....  }

Writing the output

Each signal type comes with a writer interface. The integer signal uses the IIntegerSamplesWriter interface (float: IFloatSamplesWriter, ...); a simple write includes the parameters for domain position, a conflict flag and one or more parameters describing the value.

In the IIntegerSamplesWriter example, we have three write methods for three value types (mainly used in Java); and three additional method with modified names for scripting purpose.

public interface IIntegerSamplesWriter extends ISamplesWriter{
   
    boolean write(long units, boolean conflict, int value);    
    boolean write(long units, boolean conflict, long value); 
    boolean write(long units, boolean conflict, BigInteger value);

	// scripting 
    boolean writeInt(long units, boolean conflict, int value);    
    boolean writeLong(long units, boolean conflict, long value); 
    boolean writeBig(long units, boolean conflict, BigInteger value);
}	

The parameters have the following meaning:

units
Domain position as no of domain units (e.g, domain base:ns, units = 1000 -> domain position = 1us)
conflict
Define this sample as a conflict one.
value
The given value in different formats (integer/long/BigInteger)

Handling time, frequency,...

As seen before, the domain value is given as a multiple of its domain base.

The domain base of the sources and the output are pre-set by the system, in case of the output, you can override the domain base in the plot dialogue (required if there are no inputs; just do in case you know what you do). You can get the actual domain base by using the getDomainBase() method.

If you are using a SamplesIterator, the current domain position is returned by the next() method.

for (var iter <:ISamplesIterator:> = new SamplesIterator(input); iter.hasNext();) {
    var current <:Long:> = iter.next(out);

Please be aware that the value is returned as a Long object. This Long object is not converted into any JavaScript type (as it is done for Integer and Float values).

Java reference for scripts

Java and JavaScript

Impulse is build on Java and signal script uses JavaScript to operate on Java object (the input signal pointer, the output writer,...).

Its up to the user to work with JavaScript objects but its usually not required and to our understanding not useful. Primitives from java are converted into JavaScript primitives. But there are limits: JavaScript has just one number type (64 float), so a long value can not be converted and will be given as a Long objects.

If Nashorn is used (up from Java 7), impulse automatically loads the Rhino compatibility library. But have in mind that Rhino and Nashorn still have a slightly different interface to java. Please see Scripting Java (Rhino) and Rhino Migration Guide (to Nashorn).

Content assist and psoido typing

In the 1.4 version templates, you will find snippets like this:<:IFloatSamplesWriter:>.

These snippets are doing psoido typing - They tell the environment the java type of a variable to let the system prepare content assist proposals. The snipperts do not have any impact on the java engine and are removed before processing. You don't need to use them, but they aar equite helpful if you don't want to read reference docs all the time.

Java reference for scripts Show examples More about Extending impulse