Sensor Channel API Specification

Contents

1 Overview

The Sensor API provides clients with the access to the data provided by various sensors of a device.


API categorypublic
API typec++
API librariesSensrvClient.lib
Location/sf/os/deviceio/deviceio_pub/sensor_channel_api
Buildfiles/sf/os/deviceio/deviceio_pub/sensor_channel_api/group/bld.inf


1.1 Description

The Sensor API consist of the following two parts:

The Sensor API is a library API providing methods to listen for data provided by sensor channels. Sensor channels may also support condition listening to get notified when some limit is met for a channel. Sensor channels are configured with properties such as data rate.

Sensor channel measures the acceleration of the device. As shown in the following diagram, three-dimensional Cartesian coordinate system is used to illustrate direction of the acceleration. The x- and y- axes define a plane where z-axis direction is perpendicular to the xy plane. When a phone is moving along an axis, the acceleration is positive if movement is towards the positive direction and negative if movement is toward the negative direction. For example, when a phone is moving along x-axis to the direction of -x, the acceleration is negative.

Three-dimensional Cartesian coordinate system with z-axis pointing away from the phone display.
Three-dimensional Cartesian coordinate system with z-axis pointing away from the phone display.

As shown in the following diagram, there are six basic orientations for a phone. In display up and display down orientation, the gravitation is along the y-axis. In phone left side up and phone right side up orientation, the gravitation is along the x-axis. In display upwards and display downwards orientation, the gravitation is along the z-axis.

Six basic orientations of a phone
Six basic orientations of a phone

1.2 Use Cases

Sensor channel data listening related use cases

Sensor channel properties related use cases:

Sensor channel conditions related use cases:

1.3 Class Structure

Summary of API classes and header files
ClassesFiles
CSensrvChannel /epoc32/include/sensrvchannel.h CSensrvChannelCondition /epoc32/include/sensrvchannelcondition.h CSensrvChannelConditionSet /epoc32/include/sensrvchannelconditionset.h CSensrvChannelFinder /epoc32/include/sensrvchannelfinder.h MSensrvChannelConditionListener /epoc32/include/sensrvchannelconditionlistener.h MSensrvChannelListener /epoc32/include/sensrvchannellistener.h MSensrvDataListener /epoc32/include/sensrvdatalistener.h MSensrvPropertyListener /epoc32/include/sensrvpropertylistener.h TSensrvChannelInfo /epoc32/include/sensrvchannelinfo.h TSensrvProperty /epoc32/include/sensrvproperty.h No classes/epoc32/include/sensrvtypes.h

The channel finder CSensrvChannelFinder provides interfaces to search and find sensor channels that the system provides. Clients can listen if new channels are installed to the system through the MSensrvChannelListener callback interface.

The Sensor channel CSensrvChannel provides methods to open and control a sensor channel. The class provides operations to:

data is also provided. New data availability is informed through the MSensrvDataListener callback interface.

Sensor API class structure
Sensor API class structure

Clients create an instance of C classes using the standard NewL two phase construction. If a callback interface is required, the client must implement the appropriate M class and provide its pointer when required.

2 Using The API

To use a sensor channel, a client must find a channel and open it. The CSensrvChannelFinder class provides functionality to find channels. The CSensrvChannel class provides functionality to open and control channels.

The supported sensor channels are declared in the sensor definitions API. The number of implemented channels may vary between products. For each channel a channel type ID constant and a short description is provided. Double tapping channel declaration is shown below as an example. There are two types of channels: raw data channels and event channels. Raw data channels provide data continuously and event channels provide data when an appropriate event occurs. The data type describes what type of data the channel provides.

    /**
    * - Name:          Double tapping event channel type
    * - Type:          Event
    * - Datatype:      TSensrvTappingData
    * - Description:   Double tapping events
    */
    const TSensrvChannelTypeId KSensrvChannelTypeIdAccelerometerDoubleTappingData = 0x10205081;

