BltTofApi Quick Start Guide

Aus BECOM Systems Support
Version vom 22. August 2023, 20:35 Uhr von en>Peter (1 Version importiert)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Zur Navigation springen Zur Suche springen

Overview

In order to create a common interface for our products we define the interfaces between a ToF device and an application. The main part of this model is the common BltTofApi which is written in C for platform independency. The BltTofSuite and any application able to access the BltTofApi interface is compatible with any device with existing lib implementing the BltTofApi.

Interfacing concept

Every ToF system built by or for Bluetechnix shall be accessible by this common interface. A lib implementing this interface shall be written in C only and compile on any platform. The interface is kept as simple as possible and covers all functionalities of all ToF sensors.


Before implementing and building the following examples, please check the Section BltTofApi Build instructions.
When encountering errors with the API consult the list of error codes in Section BltTofApi Error codes.
The API supports a callback (infoEvent) which can be used for further investigation of the (non-)functionality, described in Section BltTofApi Events description

SDK Example

This example code is supposed to explain the most important functions exposed to the user. For more detailed information you can also check the header files in the SDK package

<source lang="c" collapse="true" first-line="2" highlight="[4,6]" title="title">

  1. include <stdio.h>
  2. include <stdlib.h>
  3. include <string.h>
  4. include <bta.h>
  1. if defined(PLAT_LINUX) || defined(linux)
   #include <unistd.h>
  1. elif defined(PLAT_WINDOWS) || defined(WIN32) || defined(WIN64)
   #include <windows.h>
  1. else
   #error "No platform defined"
  1. endif


static void BTA_CALLCONV discoveryCallback(BTA_DeviceInfo *deviceInfo) {

   printf("discoveryCallback: device 0x%x, serial %d \n", deviceInfo->deviceType, deviceInfo->serialNumber);

}


static void errorHandling(BTA_Status status) {

   if (status != BTA_StatusOk) {
       char statusString[100];
       BTAstatusToString(status, statusString, strlen(statusString));
       printf("error: %s (%d)\n", statusString, status);
       printf("Hit <Return> to end the example\n");
       fgetc(stdin);
       exit(0);
   }

}

static void wait(int ms) {

   #if defined(PLAT_LINUX) || defined(linux)
       usleep(ms * 1000);
   #elif defined(PLAT_WINDOWS) || defined(WIN32) || defined(WIN64)
       Sleep(ms);
   #endif

}


// Example for an implementation of the infoEvent callback handler static void BTA_CALLCONV infoEvent(BTA_EventId eventId, int8_t *msg) {

   char eventIdString[100];
   BTAeventIdToString(eventId, eventIdString, sizeof(eventIdString));
   printf("   infoEvent: (%s) %s\n", eventIdString, msg);

}


// Example for an implementation of the frameArrived callback handler static void BTA_CALLCONV frameArrived(BTA_Frame *frame) {

   BTA_Status status;
   BTA_Frame *frameClone;
   status = BTAcloneFrame(frame, &frameClone);
   errorHandling(status);
   // The frameClone pointer can now be stored for later processing, or
   // a new thread can handle that, // but this function should return now.
   // Wherever the processing is done, before loosing the last reference to it,
   // the clone must be free'd
   BTAfreeFrame(&frameClone);
   // (Do not free or alter the frame that was passed as a parameter)

}


  1. ifdef FOR_EXPERTS_ONLY

// Example for an implementation of the progressReport callback handler static void BTA_CALLCONV progressReport(BTA_Status status, uint8_t percentage) {

   if (percentage == 0 && status == BTA_StatusOk) {
       printf("Flash update started");
   }
   else if (percentage == 100 && status == BTA_StatusOk) {
       printf("Flash update finished with success");
   }
   else if (status == BTA_StatusOk){
       printf("Flash update progress: %d", percentage);
   }
   else {
       char statusString[100];
       BTAstatusToString(status, statusString, strlen(statusString));
       printf("Flash update failed: %s", statusString);
   }

}

  1. endif


