Cepton
Cepton

Migrate SDK v1 to SDK v2: A Quick Guide

1. Table of Contents

2. Migrating to SDK v2

What are the reasons for migrating from SDK v1 to SDK v2? If you want your old code work with Vista-X or Nova sensors, you will have to migrate. We made this breaking change to SDK v2 be light weight and modular; and more importantly, SDK v2 allows us to share our source code without having to deal with old proprietary legacy code. Read more about SDK v2 compared with SDK v1 at Future-proof Cepton lidar applications with SDK v2

Before starting migrating, you probably want to get familiar with SDK v2 first. Here are some resources:

3. Summary of what's changed

3.1. Naming convention is changed from snake_case to camelCase

We do this mostly because it is an easy way to avoid all possible naming clashes without being awkward. The goal is to support both SDK v1 and SDK v2 usage in the same application.

3.2. Network is no longer started by default

As we get more ways to connect to current and future generations of lidars, starting ethernet UDP communication by default is no longer a good idea. Having CeptonStartNetworking and CeptonStopNetworking will enable more flexibility in apps.

3.3. SDK v2 avoids floating point numbers

Some embedded environments doesn't have float point number support. Using floats in general introduces more CPU overhead. Avoiding overhead is a key design goal for SDK v2. Having fixed point only does not degrade accuracy and usually leads to more optimized code.

3.4. More hooks exposed

In SDK v2, more hooks to perform parsing and point stream processing at different levels. This enables different types of sensors to be supported with ease.

4. Migration by Example

Let's start with an SDK v1 subroutine and line-by-line migrate it to SDK v2:

SDKv1:

int InitializeSDK() {
  CeptonSensorErrorCode err;
  CeptonSDKOptions opts = cepton_sdk_create_options();
  if (streaming) {
    opts.frame.mode = CEPTON_SDK_FRAME_STREAMING;
  } else if (natural_frame) {
    // Use cover mode as the default mode
    opts.frame.mode = CEPTON_SDK_FRAME_COVER;
  } else if (fixed_20hz_frame) {
    opts.frame.mode = CEPTON_SDK_FRAME_TIMED;
    opts.frame.length = 0.05f; // 1/20th of a second
  }

  if (!capture_file.empty())
    opts.control_flags |= CEPTON_SDK_CONTROL_DISABLE_NETWORK;
  err = cepton_sdk_initialize(CEPTON_SDK_VERSION, &opts, CbCeptonSensorError,
                              nullptr);
  if (err != CEPTON_SUCCESS) {
    ReportError(err, "initialize");
    return -1;
  }

  if (!capture_file.empty()) {
    // attempt to discover the sensors in the pcap
    err = cepton_sdk_capture_replay_open(capture_file.c_str());
    if (err != CEPTON_SUCCESS) {
      ReportError(err, "load capture");
      return -1;
    }
    err = cepton_sdk_capture_replay_set_speed(0);
    if (err != CEPTON_SUCCESS) {
      ReportError(err, "set replay speed");
      return -1;
    }
    err = cepton_sdk_capture_replay_resume_blocking(2.0f);
    if (err != CEPTON_SUCCESS) {
      ReportError(err, "pre-replay");
      return -1;
    }
    err = cepton_sdk_capture_replay_seek(0);
    if (err != CEPTON_SUCCESS) {
      ReportError(err, "rewind");
      return -1;
    }
  }

  err = cepton_sdk_listen_image_frames(CbCeptonSensorImageData, this);
  if (err != CEPTON_SUCCESS) {
    ReportError(err, "listen frames");
    return -1;
  }

  if (!capture_file.empty()) {
    err = cepton_sdk_capture_replay_resume();
    if (err != CEPTON_SUCCESS) {
      ReportError(err, "load capture");
      return -1;
    }
  }

  return 0;
}

4.1. Initialization

For SDK v1 you need to construct an initialization object:

  CeptonSDKOptions opts = cepton_sdk_create_options();
  if (streaming) {
    opts.frame.mode = CEPTON_SDK_FRAME_STREAMING;
  } else if (natural_frame) {
    // Use cover mode as the default mode
    opts.frame.mode = CEPTON_SDK_FRAME_COVER;
  } else if (fixed_10hz_frame) {
    opts.frame.mode = CEPTON_SDK_FRAME_TIMED;
    opts.frame.length = 0.1f;
  }

  if (!capture_file.empty())
    opts.control_flags |= CEPTON_SDK_CONTROL_DISABLE_NETWORK;
  err = cepton_sdk_initialize(CEPTON_SDK_VERSION, &opts, CbCeptonSensorError,
                              nullptr);