Channel data types are declared in each sensors header file. A channel data type is the type of an object which a sensor channel provides. Double tapping channel data type declaration is shown as follows.

    class TSensrvTappingData
        {
    public:
        /**
        * Channel data type ID number
        */
        static const TSensrvChannelDataTypeId KDataTypeId = 0x1020507F;

        /**
        * Channel data type enumerations
        */
        enum TSensrvAccelerometerAxisDataIndexes
              {
              iTimeStamp = 0,
              iDirection
              };
            };

    public:
        /**
        * - Item name:   Sampling time.
        * - Item Index:  0
        * - Conditions:  None
        * - Description: Timestamp for a sample.
        */
        TTime iTimeStamp;

        /**
        * - Item name:   Tapping direction bitmask
        * - Item Index:  1
        * - Conditions:  Binary
        * - Description: Direction bitmask of the tapping event.
        *                See constant definitions above.
        */
        TUint32 iDirection;
        };

The channel data type ID, for example TSensrvTappingData::KDataTypeId , is a unique ID for each data type to be able to separate data types from each other. The data type ID is used in TSensrvChannelInfo to define data type used in a channel.

The channel data type index, for example TSensrvTappingData::iDirection , is used to point to an attribute inside a data type. Attributes of the TSensrvTappingData class are iTimeStamp and iDirection . The channel data type index is used in:

Channel properties are declared in the sensrvgeneralproperties.h and sensor specific files. General properties for all channel types are declared in the sensrvgeneralproperties.h file. Accelerometer specific properties are declared in the sensrvaccelerometersensor.h file. For each property, a property ID constant and a short description are provided. The property type specifies the type of the value the property contains. It can be TInt , TReal or TBuf . The property scope can be defined for:

A mandatory section specifies if the property is required for all channels. The capability section specifies the required capabilities to change value of the property. The Accuracy property is shown below as an example.

    /**
    * - Name:         Accuracy of the channel data
    * - Type:         TReal
    * - Scope:        Channel item property
    * - Mandatory:    No
    * - Capability:   None
    * - Description:  Returns the accuracy of this channel of the sensor as a
    *                 percentage of reading (=data value).
    */
    const TSensrvPropertyId KSensrvPropIdChannelAccuracy = 0x000000008;

Example content of the Accuracy property is shown below. Properties defined are channel data item specific. The item index defines the data item which the property is related to. If the property is a sensor or channel property, the item index is KSensrvItemIndexNone .

    iPropertyId   = KSensrvPropIdChannelAccuracy
    iItemIndex    = KSensrvItemIndexNone
    iArrayIndex   = ESensrvSingleProperty
    iRealValue    = 10.0
    iReadOnly     = ETrue
    iRealValueMax = n/a
    iRealValueMin = n/a
    iPropertyType = ESensrvRealProperty
    iSecurityInfo = n/a

2.1 Finding, opening and closing a channel

The following example shows how to find, open and close a double tapping channel. An instance of CSensrvChannelFinder is created to be able to find channels. The found channels are stored into the RSensrvChannelInfoList type of object. All double tapping channels provided by the device are queried by setting the channel type as KSensrvChannelTypeIdAccelerometerDoubleTappingData to the TSensrvChannelInfo object which is used as search criteria. After calling the FindChannelsL() method, channelInfoList contains all the found double tapping channels. If there are several found channels, the client can select the correct one by examining the content of channel information objects inside channelInfoList.

To construct the CSensrvChannel object properly, a channel information object from channelInfoList must be used as a parameter in the NewL() constructor. After successful construction, the channel can be opened using the OpenChannelL() method. For an opened channel, the client can set and retrieve channel properties, add channel conditions and listen for sensor data. When the channel is not needed anymore, it must be closed using the CloseChannel() method.

    //Construct a channel finder.
    CSensrvChannelFinder* channelFinder;
    channelFinder = CSensrvChannelFinder::NewL();
    CleanupStack::PushL( channelFinder );

    //List of found channels.
    RSensrvChannelInfoList channelInfoList;
    CleanupClosePushL( channelInfoList );

    //Create and fill channel search criteria.
    //In this example double tapping channel is searched.
    TSensrvChannelInfo channelInfo;
    channelInfo.iChannelType = KSensrvChannelTypeIdAccelerometerDoubleTappingData;

    //Find the double tapping channel
    channelFinder->FindChannelsL( channelInfoList, channelInfo );

    if( channelInfoList.Count() != 1 )
        {
        //The device doesn't support double tapping channel or
        //there are several double tapping channels.
        }
    else
        {
        //double tapping channel found
        }

    //Open the double tapping channel.
    //When the channel object is created the channel info object
    //must be an object returned by CSensrvChannelFinder::FindChannelsL().
    CSensrvChannel* sensorChannel;
    sensorChannel = CSensrvChannel::NewL( channelInfoList( 0 ) );
    CleanupStack::PushL( sensorChannel );
    sensorChannel->OpenChannelL();

    //
    //Double tapping channel is now open.
    //

    //Close the double tapping channel.
    sensorChannel->CloseChannel();

    CleanupStack::PopAndDestroy( sensorChannel );
    CleanupStack::PopAndDestroy( &channelInfoList ); //Close() is being called on "channelInfoList"
    CleanupStack::PopAndDestroy( channelFinder );