int main() {

   BTA_Status status;


   // BtaP100Lib only (until now)
   // Discovery
   //----------------------------------------------------------------------------------------------
   // The feature of listing available devices is currently only supported by BtaP100Lib.
   BTA_DiscoveryConfig discoveryConfig;
   BTAinitDiscoveryConfig(&discoveryConfig);
   status = BTAstartDiscovery(&discoveryConfig, &discoveryCallback);


   // Initialization
   //----------------------------------------------------------------------------------------------
   // First, the library must be configured via the configuration c-structure.
   // The configuration structure must be initialized with standard values using the function
   // BTAinitConfig. The specific implementation of the library defines which parameters are 
   // required and which can be left out. The required parameters must then be set to a valid
   // value before calling BTAopen.
   BTA_Config config;
   printf("BTAinitConfig()\n");
   status = BTAinitConfig(&config);
   errorHandling(status);
   
   // Connection Parameters
   //----------------------------------------------------------------------------------------------
   // Depending on the library and the device, different connection parameters must be set.
   // Redundant connections (like TCP and UDP control connections) are used sequencially and
   // exclusively
   // (UDP is tried first and only if it fails, TCP is connected)
   // Unnecessary information is ignored (BtaP100Lib ignores ethernet parameters)


   // BtaEthLib only
   // UDP data connection (normally multicast address)
   uint8_t udpDataIpAddr[] = { 224, 0, 0, 1 };
   config.udpDataIpAddr = udpDataIpAddr;
   config.udpDataIpAddrLen = 4;
   config.udpDataPort = 10002;
   // TCP control connection (device's IP address)
   uint8_t tcpDeviceIpAddr[] = { 192, 168, 0, 10 };
   config.tcpDeviceIpAddr = tcpDeviceIpAddr;
   config.tcpDeviceIpAddrLen = 4;
   config.tcpControlPort = 10001;
   // UDP control outbound connection (device's IP address)
   uint8_t udpControlOutIpAddr[] = { 192, 168, 0, 10 };
   config.udpControlOutIpAddr = udpControlOutIpAddr;
   config.udpControlOutIpAddrLen = 4;
   config.udpControlOutPort = 10003;
   // UDP control inbound connection (normally the host's IP address)
   uint8_t udpControlInIpAddr[] = { 192, 168, 0, 1 };
   config.udpControlInIpAddr = udpControlInIpAddr;
   config.udpControlInIpAddrLen = 4;
   config.udpControlInPort = 10004;
   
   // Optional settings for advanced functionalities
   // If you want to receive status updates from the library,
   // register a callback function for informative events.
   config.infoEvent = &infoEvent;
   // ...and set the verbosity for infoEvents.
   config.verbosity = 9;
   // If you want to receive the frames immediately when they arrive,
   // register a callback function for incoming frames.
   config.frameArrived = &frameArrived;
   // Choose whether and how queueing of frames is done.
   // Queueing does not affect the frameArrived callback at all
   // If you don't specify frame queuing, you can still use the frameArrived callback,
   // but you will get an error on BTAgetFrame()
   // We choose 'DropOldest', so we always get the most recent frame
   config.frameQueueMode = BTA_QueueModeDropOldest;
   // Set the length of the frame queue.
   config.frameQueueLength = 1;
   
   // The frame mode can be configured in order to get the desired data channels in a frame from
   // the sensor / library:
   config.frameMode = BTA_FrameModeDistAmp;
   // BtaP100Lib only
   // If this parameter is left empty, the default lens configuration will be used.
   // Otherwise, use the name (and path) of a valid lens calibration file encoded in ASCII.
   config.calibFileName = 0; //(uint8_t *)"calibFile.bin";


   // Connecting
   //----------------------------------------------------------------------------------------------
   // Now that the configuration structure is filled in, the connection is ready to be opened
   // The first infoEvents should fire while the library is connecting to the sensor. That, however
   // depends on the library implementation and the configured verbosity.
   BTA_Handle btaHandle;
   printf("BTAopen()\n");
   status = BTAopen(&config, &btaHandle);
   errorHandling(status);
   // Connection Status
   //----------------------------------------------------------------------------------------------
   // It is possible to get the service state and connection state (not all libraries are able to
   // detect their connection status):
   
   printf("Service running: %d\n", BTAisRunning(btaHandle));
   printf("Connection up: %d\n", BTAisConnected(btaHandle));


   // Querying Device Information
   //----------------------------------------------------------------------------------------------
   // The following example shows, how general information on the device can be retrieved.
   // The resulting struct may be only partly filled depending on the device’s capabilities.
   BTA_DeviceInfo *deviceInfo;
   printf("BTAgetDeviceInfo()\n");
   status = BTAgetDeviceInfo(btaHandle, &deviceInfo);
   errorHandling(status);
   printf("Device type: 0x%x\n", deviceInfo->deviceType);
   printf("BTAfreeDeviceInfo()\n");
   BTAfreeDeviceInfo(deviceInfo);
   
   // Frame-Rate
   //----------------------------------------------------------------------------------------------
   // Read and change the frame rate via these functions
   float frameRate;
   printf("BTAgetFrameRate()\n");
   status = BTAgetFrameRate(btaHandle, &frameRate);
   errorHandling(status);
   printf("Framerate is %f\n", frameRate);
   printf("BTAsetFrameRate(%f)\n", frameRate);
   status = BTAsetFrameRate(btaHandle, frameRate);
   errorHandling(status);
   
   // Integration time
   //----------------------------------------------------------------------------------------------
   // Read and change the main integration time via this functions:
   uint32_t integrationTime;
   printf("BTAgetIntegrationTime()\n");
   status = BTAgetIntegrationTime(btaHandle, &integrationTime);
   errorHandling(status);
   printf("Integration time is %d\n", integrationTime);
   printf("BTAsetIntegrationTime(%d)\n", integrationTime);
   status = BTAsetIntegrationTime(btaHandle, integrationTime);
   errorHandling(status);
   
   // Modulation frequency
   //----------------------------------------------------------------------------------------------
   // Read and change the main modulation frequency via this functions
   uint32_t modulationFrequency;
   printf("BTAgetModulationFrequency()\n");
   status = BTAgetModulationFrequency(btaHandle, &modulationFrequency);
   errorHandling(status);
   printf("Modulation frequency is %d\n", modulationFrequency);
   printf("BTAsetModulationFrequency(%d)\n", modulationFrequency);
   status = BTAsetModulationFrequency(btaHandle, modulationFrequency);
   errorHandling(status);
   
   // Global offset
   //----------------------------------------------------------------------------------------------
   // Global stands for ‘all pixels’, meaning, that this offset is applied to all pixels.
   // It is, for all current devices, valid for the currently set modulation frequency.
   // It can only and should be set for all predefined modulation frequencies (see device’s SUM).
   // When changing the modulation frequency, the global offset reads differently.
   float offset;
   printf("BTAgetGlobalOffset()\n");
   status = BTAgetGlobalOffset(btaHandle, &offset);
   errorHandling(status);
   printf("Global offset is %f\n", offset);
   printf("BTAsetGlobalOffset(%f)\n", offset);
   status = BTAsetGlobalOffset(btaHandle, offset);
   errorHandling(status);
   
   // Frame mode
   //----------------------------------------------------------------------------------------------
   // The frame mode defines what channels the sonsor delivers to the library and/or
   // what data the library puts into a frame
   printf("BTAsetFrameMode(BTA_FrameModeXYZAmp)\n");
   status = BTAsetFrameMode(btaHandle, BTA_FrameModeXYZAmp);
   errorHandling(status);
   
   // Register read/write
   //----------------------------------------------------------------------------------------------
   // Register operations are done via readRegister and writeRegister.
   // Most devices support mult-read and multi-write. The last parameter can be used to take
   // advantage of that feature.
   // For a normal read/write null can be passed.
   // Example for one register at address 0x20:
   uint32_t regValue;
   printf("BTAreadRegister()\n");
   status = BTAreadRegister(btaHandle, 0x20, &regValue, 0);
   errorHandling(status);    
   printf("BTAwriteRegister()\n");
   status = BTAwriteRegister(btaHandle, 0x20, &regValue, 0);
   errorHandling(status);
   //The application must guarantee that register accesses are done exclusively. A read/write is
   // only called when the last read/write returned.
   
   // Wait a little, so the frame in the lib's queue is fresh regarding the just written parameters
   wait(2000);
   // Frame Retrieval
   //----------------------------------------------------------------------------------------------
   // Once the connection is established, the frameArrived callback (if not null) is called
   // whenever a frame is received from the sensor.
   // But a frame can also actively be requested from the library
   BTA_Frame *frame;
   printf("BTAgetFrame()\n");
   status = BTAgetFrame(btaHandle, &frame, 300);
   errorHandling(status);
   
   // The frame structure contains all the necessary information which can be accessed directly
   // or using the helper functions.
   // For getting the buffer with amplitudes, for example:
   uint16_t *amplitudes;
   BTA_DataFormat dataFormat;
   BTA_Unit unit;
   uint16_t xRes, yRes;
   printf("BTAgetAmplitudes()\n");
   status = BTAgetAmplitudes(frame, (void **)&amplitudes, &dataFormat, &unit, &xRes, &yRes);
   errorHandling(status);
   if (dataFormat == BTA_DataFormatUInt16) {
       // This dataformat tells us that it's ok to interpret the data as (uint16_t *)
       if (unit == BTA_UnitUnitLess) {
           printf("Got amplitude data\n");
           // -> access amplitude data simply as amplitudes[i]
           uint32_t ampAvg = 0;
           for (int y = 0; y < yRes; y++) {
               for (int x = 0; x < xRes; x++) {
                   ampAvg += amplitudes[x + y*xRes];
               }
           }
           if (xRes != 0 && yRes != 0) {
               printf("The average amplitude is %d\n", ampAvg / xRes / yRes);
           }
       }
   }
   else if (dataFormat == BTA_DataFormatFloat32) {
       // This dataformat tells us that it's ok to interpret the data as (float *)
       if (unit == BTA_UnitUnitLess) {
           printf("Got amplitude data\n");
           // -> access amplitude data simply as ((float *)amplitudes)[i]
           float ampAvg = 0;
           for (int y = 0; y < yRes; y++) {
               for (int x = 0; x < xRes; x++) {
                   ampAvg += ((float *)amplitudes)[x + y*xRes];
               }
           }
           if (xRes != 0 && yRes != 0) {
               printf("The average amplitude is %d\n", (int)ampAvg / xRes / yRes);
           }
       }
   }
   // The first pixel value in the buffer corresponds to the
   // upper left pixel (sensor point of view).
   // In order to extract the Cartesian coordinates, do the following:    
   void *xCoordinates, *yCoordinates, *zCoordinates;
   printf("BTAgetXYZcoordinates()\n");
   status = BTAgetXYZcoordinates(frame, &xCoordinates, &yCoordinates, &zCoordinates,
                                 &dataFormat, &unit, &xRes, &yRes);
   errorHandling(status);
   if (dataFormat == BTA_DataFormatSInt16) {
       // This dataformat tells us that it's ok to cast (void *) to (int16_t *)
       if (unit == BTA_UnitMillimeter) {
           printf("Got 3D data\n");
           // -> cast the void* to int16_t* and access data points simply as:
           //   ((int16_t *)xCoordinates)[i]
           //   ((int16_t *)yCoordinates)[i]
           //   ((int16_t *)zCoordinates)[i]
           uint32_t radiusMin = 0xffffffff;
           int16_t xCoordinate = 0, yCoordinate = 0, zCoordinate = 0;
           for (int y = 0; y < yRes; y++) {
               for (int x = 0; x < xRes; x++) {
                   if (((int16_t *)zCoordinates)[x + y*xRes] > 0) {
                       uint32_t radius = ((int16_t *)xCoordinates)[x + y*xRes] * 
                                         ((int16_t *)xCoordinates)[x + y*xRes];
                       radius += ((int16_t *)yCoordinates)[x + y*xRes] *
                                 ((int16_t *)yCoordinates)[x + y*xRes];
                       radius += ((int16_t *)zCoordinates)[x + y*xRes] *
                                 ((int16_t *)zCoordinates)[x + y*xRes];
                       if (radius < radiusMin) {
                           radiusMin = radius;
                           xCoordinate = ((int16_t *)xCoordinates)[x + y*xRes];
                           yCoordinate = ((int16_t *)yCoordinates)[x + y*xRes];
                           zCoordinate = ((int16_t *)zCoordinates)[x + y*xRes];
                       }
                   }
               }
           }
           printf("The nearest point is (%d, %d, %d) [mm]\n", xCoordinate, yCoordinate, zCoordinate);
       }
   }
   else if (dataFormat == BTA_DataFormatFloat32) {
       // This dataformat tells us that it's ok to cast (void *) to (float *)
       if (unit == BTA_UnitMeter) {
           printf("Got 3D data\n");
           // -> cast the void* to (float *) and access data points simply as:
           //   ((float *)xCoordinates)[i]
           //   ((float *)yCoordinates)[i]
           //   ((float *)zCoordinates)[i]
           float radiusMin = 0xffffffff;
           float xCoordinate = 0, yCoordinate = 0, zCoordinate = 0;
           for (int y = 0; y < yRes; y++) {
               for (int x = 0; x < xRes; x++) {
                   if (((float *)zCoordinates)[x + y*xRes] > 0) {
                       float radius = ((float *)xCoordinates)[x + y*xRes] * 
                                      ((float *)xCoordinates)[x + y*xRes];
                       radius += ((float *)yCoordinates)[x + y*xRes] *
                                 ((float *)yCoordinates)[x + y*xRes];
                       radius += ((float *)zCoordinates)[x + y*xRes] *
                                 ((float *)zCoordinates)[x + y*xRes];
                       if (radius < radiusMin) {
                           radiusMin = radius;
                           xCoordinate = ((float *)xCoordinates)[x + y*xRes];
                           yCoordinate = ((float *)yCoordinates)[x + y*xRes];
                           zCoordinate = ((float *)zCoordinates)[x + y*xRes];
                       }
                   }
               }
           }
           if (radiusMin < 0xffffffff) {
               printf("The nearest point is (%d, %d, %d) [mm]\n", (int)(1000*xCoordinate),
                                                                  (int)(1000*yCoordinate),
                                                                  (int)(1000*zCoordinate));
           }
       }
   }
   // The Channels X, Y and Z are given in the predefine Cartesian coordinate system.
   // See Figure "ToF coordinate system".


   // Example for extracting RGB565 data out of a BTA_Frame
   if (frame) {
       if (frame->channels) {
           for (int i = 0; i < frame->channelsLen; i++) {
               if (frame->channels[i]->id == BTA_ChannelIdColor) {
                   dataFormat = frame->channels[i]->dataFormat;
                   if (dataFormat == BTA_DataFormatRgb565) {
                       // Previously we selected a frameMode without an RGB channel, 
                       // so this will never be true unless we change the frameMode
                       unit = frame->channels[i]->unit;
                       xRes = frame->channels[i]->xRes;
                       yRes = frame->channels[i]->yRes;
                       void *colorBuffer = frame->channels[i]->data;
                       if (colorBuffer) {
                           for (int xy = 0; xy < xRes*yRes; xy++) {
                               uint16_t pixelRgb565 = ((uint16_t *)colorBuffer)[xy];
                               uint8_t b = ((uint8_t)(((pixelRgb565 & 0x001f) << 3) |
                                           ((pixelRgb565 & 0x001f) >> 2)));
                               uint8_t g = ((uint8_t)(((pixelRgb565 & 0x07e0) >> 3) |
                                           ((pixelRgb565 & 0x07e0) >> 9)));
                               uint8_t r = ((uint8_t)(((pixelRgb565 & 0xf800) >> 8) |
                                           ((pixelRgb565 & 0xf800) >> 13)));
                               printf("r %d g %d b %d\n", r, g, b);
                           }
                       }
                   }
               }
           }
       }
   }


   // Frame Cleanup
   //----------------------------------------------------------------------------------------------
   // A successful call to getFrame or cloneFrame always demands for a call to BTAfreeFrame
   printf("BTAfreeFrame()\n");
   status = BTAfreeFrame(&frame);
   errorHandling(status);


   #ifdef FOR_EXPERTS_ONLY
   // Flash update
   //----------------------------------------------------------------------------------------------
   // A transfer of data to the device, typically to be saved in the device’s flash memory can be
   // performed by calling the library’s function BTAflashUpdate. It is mainly used to perform a 
   // firmware update. The struct BTA_FlashUpdateConfig is passed containing all the information
   // needed. The library defines which fields in the structure have to be filled depending on
   // the type of the update and/or data. Thus, the user must know how to configure the update
   // and what data to pass as parameter.
   // An example for updating the pixel list is:
   BTA_FlashUpdateConfig flashUpdateConfig;
   flashUpdateConfig.target = BTA_FlashTargetPixelList;
   uint32_t dummydata = 0x12345678;
   flashUpdateConfig.data = (uint8_t *)&dummydata;
   flashUpdateConfig.dataLen = 4;
   printf("BTAflashUpdate()\n");
   status = BTAflashUpdate(btaHandle, &flashUpdateConfig, (FN_BTA_ProgressReport)&progressReport);
   errorHandling(status);
   // This function simplifies the process of a firmware update allowing the user to pass a
   // connection handle and the name of a binary firmware file. Internally it uses BTAflashUpdate()
   printf("BTAfirmwareUpdate()\n");
   status = BTAfirmwareUpdate(btaHandle, (uint8_t *)"fw.bin", (FN_BTA_ProgressReport)&progressReport);
   errorHandling(status);
   #endif


   #ifdef THIS_SECTION_COMPROMISES_REGISTER_VALUES_PERMANENTLY    
   // Storing and restoring register settings
   //----------------------------------------------------------------------------------------------
   // The register values can be stored in flash memory in order to be preserved beyond a
   // reboot/power cycle
   printf("BTAwriteCurrentConfigToNvm()\n");
   status = BTAwriteCurrentConfigToNvm(btaHandle);
   errorHandling(status);
   // The restoring of the default configuration typically requires a reboot in order to take
   // immediate effect
   printf("BTArestoreDefaultConfig()\n");
   status = BTArestoreDefaultConfig(btaHandle);
   errorHandling(status);
   #endif
   
   // Grabbing the stream
   //----------------------------------------------------------------------------------------------
   // The Blt libraries are able to write frames to disk. Very conveniently, all the frame data
   // is stored in a *.bltstream file. These files can later be replayed by the BtaStreamLib as
   // if the same sensor was connected.
   BTA_GrabbingConfig grabbingConfig;
   printf("BTAinitGrabbingConfig()\n");
   status = BTAinitGrabbingConfig(&grabbingConfig);
   errorHandling(status);
   grabbingConfig.filename = (uint8_t *)"test.bltstream"; // (ASCII coded)
   printf("BTAstartGrabbing()\n");
   status = BTAstartGrabbing(btaHandle, &grabbingConfig);
   errorHandling(status);
   // Grabbing is taking place. Let it grab for 2 sec
   wait(2000);
   
   printf("BTAstopGrabbing()\n");
   status = BTAstartGrabbing(btaHandle, 0);
   errorHandling(status);
   // Device Reset
   //----------------------------------------------------------------------------------------------
   // If the device and the library choose to implement this functionality, a device reset can be
   // performed
   status = BTAsendReset(btaHandle);
   errorHandling(status);
   // Disconnecting
   //----------------------------------------------------------------------------------------------
   // When work is done and no other threads need to access the library's functions,
   // disconnect the sensor and stop the service by simply calling BTAclose
   printf("BTAclose()\n");
   status = BTAclose(&btaHandle);
   errorHandling(status);


   printf("Hit <Return> to end the example\n");
   fgetc(stdin);

}

</source>