This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

LAUNCHXL-CC26X2R1: LAUNCHXL-CC26X2R1: Timeout: Master node to set connectionless aoa state (numAnt issue)

Part Number: LAUNCHXL-CC26X2R1

Hi Ti,

I had change the ant control to our design. my configuration as below. it can work well on AOA, but error on CL-AOA (the same error like this thread)

        if cl_aoa:
            cl_aoa_params = {
                "cl_aoa_role": "AOA_MASTER",  # AOA_MASTER - currently only master role supported
                "cl_aoa_result_mode": "AOA_MODE_RAW",  # AOA_MODE_ANGLE, AOA_MODE_PAIR_ANGLES, AOA_MODE_RAW
                "cl_aoa_slot_durations": 2,
                "cl_aoa_sample_rate": 4,  # 1Mhz (BT5.1 spec), 2Mhz, 3Mhz or 4Mhz - this enables oversampling
                "cl_aoa_sample_size": 2,  # 8 bit sample (as defined by BT5.1 spec), 16 bit sample (higher accuracy)
                "cl_aoa_sampling_control": int('0x11', 16),
                # bit 0   - 0x00 - default filtering, 0x01 - RAW_RF no filtering (use this mode for post process angle calculation),
                # bit 4,5 - default: 0x10 - ONLY_ANT_1, optional: 0x20 - ONLY_ANT_2
                "max_sample_cte": 1,
                "cl_aoa_pattern_len": 16,
                "cl_aoa_ant_pattern": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
            }

I just modify setting of python to reduce ant_num, it become well on CL-AOA (master image still config 16 ANT array). looks like corrupt at  llPeriodicScanSet_t *pPeriodicScan = llGetPeriodicScan( syncHandle );

                                      "cl_aoa_pattern_len": 16, ==>15
                                      "cl_aoa_ant_pattern": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] ==> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

