Skip to content

Feed Sensor Data

Feed sensor data, such as position, acceleration, compass or temperature.

Feed Sensor Data - cpp example screenshot

Setup

First, get an API key token, see the Getting Started guide.

Prerequisites

It is required that you complete the Environment Setup - CPP Examples guide before starting this guide.

Setting your API key token

To set your API key token, define it like this:

#define API_TOKEN "YOUR_API_KEY_TOKEN"

replacing YOUR_API_KEY_TOKEN with your actual API key token text, within the quotes.

This can be done in the main() function, before the following line:

#if defined(API_TOKEN)

or outside the main() function, further up, or in a header file.

Although it is also possible to define your API key token as the text value of the GEM_TOKEN environment variable, this is not recommended, as it is not secure.

Use case

Feed data obtained from external sensors to the SDK.

How to use the sample

This example does not have a map viewport as usual; instead, the sensor data is printed in the debug console.

How it works

  1. Create an instance of Environment and set your API key token:

1int main( int argc, char** argv )
2{
3   std::string projectApiToken = "";
4#define API_TOKEN "YOUR_API_KEY_TOKEN"
5#if defined(API_TOKEN)
6   projectApiToken = std::string( API_TOKEN );
7#endif
8   // Sdk objects can be created & used below this line
9   Environment::SdkSession session(projectApiToken, { argc > 1 ? argv[1] : "" });
  1. The SDK is initialized with your API key token string and the log file path, where to write the application logs. Note that the log file path is not initialized by default, the empty string above, after the API key token, which means that no logs are written. The log file path is initialized with the first command line argument, if any. Create a gem::MapView interactive map object, using an OpenGL context for 3D graphics, the SDK environment created above, and a touch event listener for interactive user touch and mouse input, such as pan and zoom.

1GEM_LOGE("FEED SENSOR DATA EXAMPLE   ###   ###   ###   ###");
2
3std::thread thread1(pushDataThread, "pushDataThread");
4while( thread1.joinable() )
5{
6   WAIT_TIME_OUT( 1000 );
7}
8return 0;
  1. There are 2 threads - one thread pushDataThread feeds sensor data into the SDK (which in a real situation would be obtained from external sensors). The pushDataThread() first creates a gem::sense::DataTypeList containing the types of data to be pushed. Position data should always be included, because there can be no navigation without position. All other types are optional. In this example, 3 data types are pushed - Position, Acceleration and Gyroscope. In a real life situation, the sensor data is read from external sensors; in this example, each of the 3 sensor data types is in a separate header file containing sample data. A DataReceiver listener is added for each type of data the SDK is interested in receiving. The data is pushed, waiting between records, just like real data, and note that the different sensors do not have to be pushed at the same rate.

 1void pushDataThread(std::string pushDataThread) //gem::sense::DataSourcePtr externalDataSource
 2{
 3   int startTimeSec = (int)positionData[0][0];
 4   int accelerationIndex = 0, gyroscopeIndex = 0, positionIndex = 0;
 5
 6   // Create a list of the sensor data types that will be input into the SDK
 7   gem::sense::DataTypeList availableDataTypes;
 8   availableDataTypes.push_back(gem::sense::EDataType::Position); // Position is mandatory
 9   availableDataTypes.push_back(gem::sense::EDataType::Acceleration);
10   availableDataTypes.push_back(gem::sense::EDataType::Gyroscope);
11
12   // Create an external data source to input the above data types into the SDK
13   auto externalDataSourcePair = gem::sense::produceExternalDataSource(availableDataTypes);
14   auto externalDataSource = externalDataSourcePair.first;
15   if ( externalDataSource )
16   {
17      // Add a listener for the data type(s) desired to be received back from the SDK
18      auto listener = gem::StrongPointerFactory<DataReceiver>();
19      externalDataSource->addListener(listener, gem::sense::EDataType::Position);
20      externalDataSource->addListener(listener, gem::sense::EDataType::ImprovedPosition);
21
22      // Start the data source
23      externalDataSource->start();
24      while (true)
25      {
26         GEM_LOGE("PUSH ACCELERATION DATA (input)");
27         while (true)
28         {
29             if (accelerationIndex >= TOTAL_ACCELERATION_RECORDS || startTimeSec < (int)accelerationData[accelerationIndex][0])
30             {
31                 break;
32             }
33             externalDataSource->pushData(
34                 gem::sense::produceAccelerationData((gem::LargeInteger)(accelerationData[accelerationIndex][0] * 1000.0),
35                     (double)accelerationData[accelerationIndex][1],
36                     (double)accelerationData[accelerationIndex][2],
37                     (double)accelerationData[accelerationIndex][3],
38                     gem::sense::EUnitOfMeasurement::G
39                 ).first); // ios data is in G, android data is in m/s^2
40             accelerationIndex++;
41         }
42         GEM_LOGE("PUSH GYROSCOPE DATA (input)");
43         while (true)
44         {
45             if (gyroscopeIndex >= TOTAL_GYROSCOPE_RECORDS || startTimeSec < (int)gyroscopeData[gyroscopeIndex][0])
46             {
47                 break;
48             }
49             externalDataSource->pushData(
50                 gem::sense::produceRotationRateData((gem::LargeInteger)(gyroscopeData[gyroscopeIndex][0] * 1000.0),
51                     (double)gyroscopeData[gyroscopeIndex][1],
52                     (double)gyroscopeData[gyroscopeIndex][2],
53                     (double)gyroscopeData[gyroscopeIndex][3]
54                 ).first);
55             gyroscopeIndex++;
56         }
57         while (true)
58         {
59             if (positionIndex >= TOTAL_POSITION_RECORDS || startTimeSec < (int)positionData[positionIndex][0])
60             {
61                 break;
62             }
63             GEM_LOGE("PUSH POSITION DATA RECORD %d t = %d sec (input) lon,lat( %.8f, %.8f ); gyro rec %d; acceleration rec %d",
64                 positionIndex, startTimeSec, (double)positionData[positionIndex][3], (double)positionData[positionIndex][2], gyroscopeIndex, accelerationIndex);
65             externalDataSource->pushData(
66                 gem::sense::producePositionData((gem::LargeInteger)(positionData[positionIndex][0] * 1000.0),
67                     (double)positionData[positionIndex][2], // Latitude
68                     (double)positionData[positionIndex][3], // Longitude
69                     (double)positionData[positionIndex][4], // Altitude (m)
70                     (double)positionData[positionIndex][6], // Heading (deg east of north)
71                     (double)positionData[positionIndex][5]  // Speed (m/s)
72                 ).first);
73             positionIndex++;
74         }
75         startTimeSec++;
76
77         // Push / feed / input one position / second to the SDK, along with all other sensor measurements during that second
78         std::this_thread::sleep_for(std::chrono::milliseconds(1000));
79      }
80      GEM_LOGE("DATA INPUT COMPLETE");
81   }
82}
  1. The other thread is the SDK receiving the sensor data and printing it to the debug console, also indicating the type of data received. A custom DataSourceListener class is defined to enable receiving processed data from the SDK. Although only 3 types of sensors are fed into the SDK, this class shows how to detect all possible processed sensor output from the SDK. The custom DataSourceListener must implement the onNewData() function to enable receiving processed data from the SDK.

 1class DataReceiver : public gem::sense::IDataSourceListener
 2{
 3public:
 4   DataReceiver() {}
 5   void onNewData(gem::sense::DataPtr data) override
 6   {
 7      auto dataType = data.get()->getType();
 8      switch((int)dataType)
 9      {
10      case (int)gem::sense::EDataType::Acceleration:
11         GEM_LOGE("RECEIVED NEW DATA - Acceleration"); break;
12      case (int)gem::sense::EDataType::Activity:
13         GEM_LOGE("RECEIVED NEW DATA - Activity"); break;
14      case (int)gem::sense::EDataType::Attitude:
15         GEM_LOGE("RECEIVED NEW DATA - Attitude"); break;
16      case (int)gem::sense::EDataType::Battery:
17         GEM_LOGE("RECEIVED NEW DATA - Battery"); break;
18      case (int)gem::sense::EDataType::Camera:
19         GEM_LOGE("RECEIVED NEW DATA - Camera"); break;
20      case (int)gem::sense::EDataType::Compass:
21         GEM_LOGE("RECEIVED NEW DATA - Compass"); break;
22      case (int)gem::sense::EDataType::MagneticField:
23         GEM_LOGE("RECEIVED NEW DATA - MagneticField"); break;
24      case (int)gem::sense::EDataType::Orientation:
25         GEM_LOGE("RECEIVED NEW DATA - Orientation"); break;
26      case (int)gem::sense::EDataType::Position:
27      {
28         auto dataPosition = data.get()->cast<gem::sense::IPosition>();
29         double alt = dataPosition.get()->getAltitude();
30         double lat = dataPosition.get()->getLatitude();
31         double lon = dataPosition.get()->getLongitude();
32         GEM_LOGE("RECEIVED NEW DATA - Position lon,lat( %.8f, %.8f ); alt %f (inout)", lon, lat, alt);
33         break;
34      }
35      case (int)gem::sense::EDataType::ImprovedPosition:
36      {
37         auto dataImprovedPosition = data.get()->cast<gem::sense::IImprovedPosition>();
38         unsigned long timeStamp = dataImprovedPosition.get()->getSatelliteTime();
39         double alt = dataImprovedPosition.get()->getAltitude();
40         double lat = dataImprovedPosition.get()->getLatitude();
41         double lon = dataImprovedPosition.get()->getLongitude();
42         double speed = dataImprovedPosition.get()->getSpeed();
43         double speedLimit = dataImprovedPosition.get()->getRoadSpeedLimit();
44         double heading = dataImprovedPosition.get()->getCourse();
45         double horizAccuracy = dataImprovedPosition.get()->getHorizontalAccuracy();
46         GEM_LOGE("RECEIVED NEW DATA - ImprovedPosition[map matched] timestamp( %lu ); "
47            "lon,lat( %.8f, %.8f ); alt %dm; heading %.1f(deg); horizAcc %.1fm; "
48            "speed %f(m/sec), %.1f(km/h); speed limit %.1f(m/s) (output)",
49            timeStamp, lon, lat, (int)alt, heading, horizAccuracy, speed, speed*3.6, speedLimit );
50         break;
51      }
52      case (int)gem::sense::EDataType::Gyroscope:
53         GEM_LOGE("RECEIVED NEW DATA - Gyroscope"); break;
54      case (int)gem::sense::EDataType::Temperature:
55         GEM_LOGE("RECEIVED NEW DATA - Temperature"); break;
56      case (int)gem::sense::EDataType::Notification:
57         GEM_LOGE("RECEIVED NEW DATA - Notification"); break;
58      case (int)gem::sense::EDataType::MountInformation:
59         GEM_LOGE("RECEIVED NEW DATA - MountInformation"); break;
60      default:
61         GEM_LOGE("RECEIVED NEW DATA - UNDEFINED"); break;
62      }
63   }
64};

C++ Examples

Maps SDK for C++ Examples can be downloaded or cloned with Git