2.2 Listening for channel data

The channel must be opened before starting to listen for channel data. The following example shows how to start listening to a double tapping channel and receive data from it. Channel data is received into the receiving buffer and it can be read using the GetData() method. When new data is available in the receiving buffer, a DataReceived() notification is delivered through the data listener callback interface-MSensrvDataListener .

In case of double tapping channel, the desired count and maximum count parameters are set to one to get a DataReceived() notification per one double tapping. The buffering period is set to zero to get the DataReceived() notification only when double tapping is done. Channel data can be read from the receiving buffer using the GetData() method. The receiving buffer is allocated from the heap in the client's thread and its size is the channel data item size multiplied by the maximum number of data items. There are two receiving buffers for one client. For example, if the channel data item size is 20 bytes and the maximum count is 10, the memory consumption is 400 bytes (20bytes*10*2=400 bytes). On the other hand, small desired data count increases the interprocess communication. The client needs to provide a pointer to the data listener for the channel to be able to receive DataReceived() notifications.

    iSensorChannel->StartDataListeningL( this, //this object is data listener for this channel
                                         1, //aDesiredCount is one, i.e. each double tapping is notified separately
                                         1, //aMaximumCount is one, i.e. object count in receiving data buffer is one
                                         0 );//buffering period is not used

To implement data listener the client needs to inherit from the MSensrvDataListener interface class and implement declared pure virtual methods. When a new data is available in the sensor channel and data listening is started, the DataReceived() method is called by the Sensor API. The following example shows how to handle double tapping data received notifications. First the channel type of the received data is checked, and then data object is got with the GetData() method. The aCount parameter tells the number of data objects in the channels receiving buffer and it can be zero if the buffering period is used when data listening is started. The aDataLost parameter tells the number of the lost data objects, for exmaple, in heavy load situations.

    void CTestClass::DataReceived( CSensrvChannel& aChannel,
                               TInt aCount,
                               TInt aDataLost )
    {

    if ( aChannel.GetChannelInfo().iChannelType == KSensrvChannelTypeIdAccelerometerDoubleTappingData )
        {
        TSensrvTappingData tappingData;
        TPckg<TSensrvTappingData> tappingPackage( tappingData );

        aChannel.GetData( tappingPackage );
        }

    }

When data listening is not needed anymore it must be stopped with the StopDataListening() method.

2.3 Listening for channel changes

The channel finder CSensrvChannelFinder provides functionality to listen for channel changes, such as when new channels are installed to the system or old channels are removed. Listening is started and stopped with the SetChannelListenerL() method. When a channel change occurs, a ChannelChangeDetected() notification is delivered through the channel listener callback interface MSensrvChannelListener .

There is one restriction on the ChannelChangeDetected() notification: If a sensor changes the existing channel registration, there must be an open channel to that sensor to prevent sensor driver unloading. In practise, this means that at least one channel from this sensor must have a client to keep the channel open and sensor driver loaded. If the sensor driver is unloaded while new channels become available, it cannot notify new channels until some existing channel opening causes the sensor driver to be reloaded.

2.4 Retrieving channel properties

