Channel Sounding

This chapter serves as a guide to the Texas Instruments Channel Sounding feature including application implementation, drivers, and middle layers of the stack. Bluetooth 6.0 specification Channel Sounding extends the capabilities of Bluetooth-based ranging beyond traditional RSSI and direction finding (DF) methods, enabling more accurate and cost-effective ranging designs.

The guide will cover the main principles of the Channel Sounding process, the process of adding Channel Sounding to a project, and an out of the box example project available within the SimpleLink Low Power F3 SDK.

The Channel Sounding chapters will cover:

The CC23xx or CC27xx LaunchPad allows for quick and easy development of BLE applications, such as Channel Sounding. To run the Channel Sounding example, you will need two TI CC23xx or CC27xx.

Note: To run the CS demo, one must use CC27xx as car node in order to support distance calculation.

Channel Sounding Overview

What is Channel Sounding? Bluetooth Channel Sounding (CS), introduced in the Bluetooth 6.0 specification, enables high-accuracy distance measurement between two BLE devices using phase-based ranging (PBR). This technique estimates the distance by measuring the phase difference between a received unmodulated tone and the device’s local oscillator (LO) across multiple RF frequencies in the 2.4 GHz band. Both devices—the initiator and the reflector—exchange tones and record phase information. These phase differences, when plotted against frequency, yield a curve whose slope directly corresponds to the physical distance between devices. Advanced signal processing helps correct for multipath interference, enhancing precision even in complex radio environments.

Why is Channel Sounding Important? Channel Sounding delivers high-level distance accuracy without needing external infrastructure, making it ideal for real-time location systems (RTLS), car access, and secure proximity-based services. It also introduces built-in security mechanisms like random frequency hopping, Round Trip Time (RTT) validation, and attack detection metrics (NADM) to defend against spoofing or relay attacks. Importantly, it integrates efficiently with existing Bluetooth LE protocols, supporting low-power, low-cost, and scalable deployment across consumer, industrial, and automotive applications.

Two BLE supported devices are needed for Channel Sounding for this version of the SimpleLink Low Power F3 SDK.

  • Car Node

  • Key Node

The car node is the initiator device and the key node is the reflector device. The initiator is the device that is in control of the channel sounding configuration, synchronization, and begins each channel sounding subevent. Firstly, Mode 0 steps are sent from the initiator to the reflector. The Mode 0 steps synchronize the reflector to the initiators configuration and sets up the timing for the unmodulate tones, or subevents. Next, Mode 1, 2, or 3 steps can be completed. These steps include (RTT), (PBR) or a combination of both. Each step is an unmodulated tone sent between the initiator and the reflector device. The phase change or the time of flight of the tone is then used to calculate the distance, and/or detect spoofing, or man in the middle attacks.

How to enable Channel Sounding example

Channel sounding example works using CCS and Python:

  • CCS is used to flash one CC23xx or CC27xx with an initiator code and the second with one with a reflector code.

  • Python is used to control car node and plot results of CS procedure.

The following section will go through the SW flow behind TI’s CS demo.

@startuml

participant PC
participant "Car Node"
participant "Key Node"