SDK v2 is simpler:

  err = CeptonInitialize(CEPTON_API_VERSION, CbCeptonSensorError);
  if (err != CEPTON_SUCCESS) ReportError(err, "CeptonInitialize");

  ...

  // Enable legacy
  CeptonEnableLegacyTranslation();

Here the decision of frame modes and whether the networking is used comes later. Frame mode is specified at the frame data callback registration time, not at initialization. Networking is started explicitly.

In SDK v2 you can explicitly call CeptonEnableLegacyTranslation to enable legacy sensors. For legacy sensor support to work you also need to have the SDK v1 dynamic library (cepton_sdk.dll or libcepton_sdk.so) next to the SDK v2 library.

4.2. Load and play capture files

  if (!capture_file.empty()) {
    // attempt to discover the sensors in the pcap
    err = cepton_sdk_capture_replay_open(capture_file.c_str());
    if (err != CEPTON_SUCCESS) {
      ReportError(err, "load capture");
      return -1;
    }
    err = cepton_sdk_capture_replay_set_speed(0);
    if (err != CEPTON_SUCCESS) {
      ReportError(err, "set replay speed");
      return -1;
    }
    err = cepton_sdk_capture_replay_resume_blocking(2.0f);
    if (err != CEPTON_SUCCESS) {
      ReportError(err, "pre-replay");
      return -1;
    }
    err = cepton_sdk_capture_replay_seek(0);
    if (err != CEPTON_SUCCESS) {
      ReportError(err, "rewind");
      return -1;
    }
  }

The capture code is simplified in addition to the naming convention differences. Here is the SDK v2 code:

  if (!capture_file.empty()) {
    err = CeptonReplayLoadPcap(capture_file.c_str(), 0, &capture_handle);
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonReplayLoadPcap");
    err = CeptonReplaySetSpeed(capture_handle, 0);  // No delay replay
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonReplaySetSpeed");
    err = CeptonReplayPlay(capture_handle);
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonStartReplay");
  } else {
    err = CeptonStartNetworking();
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonStartNetworking");
  }

In stead of separate logic to decide on DISABLE_NETWORK before initialization for v1 code, we just added an else clause to explicitly start the networking through CeptonStartNetworking

4.3. Receiving frame data

Here is the SDK v1 code:

  err = cepton_sdk_listen_image_frames(CbCeptonSensorImageData, nullptr);
  if (err != CEPTON_SUCCESS) {
    ReportError(err, "listen frames");
    return -1;
  }

Here is the SDK v2 code:

  // Listen to point data
  if (streaming) {
    err = CeptonListenPoints(CbCeptonSensorImageData, nullptr);
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonListenPoints");
  } else if (natural_frame) {
    err = CeptonListenFrames(CEPTON_AGGREGATION_MODE_NATURAL, CbCeptonSensorImageData, nullptr);
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonListenFrames");
  } else if (fixed_20hz_frame) {
    // 20Hz, frame time is 0.05s = 50,000us
    err = CeptonListenFrames(50000, CbCeptonSensorImageData, nullptr);
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonListenFrames");
  }

In SDK v2 we make a distinction between frames and raw streams of points. The points gets aggregated into frames inside the SDK in both v1 and v2. In SDK v2 we make these aggregation interfaces explicit so you can have your own frame aggregators if needed.

4.4. Final SDK v2 code

int InitializeSDK() {
  int err;
  err = CeptonInitialize(CEPTON_API_VERSION, CbCeptonSensorError);
  if (err != CEPTON_SUCCESS) ReportError(err, "CeptonInitialize");
  sdk_initialized = true;

  // Enable legacy
  err = CeptonEnableLegacyTranslation();
  if (err != CEPTON_SUCCESS) ReportError(err, "CeptonEnableLegacyTranslation");

  // Listen to point data
  if (streaming) {
    err = CeptonListenPoints(CbCeptonSensorImageData, nullptr);
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonListenPoints");
  } else {
    err = CeptonListenFrames(aggregation_mode, CbCeptonSensorImageData, nullptr);
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonListenFrames");
  }

  // Listen to sensor detection
  err = CeptonListenSensorInfo(CbCeptonSensorInfo, nullptr);
  if (err != CEPTON_SUCCESS) ReportError(err, "CeptonListenSensorInfo");

  if (!capture_file.empty()) {
    err = CeptonReplayLoadPcap(capture_file.c_str(), 0, &capture_handle);
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonReplayLoadPcap");
    err = CeptonReplaySetSpeed(capture_handle, 0);  // No delay replay
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonReplaySetSpeed");
    err = CeptonReplayPlay(capture_handle);
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonStartReplay");
  } else {
    err = CeptonStartNetworking();
    if (err != CEPTON_SUCCESS) ReportError(err, "CeptonStartNetworking");
  }
  return 0;
}