The channel must be opened before its properties can be accessed. The channel properties are capsulated to the TSensrvProperty class and can be queried with the GetPropertyL() method. The following example shows how to check accuracy of the channel. GetPropertyL() leaves if the channel does not support the Accuracy property. Leave also occurs if the Accuracy property is defined as the channel item property. This means that the item index must point to valid a channel item index, for example, TSensrvAccelerometerAxisData::iAxisX .

    TSensrvProperty property;
    TReal propertyValue( 0 );

    iSensorChannel->GetPropertyL( KSensrvPropIdChannelAccuracy,
                                  KSensrvItemIndexNone,
                                  property );

    // KSensrvPropIdDataType is specified as TReal of type in sensrvgeneralproperties.h.
    // Type of the property can also be checked at runtime with PropertyType() method.
    if( property.PropertyType() == ESensrvRealProperty )
        {
        property.GetValue( propertyValue );
        }

2.5 Setting channel properties

The channel must be opened before its properties can be accessed. Channel properties can be changed with the SetProperty() method. In the following example, the x-axis of accelerometer channel is deactivated. The axis active property KSensrvPropIdAxisActive with the item index is first got with the GetPropertyL() method. If the axis is active it is deactivated by setting a new value to the previously got property. Updated property is set with the SetProperty() method.

    TSensrvProperty property;
    TInt err( KErrNone );
    TInt axisActive( 0 );

    iSensorChannel->GetPropertyL( KSensrvPropIdAxisActive,
                                  TSensrvAccelerometerAxisData::Index::iAxisX,
                                  property );

    property.GetValue( axisActive );

    if( 1 == axisActive )
        {
        property.SetValue( 0 );//Other value than one means that sensor axis is deactivated.
        err = iSensorChannel->SetProperty( property );
        if( KErrNone == err )
            {
            //Accelerometer x-axis was succesfully deactivated
            }
        }
    else
        {
        //Accelerometer x-axis is allready deactive
        }

2.6 Retrieving values of array properties

A property that defines multiple discrete values inside one property ID is called an array property. Array properties can be identified with array index, which can be queried using the GetArrayIndex() method. For array properties, the array index is not the ESensrvSingleProperty . An example of array property is illustrated in the KSensrvPropIdDataRate property declared in the sensorgeneralproperties.h file.

The following example shows how to get the current data rate of the channel. The data rate is declared as an array property. First, read the KSensrvPropIdDataRate property using the GetPropertyL() method. If the property is an array property, the result of the GetPropertyL() call is a property with the ESensrvArrayPropertyInfo array index, otherwise the array information index is ESensrvSingleProperty . In case of an array property, the value of the current data rate is in the KSensrvPropIdDataRate property, whose array index is same as the array property's value.

    TSensrvProperty property;
    TInt err( KErrNone );
    TInt datarate( 0 );

    iSensorChannel->GetPropertyL( KSensrvPropIdDataRate,
                                  KSensrvItemIndexNone,
                                  property );

    if( ESensrvArrayPropertyInfo == property.GetArrayIndex() )
        {
        //Current data rate in use is in KSensrvPropIdDataRate property
        //which array index is declared in array propertys value.
        TInt arrayIndex( 0 );
        property.GetValue( arrayIndex );

        iSensorChannel->GetPropertyL( KSensrvPropIdDataRate,
                                      KSensrvItemIndexNone,
                                      arrayIndex,
                                      property );

        property.GetValue( datarate );
        }
    else
        {
        //KSensrvPropIdDataRate is a single property and current data rate can be read diretly from it.
        property.GetValue( datarate );
        }

2.7 Scaling channel data

The value of the channel data item can represent the actual value of the measured quantity. The channel data item can also represent the relative value which is scaled to between maximum and minimum value of the measured quantity. The KSensrvPropIdChannelDataFormat property defines if channel data items are in the scaled format. For scaled data items, the KSensrvPropIdScaledRange property defines the range of the data item value, and the KSensrvPropIdMeasureRange property defines the range for the measured quantity.