group Channel Sounding

    "Key Node" -> "Key Node" : GapAdv_enable()
    PC -> "Car Node" : GapScan_enable()

    "Car Node" -> "Key Node" : Send Connection Indication packet

    "Car Node" -> "Key Node" : Pairing_Request
    "Key Node" -> "Car Node" : Pairing_Response

    rnote over "Car Node"
        GAP_BOND_PAIRING_STATE_STARTED
    end note

    rnote over "Key Node"
        GAP_BOND_PAIRING_STATE_STARTED
    end note

    "Car Node" -> "Key Node" : Encryption_Request
    "Key Node" -> "Car Node" : Encryption_Response

    rnote over "Car Node"
        GAP_BOND_PAIRING_STATE_COMPLETE
    end note

    rnote over "Key Node"
        GAP_BOND_PAIRING_STATE_COMPLETE
    end note

    "Car Node" -> PC : Connection_Data

    PC -> "Car Node" : CS_CMD_READ_LOCAL_CAP

    PC -> "Car Node" : CS_CMD_READ_REMOTE_CAP
    "Car Node" -> "Key Node" : LLCP_CS_Capabilities_Request
    "Key Node" -> "Car Node" : LLCP_CS_Capabilities_Response
    "Car Node" -> PC : NWP_CS_READ_REMOTE_CAPS

    PC -> "Car Node" : CS_CMD_SECURITY_ENABLE
    "Car Node" -> "Key Node" : LLCP_CS_Security_Request
    "Key Node" -> "Car Node" : LLCP_CS_Security_Response
    "Car Node" -> PC : NWP_CS_SECURITY_ENABLE_COMPLETE

    PC -> "Car Node" : CS_CMD_SET_DEFAULT_SETTINGS

    PC -> "Car Node" : CS_CMD_CREATE_CONFIG
    "Car Node" -> "Key Node" : LLCP_CS_Config_Request
    "Key Node" -> "Car Node" : LLCP_CS_Config_Response
    "Car Node" -> PC : NWP_CS_CONFIG_COMPLETE

    PC -> "Car Node" : CS_CMD_PROCEDURE_ENABLE
    "Car Node" -> "Key Node" : LLCP_CS_Request
    "Key Node" -> "Car Node" : LLCP_CS_Response
    "Car Node" -> "Key Node" : LLCP_CS_Indication
    "Car Node" -> PC : NWP_CS_CONFIG_COMPLETE

    rnote over "Car Node"
        Channel Sounding Procedure
    end note

    rnote over "Key Node"
        Channel Sounding Procedure
    end note

    "Car Node" -> PC : NWP_CS_APP_DISTANCE_RESULTS

end

@enduml

Channel Sounding Example Project Software Flow

Prerequisites

Hardware:

  • 2 x SimpleLink CC23xx or CC27xx LaunchPad Target

  • 2 x SimpleLink™ LaunchPad™ XDS110 Debugger

  • 2 x USB cable

  • (Optional) 1 x Battery Pack for Reflector

Software:

Prepare CCS and Python environment

1. For the initiator node, import <SDK>/examples/rtos/LP_EM_CC2745R10/ble/car_node and set the example to initiate a pairing request within Bond Manager. Procedure to do this is described below. Flash the car_node project onto one of the two CC23xx or CC27xx devices and keep it connected to your PC. The COM port of the ble_device_car_node_with_distance.py script should be set to the COM port of the car node.

../_images/ConfigureInitiatePairing_CarNode.png

Configure Car Node to Initiate Pairing

../_images/ConfiguringComPort_CarNode.png

Configure COM Port for Car Node in Python

  1. For reflector node, import <SDK>/examples/rtos/LP_EM_CC2745R10/ble/key_node. Flash the key_node project onto the second CC23xx or CC27xx device. The reflector does not need to remain connected to your PC. It only needs to be powered.

Note: Advertising data in the key node project is used by python. If it is changed in CCS, make sure to change it in python script too.

  1. (Optional) To configure 2x2 antenna setup for both CC23xx or CC27xx:

    • Python:

      • Go to <SDK>/tools/ble/ble_agent/examples/ble_device_car_node_with_distance.py. Change in cs_set_procedure_params and cs_set_procedure_params_repeat “aci” from 0 to 7 and “preferred_peer_antenna” from 0b0001 to 0b0011;

      • To use other antenna configurations, refer to the ACI map below. Additionally, the preferred_peer_antenna bitmap will dictate the number of antennas on the peer (reflector).

    ACI

    Ant Config

    ACI=0

    1x1

    ACI=1

    2x1

    ACI=2

    3x1

    ACI=3

    4x1

    ACI=4

    1x2

    ACI=5

    1x3

    ACI=6

    1x4

    ACI=7

    2x2

    Note: The antenna configurations above are oriented initiator x reflector.

    • CCS:

      • Go to Syscfg -> RF STACKS -> BLE -> Channel sounding configuration -> Number of Antennas. Select 2 antennas, and configure the Antenna Mux Bitmap 0xC6. For different antenna configurations, refer to the note within the Antenna Mux Bitmap section of SysConfig or check the example below.

      • Syscfg -> TI DRIVERS -> RCL -> RCL Observable -> Additional RF GPIO Signals. Add PBEGPO2 and PBEGPO3.