5. Migration Reference: Functions

5.1. cepton_get_error_code_name()

SDK v2 counter part is:

/**
 * Returns empty string if error code is invalid.
 *
 * @return Error code name string converted from int. "" if unrecognized.
 */
CEPTON_EXPORT const char *CeptonGetErrorCodeName(int error_code);

5.2. cepton_is_error_code() and cepton_is_fault_code()

These functions are no longer supported. Please use CeptonSensorErrorCode definitions directly. As a convention: Error code values stay in range between -1 to -999 and fault code values start from -1000 and onwards. Here is a code snippet that is equivalent to SDK v1:

bool IsErrorCode(int code) { return code > -1000 && code < 0; }
bool IsFaultCode(int code) { return code <= -1000; }

5.3. cepton_sdk_get_error()

SDK v2 does not keep a global error state anymore. All errors are immediately reflected in the return value for SDK function call.

5.4. cepton_is_sora(), cepton_is_hr80() and cepton_is_vista()

These functions are no longer supported. Please use CeptonSensorModel definitions directly. The SDK v1 implementations are:

bool IsSora(CeptonSensorModel m) {
  return m == CEPTON_MODEL_SORA_P90 ||
    m == CEPTON_MODEL_SORA_P61 ||
    m == CEPTON_MODEL_SORA_X90;
}

bool IsHR80(CeptonSensorModel m) {
  return m == 3 || m == 6; // SDK v2 doesn't have these definitions anymore
}

bool IsVista(CeptonSensorModel m) {
  return !IsSora(m) && !IsHR80(m);
}

5.5. cepton_sdk_get_version_*()

In SDK v1 we have offered four functions:

  • cepton_sdk_get_version_major()
  • cepton_sdk_get_version_minor()
  • cepton_sdk_get_version_patch()
  • cepton_sdk_get_version_string()

In SDK v2, there is only one function:

/**
 * Get the SDK version (not the same as API version)
 * @return major/minor/patch/build in 4 bytes (little endian 32bit integer).
           e.g. 2.1.15.0 will be 0x000F0102
 */
CEPTON_EXPORT uint32_t CeptonGetSdkVersion(void);

Here is a possible implementation for cepton_sdk_get_version_string():

const char* cepton_sdk_get_version_string() {
  const uint32_t version = CeptonGetSdkVersion();
  const uint8_t major = version & 0xFF;
  const uint8_t minor = (version >> 8) & 0xFF;
  const uint8_t patch = (version >> 16) & 0xFF;
  const uint8_t build = (version >> 24) & 0xFF;
  return std::to_string(major) + "." +
    std::to_string(minor) + "." +
    std::to_string(patch) + "." +
    std::to_string(build);
}

5.6. cepton_sdk_is_initialized()

/// Returns 1 if sdk is initialized. 0 if not.
CEPTON_EXPORT int CeptonIsInitialized(void);

5.7. cepton_sdk_create_options()

There is no corresponding function to create options. SDK v2 doesn't use initialization options. See sections for CeptonSDKControl enum for more details

5.8. cepton_sdk_initialize()

SDK v2's initialization look like this:

/**
 * Initialize SDK.
 * Must be called before any other sdk functions.
 *
 * @param api_version `CEPTON_API_VERSION`, to ensure header and library match
 * @param cb Global error and fault callback.
 */
CEPTON_EXPORT int CeptonInitialize(int api_version,
                                   CeptonSensorErrorCallback cb);

Please refer to the section for CeptonSDKControl enum structure to see how individual options translate into SDK v2 function calls.

5.9. cepton_sdk_deinitialize()

SDK v2's corresponding function look like this:

/**
 * Deinitialize SDK.
 */
CEPTON_EXPORT int CeptonDeinitialize(void);

5.10. cepton_sdk_get_control_flags(), cepton_sdk_set_control_flags() and cepton_sdk_has_control_flag()

There is no equivalent function. Please refer to CeptonSDKControl enum section

5.11. cepton_sdk_clear()