Can you help to check if any limitation in this API


  • Hey Valery,

    I've assigned your post to an expert. Can you tell me what SDK you're using?

  • thx SDK4.4

  • Hi Valery,

    It looks like you are using SDK 4.40.04.04.

    Could you please instead use SDK 4.40.00.44 (https://www.ti.com/tool/download/SIMPLELINK-CC13X2-26X2-SDK/4.40.00.44)

    It seems like an issue has been introduced in the SDK 4.40.04.04 - I will look into this.

    Best regards,

  • Hi Clement,

    SDK 4.40.00.44 have same issue.

    another question, Do you know if SDK 4.40.04.44 supported multi-tag CL-AOA?   It will fail when running CL-AOA enable for 2nd tag .

  • Hi Valery,

    I have verified and there is apparently no difference between SDK 4.40.00.44 and SDK 4.40.04.04.

    WIt that being said, I am a bit confused with your latest message:

    - Do you manage to run the out-of-the-box CL-AoA example without modification?

    - Could you give me a list of the issues opened at the moment?

    For the rest, it is possible to run the example with several tags. It will require to synchronize with all the tags and turn on the sampling of the CTE received for all of them. For the moment only one locator (rtls_master) is supported.

    Best regards,

  • - Do you manage to run the out-of-the-box CL-AoA example without modification?

    ==>I mean I have modify ANT configuration of CL-AOA, and  change the setting of CL-AOA python script(ANT setting, COM setting)。but it can test finished only when cl_aoa_patten_len <=15

    ==>About multi-tag, I will upload my change of py script, can you help to check if i miss any process

    cl-aoa-multitag.zip

    - Could you give me a list of the issues opened at the moment?

    1. ANT num must <=15

    2. I can't enable IQ for muti-tag

    3. Slave broadcast AoD CTE packet

  • Dear

        Do you have any new info and suggestion

  • Hi,

    1. ANT num must <=15 / corruption of llPeriodicScanSet_t *pPeriodicScan = llGetPeriodicScan( syncHandle );

    Could you confirm you manage to have this working for connection-AoA? Could you specify which part of the llPeriodicScanSet_t is corrupted?

    2. I can't enable IQ for muti-tag

    You do not verify if the slaves are properly synced and start aoa on all of them

            # YOUR CODE
            #            for slave in slave_list:
            # #               if rtlsUtil.padv_get_sync_handle_by_slave(slave):
            #                synced_slave = slave
            #                print(f"Sync creating:{slave}, and Connectionless AOA starting")                        
            #                rtlsUtil.cl_aoa_start(cl_aoa_params, slave)
    
            #MY SUGGESTION                    
                        for slave in slave_list:
                            if rtlsUtil.padv_get_sync_handle_by_slave(slave) != -1:
                                synced_slave = slave
                                print(f"Sync created with first slave on advertisers list: {synced_slave}. "
                                      f"Periodic advertise report enabled automatically")

    3. Slave broadcast AoD CTE packet

    We cannot reproduce the issue.

    Regards,

  • 1. ANT num must <=15

    I uploaded script and log of 16 ANT of connection_AOA

    connection_AOA_16ANTS.zip

    2. I can't enable IQ for muti-tag

    it caused by sync_handle = -1,and SYNC_EST event just provide syncHandle of 58:93:D8:6A:A9:5F, not 58:93:D8:6A:A9:1F

    RTLS_EVT_SYNC_EST', 'payload': {'opcode': 14, 'status': 'RTLS_SUCCESS', 'syncHandle': 0, 'advSid': 1, 'advAddrType': 0, 'advAddress': '58:93:D8:6A:A9:5F', 'advPhy': 1, 'periodicAdvInt': 80, 'advClockAccuracy': 5}}

    I can catch ADV_EXT_IND packet of 58:93:D8:6A:A9:5F and 58:93:D8:6A:A9:1F in sniffer, do you know why HCI didn't report HCI_BLE_PERIODIC_ADV_SYNCH_ESTABLISHED_EVENT of 58:93:D8:6A:A9:1F

  • I am out of the office until April 6th. Please do not expect an answer before then.

  • Hi Valery,

    Thank you for your patience.

    2. I can't enable IQ for muti-tag

    I have been working on this issue. It looks like the periodic advertisers has a problem. This leas the master to synchronize with only one device. I have managed to reproduce your issue. As a workaround, I suggest to not use this periodic advertiser list and instead synchronize with each device individually. To keep it simple, I have modified the

    Here the Python code I have modified:

            else:
                # Option 2
    
                # Scan and filter for relevant slaves that supports periodic advertise
                scan_results = rtlsUtil.scan(scan_time_sec)
                print(f"Scan results found: {scan_results}")
                slave_list = filter_scan_results(scan_results)
                slave = slave_list[0]
    
                # Synchronization timeout for the periodic advertising train Range: 0x000A to 0x4000 Time = N*10 ms Time Range
                # For this example, the timeout value set to be 10 times bigger then the periodic advertise interval.
                sync_timeout = int(slave_list[0]['periodicAdvInt'] * 1.25 * 10)
    
                # time out must satisfy the timeout condition. other wise sync might be lost.
                if sync_skip != 0:
                    timeout_condition = sync_timeout >= sync_skip * slave['periodicAdvInt']
                else:
                    timeout_condition = sync_timeout >= slave['periodicAdvInt']
    
                if timeout_condition:
                    if use_adv_list:
                        print("Using advertisers list to create sync")
                        print(f"slave_list: {slave_list}")
                        # Add all devices to periodic advertiser list
                        for slave in slave_list:
                            rtlsUtil.padv_add_device_to_periodic_adv_list(slave)
                            print(f"slave {slave} has been added to advertisers list")
    
    
                            rtlsUtil.padv_create_sync(slave,
                                                      USE_GIVEN_ADDRESS_AND_REPORT_ENABLE,
                                                      sync_skip,
                                                      sync_timeout,
                                                      sync_cte_type)
        
                            # Scan again for sync established event - reports will be enabled automatically
                            rtlsUtil.scan(scan_time_sec)
                            time.sleep(3)
    
                        synced_slave = [None, None]
                        i=0
                        for slave in slave_list:
                            #if rtlsUtil.padv_get_sync_handle_by_slave(slave) == 0:
                            synced_slave[i] = slave
                            i=i+1
                            print(f"Sync created with slave {i} on advertisers list: {synced_slave}. "
                                  f"Periodic advertise report enabled automatically")
                        # If non of the slaves got synced, raise an exception
                        if synced_slave is None:
                            raise Exception("No slave from periodic advertise list got synced")
    
                        if cl_aoa:
                            i=0
                            print(f"synced_slave vaue: {synced_slave}.")
                            for slave in synced_slave:
                                print(f"slave vaue: {slave}.")
                                rtlsUtil.cl_aoa_start(cl_aoa_params, slave)
                                print(f"Connectionless AOA started for {i}.")
                                i=i+1
    
                            time.sleep(5)
    
                            i=0
                            for slave in synced_slave:
                                rtlsUtil.cl_aoa_stop(cl_aoa_params, slave)
                                print(f"Connectionless AOA stopped for {i}")
                                i=i+1
    
                            for slave in synced_slave:
                                sync_handle = rtlsUtil.padv_get_sync_handle_by_slave(slave)
                                rtlsUtil.padv_terminate_sync(sync_handle)
                                print(f"Sync terminated for sync handle: {sync_handle}")
    

    Here is the whole file: /cfs-file/__key/communityserver-discussions-components-files/538/rtls_5F00_connectionless_5F00_aoa_5F00_example_5F00_with_5F00_rtls_5F00_util.py

    1. ANT num must <=15

    I have run a test with the file provided right before and changed the cl_aoa_params to the following:

                cl_aoa_params = {
                    "cl_aoa_role": "AOA_MASTER",  # AOA_MASTER - currently only master role supported
                    "cl_aoa_result_mode": "AOA_MODE_RAW",  # AOA_MODE_ANGLE, AOA_MODE_PAIR_ANGLES, AOA_MODE_RAW
                    "cl_aoa_slot_durations": 2,
                    "cl_aoa_sample_rate": 1,  # 1Mhz (BT5.1 spec), 2Mhz, 3Mhz or 4Mhz - this enables oversampling
                    "cl_aoa_sample_size": 1,  # 8 bit sample (as defined by BT5.1 spec), 16 bit sample (higher accuracy)
                    "cl_aoa_sampling_control": int('0x11', 16),
                    # bit 0   - 0x00 - default filtering, 0x01 - RAW_RF no filtering (use this mode for post process angle calculation),
                    # bit 4,5 - default: 0x10 - ONLY_ANT_1, optional: 0x20 - ONLY_ANT_2
                    "max_sample_cte": 1,
                    "cl_aoa_pattern_len": 16,
                    "cl_aoa_ant_pattern":  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
                }
    

    The whole file is the following: /cfs-file/__key/communityserver-discussions-components-files/538/rtls_5F00_connectionless_5F00_aoa_5F00_example_5F00_with_5F00_rtls_5F00_util.py

    Everything seems to work as expected:

    [2021-04-08 13:09:16,509]  rtlsnode -     INFO - MASTER  : 04:EE:03:6C:F2:4A <-- {'type': 'Command', 'command': 'RTLS_CMD_CL_AOA_ENABLE', 'payload': {'aoaRole': 'AOA_MASTER', 'aoaResultMode': 'AOA_MODE_RAW', 'syncHandle': 0, 'enable': 1, 'slotDuration': 2, 'sampleRate': 1, 'sampleSize': 1, 'sampleCtrl': 17, 'maxSampleCte': 1, 'numAnt': 16, 'pAntPattern': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]}}
    [2021-04-08 13:09:16,527]      root -     INFO - MASTER  : 04:EE:03:6C:F2:4A --> {'type': 'Event', 'command': 'RTLS_EVT_PERIODIC_ADV_RPT', 'payload': {'opcode': 15, 'syncHandle': 0, 'txPower': 0, 'rssi': -42, 'cteType': 0, 'dataStatus': 0, 'dataLen': 11, 'data': '50:65:72:69:6F:64:69:63:41:64:76'}}
    [2021-04-08 13:09:16,580]      root -     INFO - MASTER  : 04:EE:03:6C:F2:4A --> {'type': 'Response', 'command': 'RTLS_CMD_CL_AOA_ENABLE', 'payload': {'status': 'RTLS_SUCCESS'}}
    [2021-04-08 13:09:16,580]      root -     INFO - MASTER  : 04:EE:03:6C:F2:4A --> {'type': 'Event', 'command': 'RTLS_EVT_CL_AOA_ENABLE', 'payload': {'status': 17, 'syncHandle': 0}}
    [2021-04-08 13:09:16,580]      root -     INFO - MASTER  : 04:EE:03:6C:F2:4A --> {'type': 'Event', 'command': 'RTLS_EVT_PERIODIC_ADV_RPT', 'payload': {'opcode': 15, 'syncHandle': 1, 'txPower': 0, 'rssi': -36, 'cteType': 0, 'dataStatus': 0, 'dataLen': 11, 'data': '50:65:72:69:6F:64:69:63:41:64:76'}}
    [2021-04-08 13:09:16,609]      root -     INFO - Connectionless AOA Started
    [2021-04-08 13:09:16,612]  rtlsnode -     INFO - MASTER  : 04:EE:03:6C:F2:4A <-- {'type': 'Command', 'command': 'RTLS_CMD_CL_AOA_ENABLE', 'payload': {'aoaRole': 'AOA_MASTER', 'aoaResultMode': 'AOA_MODE_RAW', 'syncHandle': 1, 'enable': 1, 'slotDuration': 2, 'sampleRate': 1, 'sampleSize': 1, 'sampleCtrl': 17, 'maxSampleCte': 1, 'numAnt': 16, 'pAntPattern': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]}}
    [2021-04-08 13:09:16,633]      root -     INFO - MASTER  : 04:EE:03:6C:F2:4A --> {'type': 'Event', 'command': 'RTLS_EVT_PERIODIC_ADV_RPT', 'payload': {'opcode': 15, 'syncHandle': 0, 'txPower': 0, 'rssi': -40, 'cteType': 0, 'dataStatus': 0, 'dataLen': 11, 'data': '50:65:72:69:6F:64:69:63:41:64:76'}}
    [2021-04-08 13:09:16,687]      root -     INFO - MASTER  : 04:EE:03:6C:F2:4A --> {'type': 'Response', 'command': 'RTLS_CMD_CL_AOA_ENABLE', 'payload': {'status': 'RTLS_SUCCESS'}}
    [2021-04-08 13:09:16,688]      root -     INFO - MASTER  : 04:EE:03:6C:F2:4A --> {'type': 'Event', 'command': 'RTLS_EVT_CL_AOA_ENABLE', 'payload': {'status': 17, 'syncHandle': 1}}
    [2021-04-08 13:09:16,688]      root -     INFO - MASTER  : 04:EE:03:6C:F2:4A --> {'type': 'Event', 'command': 'RTLS_EVT_PERIODIC_ADV_RPT', 'payload': {'opcode': 15, 'syncHandle': 1, 'txPower': 0, 'rssi': -37, 'cteType': 0, 'dataStatus': 0, 'dataLen': 11, 'data': '50:65:72:69:6F:64:69:63:41:64:76'}}
    [2021-04-08 13:09:16,714]      root -     INFO - Connectionless AOA Started

    Here is the whole log: /cfs-file/__key/communityserver-discussions-components-files/538/04_5F00_08_5F00_2021_5F00_13_5F00_08_5F00_35_5F00_rtls_5F00_connectionless_5F00_aoa_5F00_example_5F00_with_5F00_rtls_5F00_util.log

    Best regards,

  • thx Clement,

    The workaround of muti-tag is workable when will TI release next ver. SDK?

    for 16-ANT number issue, i still have same problem even use your script or rollback to clean SDK+16ANT setting.

       a. Can you share your change in SDK for 16 ANT test?

       b. I have try to use breakpoint to debug why Master didn't response 'RTLS_CMD_CL_AOA_ENABLE'

          but didn't find any error and then i free the breakpoint, i can get the rsp of  'RTLS_CMD_CL_AOA_ENABLE' from tag1, but tag2 not. (attach the log)

       Is it timing issue?

    04_10_2021_13_08_04_rtls_connectionless_aoa_example_with_rtls_util.zip

  • Hi,

    1. ANT num must <=15

    a. The only changes made are in the ble_user_config.c. 

    // BOOSTXL-AOA configurations
    // Maximum number of antennas
     #define ANTENNA_TABLE_SIZE 16
    // BitMask of all the relevant GPIOs which needed for the antennas
    #define ANTENNA_IO_MASK  BV(27)|BV(28)|BV(29)|BV(30)
    
    // Antenna GPIO configuration per id (relevant for BOOSTXL-AOA)
    antennaIOEntry_t antennaTbl[ANTENNA_TABLE_SIZE] = {
                                                                   0,
                                                              BV(27),
                                                       BV(28)       ,
                                                       BV(28)|BV(27),
                                                BV(29)              ,
                                                BV(29)|       BV(27),
                                                BV(29)|BV(28)       ,
                                                BV(29)|BV(28)|BV(27),
                                         BV(30)                     ,
                                         BV(30)|              BV(27),
                                         BV(30)|       BV(28)       ,
                                         BV(30)|       BV(28)|BV(27),
                                         BV(30)|BV(29)              ,
                                         BV(30)|BV(29)|       BV(27),
                                         BV(30)|BV(29)|BV(28)       ,
                                         BV(30)|BV(29)|BV(28)|BV(27)};

    I have of course modified the Python script - I guess this is already done on your side.

    import os
    import sys
    import time
    import json
    import queue
    import threading
    import datetime
    import subprocess
    import shutil
    import traceback
    
    import pandas as pd
    # Uncomment line below for local debug of packages
    # sys.path.append(r"../unpi")
    # sys.path.append(r"../rtls")
    # sys.path.append(r"../rtls_util")
    
    from rtls_util import RtlsUtil, RtlsUtilLoggingLevel, RtlsUtilException, RtlsUtilTimeoutException, \
        RtlsUtilNodesNotIdentifiedException, RtlsUtilScanNoResultsException, RtlsUtilMasterNotFoundException
    
    headers = ['pkt', 'sample_idx', 'rssi', 'ant_array', 'channel', 'i', 'q']
    TABLE = None
    POST_ANALYZE_FILE_LIST = set()
    data_saved = False
    
    # Options constants:
    #   Clear Bit 0 - Use the advSID, advAddrType, and advAddress parameters to determine which advertiser to listen to.
    #   Set Bit 0   - Use the Periodic Advertiser List to determine which advertiser to listen to.
    #   Clear Bit 1 - Reporting initially enabled.
    #   Set Bit 1   - Reporting initially disabled.
    
    USE_GIVEN_ADDRESS_AND_REPORT_ENABLE = 0
    USE_LIST_AND_REPORT_ENABLE = 1
    USE_GIVEN_ADDRESS_AND_REPORT_DISABLE = 2
    USE_LIST_AND_REPORT_DISABLE = 3
    
    # Dummy slave to send when using periodic advertise list for sync
    dummy_slave = {'addr': 'FF:FF:FF:FF:FF:FF',
                   'addrType': 0,
                   'rssi': 0,
                   'advSID': 0,
                   'periodicAdvInt': 0
                   }
    
    
    def get_date_time():
        return datetime.datetime.now().strftime("%m_%d_%Y_%H_%M_%S")
    
    
    def get_csv_file_name_for(identifier, sync_handle, with_date=True):
        data_time = get_date_time()
        logging_file_path = os.path.join(os.path.curdir, os.path.basename(__file__).replace('.py', '_log'))
    
        if not os.path.isdir(logging_file_path):
            os.makedirs(logging_file_path)
    
        identifier = identifier.lower().replace(":", "")
        if with_date:
            filename = os.path.join(logging_file_path, f"{data_time}_rtls_raw_iq_samples_{identifier}_{sync_handle}.csv")
        else:
            filename = os.path.join(logging_file_path, f"rtls_raw_iq_samples_{identifier}_{sync_handle}.csv")
    
        return filename
    
    
    # User function to proces
    def results_parsing_cl_aoa(q):
        global TABLE, POST_ANALYZE_FILE_LIST
        pkt_count = 0
    
        while True:
            try:
                data = q.get(block=True, timeout=0.5)
                if isinstance(data, dict):
                    data_time = datetime.datetime.now().strftime("[%m:%d:%Y %H:%M:%S:%f] :")
    
                    if 'type' in data and data['type'] == 'RTLS_CMD_CL_AOA_RESULT_RAW':
                        # print(f"{data_time} {json.dumps(data)}")
    
                        if TABLE is None:
                            TABLE = pd.DataFrame(columns=headers)
    
                        identifier = data["identifier"]
                        syncHandle = data['payload']['syncHandle']
                        channel = int(data['payload']['channel'])
                        offset = int(data['payload']['offset'])
                        rssi = data['payload']['rssi']
                        antenna = data['payload']['antenna']
                        samplesLength = data['payload']["samplesLength"]
    
                        for indx, sample in enumerate(data['payload']['samples']):
                            sample_idx = offset + indx
                            sample_i = sample['i']
                            sample_q = sample['q']
    
                            row = {
                                'pkt': 0,
                                'sample_idx': sample_idx,
                                'rssi': rssi,
                                'ant_array': antenna,
                                'channel': channel,
                                'i': sample_i,
                                'q': sample_q
                            }
                            TABLE = TABLE.append(row, ignore_index=True)
    
                        df_by_channel = TABLE.loc[TABLE['channel'] == channel]
    
                        if len(df_by_channel) == samplesLength:
                            df_by_channel = df_by_channel.sort_values(by=['sample_idx'])
                            df_by_channel.loc[:, "pkt"] = df_by_channel.loc[:, "pkt"].replace(to_replace=0, value=pkt_count)
    
                            csv_file_name = get_csv_file_name_for(identifier, syncHandle, with_date=False)
                            POST_ANALYZE_FILE_LIST.add(csv_file_name)
    
                            if os.path.isfile(csv_file_name):
                                df_by_channel.to_csv(csv_file_name, mode='a', index=False, header=False)
                            else:
                                df_by_channel.to_csv(csv_file_name, index=False)
                            print(f"{data_time} Added new set of IQ into {csv_file_name}")
    
                            pkt_count += 1
                            TABLE = TABLE.loc[TABLE['channel'] != channel]
                    else:
                        print(f"{data_time} {json.dumps(data)}")
    
                elif isinstance(data, str) and data == "STOP":
                    print("STOP Command Received")
                    for entry in list(POST_ANALYZE_FILE_LIST)[:]:
                        date_time = get_date_time()
                        file_name = os.path.basename(entry)
                        dir_name = os.path.dirname(entry)
                        new_entry = os.path.abspath(os.path.join(dir_name, f"{date_time}_{file_name}"))
                        os.rename(entry, new_entry)
                        POST_ANALYZE_FILE_LIST.remove(entry)
                        POST_ANALYZE_FILE_LIST.add(new_entry)
    
                    break
                else:
                    pass
            except queue.Empty:
                continue
    
    
    def results_parsing_padv_events(q):
        while True:
            try:
                data = q.get(block=True, timeout=0.5)
                if isinstance(data, dict):
                    data_time = datetime.datetime.now().strftime("[%m:%d:%Y %H:%M:%S:%f] :")
                    print(f"{data_time} {json.dumps(data)}")
                elif isinstance(data, str) and data == "STOP":
                    print("STOP Command Received")
                    break
                else:
                    pass
            except queue.Empty:
                continue
    
    
    def filter_scan_results(scan_results, slave_address=None):
        print("Filtering scan results for periodic advertisers")
        ret_slave_list = []
        for res in scan_results:
            if slave_address is None:
                if res['periodicAdvInt'] != 0:
                    ret_slave_list.append(res)
            else:
                if res['periodicAdvInt'] != 0 and res['addr'] == slave_address:
                    ret_slave_list.append(res)
    
        if ret_slave_list:
            return ret_slave_list
        else:
            raise Exception("Slave list is empty. Non of the slaves match to definition")
    
    
    def aoa_post_analyze(selected_ant=16):
        global POST_ANALYZE_FILE_LIST
        aoa_exe_path = os.path.abspath("../rtls_ui/aoa/aoa.exe")
        support_files_ant1 = os.path.abspath("../rtls_ui/aoa/support_files/Ant1")
        support_files_ant2 = os.path.abspath("../rtls_ui/aoa/support_files/Ant2")
    
        # First, Check if cl_aoa_sampling_control configured to raw RF mode
        if selected_ant & 0x01:
            if os.path.isfile(aoa_exe_path):
                if len(POST_ANALYZE_FILE_LIST) > 0:
                    for fp in POST_ANALYZE_FILE_LIST:
                        dir_name = os.path.splitext(fp)[0]
                        if selected_ant >= 32:
                            shutil.copytree(support_files_ant2, dir_name)
                        else:
                            shutil.copytree(support_files_ant1, dir_name)
    
                        shutil.copy2(fp, dir_name)
                        csv_file = os.path.join(dir_name, os.path.basename(fp))
    
                        print("----------------------------------------------------------------------------\n")
                        print(f"Analyzing file : {fp}")
    
                        print("\nAlgorithm : Algo1")
                        output = subprocess.check_output(
                            f'{aoa_exe_path} --pct_file {csv_file} --algo algo1 --search_speed fast').decode()
                        output = output.replace('\r\n', '\r\n\t\t')
                        print(output)
    
                        print("\nAlgorithm : Algo2")
                        output = subprocess.check_output(
                            f'{aoa_exe_path} --pct_file {csv_file} --algo algo2 --search_speed fast').decode()
                        output = output.replace('\r\n', '\r\n\t\t')
                        print(output)
    
                        print("----------------------------------------------------------------------------\n")
        else:
            print("Cannot calculate angle if not configured to raw RF mode")
    
    
    def scan_for_sync_est(scan_time_sec, rtlsUtil, slave=None, retry=5):
    
        num_of_try = 0
    
        while num_of_try < retry:
            rtlsUtil.scan(scan_time_sec, slave['addr'], slave['advSID'])
            sync_handle = rtlsUtil.padv_get_sync_handle_by_slave(slave)
            if sync_handle != -1:
                print(f"Sync with: {slave['addr']} succeed with handle: {sync_handle}")
                return True
            else:
                if rtlsUtil.sync_failed_to_be_est:
                    print(f"Sync faild to be established for slave: {slave['addr']} SID: {slave['advSID']} ")
                    rtlsUtil.sync_failed_to_be_est = False
                    return False
                else:
                    num_of_try += 1
                    print(f"did not get sync established event. retry no. {num_of_try}")
    
        return False
    
    
    # Main Function
    def main():
        global POST_ANALYZE_FILE_LIST
        # Predefined parameters
        slave_bd_addr_list = None # ["80:6F:B0:EE:B9:2C", "80:6F:B0:EE:A8:A8", "80:6F:B0:EE:9A:BC"] # Up to 40 slaves to sync.
        scan_time_sec = 10                                                                          # If BD address is unknown, set None.
        num_of_scan_retry = 5   # Number of scan retry in case sync established event didn't occurred
        # Sync parameters
        sync_skip = 0         # The maximum number of periodic advertising events that can be skipped after a successful receive (Range: 0x0000 to 0x01F3)
        sync_cte_type = 0     # Clear All Bits(0) - Sync All
                              # Set Bit 0(1) - Do not sync to packets with an AoA CTE
                              # Set Bit 1(2) - Do not sync to packets with an AoD CTE with 1 us slots
                              # Set Bit 2(4) - Do not sync to packets with an AoD CTE with 2 us slots
                              # Set Bit 4(16) - Do not sync to packets without a CTE
    
        # Flag to determine whether to sync a device from advertisers list or according to bd address and adv SID
        use_adv_list = True
        # Flag to enable periodic advertise reports to be printed
        padv_report = True
        th_padv_parsing = None
        # Flag to enable connectionless aoa reports to be printed and parsed
        cl_aoa = True
        th_cl_aoa_parsing = None
    
        # Replacing python file extension from py to log for output logs + adding data time stamp to file
        data_time = datetime.datetime.now().strftime("%m_%d_%Y_%H_%M_%S")
        logging_file_path = os.path.join(os.path.curdir, os.path.basename(__file__).replace('.py', '_log'))
        if not os.path.isdir(logging_file_path):
            os.makedirs(logging_file_path)
        logging_file = os.path.join(logging_file_path, f"{data_time}_{os.path.basename(__file__).replace('.py', '.log')}")
    
        # Initialize RTLS Util instance
        rtlsUtil = RtlsUtil(logging_file, RtlsUtilLoggingLevel.INFO)
        # Update general time out for all action at RTLS Util [Default timeout : 10 sec]
        rtlsUtil.timeout = 10
    
        all_nodes = []
        try:
            devices = [
                {"com_port": "COM3", "baud_rate": 460800, "name": "CC26x2 Master"}
            ]
            # Setup devices
            master_node, passive_nodes, all_nodes = rtlsUtil.set_devices(devices)
            print(f"Master : {master_node} \nPassives : {passive_nodes} \nAll : {all_nodes}")
    
            # Reset devices for initial state of devices
            rtlsUtil.reset_devices()
            print("Devices Reset")
    
            if padv_report:
                ## Setup thread to pull out received data from devices on screen
                th_padv_parsing = threading.Thread(target=results_parsing_padv_events, args=(rtlsUtil.padv_event_queue,))
                th_padv_parsing.setDaemon(True)
                th_padv_parsing.start()
                print("Periodic Advertise Callback Set")
    
            if cl_aoa:
                cl_aoa_params = {
                    "cl_aoa_role": "AOA_MASTER",  # AOA_MASTER - currently only master role supported
                    "cl_aoa_result_mode": "AOA_MODE_RAW",  # AOA_MODE_ANGLE, AOA_MODE_PAIR_ANGLES, AOA_MODE_RAW
                    "cl_aoa_slot_durations": 2,
                    "cl_aoa_sample_rate": 1,  # 1Mhz (BT5.1 spec), 2Mhz, 3Mhz or 4Mhz - this enables oversampling
                    "cl_aoa_sample_size": 1,  # 8 bit sample (as defined by BT5.1 spec), 16 bit sample (higher accuracy)
                    "cl_aoa_sampling_control": int('0x11', 16),
                    # bit 0   - 0x00 - default filtering, 0x01 - RAW_RF no filtering (use this mode for post process angle calculation),
                    # bit 4,5 - default: 0x10 - ONLY_ANT_1, optional: 0x20 - ONLY_ANT_2
                    "max_sample_cte": 1,
                    "cl_aoa_pattern_len": 16,
                    "cl_aoa_ant_pattern":  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
                }
    
                # Setup thread to pull out received data from devices on screen
                th_cl_aoa_parsing = threading.Thread(target=results_parsing_cl_aoa, args=(rtlsUtil.cl_aoa_results_queue,))
                th_cl_aoa_parsing.setDaemon(True)
                th_cl_aoa_parsing.start()
                print("Connectionless AOA Callback Set")
    
            # Code below demonstrates two different ways to sync with periodic advertiser for connectionless AOA
            # 1. Then user knows slave bd address and advertise SID
            # 2. Then user doesn't mind which slave to use. flow will be as follow:
            #      a. Scan for periodic advertisers
            #      b. Filter scan results for periodic interval different then 0
            #      c. Create sync
            if slave_bd_addr_list is not None:
                # Option 1
                sync_slave_list = []
                for slave_bd_addr in slave_bd_addr_list:
                    # Scan and filter for desired slave
                    scan_results = rtlsUtil.scan(scan_time_sec)
                    print(f"Scan results found: {scan_results}")
    
                    # Filter scan results for advertisers with periodic interval > 0
                    slave_list = filter_scan_results(scan_results, slave_bd_addr)
                    slave = slave_list[0]
    
                    # Synchronization timeout for the periodic advertising train Range: 0x000A to 0x4000 Time = N*10 ms Time Range
                    # For this example, the timeout value set to be 10 times bigger then the periodic advertise interval.
                    sync_timeout = int((slave['periodicAdvInt'] * 1.25) * 10)
    
                    # time out must satisfy the timeout condition. other wise sync might be lost.
                    if sync_skip != 0:
                        timeout_condition = sync_timeout >= sync_skip * slave['periodicAdvInt']
                    else:
                        timeout_condition = sync_timeout >= slave['periodicAdvInt']
    
                    if timeout_condition:
                        rtlsUtil.padv_create_sync(slave,
                                                  USE_GIVEN_ADDRESS_AND_REPORT_DISABLE,
                                                  sync_skip,
                                                  sync_timeout,
                                                  sync_cte_type)
    
                        # Scan again for sync established event. scan again if not sync established event occurred
                        sync_est_status = scan_for_sync_est(scan_time_sec, rtlsUtil, slave, num_of_scan_retry)
                        if sync_est_status:
                            print(f"Sync established with: {slave}")
                            sync_slave_list.append(slave)
                        else:
                            print(f"Failed to establish sync with: {slave}")
    
                        if padv_report:
                            # Enable periodic advertise reports and sleep for 5 seconds
                            rtlsUtil.padv_periodic_receive_enable(rtlsUtil.padv_get_sync_handle_by_slave(slave))
                            print("Periodic report enabled")
                            time.sleep(5)
    
                        if cl_aoa:
                            rtlsUtil.cl_aoa_start(cl_aoa_params, slave)
                            print("Connectionless AOA started")
    
                            ## Sleep code to see in the screen receives data from devices
                            timeout_sec = 15
                            print("Going to sleep for {} sec".format(timeout_sec))
                            timeout = time.time() + timeout_sec
                            while timeout >= time.time():
                                time.sleep(0.01)
    
                            rtlsUtil.cl_aoa_stop(cl_aoa_params, slave)
                            print("Connectionless AOA stopped")
    
                    else:
                        raise Exception("Timeout condition does not satisfied")
    
                for s_slave in sync_slave_list:
                    sync_handle = rtlsUtil.padv_get_sync_handle_by_slave(s_slave)
                    rtlsUtil.padv_terminate_sync(sync_handle)
                    print(f"Sync terminated for sync handle: {sync_handle}")
    
            else:
                # Option 2
    
                # Scan and filter for relevant slaves that supports periodic advertise
                scan_results = rtlsUtil.scan(scan_time_sec)
                print(f"Scan results found: {scan_results}")
                slave_list = filter_scan_results(scan_results)
                slave = slave_list[0]
    
                # Synchronization timeout for the periodic advertising train Range: 0x000A to 0x4000 Time = N*10 ms Time Range
                # For this example, the timeout value set to be 10 times bigger then the periodic advertise interval.
                sync_timeout = int(slave_list[0]['periodicAdvInt'] * 1.25 * 10)
    
                # time out must satisfy the timeout condition. other wise sync might be lost.
                if sync_skip != 0:
                    timeout_condition = sync_timeout >= sync_skip * slave['periodicAdvInt']
                else:
                    timeout_condition = sync_timeout >= slave['periodicAdvInt']
    
                if timeout_condition:
                    if use_adv_list:
                        print("Using advertisers list to create sync")
                        # Add all devices to periodic advertiser list
                        for slave in slave_list:
                            rtlsUtil.padv_add_device_to_periodic_adv_list(slave)
                            print(f"slave {slave} has been added to advertisers list")
    
                        # Slave information has no meaning when using advertiser list to create sync
                        rtlsUtil.padv_create_sync(dummy_slave,
                                                  USE_LIST_AND_REPORT_ENABLE,
                                                  sync_skip,
                                                  sync_timeout,
                                                  sync_cte_type)
    
                        # Scan again for sync established event - reports will be enabled automatically
                        rtlsUtil.scan(scan_time_sec)
                        time.sleep(3)
    
                        # find sync handle 0 to perform connectionless aoa
                        synced_slave = None
                        for slave in slave_list:
                            if rtlsUtil.padv_get_sync_handle_by_slave(slave) == 0:
                                synced_slave = slave
                                print(f"Sync created with first slave on advertisers list: {synced_slave}. "
                                      f"Periodic advertise report enabled automatically")
                        # If non of the slaves got synced, raise an exception
                        if synced_slave is None:
                            raise Exception("No slave from periodic advertise list got synced")
    
                        if cl_aoa:
    
                            rtlsUtil.cl_aoa_start(cl_aoa_params, synced_slave)
                            print("Connectionless AOA started")
                            time.sleep(5)
                            rtlsUtil.cl_aoa_stop(cl_aoa_params, synced_slave)
                            print("Connectionless AOA stopped")
    
                        sync_handle = rtlsUtil.padv_get_sync_handle_by_slave(synced_slave)
                        rtlsUtil.padv_terminate_sync(sync_handle)
                        print(f"Sync terminated for sync handle: {sync_handle}")
                    else:
    
                        # Create sync with first slave from slave list
                        rtlsUtil.padv_create_sync(slave_list[0],
                                                  USE_GIVEN_ADDRESS_AND_REPORT_ENABLE,
                                                  sync_skip,
                                                  sync_timeout,
                                                  sync_cte_type)
    
                        # Scan again for sync established event - reports will be enabled automatically
                        sync_est_status = scan_for_sync_est(scan_time_sec, rtlsUtil, slave_list[0], num_of_scan_retry)
                        if sync_est_status:
                            print(f"Sync established with: {slave}. Periodic advertise reports enabled automatically")
                        else:
                            print(f"Failed to establish sync with: {slave}")
                        time.sleep(3)
    
                        if cl_aoa:
                            rtlsUtil.cl_aoa_start(cl_aoa_params, slave_list[0])
                            print("Connectionless AOA started")
    
                            ## Sleep code to see in the screen receives data from devices
                            timeout_sec = 15
                            print("Going to sleep for {} sec".format(timeout_sec))
                            timeout = time.time() + timeout_sec
                            while timeout >= time.time():
                                time.sleep(0.01)
    
                            rtlsUtil.cl_aoa_stop(cl_aoa_params, slave_list[0])
                            print("Connectionless AOA stopped")
    
                        sync_handle = rtlsUtil.padv_get_sync_handle_by_slave(slave_list[0])
                        rtlsUtil.padv_terminate_sync(sync_handle)
                        print(f"Sync terminated for sync handle: {sync_handle}")
                else:
                    raise Exception("Timeout condition does not satisfied for all slaves")
    
        except RtlsUtilMasterNotFoundException as ex:
            print(f"=== ERROR: {ex} ===")
            sys.exit(1)
        except RtlsUtilNodesNotIdentifiedException as ex:
            print(f"=== ERROR: {ex} ===")
            print(ex.not_indentified_nodes)
            sys.exit(1)
        except RtlsUtilTimeoutException as ex:
            print(f"=== ERROR: {ex} ===")
            exc_type, exc_value, exc_traceback = sys.exc_info()
            traceback.print_tb(exc_traceback, file=sys.stdout)
            sys.exit(1)
        except RtlsUtilException as ex:
            print(f"=== ERROR: {ex} ===")
            exc_type, exc_value, exc_traceback = sys.exc_info()
            traceback.print_tb(exc_traceback, file=sys.stdout)
            sys.exit(1)
        except Exception as ex:
            print(f"=== ERROR: {ex} ===")
            exc_type, exc_value, exc_traceback = sys.exc_info()
            traceback.print_tb(exc_traceback, file=sys.stdout)
            sys.exit(1)
        finally:
    
            if padv_report:
                rtlsUtil.padv_event_queue.put("STOP")
                print("Try to stop Periodic Advertise event parsing thread")
                if th_padv_parsing:
                    th_padv_parsing.join()
    
            if cl_aoa:
                rtlsUtil.cl_aoa_results_queue.put("STOP")
                print("Try to stop Connectionless AOA results parsing thread")
                if th_cl_aoa_parsing:
                    th_cl_aoa_parsing.join()
    
            rtlsUtil.done()
            print("Done")
    
            # Flag to check if any data csv has been saved successfully
            data_saved = True if POST_ANALYZE_FILE_LIST else False
    
            if cl_aoa and data_saved:
                print("Executing analyze on results")
                if cl_aoa_params:
                    aoa_post_analyze(cl_aoa_params['cl_aoa_sampling_control'])
                else:
                    aoa_post_analyze()
    
    
    if __name__ == '__main__':
        main()
    

    I have verified, the pins are toggling properly:

    2. I can't enable IQ for muti-tag

    The workaround should remain usable across the SDK updates. At least, I can tell you this workaround will work for the next SDK version.
    This workaround will still be required for next SDK version. I cannot tell you when the fix will be implemented.

    b. I do not manage to reproduce your observation. I do receive IQ reports for both devices (see here: /cfs-file/__key/communityserver-discussions-components-files/538/04_5F00_12_5F00_2021_5F00_15_5F00_00_5F00_10_5F00_rtls_5F00_connectionless_5F00_aoa_5F00_example_5F00_with_5F00_rtls_5F00_util_5F00_twoTargetsFix.log)

    Have you tried a couple of times to verify this is not an unlucky event? In addition, in your case, the IQ reports seems slightly larger. Maybe it could be an issue with the UART throughput not being able to transmit data for both devices? Our SimpleLink Academy shows how to increase the UART baud rate.

    Best regards,

  • thx. Clement.

    1. ANT num must <=15

    Yes, i do the same change with you, and baud rate already change to 3Mbps.

    The main problem is that even if there is only one tag, almost can't get response of RTLS_CMD_CL_AOA_ENABLE

    I will try to find out the problem.

    thank you