../_images/ConfigureAdditionalRFGPIOSignals.png

Configure PBEGPOs for Multiple Antennas

  • Antenna Mux Bitmap value example: In case of multiple antenna usage, the device needs to know how to use a specific antenna. Antenna Mux Bitmap value corresponds to the signal levels of 2 pins the device uses to select an antenna. This value is a translation of antenna control based on a truth table. Below is how to determine this value regarding hardware configuration for 4 antennas.

    1. Hardware configuration:

      ../_images/ditaa-299db276d8d9a702f3c40fc2aa1467be1bd1b0f3.png
    2. Swith truth table:

      PBEGPO3

      PBEGPO2

      Antenna On

      0

      0

      Antenna 2

      0

      1

      Antenna 3

      1

      0

      Antenna 4

      1

      1

      Antenna 1

    3. Antenna Muxing Bitmap calculation

      Antenna 4

      Antenna 3

      Antenna 2

      Antenna 1

      PBOGPO3

      PBEGPO2

      PBOGPO3

      PBEGPO2

      PBOGPO3

      PBEGPO2

      PBOGPO3

      PBEGPO2

      1

      0

      0

      1

      0

      0

      1

      1

      0x9

      0x3

      Antenna Muxing Bitmap = 0x93

4. (Optional) Enabling extended debug outputs additional data to the serial terminal. Extended debug output includes data about Mode 0 steps including RSSI, packetQuality, and measuredFreqOffset, the distance of each antenna path according to each algorithm (MuSIC and NN), local and remote phase data, local and remote Antenna permutation index which is ordered by channels, and more. To enable this, add the CS_PROCESS_EXT_RESULTS define to the predefines of the Car Node example project. See the image below:

../_images/ProjectProperties.png

Navigate to project properties

../_images/ProjectProperties_predefines.png

Navigate to predefines

../_images/ProjectProperties_add_predefines.png

Add Extended Debug predefine

Press ok, and continue.

Additionally, the python script needs to be altered to enable extended debug output on the serial terminal. To do this, set extended_results to true in the wait_distance function, the cs_proc_enable function, and the start_cs function. See the images below:

../_images/pythonExtendedDebugEnable.png

Add Extended Debug predefine

../_images/pythonExtendedDebugEnable2.png

Add Extended Debug predefine

../_images/pythonExtendedDebugEnable3.png

Add Extended Debug predefine

  1. Install Python 3.10.11

Note: Python 3.10 should be installed at C:\Python310

  1. During installation select “Add Environment Variable”, otherwise you can add it later, shown in the images below:

../_images/NavigateToEnvVars.png

Open System Properties

../_images/PathEnvVars.png

Open System Properties

../_images/EnvironmentVariables.png

Edit User Environment Variables

  1. Open a command line tool such as Windows Power Shell and install and activate virtual environment. In case your network is behind a proxy, use the proxy command shown.

Setup virtual environment
1    cd <ble_agent folder (SDK/tools/ble/ble_agent)>
2    c:\Python310\python.exe -m pip install virtualenv [--proxy <www.proxy.com>]
3    c:\Python310\python.exe -m venv .venv
4    .venv\Scripts\activate
  1. Setup external packages in case your network is behind a proxy, use –proxy

Upgrade python packages
1    python -m pip install --upgrade pip [--proxy <www.proxy.com>]
2    pip install --upgrade wheel [--proxy <www.proxy.com>]
3    pip install -r requirements.txt [--proxy <www.proxy.com>]