In SDK v2 one can unload individual capture by handle through CeptonReplayUnloadPcap(handle). The matching function also exists in SDK v2 as CeptonReplayUnloadAll()

5.12. cepton_sdk_set_port() and cepton_sdk_get_port()

In SDK v2, port can be specified when starting network using CeptonStartNetworkingOnPort(port)

5.13. cepton_sdk_set_frame_options(), cepton_sdk_create_frame_options()

See CeptonSDKFrameOptions structure section

5.14. cepton_sdk_listen_image_frames() and cepton_sdk_unlisten_image_frames()

If operating in CEPTON_SDK_FRAME_STREAMING mode, these are equivalent to:

typedef void (*CeptonPointsCallback)(CeptonSensorHandle handle,
                                     int64_t start_timestamp, size_t n_points,
                                     size_t stride, const uint8_t *points,
                                     void *user_data);

CEPTON_EXPORT int CeptonListenPoints(CeptonPointsCallback callback,
                                     void *user_data);

/// Clears image frame callback.
CEPTON_EXPORT int CeptonUnlistenPoints(CeptonPointsCallback callback,
                                       void *user_data);

If operating in frame mode, please use these instead:

/**
 * CeptonListenFrames
 * @return
 *  CEPTON_ERROR_INVALID_STATE if existing listeners are using a different
 *     aggregation mode.
 * NOTE: For FIXED modes, every frame is complete. For NATURAL mode, the first
 * frame right after the first listener is registered collects only points up to
 * the first frame boundary and should not be considered a complete frame.
 */
CEPTON_EXPORT int CeptonListenFrames(int aggregationMode,
                                     CeptonPointsCallback callback,
                                     void *user_data);

/**
 * CeptonUnlistenFrames
 *
 * NOTE: The last unlisten call will destroy all frame buffers and stop frame
 * aggregation altogether. After that aggregationMode can be changed.
 */
CEPTON_EXPORT int CeptonUnlistenFrames(CeptonPointsCallback callback,
                                       void *user_data);

5.15. cepton_sdk_get_n_sensors()

In SDK v2:

/**
 * Get number of sensors attached.
 * Use to check for new sensors. Sensors are not deleted until deinitialization.
 */
CEPTON_EXPORT size_t CeptonGetSensorCount(void);

5.16. cepton_sdk_get_sensor_handle_by_serial_number()

In SDK v2:

/**
 * Get sensor information by serial number
 * @param serial_number
 * @param info
 * @return 0 for success (info populated), negative for failure
 */
CEPTON_EXPORT int CeptonGetSensorInformationBySerialNumber(
    uint32_t serial_number, struct CeptonSensor *info);

Handle is part of the CeptonSensor data structure.

5.17. cepton_sdk_get_sensor_information_by_index()

In SDK v2:

/**
 * Returns sensor information by sensor index.
 * Useful for getting information for all sensors.
 *
 * Returns error if index invalid.
 *
 * @param idx Sensor index.
 * @param info Sensor information structure to be filled.
 * @return 0 for success (info populated), negative for failure
 */
CEPTON_EXPORT int CeptonGetSensorInformationByIndex(size_t idx,
                                                    struct CeptonSensor *info);

5.18. cepton_sdk_get_sensor_information()

In SDK v2:

/**
 * Get sensor information by the handle
 * @param handle Sensor handle
 * @param info Sensor information structure to be filled.
 * @return 0 for success (info populated), negative for failure
 */
CEPTON_EXPORT int CeptonGetSensorInformation(CeptonSensorHandle handle,
                                             struct CeptonSensor *info);

5.19. cepton_sdk_listen_info_packets() and cepton_sdk_unlisten_info_packets()

In SDK v2:

typedef void (*CeptonSensorInfoCallback)(CeptonSensorHandle handle,
                                         const struct CeptonSensor *info,
                                         void *user_data);

/**
 * Useful for listening info packet coming from sensor.
 * @param cb Callback.
 */
CEPTON_EXPORT int CeptonListenSensorInfo(CeptonSensorInfoCallback callback,
                                         void *user_data);

/// Clears Sensor Info callback.
CEPTON_EXPORT int CeptonUnlistenSensorInfo(CeptonSensorInfoCallback callback,
                                           void *user_data);

5.20. cepton_sdk_listen_serial_lines() and cepton_sdk_unlisten_serial_lines()

In SDK v2:

/**
 * @param handle Sensor handle.
 * @param str Serial line string. Owned by SDK.
 */