The following example reads the maximum value of measure range for data items (KSensrvPropIdScaledRange) and the maximum value of the measured quantity (KSensrvPropIdMeasureRange) . The example has considered that the KSensrvPropIdMeasureRange property can be defined as an array property.

    TSensrvProperty property;
    TInt  channelDataFormat( ESensrvFormatAbsolute );
    TInt  channelDataScaledRange( 1 );
    TReal channelDataMeasureRangeMaxValue( 1 );

    //Read channel data format
    iSensorChannel->GetPropertyL( KSensrvPropIdChannelDataFormat, KSensrvItemIndexNone, property );
    property.GetValue( channelDataFormat );

    if( ESensrvFormatScaled == channelDataFormat )
        {
        //Read data item scaled range
        iSensorChannel->GetPropertyL( KSensrvPropIdScaledRange, KSensrvItemIndexNone, property );
        property.GetMaxValue( channelDataScaledRange );

        //Read data item measure range
        iSensorChannel->GetPropertyL( KSensrvPropIdMeasureRange, KSensrvItemIndexNone, property );

        if( ESensrvArrayPropertyInfo == property.GetArrayIndex() )
            {
            TInt arrayIndex( 0 );
            property.GetValue( arrayIndex );//Value points to array index currently in use
            iSensorChannel->GetPropertyL( KSensrvPropIdMeasureRange,
                                          KSensrvItemIndexNone,
                                          arrayIndex,
                                          property );
            }
        else
            {
            //Single property
            }
        property.GetMaxValue( channelDataMeasureRangeMaxValue );
        }
    else
        {
        //No scaling needed.
        //Value of the data item represents actual value of the measured quantity.
        }

The scaled channel data item value can be converted to absolute value by dividing the channel data item value with the maximum value of scaled range of the channel and multiplying it with the maximum value of the measured quantity. For example, the accelerometer channel provides the following properties:

In the above example, the accelerometer channel data item value 64 means 1,01g absolute value (64 / 127 * 2g = 1.01g). The value of the channel data item can also be scaled and the scaling factor is published in the KSensrvPropIdChannelScale property.

2.8 Listening for channel property changes

The channel must be opened before listening for channel property changes. The client can use the property listener to get notifications about changed properties. If the client itself changes a property value, no notification is received. Property change listening is started and stopped with the SetPropertyListenerL() method. When property changes occurs, a PropertyChanged() notification is delivered through the property listener callback interface MSensrvPropertyListener .

2.9 Listening for channel data with conditions

The channel must be opened before listening for channel data with conditions. The following example shows how to start listening to double taps coming from the x-axis direction. When the condition is met, a ConditionMet() notification is delivered throught the data listener callback interface MSensrvChannelConditionListener .

The direction from tapping data ( TSensrvTappingData ) is used as a condition in this example. The direction value is set to the x-axis plus and minus to get notifications from double tap in both x-axis directions. A condition set is created for a container for one or more conditions. A condition is created to hold a channel condition item, see detailed comments from the example below. The condition is added to the condition set and the condition set is added to the channel. The client has ownership to this condition set and it must ensure that the condition set object is valid until the condition set is removed from the channel or the channel is destroyed. After the condition set is added to the channel, the condition listening is started with the StartConditionListeningL() method. The client needs to provide a pointer to the condition listener for the channel to be able to receive the ConditionMet() notification.

    CSensrvChannelCondition* condition = NULL;

    // The condition for double tapping channel is set so that
    // double tap to X-axis triggers condition met notification
    TSensrvTappingData doubleTappingCondition;
    TPckgC<TSensrvTappingData> doubleTappingConditionPckg( doubleTappingCondition );
    doubleTappingCondition.iTimeStamp = 0;
    doubleTappingCondition.iDirection = KSensrvAccelerometerDirectionXplus | KSensrvAccelerometerDirectionXminus;

    // In this example logical operator to be used in the condition set
    // does not have any effect because only one condition is added
    // to the condition set
    iConditionSet = CSensrvChannelConditionSet::NewL( ESensrvOrConditionSet );

    // The binary condition (ESensrvBinaryCondition) is used because double tapping
    // channel provides bitmask values.
    // Binary and (ESensrvOperatorBinaryAnd) operator is used because operator
    // checks if a bitmask data value got from double tapping channel has set at least
    // one of the bits set in the condition value.
    // In other words double tapping direction can be positive or negative.
    // Item index (3rd parameter) defines which attribute in data item is used for condition evaluation.
    // TSensrvTappingData::Index::iDirection means that iDirection is used
    // for condition from TSensrvTappingData class.
    // Last parameter (doubleTappingConditionPckg) contains value for
    // condition evaluation encapsulated in the package descriptor.
    condition = CSensrvChannelCondition::NewLC( ESensrvBinaryCondition,
                                                ESensrvOperatorBinaryAnd,
                                                TSensrvTappingData::Index::iDirection,
                                                doubleTappingConditionPckg );

    //Add the condition to condition set
    iConditionSet->AddChannelConditionL( condition );

    //Don't delete the condition because the ownership is transferred to the condition set
    CleanupStack::Pop( condition );

    // Add the condition set for the double tapping channel
    iDoubleTappingConditionChannel->AddConditionL( *iConditionSet );

    // Start condition listening
    //  aObjectCount is one, i.e. each double tapping condition is notified separately
    //  buffering period is not used, i.e. it is set to zero
    iDoubleTappingConditionChannel->StartConditionListeningL( iDataListener2,
                                                              1,
                                                              0 );