How to run Channel Sounding example

Python

Execute Python Script in the previously configured environment using:

Activate virtual environment
1    cd \simplelink_lowpower_f3_source_sdk_9_10_00_70_eng\tools\ble\ble_agent
2    .venv\Scripts\activate
3    cd examples
4    python ble_device_car_node_with_distance.py

The script will automatically initiate a connection between the car node and key node and start a CS procedure. See an example of the output below:

../_images/DistanceOutput_Terminal.png

Distance Output in Terminal

Enabling Channel Sounding in the Application

Channel Sounding (CS) is a connection based ranging technology. In order to use CS, a connection must be established. The first step to enabling CS is to create a connection. To enable CS within an example project, it is recommended to continuously scan and advertise to easily create a connection.

After a connection is established, begin the process of enabling CS.

  1. Enable the CS callbacks, and enable the CS event handler.

Register CS Callback and Event Handler
 1bStatus_t ChannelSounding_start()
 2{
 3bStatus_t status = USUCCESS;
 4
 5// Register to command complete events
 6status = BLEAppUtil_registerEventHandler(&csCmdCompleteEvtHandler);
 7
 8if( status == USUCCESS )
 9{
10    // Register Channel Sounding Callbacks
11    status = BLEAppUtil_registerCsCB();
12}
13
14// Return status value
15return status;
16}

2. Channel Sounding Capabilities of both the local and remote device need to be communicated. The local device is the initiator device and the remote device is the reflector. See the code snippet below:

Read Local CS Capabilities
1bStatus_t Application_readLocalCsCapabilities(void)
2{
3    csCapabilities_t localCsCapabilities;
4
5    status = CS_ReadLocalSupportedCapabilities(&localCsCapabilities);
6
7    return status;
8}
cs.h :: ChannelSounding_readRemoteCapabilitiesParams_t - Remote Capabilities Parameters Structure
1typedef struct
2{
3    uint16_t connHandle;
4} ChannelSounding_readRemoteCapabilitiesParams_t
Read Remote CS Capabilities
 1bStatus_t Application_readRemoteCsCapabilities(ChannelSounding_readRemoteCapabilitiesParams_t* pRemoteCapabilitiesParams)
 2{
 3    bStatus_t status = FAILURE;
 4
 5    if(pRemoteCapabilitiesParams != NULL)
 6    {
 7        status = CS_ReadRemoteSupportedCapabilities(&localCsCapabilities);
 8    }
 9
10    return status;
11}

Once the function CS_ReadRemoteSupportedCapabilities() is called, the local device sends a request to the remote device to read its capabilities. The remote device will respond to the local device with its capabilities. The Channel Sounding Capabilities can be read within the CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMPLETE_EVENT. Within this event, the capabilities will need to be populated into a structure. The data is received through the pMsgData pointer, used in the event handler. See an example of the capabilities structure below:

cs_types.h :: csCapabilities_t - Remote Capabilities Structure
 1typedef struct
 2{
 3    uint8_t  optionalModes;      //!< indicates which of the optional CS modes are supported
 4    uint8_t  rttCap;             //!< indicate which of the time-of-flight accuracy requirements are met
 5    uint8_t  rttAAOnlyN;         //!< Number of CS steps of single packet exchanges needed
 6    uint8_t  rttSoundingN;       //!< Number of CS steps of single packet exchanges needed
 7    uint8_t  rttRandomPayloadN;  //!< Num of CS steps of single packet exchange needed
 8    uint16_t nadmSounding;       //!< NADM Sounding Capability
 9    uint16_t nadmRandomSeq;      //!< NADM Random Sequence Capability
10    uint8_t  optionalCsSyncPhy;  //!< supported CS sync PHYs, bit mapped field
11    uint8_t  numAntennas;        //!< the number of antenna elements that are available for CS tone exchanges
12    uint8_t  maxAntPath;         //!< max number of antenna paths that are supported
13    uint8_t  role;               //!< initiator or reflector or both
14    uint8_t  rfu0;               //!< reserved for future use
15    uint8_t  noFAE;              //!< No FAE
16    uint8_t  chSel3c;            //!< channel selection 3c support
17    uint8_t  csBasedRanging;     //!< CS based ranging
18    uint8_t  rfu1;               //!< reserved for future use
19    uint8_t  numConfig;          //!< Number of CS configurations supported per conn
20    uint16_t maxProcedures;      //!< Max num of CS procedures supported
21    uint8_t  tSwCap;             //!< Antenna switch time capability
22    uint16_t tIp1Cap;            //!< tIP1 Capability
23    uint16_t tIp2Cap;            //!< tTP2 Capability
24    uint16_t tFcsCap;            //!< tFCS Capability
25    uint16_t tPmCsap;            //!< tPM Capability
26    uint8_t  snrTxCap;           //!< Spec defines an additional byte for RFU
27} csCapabilities_t;