typedef void (*CeptonSerialReceiveCallback)(CeptonSensorHandle handle,
                                            const char *str, void *user_data);

/// Sets serial line callback.
/**
 * Useful for listening to NMEA data from GPS attached to sensor.
 * Each callback contains 1 line of serial data (including newline characters).
 *
 * Returns error if callback already registered.
 *
 * @param cb Callback function
 */
CEPTON_EXPORT int CeptonListenSerialLines(CeptonSerialReceiveCallback callback,
                                          void *user_data);

/// Clears serial line callback.
CEPTON_EXPORT int CeptonUnlistenSerialLines(
    CeptonSerialReceiveCallback callback, void *user_data);

5.21. cepton_sdk_listen_network_packet() and cepton_sdk_unlisten_network_packet()

In SDK v2, the concept of listening to network packets is further expanded into the idea of a "parser". A parser can indicate that the data is handled and can be discarded with a return value of 0.

/**
 * Callback to handle incoming data. Return 0 to indicate the data is handled.
 *
 * @param handle Sensor handle
 * @param timestamp Time data is received
 * @param data Pointer to the data buffer
 * @param data_size Size of the data buffer
 * @param user_data Pass back the user data entered at register time.
 * @returns 0 to indicate data is handled.
 */
typedef int (*CeptonParserCallback)(CeptonSensorHandle handle,
                                    int64_t timestamp, const uint8_t *data,
                                    size_t data_size, void *user_data);

/**
 * Register a parser. Unique parsers are determined by the pair of callback and
 * user_data. Save callback with different user_data are considered different
 * parsers
 */
CEPTON_EXPORT int CeptonRegisterParser(CeptonParserCallback callback,
                                       void *user_data);

/**
 * Remove a parser. Only remove when both callback and user_data matches
 */
CEPTON_EXPORT int CeptonUnregisterParser(CeptonParserCallback callback,
                                         void *user_data);

5.22. cepton_sdk_mock_network_receive()

In SDK v2, explicitly feeding data to SDK is considered a key part of the SDK operation:

/**
 * Blocks while processing, and calls parser callbacks synchronously before
 * returning.
 *
 * @param handle Sensor handle, arbitrary value to indicate same sensor.
 * Networking uses IPv4
 * @param timestamp Unix timestamp [microseconds].
 * @param buffer Packet bytes.
 * @param buffer_size Packet size.
 */
CEPTON_EXPORT int CeptonReceiveData(CeptonSensorHandle handle,
                                    int64_t timestamp, const uint8_t *buffer,
                                    size_t buffer_size);

5.23. cepton_sdk_capture_replay_is_open()

SDK v2 supports multiple concurrent captures. It is expected that the application keeps the CeptonReplayHandle for each opened capture in SDK. The logic to keep track if any capture is open and how many capture is currently open belongs to the application. In the simplest case when only one capture is possible, here is a bit of code to get started:

CeptonReplayHandle capture = nullptr;

int OpenCapture(std::string file) {
  return CeptonReplayLoadPcap(file.c_str(), 0, &capture);
}

int CloseCapture() {
  capture = nullptr;
  return CeptonReplayUnloadAll();
}

bool IsCaptureOpen() {
  return capture != nullptr;
}

5.24. cepton_sdk_capture_replay_open()

In SDK v2:

/**
 * Load pcap. By default, it will start indexing and plays async. Will need to
 * be unloaded in the end.
 * @param pcapFileName pcap file path for replay.
 * @param flags see enum CeptonReplayFlags
 * @param pHandle this will need to be passed in other replay calls.
 * @return CEPTON_SUCCESS or error code
 *  CEPTON_ERROR_INVALID_STATE  too many loaded pcaps
 *  CEPTON_ERROR_FILE_IO        load failed, file IO or format error
 */
CEPTON_EXPORT int CeptonReplayLoadPcap(const char *pcapFileName, uint32_t flags,
                                       CeptonReplayHandle *pHandle);

5.25. cepton_sdk_capture_replay_close()

In SDK v2, if application keeps the replay handle, the close can be done individually. Otherwise just call UnloadAll to emulate old SDK behavior.

/**
 * Unload the pcap.
 * @param {CeptonReplayHandle} pcap handle
 */
CEPTON_EXPORT int CeptonReplayUnloadPcap(CeptonReplayHandle);

/**
 * Unload all loaded pcaps.
 */
CEPTON_EXPORT void CeptonReplayUnloadAll();

5.26. cepton_sdk_capture_replay_get_filename()

