Telemetry Stream Design Pattern

Intent

Decouple the process of forwarding telemetry data to the ground from the characteristics of the physical channel over which the data are sent.

Based On

This is pattern is derived from the telemetry management design pattern of the AOCS Framework.

Motivation

The telemetry data are collected from the modules that make up the on-board applications and are then formatted in packets that are forwarded to the ground. The characteristics of the physical channel to which telemetry data are written by the OBS are obviously application-specific. Common mechanisms are:

Variations in the characteristics of the telemetry channel are one of the causes of the non-reusability of telemetry handling components. This design pattern proposes to decouple the component responsible for telemetry handling from the component responsible for modeling the telemetry channel through an abstract interface.

Dictionary Entries

The following abstractions or domain-wide concepts are defined to support the implementation of this design pattern:

Structure

This design pattern is built around an abstract interface to represent the telemetry stream abstraction: components modeling a concrete telemetry stream must implement abstract interface TelemetryStream. This interface defines the operations that a telemetry manager component may need to perform to control the forwarding of telemetry data to the telemetry channel. The basic operation is a write operation through which one item of data (one byte, one float, one integer, etc) is pushed down the telemetry channel. Additionally, housekeeping operations are required to reset the channel, to flush it, etc.

The structure of the design pattern is shown in the figure below. The telemetry manager component holds a reference to a concrete telemetry stream component that it sees only through the abstract interface TelemetryStream. The concrete telemetry stream component is therefore a plug-in component for the telemetry manager and can be changed without impact on the code of the telemetry manager. The design pattern assumes that the telemetry manager has collected the telemetry data corresponding to a given telemetry frame and has packetized them as a collection of uniform data items (e.g. as an array of bytes). It then writes them to the telemetry stream one by one by calling its write operation and finally perform whatever stream management operations are required.

Participants

Collaborations

The typical operational scenario for this design pattern is:

Consequences

Applicability

This design pattern is useful when:

Implementation Issues

What operations should be defined by the TelemetryStream interface? Since behind this interface there may be any concrete telemetry channel, the interface should define a sufficiently large set of operations to model them all. On the other hand, too large a set of operations makes the management of the telemetry stream on the part of the telemetry handling component too complex. One possible set is:

	
    interface TelemetryStream {
	write(int d);
                    // write a bit field
	write(int d, int firstBit, int lastBit);
	write(short d);
                    // write a bit field
	write(short d, int firstBit, int lastBit);
	write(byte d);
                    // write a bit field
	write(byte d, int firstBit, int lastBit);	
	write(float d);
	write(doube d);

	// housekeeping operations
	void reset();
	void flush();
	int getCapacity();
    }
This version of the TelemetryStream abstract interface provides several write methods, one for each type of elementary data that might have to be sent to the telemetry channel. It also provides methods to send only some bits extracted from an integer, short or byte variable. A more economical version might assume that telemetry data are always packetized as collection of bytes and would then offer only one single write method that takes a byte as an argument. Additionally, the interface declares methods to flush and to reset the telemetry stream. Their semantics might dictate that a telemetry stream should be reset before its user begins writing a new frame to it and that it should be flushed after the writing of a frame has been completed. Finally, the telemetry stream interface foresees a method to retrieve the stream capacity (expressed, for instance, as the maximum size of a telemetry frame that can be forwarded to the ground in one single flush operation).

This pattern and the telemeterable pattern are closely related since both cover the management of telemetry data. It is possible, and in some cases advantageous, to combine them. The basic idea behind the telemeterable pattern is that responsibility for constructing the telemetry image associated to a given component should reside with the component itself. In the standard implementation, the component implements the telemeterable interface that defines methods through which the component can return its telemetry image as, for instance, a stream of bytes. Responsibility for writing this image to the telemetry stream, however, rests with the telemetry manager component. In an alternative implementation, the telemeterable component is passed a reference to the telemetry stream and becomes responsible for constructing the telemetry image and for writing it to the telemetry stream. The telemetry manager is now responsible solely for performing the management of the telemetry collection and forwarding process. In this spirit, the telemeterable interface is defined as follows:

    interface Telemeterable {
        void writeToTelemetry(TelemetryStream* concreteTmStream);
        void setTelemetryFormat(int tmFmt);
        int getTelemetryImageSize();
    }
The telemetry management process in the telemetry manager is then implemented as follows:
	
    TelemetryList* tmList;     // list of objects to send to TM
    TelemetryStream* tmStream;
	. . .
    void activate() {	// activation method for TM handler
        tmStream->reset();
        tmStreamCapacity = tmStream->getCapacity();
            for (all telemeterable items t in tmList) do
            {	  
                size += t->getTelemetryImageLength();
                if (TM image size is compatible with TM stream capacity)
                    t->writeToTelemetry(tmStream);
	    else
		. . .    // handle error
   	  }
   	  tmStream->flush();
    }
The telemetry handling code is now much cleaner than would be the case with the separate instantiation of the telemeterable and telemetry stream patterns. The code above is clearly entirely application-independent and can form the basis for the construction of a reusable telemetry manager component.

OBS Framework Mapping

The implementation of this design pattern in the OBS Framework is supported by the following classes:

Sample Code

Consider a telemetry manager component that collects the telemetry data in an array of bytes. The telemetry data can be forwarded to the telemetry channel using a telemetry stream component. The relevant code looks like this:

	TelemetryStream* tmStream;
	byte tmData[N];
	. . .
	tmStream->reset();
	if (tmStream->getCapacity()<N)
	. . . // too many telemetry data, error!
	for (int j:=0; j++, j<N)
		tmStream->write(tmData[j]);
	tmStream->flush();
This code assumes a specific set of housekeeping operations for the telemetry stream and a very simple protocol for using them. This protocol dictates that the telemetry stream should be reset before the data writing operations for a new frame begins and should be flushed at the end of the data writing operation. The specific operations to be declared by the TelemetryStream interface and their semantics are defined when the design pattern is instantiated and, clearly, other solutions than that implied by the above pseudo-code are possible. The important point to note is that the code handling the writing operations is independent of the telemetry stream because it sees the concrete stream as an instance of abstract type TelemetryStream.

If the telemetry manager is encapsulated in a component, then the telemetry stream becomes a plug-in component for it:

	
    class TelemetryManager {

        TelemetryStream* tmStream;
        byte tmData[N];
	. . .

        void acquireTmData() {
	. . .
        }

        void forwardTmData() {
            tmStream->reset();
            if (tmStream->getCapacity()<N)
                . . .  // too many telemetry data, error!
            for (int j:=0; j++, j<N)
                tmStream->write(tmData[j]);
            tmStream->flush();
        }

        void loadTmStream(TelemetryStream* ts) {
        	tmStream = ts;
        }
    }
During application initialization, the telemetry manager component must be configured by loading into it a concrete telemetry stream. This initialization code might look like this:
	TelemetryStream* tmStream = new ConcreteTelemetryStream();
	. . .
	TelemetryManager* tmManager = new TelemetryManager ();
	TmHandler->loadTmStream(tmStream);
Two points deserve to be noted. First, the telemetry stream is created as an instance of some concrete telemetry stream class but is always handled as an instance of the abstract TelemetryStream abstract interface. Second, a change of telemetry stream component would have no impact on the telemetry manager component: the code used to configure it would have to be changed but the internal component code would remain unaffected.

Remarks

None

Author

A. Pasetti (P&P Software)

Last Modified

2002-06-22

Contact Us | The OBS Framework Project