3. When local and remote capabilities are read, the next step is to enable CS security. This is done by calling the CS_SecurityEnable() function. Once the function is called, wait for the CS_SECURITY_ENABLE_COMPLETE_EVENT event to be received. The event will contain the status of the security enable request.

cs.h :: CS_securityEnableCmdParams_t - CS Security Structure
1typedef struct
2{
3    uint16_t connHandle;             //!< Connection handle
4} CS_securityEnableCmdParams_t;
Enabling CS Security
 1bStatus_t Application_SecurityEnable(CS_securityEnableCmdParams_t* pSecurityEnableParams)
 2{
 3    bStatus_t status = FAILURE;
 4
 5    if(pSecurityEnableParams = NULL)
 6    {
 7        status = CS_SecurityEnable((CS_securityEnableCmdParams_t *) pCSsecurityEnableParams);
 8    }
 9
10    return status;
11}

4. No the default settings need to be configured. The CS_SetDefaultSettings function uses the CS_setDefaultSettingsCmdParams_t structure to set the default settings. These parameters include the connection handle, roleEnable flag, csSyncAntennaSelection, and maxTxPower.

cs.h :: CS_setDefaultSettingsCmdParams_t - Default Settings Parameters Structure
1typedef struct
2{
3uint16_t connHandle;              //!< Connection handle
4uint8_t  roleEnable;              //!< Role enable flag
5uint8_t  csSyncAntennaSelection;  //!< CS sync antenna selection
6int8_t   maxTxPower;              //!< Maximum TX power in dBm
7} CS_setDefaultSettingsCmdParams_t;
Set Default Settings for CS
 1bStatus_t Application_SetDefaultSettings(CS_setDefaultSettingsCmdParams_t* pDefaultSettings)
 2{
 3    bStatus_t status = FAILURE;
 4
 5    if(pDefaultSettings != NULL)
 6    {
 7        status = CS_SetDefaultSettings((CS_setDefaultSettingsCmdParams_t *) pDefaultSettings);
 8    }
 9
10    return status;
11}

5. Configure the CS configuration. The CS configuration outlines the parameters used by the local and remote devices when executing CS procedures. The CS_CreateConfig() function uses the CS_createConfigCmdParams_t structure to create the configuration. See the structure and the example function code below:

cs.h :: CS_createConfigCmdParams_t - CS Configuration Structure
 1typedef struct
 2{
 3uint16_t    connHandle;              //!< Connection handle
 4uint8_t     configID;                //!< Configuration ID
 5uint8_t     createContext;           //!< Create context flag
 6uint8_t     mainMode;                //!< Main mode @ref CS_Mode
 7uint8_t     subMode;                 //!< Sub mode @ref CS_Mode
 8uint8_t     mainModeMinSteps;        //!< Minimum steps for main mode
 9uint8_t     mainModeMaxSteps;        //!< Maximum steps for main mode
10uint8_t     mainModeRepetition;      //!< Main mode repetition
11uint8_t     modeZeroSteps;           //!< Steps for mode zero
12uint8_t     role;                    //!< Role @ref CS_Role
13uint8_t     rttType;                 //!< RTT type @ref CS_RTT_Type
14uint8_t     csSyncPhy;               //!< CS sync PHY @ref CS_Sync_Phy_Supported
15csChm_t     channelMap;              //!< Channel map @ref csChm_t
16uint8_t     chMRepetition;           //!< Channel map repetition
17uint8_t     chSel;                   //!< Channel selection algorithm to be used @ref CS_Chan_Sel_Alg
18uint8_t     ch3cShape;               //!< Channel 3C shape
19uint8_t     ch3CJump;                //!< Channel 3C jump
20} CS_createConfigCmdParams_t;
Create CS Configuration
 1bStatus_t Application_createCsConfiguration(CS_createConfigCmdParams_t* pCreateConfigParams)
 2{
 3    bStatus_t status = FAILURE;
 4
 5    if(pCreateConfigParams != NULL)
 6    {
 7        status = CS_CreateConfig((CS_createConfigCmdParams_t *) pCreateConfigParams);
 8    }
 9
10    return status;
11}

The CS_CreateConfig() function will trigger the CS_CONFIG_COMPLETE_EVENT event. The configuration parameters will be outputted within this event, and can be stored within a structure if desired.

  1. Next, the procedure parameters are set. See the function and parameter structure in the code block below:

cs.h :: CS_setProcedureParamsCmdParams_t - CS Procedure Parameters Structure
 1typedef struct
 2{
 3uint16_t   connHandle;           //!< Connection handle
 4uint8_t    configID;             //!< Configuration ID
 5uint16_t   maxProcedureDur;      //!< Maximum procedure duration in 0.625 milliseconds
 6uint16_t   minProcedureInterval; //!< Minimum number of connection events between consecutive CS procedures
 7uint16_t   maxProcedureInterval; //!< Maximum number of connection events between consecutive CS procedures
 8uint16_t   maxProcedureCount;    //!< Maximum number of CS procedures to be scheduled (0 - indefinite)
 9uint32_t   minSubEventLen;       //!< Minimum SubEvent length in microseconds, range 1250us to 4s
10uint32_t   maxSubEventLen;       //!< Maximum SubEvent length in microseconds, range 1250us to 4s
11csACI_e    aci;                  //!< Antenna Configuration Index @ref csACI_e
12uint8_t    phy;                  //!< PHY @ref CS_Phy_Supported
13uint8_t    txPwrDelta;           //!< Tx Power Delta, in signed dB
14uint8_t    preferredPeerAntenna; //!< Preferred peer antenna
15uint8_t    snrCtrlI;             //!< SNR Control Initiator
16uint8_t    snrCtrlR;             //!< SNR Control Reflector
17uint8_t    enable;               //!< Is procedure enabled @ref CS_Enable
18} CS_setProcedureParamsCmdParams_t;
Set CS Procedure Paramaters
 1bStatus_t Application_SetCSProcedureParams(CS_setProcedureParamsCmdParams_t* pSetProcedureParams)
 2{
 3    bStatus_t status = FAILURE;
 4
 5    if(pSetProcedureParams != NULL)
 6    {
 7        status = CS_CreateConfig((CS_setProcedureParamsCmdParams_t *) pSetProcedureParams);
 8    }
 9
10    return status;
11}
  1. After setting the procedure parameters, the Channel Sounding procedure can be enabled. See the functions and the structures below:

cs.h :: CS_setProcedureEnableCmdParams_t - CS Procedure Enable Parameters
1typedef struct
2{
3uint16_t connHandle;  //!< Connection handle
4uint8_t  configID;    //!< Configuration ID
5uint8_t  enable;      //!< Enable or disable the procedure @ref CS_Enable
6} CS_setProcedureEnableCmdParams_t;
Enable CS Procedure
 1bStatus_t CS_ProcedureEnable(CS_setProcedureEnableCmdParams_t* pCsProcedureEnableParams)
 2{
 3bStatus_t status = FAILURE;
 4
 5if ( pCsProcedureEnableParams != NULL )
 6{
 7    status = CS_ProcedureEnable((CS_setProcedureEnableCmdParams_t *) pCsProcedureEnableParams);
 8}
 9
10return status;
11}