In SDK v2 we don't keep the replay file name anymore. Application can keep this state in local data structure if needed.

5.27. cepton_sdk_capture_replay_get_start_time()

There is no counter part for this. The start time is not well defined or meaningful. Application can keep the timestamp when the pcap is first loaded. To completely emulate SKD v1 behavior, application needs to keep track of all Pause and Resume actions and account for time gaps caused by them.

5.28. cepton_sdk_capture_replay_get_position()

In SDK v2, we return integer value in microseconds, not float value in seconds.

/**
 * Get the seek position.
 * @return microseconds from the start of the pcap if positive. Error code if
 * return value is negative.
 */
CEPTON_EXPORT int64_t CeptonReplayGetSeekPosition(CeptonReplayHandle);

5.29. cepton_sdk_capture_replay_get_length()

In SDK v2, we return integer value in microseconds, not float value in seconds.

/**
 * Get length of the pcap file.
 * NOTE: If indexing is not done this call will block until it is.
 * It starts indexing if pcap was loaded with flag to not do indexing.
 * @param {CeptonReplayHandle} pcap handle
 * @return length in microseconds of the pcap file. Error code if
 * return value is negative.
 */
CEPTON_EXPORT int64_t CeptonReplayGetLength(CeptonReplayHandle);

5.30. cepton_sdk_capture_replay_is_end()

In SDK v2:

/**
 * Checks if async playback has finished.
 * @return 1 if playback is finished, 0 if not. Negative for error code.
 */
CEPTON_EXPORT int CeptonReplayIsFinished(CeptonReplayHandle handle);

5.31. cepton_sdk_capture_replay_rewind()

Deprecated in SDK v1. Equivalent SDK v2 code:

CeptonReplaySeek(handle, 0);

5.32. cepton_sdk_capture_replay_seek()

In SDK v2:

/**
 * Seek to position.
 * NOTE: This call will block until indexing has been completed up to the
 * required seek position. The position is based on the recorded file and not
 * affected by the current playing speed.
 * @param {CeptonReplayHandle} pcap handle
 * @param position microseconds from starting of the pcap
 * @return Error code
 */
CEPTON_EXPORT int CeptonReplaySeek(CeptonReplayHandle, int64_t position);

5.33. cepton_sdk_capture_replay_set_enable_loop()

In SDK v2:

/**
 * Sets whether the pcap should auto-replay when reaching the end.
 * This is equivalent to the flag CEPTON_REPLAY_FLAG_PLAY_LOOPED at load time
 * @param {CeptonReplayHandle} pcap handle
 * @param {int} autoReplay 1 to replay automatically, 0 to not replay
 */
CEPTON_EXPORT int CeptonReplaySetAutoReplay(CeptonReplayHandle, int autoReplay);

5.34. cepton_sdk_capture_replay_get_enable_loop()

There is no equivalent function to get the current state of auto-replay. Application can keep track of calls to SetAutoReplay to emulate this functionality as needed.

5.35. cepton_sdk_capture_replay_set_speed() and cepton_sdk_capture_replay_get_speed()

In SDK v2, instead of float number of speed, we use integer to for percent where 100 is equivalent to 1.0

/**
 * Sets the speed for pcap replay.
 * @param {CeptonReplayHandle} pcap handle
 * @param speed_percent 100 means 1x speed. Special value 0 for as fast as
 * possible
 * @return Error code
 */
CEPTON_EXPORT int CeptonReplaySetSpeed(CeptonReplayHandle, int speed);

/**
 * Gets replay speed.
 * @param {CeptonReplayHandle} pcap handle
 * @return replay speed (100 mean 1x). Error code if return value is negative.
 */
CEPTON_EXPORT int CeptonReplayGetSpeed(CeptonReplayHandle);

5.36. cepton_sdk_capture_replay_resume_blocking_once() and cepton_sdk_capture_replay_resume_blocking()

In SDK v2, instead of the fine grained control of resume_blocking by number of network packets or time duration, we only support a single NextFrame functionality which is the most logical way for single step.

/**
 * Play until next frame.
 * State will always be paused after this function returns.
 * @param {CeptonReplayHandle} pcap handle
 * @return Error code
 */
CEPTON_EXPORT int CeptonReplayNextFrame(CeptonReplayHandle);

Although not functionally equivalent, this will satisfy most application needs. In the case where a finer grained control is needed, register a data listener to perform pause/resume inside the listener.

5.37. cepton_sdk_capture_replay_is_running()