To implement condition listener, the client needs to inherit from the MSensrvChannelConditionListener interface class and implement declared pure virtual methods. When a channel condition set is met and condition listening is started, the ConditionMet() method is called by the Sensor Channel API. The following example shows how to handle condition met notification for double tapping channel. First the channel type of the received data and correct data buffer size is checked. The received data object is encapsulated in the package descriptor, and the channel data value which meets the condition is copied to a new package buffer. If the client wants to use the same condition set after notification, the client must add the condition set again to the channel object.

    void CTestClass::ConditionMet( CSensrvChannel& aChannel,
                               CSensrvChannelConditionSet& aChannelConditionSet,
                               TDesC8& aValue )
    {

    if( aChannel.GetChannelInfo().iChannelType == KSensrvChannelTypeIdAccelerometerDoubleTappingData )
        {
        if ( sizeof(TSensrvTappingData) == aValue.Size() )
            {
            TPckgBuf<TSensrvTappingData> dataBuf;
            dataBuf.Copy( aValue );

            //dataBuf() contains channel data value which meets the condition

            //Use the same condition set again
            if( iDoubleTappingConditionChannel )
                {
                // Add the condition set for the double tapping channel.
                // Condition listening is not stopped therefore 
                // only the new condition set needs to be added.
                iDoubleTappingConditionChannel->AddConditionL( *iConditionSet );
                }
            }
        else
            {
            //Size of the aValue is unexpected
            }
        }
    else
        {
        //The condition is not met for double tapping channel
        }

    }

2.10 Error handling

The leave mechanism and return values are used to indicate errors. Use practices such as stack cleanup and the TRAP harness to handle errors. Listener callback interfaces ( MSensrvDataListener , MSensrvPropertyListener , MSensrvChannelConditionListener and MSensrvChannelListener ) offer a callback method to inform errors during listening.

2.11 Memory and Performance Considerations

The Sensor Channel API memory consumption cdepends on the channel's data rate and/or size of the data in the receiving buffer. High data rate with a small data receiving buffer causes increased interprocess communications. On the other hand, a big data receiving buffer reserves more memory.

3 Glossary

3.1 Definitions

Definitions
Definition Description
Channel Abstraction of a sensor. Data from one physical sensor may be mapped to several channels.
Channel condition A value-operator pair targeting a single value inside a data item, indicated by an index. A condition is met when the channel data value comparison with the condition value using condition operator is true. The conditions are gathered in condition sets. There are two condition types: single limit conditions and range conditions. Range conditions are actually made up from two separate conditions representing lower and upper limits of the range.
Channel item property A single property inside channel data item.
Channel property A single channel property.
Data Item A discrete data package that contains sensor data or an event parsed from sensor data and possibly some related values, such as timestamp. Each channel provides single type of data items only.
Property Properties are configuration values of sensors and channels. Changing a property affects all clients listening to affected channel(s).
Sensor Physical sensor. Single sensor can provide multiple channels, such as raw data channel and event channels, or multiple sensor readings can be incorporated into a single channel.
Sensor property These properties affect all channels which draw data from one physical sensor.