.. _sec-channel-Sounding:

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 |SDK|.

The Channel Sounding chapters will cover: 

    - Channel Sounding overview, discussing the what and why (see :ref:`sec-channel-sounding-overview`)
    - Channel Sounding APIs (see :ref:`ble_api_reference`)
    - Enable Channel Sounding Demonstration (see :ref:`sec-channel-Sounding-Enable-Example`)

The |DEVICE| 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 |DEVICE|. 


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

.. _sec-channel-Sounding-overview: 

Channel Sounding Overview
^^^^^^^^^^^^^^^^^^^^^^^^^

What is Channel Sounding? Bluetooth Channel Sounding (:term:`CS`), introduced in the Bluetooth 6.0 specification, enables high-accuracy 
distance measurement between two BLE devices using phase-based ranging (:term:`PBR`). This technique estimates the distance by 
measuring the phase difference between a received unmodulated tone and the device’s local oscillator (:term:`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 (:term:`RTLS`), car access, and secure proximity-based services. 
It also introduces built-in security mechanisms like random frequency hopping, Round Trip Time (:term:`RTT`) validation, and attack 
detection metrics (:term:`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 |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 (:term:`RTT`), (:term:`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. 

.. _sec-channel-Sounding-Enable-Example: 

How to enable Channel Sounding example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Channel sounding example works using CCS and Python: 

*	CCS is used to flash one |DEVICE| 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.

.. uml::
    :caption: Channel Sounding Example Project Software Flow 
    :align: center

    @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


Prerequisites 
"""""""""""""

Hardware:	

*	2 x SimpleLink |DEVICE| LaunchPad Target

*	2 x SimpleLink™ LaunchPad™ XDS110 Debugger

*	2 x USB cable

* (Optional) 1 x Battery Pack for Reflector

Software:

*	|SDK|

*	TI Code Composer Studio (CCS) 
  

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 |DEVICE| 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.

.. _manage_pairing_mode:
.. figure:: resources/ConfigureInitiatePairing_CarNode.png
    :align: center

    Configure Car Node to Initiate Pairing

.. _set_com_port_carnode: 
.. figure:: resources/ConfiguringComPort_CarNode.png
    :align: center

    Configure COM Port for Car Node in Python

2.	For reflector node, import ``<SDK>/examples/rtos/LP_EM_CC2745R10/ble/key_node``. Flash the key_node project onto the second |DEVICE| 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.**

3. (Optional) To configure 2x2 antenna setup for both |DEVICE|:

    * 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.

.. _ConfigureAdditionalRFGPIOSignals: 
.. figure:: resources/ConfigureAdditionalRFGPIOSignals.png
    :align: center
    
    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:
    
        .. ditaa::
                          +-----------------+  
                          |                 |
                          |                 +-----> Antenna 1
                          |                 |
                          |                 +-----> Antenna 2
            Signal  ----->+     Switch      |
                          |                 +-----> Antenna 3
                          |                 |
                          |                 +-----> Antenna 4
                          |                 |
                          +-----+------+----+
                                ^      ^                        
                                |      |
                                |      |
                                |      |
                            PBEGPO3  PBEGPO2


    2. Swith truth table: 

        .. 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

        .. table:: 
            
            +---------------------+---------------------+---------------------+---------------------+
            |      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: 

.. _csProcessExt_project_properties:
.. figure:: resources/ProjectProperties.png
    :align: center 

    Navigate to project properties

.. _csProcessExt_predefines:
.. figure:: resources/ProjectProperties_predefines.png
    :align: center 

    Navigate to predefines

.. _csProcessExt3_added_predefine:
.. figure:: resources/ProjectProperties_add_predefines.png
    :align: center 

    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: 

.. _csProcessExt_python_enable_wait_distance:
.. figure:: resources/pythonExtendedDebugEnable.png
    :align: center 

    Add Extended Debug predefine 

.. _csProcessExt_python_enable_cs_proc_enable:
.. figure:: resources/pythonExtendedDebugEnable2.png
    :align: center 

    Add Extended Debug predefine 

.. _csProcessExt_python_enable_cs_start:
.. figure:: resources/pythonExtendedDebugEnable3.png
    :align: center 

    Add Extended Debug predefine 

5.	Install `Python 3.10.11 <https://www.python.org/downloads/release/python-31011/>`_
    
    **Note: Python 3.10 should be installed at C:\\Python310**

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

.. _system_properties_environment_vars:
.. figure:: resources/NavigateToEnvVars.png
    :align: center

    Open System Properties 

.. _system_properties_path_env_vars:
.. figure:: resources/PathEnvVars.png
    :align: center

    Open System Properties 

.. _edit_env_variables:
.. figure:: resources/EnvironmentVariables.png
    :align: center

    Edit User Environment Variables

7.	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. 

.. code-block:: console
    :caption: Setup virtual environment
    :linenos:

        cd <ble_agent folder (SDK/tools/ble/ble_agent)>
        c:\Python310\python.exe -m pip install virtualenv [--proxy <www.proxy.com>]
        c:\Python310\python.exe -m venv .venv
        .venv\Scripts\activate


8.	Setup external packages in case your network is behind a proxy, use `--proxy`

.. code-block:: console
    :caption: Upgrade python packages
    :linenos:

        python -m pip install --upgrade pip [--proxy <www.proxy.com>]
        pip install --upgrade wheel [--proxy <www.proxy.com>]
        pip install -r requirements.txt [--proxy <www.proxy.com>]

.. _sec-channel-Sounding-Run-Example: 

How to run Channel Sounding example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Python
""""""

Execute Python Script in the previously configured environment using: 

.. code-block:: console
    :caption: Activate virtual environment
    :linenos:

        cd \simplelink_lowpower_f3_source_sdk_9_10_00_70_eng\tools\ble\ble_agent
        .venv\Scripts\activate
        cd examples
        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: 

.. _DistanceOutput_Terminal:
.. figure:: resources/DistanceOutput_Terminal.png
    :align: center

    Distance Output in Terminal

Enabling Channel Sounding in the Application 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Channel Sounding (:term:`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. 

.. code-block:: c
    :caption: Register CS Callback and Event Handler
    :linenos:

    bStatus_t ChannelSounding_start()
    {
    bStatus_t status = USUCCESS;

    // Register to command complete events
    status = BLEAppUtil_registerEventHandler(&csCmdCompleteEvtHandler);

    if( status == USUCCESS )
    {
        // Register Channel Sounding Callbacks
        status = BLEAppUtil_registerCsCB();
    }

    // Return status value
    return status;
    }

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: 

.. code-block:: c
    :caption: Read Local CS Capabilities 
    :linenos:

    bStatus_t Application_readLocalCsCapabilities(void)
    {
        csCapabilities_t localCsCapabilities; 

        status = CS_ReadLocalSupportedCapabilities(&localCsCapabilities);

        return status; 
    }

.. code-block:: c
    :caption: **cs.h :: ChannelSounding_readRemoteCapabilitiesParams_t** - Remote Capabilities Parameters Structure 
    :linenos:

    typedef struct 
    {
        uint16_t connHandle; 
    } ChannelSounding_readRemoteCapabilitiesParams_t

.. code-block:: c
    :caption: Read Remote CS Capabilities 
    :linenos:

    bStatus_t Application_readRemoteCsCapabilities(ChannelSounding_readRemoteCapabilitiesParams_t* pRemoteCapabilitiesParams)
    {
        bStatus_t status = FAILURE;

        if(pRemoteCapabilitiesParams != NULL)
        {
            status = CS_ReadRemoteSupportedCapabilities(&localCsCapabilities);
        }

        return status; 
    }

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: 

.. code-block:: c
    :caption: **cs_types.h :: csCapabilities_t** - Remote Capabilities Structure
    :linenos:

    typedef struct
    {
        uint8_t  optionalModes;      //!< indicates which of the optional CS modes are supported
        uint8_t  rttCap;             //!< indicate which of the time-of-flight accuracy requirements are met
        uint8_t  rttAAOnlyN;         //!< Number of CS steps of single packet exchanges needed
        uint8_t  rttSoundingN;       //!< Number of CS steps of single packet exchanges needed
        uint8_t  rttRandomPayloadN;  //!< Num of CS steps of single packet exchange needed
        uint16_t nadmSounding;       //!< NADM Sounding Capability
        uint16_t nadmRandomSeq;      //!< NADM Random Sequence Capability
        uint8_t  optionalCsSyncPhy;  //!< supported CS sync PHYs, bit mapped field
        uint8_t  numAntennas;        //!< the number of antenna elements that are available for CS tone exchanges
        uint8_t  maxAntPath;         //!< max number of antenna paths that are supported
        uint8_t  role;               //!< initiator or reflector or both
        uint8_t  rfu0;               //!< reserved for future use
        uint8_t  noFAE;              //!< No FAE
        uint8_t  chSel3c;            //!< channel selection 3c support
        uint8_t  csBasedRanging;     //!< CS based ranging
        uint8_t  rfu1;               //!< reserved for future use
        uint8_t  numConfig;          //!< Number of CS configurations supported per conn
        uint16_t maxProcedures;      //!< Max num of CS procedures supported
        uint8_t  tSwCap;             //!< Antenna switch time capability
        uint16_t tIp1Cap;            //!< tIP1 Capability
        uint16_t tIp2Cap;            //!< tTP2 Capability
        uint16_t tFcsCap;            //!< tFCS Capability
        uint16_t tPmCsap;            //!< tPM Capability
        uint8_t  snrTxCap;           //!< Spec defines an additional byte for RFU
    } 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.

.. code-block:: c
    :caption: **cs.h :: CS_securityEnableCmdParams_t** - CS Security Structure 
    :linenos:

    typedef struct
    {
        uint16_t connHandle;             //!< Connection handle
    } CS_securityEnableCmdParams_t;

.. code-block:: c
    :caption: Enabling CS Security
    :linenos:

    bStatus_t Application_SecurityEnable(CS_securityEnableCmdParams_t* pSecurityEnableParams)
    {
        bStatus_t status = FAILURE;

        if(pSecurityEnableParams = NULL)
        {
            status = CS_SecurityEnable((CS_securityEnableCmdParams_t *) pCSsecurityEnableParams);
        }

        return status; 
    }

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. 

.. code-block:: c
    :caption: **cs.h :: CS_setDefaultSettingsCmdParams_t** - Default Settings Parameters Structure 
    :linenos:

    typedef struct
    {
    uint16_t connHandle;              //!< Connection handle
    uint8_t  roleEnable;              //!< Role enable flag
    uint8_t  csSyncAntennaSelection;  //!< CS sync antenna selection
    int8_t   maxTxPower;              //!< Maximum TX power in dBm
    } CS_setDefaultSettingsCmdParams_t;

.. code-block:: c
    :caption: Set Default Settings for CS 
    :linenos:

    bStatus_t Application_SetDefaultSettings(CS_setDefaultSettingsCmdParams_t* pDefaultSettings)
    {
        bStatus_t status = FAILURE;

        if(pDefaultSettings != NULL)
        {
            status = CS_SetDefaultSettings((CS_setDefaultSettingsCmdParams_t *) pDefaultSettings);
        }

        return status; 
    }

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: 

.. code-block:: c
    :caption: **cs.h :: CS_createConfigCmdParams_t** - CS Configuration Structure
    :linenos:

    typedef struct
    {
    uint16_t    connHandle;              //!< Connection handle
    uint8_t     configID;                //!< Configuration ID
    uint8_t     createContext;           //!< Create context flag
    uint8_t     mainMode;                //!< Main mode @ref CS_Mode
    uint8_t     subMode;                 //!< Sub mode @ref CS_Mode
    uint8_t     mainModeMinSteps;        //!< Minimum steps for main mode
    uint8_t     mainModeMaxSteps;        //!< Maximum steps for main mode
    uint8_t     mainModeRepetition;      //!< Main mode repetition
    uint8_t     modeZeroSteps;           //!< Steps for mode zero
    uint8_t     role;                    //!< Role @ref CS_Role
    uint8_t     rttType;                 //!< RTT type @ref CS_RTT_Type
    uint8_t     csSyncPhy;               //!< CS sync PHY @ref CS_Sync_Phy_Supported
    csChm_t     channelMap;              //!< Channel map @ref csChm_t
    uint8_t     chMRepetition;           //!< Channel map repetition
    uint8_t     chSel;                   //!< Channel selection algorithm to be used @ref CS_Chan_Sel_Alg
    uint8_t     ch3cShape;               //!< Channel 3C shape
    uint8_t     ch3CJump;                //!< Channel 3C jump
    } CS_createConfigCmdParams_t;

.. code-block:: c
    :caption: Create CS Configuration 
    :linenos:

    bStatus_t Application_createCsConfiguration(CS_createConfigCmdParams_t* pCreateConfigParams)
    {
        bStatus_t status = FAILURE;

        if(pCreateConfigParams != NULL)
        {
            status = CS_CreateConfig((CS_createConfigCmdParams_t *) pCreateConfigParams);
        }

        return status; 
    }

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. 

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

.. code-block:: c
    :caption: **cs.h :: CS_setProcedureParamsCmdParams_t** - CS Procedure Parameters Structure 
    :linenos:

    typedef struct
    {
    uint16_t   connHandle;           //!< Connection handle
    uint8_t    configID;             //!< Configuration ID
    uint16_t   maxProcedureDur;      //!< Maximum procedure duration in 0.625 milliseconds
    uint16_t   minProcedureInterval; //!< Minimum number of connection events between consecutive CS procedures
    uint16_t   maxProcedureInterval; //!< Maximum number of connection events between consecutive CS procedures
    uint16_t   maxProcedureCount;    //!< Maximum number of CS procedures to be scheduled (0 - indefinite)
    uint32_t   minSubEventLen;       //!< Minimum SubEvent length in microseconds, range 1250us to 4s
    uint32_t   maxSubEventLen;       //!< Maximum SubEvent length in microseconds, range 1250us to 4s
    csACI_e    aci;                  //!< Antenna Configuration Index @ref csACI_e
    uint8_t    phy;                  //!< PHY @ref CS_Phy_Supported
    uint8_t    txPwrDelta;           //!< Tx Power Delta, in signed dB
    uint8_t    preferredPeerAntenna; //!< Preferred peer antenna
    uint8_t    snrCtrlI;             //!< SNR Control Initiator
    uint8_t    snrCtrlR;             //!< SNR Control Reflector
    uint8_t    enable;               //!< Is procedure enabled @ref CS_Enable
    } CS_setProcedureParamsCmdParams_t;

.. code-block:: c
    :caption: Set CS Procedure Paramaters
    :linenos:

    bStatus_t Application_SetCSProcedureParams(CS_setProcedureParamsCmdParams_t* pSetProcedureParams)
    {
        bStatus_t status = FAILURE;

        if(pSetProcedureParams != NULL)
        {
            status = CS_CreateConfig((CS_setProcedureParamsCmdParams_t *) pSetProcedureParams);
        }

        return status; 
    }

7. After setting the procedure parameters, the Channel Sounding procedure can be enabled. See the functions and the structures below: 

.. code-block:: c
    :caption: **cs.h :: CS_setProcedureEnableCmdParams_t** - CS Procedure Enable Parameters
    :linenos:

    typedef struct
    {
    uint16_t connHandle;  //!< Connection handle
    uint8_t  configID;    //!< Configuration ID
    uint8_t  enable;      //!< Enable or disable the procedure @ref CS_Enable
    } CS_setProcedureEnableCmdParams_t;

.. code-block:: c
    :caption: Enable CS Procedure
    :linenos:

    bStatus_t CS_ProcedureEnable(CS_setProcedureEnableCmdParams_t* pCsProcedureEnableParams)
    {
    bStatus_t status = FAILURE;

    if ( pCsProcedureEnableParams != NULL )
    {
        status = CS_ProcedureEnable((CS_setProcedureEnableCmdParams_t *) pCsProcedureEnableParams);
    }

    return status;
    }

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 (:term:`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: 

.. code-block:: c
    :caption: Phase and Distance Post Processing
    :linenos:

    BleCsRanging_Status_e BleCsRangingStatus; //Status for the BleCsRanging_estimatePbr function 
    BleCsRanging_Tone_t* localResults; //Local Phase results from CS procedure 
    BleCsRanging_Tone_t* remoteResults; //Remote Phase results from CS procedure

    if (gCsProcessDb->localRole == CS_ROLE_INITIATOR)
    {
        localResults = gCsProcessDb->initPathsData;
        remoteResults = gCsProcessDb->refPathsData;
    }
    else
    {
        localResults = gCsProcessDb->refPathsData;
        remoteResults = gCsProcessDb->initPathsData;
    }

    BleCsRangingStatus = BleCsRanging_estimatePbr(&results, localResults, remoteResults, &(gCsProcessDb->config));

    if (BleCsRangingStatus != BleCsRanging_Status_Success)
    {
        status = FAILURE;
    }
    else
    {
        // Get Current time
        currTime = llGetCurrentTime();

        // Initiate filtering for the first distance measurement.
        // This should only be done before the first usage of the filtering
        if (gCsProcessDb->filteringDb.initDone == FALSE)
        {
            // Init filters DB
            gCsProcessDb->filteringDb.initDone = TRUE;
            gCsProcessDb->filteringDb.lastTimeTicks = currTime;
            gCsProcessDb->filteringDb.lastTime = ((float) currTime) / ((float) RAT_TICKS_IN_1S);
            BleCsRanging_initKalmanFilter(&gCsProcessDb->filteringDb.kalmanFilter, results.distance , 0.1, 0.0, 1, gCsProcessDb->filteringDb.lastTime);
            BleCsRanging_initMovingAverageFilter(&gCsProcessDb->filteringDb.movingAvgFilter, 4, 2);
        }
        else
        {
            // Add delta between current time and previous time difference to the filtering time
            gCsProcessDb->filteringDb.lastTime += ((float) llTimeAbs(gCsProcessDb->filteringDb.lastTimeTicks, currTime)) / ((float) RAT_TICKS_IN_1S);

            // Update last time measure to be the current time
            gCsProcessDb->filteringDb.lastTimeTicks = currTime;
        }

        // Filter the raw results and get the final distance
        results.distance = BleCsRanging_filterAll(&gCsProcessDb->filteringDb.movingAvgFilter, &gCsProcessDb->filteringDb.kalmanFilter,
                                                    BleCsRanging_FilterChain_AverageKalman, results.distance,
                                                    gCsProcessDb->filteringDb.lastTime);

        // Copy the results, while converting floats to 32bits without losing the data after the decimal point
        csResults->distance = (uint32_t) (results.distance * 100);
        csResults->quality = (uint32_t) (results.quality * 100);
        csResults->confidence = (uint32_t) (results.confidence * 100);
    }