In SDK v2, the logic is flipped but otherwise equivalent:

/**
 * Check if playback is currently paused.
 * @return 1 if replay is paused, 0 if not paused, negative for error code.
 */
CEPTON_EXPORT int CeptonReplayIsPaused(CeptonReplayHandle handle);

5.38. cepton_sdk_capture_replay_resume()

In SDK v2:

/**
 * Start playing. Resume from a paused state.
 * @param {CeptonReplayHandle} pcap handle
 */
CEPTON_EXPORT int CeptonReplayPlay(CeptonReplayHandle);

5.39. cepton_sdk_capture_replay_pause()

In SDK v2:

/**
 * Pause. Does nothing if already paused.
 * Listener Callback will not be called after return from this function.
 * @param {CeptonReplayHandle} pcap handle
 */
CEPTON_EXPORT int CeptonReplayPause(CeptonReplayHandle);

6. Migration Reference: Structures

6.1. CeptonSensorImagePoint

In SDK v1 we use image point as the low level point structure, which is consists of image_x, image_z and distance. In SDK v2 we opt to use a more direct and transformation friendly structure with x, y and z:

struct CeptonPoint {
  int16_t x;
  uint16_t y;
  int16_t z;
  uint8_t reflectivity;
  uint8_t relative_timestamp;
  uint8_t channel_id;
  uint8_t flags;
};

The code to translate from v1 to v2 (here to illustrate the relation, not meant to be used in real code):

inline void convert_image_point_to_point(float image_x, float image_z,
                                         float distance, float& x, float& y,
                                         float& z) {
  float hypotenuse_small = std::sqrt(square(image_x) + square(image_z) + 1.0f);
  float ratio = distance / hypotenuse_small;
  x = -image_x * ratio;
  y = ratio;
  z = -image_z * ratio;
}

6.2. CeptonSensorInformation

In SDK v2 this structure is redefined as CeptonSensor:

struct CeptonSensor {
  /// Size of the CeptonSensor struct
  /// plus any consecutive sensor info blocks
  uint32_t info_size;

  // per sensor info
  uint32_t serial_number;
  CeptonSensorHandle handle;

  // Model
  char model_name[28];
  uint16_t model;
  uint16_t model_reserved;
  uint32_t part_number;

  // FW
  uint32_t firmware_version;  // LSB->MSB major/minor/build/patch

  // Time
  int64_t power_up_timestamp;
  int64_t time_sync_offset;
  int64_t time_sync_drift;

  /// Config
  uint8_t return_count;
  uint8_t channel_count;
  uint8_t reserved[2];
  uint32_t status_flags;

  // Unit in 0.01 Kelvin
  uint16_t temperature;
};

enum _CeptonSensorStatusFlags {
  CEPTON_SENSOR_PTP_CONNECTED = 1,
  CEPTON_SENSOR_PPS_CONNECTED = 2,
  CEPTON_SENSOR_NMEA_CONNECTED = 4,
};

Some of the fields are added/renamed/removed. These should all be straightforward when migrating. Notable changes are:

  • segment_count in v1 becomes channel_count in v2
  • last_reported_temperature in v1 is Celsius, while temperature in v2 is in 0.01 Kelvin. Again, following on our design to avoid floating point numbers.
  • GPS and timestamp reporting changes quite drastically. Going forward we expect sensors to be synchronized in time through a offset+drift model. For more detailed information please check blog article for time synchronization.

7. Migration Reference: Enumerations and Constants

7.1. CeptonSensorErrorCode enum

This is unchanged in SDK v2. SDK v2 may add more value enums while guaranteeing name and value compatibility.

7.2. CeptonSensorModel enum

SDK v2 added CEPTON_ prefix to these definitions. This enum corresponds to the firmware model number therefore it is always compatible between SDK v1 and v2.

See the actual header for the latest list, here are some common values as defined in SDK v2:

/// Sensor model.
typedef uint16_t CeptonSensorModel;
enum {
  CEPTON_MODEL_VISTA_860_GEN2 = 7,
  CEPTON_MODEL_VISTA_X120 = 10,
  CEPTON_MODEL_SORA_P60 = 11,
  CEPTON_MODEL_VISTA_P60 = 12,
  CEPTON_MODEL_VISTA_P90 = 14,
  CEPTON_MODEL_SORA_P90 = 15,
  CEPTON_MODEL_VISTA_P61 = 16,
  CEPTON_MODEL_SORA_P61 = 17,
  CEPTON_MODEL_VISTA_X90 = 24,
  CEPTON_MODEL_SORA_X90 = 28
};