8. The CS_PROCEDURE_ENABLE_COMPLETE_EVENT will trigger when the CS procedure is complete. After this event is triggered, the CS_SUBEVENT_RESULT or CS_SUBEVENT_CONTINUE_RESULT event will be triggered. These events occur at the end of each subevent. After processing the subevent data, to prepare for post processing, the data can be sent to the BleCsRanging_estimatePbr function which will handle the (PBR) post processing. After the phase data is processed, the distance data can be post processed as well. The BLE CS Ranging Library includes distance post processing algorithms such as a Kalman filter, a moving average filter and a combination of both. Below is an example on how the phase data post processing and the distance data post processing is handled:

Phase and Distance Post Processing
 1BleCsRanging_Status_e BleCsRangingStatus; //Status for the BleCsRanging_estimatePbr function
 2BleCsRanging_Tone_t* localResults; //Local Phase results from CS procedure
 3BleCsRanging_Tone_t* remoteResults; //Remote Phase results from CS procedure
 4
 5if (gCsProcessDb->localRole == CS_ROLE_INITIATOR)
 6{
 7    localResults = gCsProcessDb->initPathsData;
 8    remoteResults = gCsProcessDb->refPathsData;
 9}
10else
11{
12    localResults = gCsProcessDb->refPathsData;
13    remoteResults = gCsProcessDb->initPathsData;
14}
15
16BleCsRangingStatus = BleCsRanging_estimatePbr(&results, localResults, remoteResults, &(gCsProcessDb->config));
17
18if (BleCsRangingStatus != BleCsRanging_Status_Success)
19{
20    status = FAILURE;
21}
22else
23{
24    // Get Current time
25    currTime = llGetCurrentTime();
26
27    // Initiate filtering for the first distance measurement.
28    // This should only be done before the first usage of the filtering
29    if (gCsProcessDb->filteringDb.initDone == FALSE)
30    {
31        // Init filters DB
32        gCsProcessDb->filteringDb.initDone = TRUE;
33        gCsProcessDb->filteringDb.lastTimeTicks = currTime;
34        gCsProcessDb->filteringDb.lastTime = ((float) currTime) / ((float) RAT_TICKS_IN_1S);
35        BleCsRanging_initKalmanFilter(&gCsProcessDb->filteringDb.kalmanFilter, results.distance , 0.1, 0.0, 1, gCsProcessDb->filteringDb.lastTime);
36        BleCsRanging_initMovingAverageFilter(&gCsProcessDb->filteringDb.movingAvgFilter, 4, 2);
37    }
38    else
39    {
40        // Add delta between current time and previous time difference to the filtering time
41        gCsProcessDb->filteringDb.lastTime += ((float) llTimeAbs(gCsProcessDb->filteringDb.lastTimeTicks, currTime)) / ((float) RAT_TICKS_IN_1S);
42
43        // Update last time measure to be the current time
44        gCsProcessDb->filteringDb.lastTimeTicks = currTime;
45    }
46
47    // Filter the raw results and get the final distance
48    results.distance = BleCsRanging_filterAll(&gCsProcessDb->filteringDb.movingAvgFilter, &gCsProcessDb->filteringDb.kalmanFilter,
49                                                BleCsRanging_FilterChain_AverageKalman, results.distance,
50                                                gCsProcessDb->filteringDb.lastTime);
51
52    // Copy the results, while converting floats to 32bits without losing the data after the decimal point
53    csResults->distance = (uint32_t) (results.distance * 100);
54    csResults->quality = (uint32_t) (results.quality * 100);
55    csResults->confidence = (uint32_t) (results.confidence * 100);
56}