7.3. CEPTON_SDK_MAX_POINTS_PER_PACKET, CEPTON_SDK_MAX_POINTS_PER_FRAME, CEPTON_SDK_MAX_POINTS_PER_SECOND and CEPTON_SDK_MAX_FRAMES_PER_SECOND

As Cepton product line-up expands, these constants does not make sense globally anymore. Instead please use the spec sheet to determine maximum constants based on the product being used.

In the future SDK v2 may offer pre-defined sensor specification values as application helper header file. Currently it is not available.

7.4. CeptonSDKControl enum

There is no corresponding function in SDK2. Here is how to handle each control used in this function:

  • CEPTON_SDK_CONTROL_DISABLE_NETWORK:

    Use CeptonStopNetworking() and CeptonStartNetworking() to directly control networking

  • CEPTON_SDK_CONTROL_ENABLE_MULTIPLE_RETURNS

    Single or dual return is no longer software controlled. To disable dual return temporarily, just filter the point cloud to ignore the points marked with SECOND_RETURN flag

  • CEPTON_SDK_CONTROL_HOST_TIMESTAMPS

    Host timestamp mode is no longer relevant in SDK v2. The SDK v2 points are sequence of points with relative timestamp. Application can choose sensor timestamp or host timestamp at the time or processing.

7.5. CeptonSDKFrameMode enum and CeptonSDKFrameOptions structure

The concept of frame mode is split into many different things in SDK v2 as we separate point stream layer from frame aggregation. Frame options can be specified when calling CeptonListenFrames().

While in SDK v1 we use floating point number to specify frame time in seconds as in CeptonSDKFrameOptions::length, SDK v2 uses fixed point microseconds as aggregationMode. So 20Hz in SDK v1 is 0.05, while in SDK v2 is 50000

Here are some rough equivalences:

  • CEPTON_SDK_FRAME_STREAMING:

    Use CeptonListenPoints you will always get points in streaming mode.

  • CEPTON_SDK_FRAME_TIMED:

    Equivalent to setting aggregationMode to number of microseconds of desired frame time in aggregationMode when calling CeptonListenFrames

  • CEPTON_SDK_FRAME_COVER:

    Equivalent to setting aggregationMode to CEPTON_AGGREGATION_MODE_NATURAL when calling CeptonListenFrames

  • CEPTON_SDK_FRAME_CYCLE:

    SDK v2 does not have a concept of cycle anymore. Some sensors naturally return to the same exact scan position every natural frame, while others may never scan the same way between two frames. Please refer to scan pattern discussions and documentations found on Developer Portal

7.6. cepton_sensor_information_size exported variable

This is no longer useful. Instead of using a global exported variable to determine if a given structure is valid by comparing the compiler sizeof with the export, SDK v2 uses the concept of stride. The structure contains its own size, and it is guaranteed that the beginning of the structure will match the header definitions if the size is bigger than sizeof of the structure.

8. Migration FAQ

8.1. Can I use SDK v1 to interact with legacy sensor and SDK v2 to interact with new sensor in the same application?

A: Yes this would work, but not recommended. We recommend migrate the code to SDK v2 and call CeptonEnableLegacyTranslation() to allow all sensors to work together.

Furthermore, the CeptonSensorErrorCode definitions are unchanged. When using both v1 and v2 headers, please make sure to include the v1 header cepton_sdk.h before cepton_sdk2.h to avoid naming clash.

8.2. How do I migrate C++ interface as provided by cepton_sdk.hpp?

A: SDK v2 does not provide any namespaced C++ binding. The equivalent C++ functions are usually only different on the surface, so this migration guide can be used to migrate C++ code too. For example, in C++ SDK v1, you may use cepton_sdk::api::initialize() instead of cepton_initialize(). For more details, please refer to cepton_sdk.hpp in SDK v1 headers directly. The SDK v1 C++ interface is header-only therefore completely accessible on the source code level.

8.3. How do I migrate python code written with cepton_sdk python module?

A: The new SDK v2 has a new cepton_sdk2 python module. The new cepton_sdk2 module supports a new frame fifo mode that makes offline data processing much easier to do. Unfortunately, the old cepton_sdk python module is not compatible with the new cepton_sdk2 python module. Currently there is no documentation on how to migrate from cepton_sdk to cepton_sdk2. Please contact our support team if you need help. Check back here for future documentation updates.