How to parse position data emitted by DWUSB GUI?

As of DWUSB GUI 0.5.0.1, our LCM compatible format is set to forward different data over different “channels”, where each “channel” is defined by a short character string. All of the data sent over these different “channels” is forwarded over the same multicast IP and port address, but the different “channels” allow different types of data to be identified and properly parsed out upon message reception. For example, the positions of any tags are sent over the “P3” channel, and the positions of all anchors and the master are sent over the “P3RPT” channel. In this way, the host application can forward any calculated tag positions individually over the “P3” channel as it computes their positions. Meanwhile, the anchors and master positions, which are all stationary, are forwarded all together over the “P3RPT” channel at fixed intervals.

In our uwbtalk_howto.txt file (located in the following archive: http://www.ciholas.com/downloads/dwusb/uwbtalk.tar.gz), we provide an example of how to properly parse the data coming over these separate “channels”. In this example, you will see a POS3_receive_callback function is subscribed to the UWBTALK_CHAN_POS3 channel (which is just a macro for the “P3” channel), and a POS3_rpt_receive_callback function is subscribed to the UWBTALK_CHAN_POS3_RPT channel (which is just a macro for the “P3RPT” channel). We require a subscription of a separate function for each channel because the data being sent over each channel has a unique format.

In response to this post, some people have requested an explanation of the parameters used in the POS3_receive_callback and POS3_rpt_receive_callback functions in the uwbtalk_howto.txt file (located in the following archive: http://www.ciholas.com/downloads/dwusb/uwbtalk.tar.gz).

Let’s start with the POS3_receive_callback. If you look at our example, you can see all of
the parameters used by this function in the following function declaration:
void POS3_receive_callback(const lcm_recv_buf_t *rbuf, const char *channel, const uwbtalk_pos3_t *pos3, void *user)

First, you have rbuf, which is the receive buffer containing the incoming data that is coming over the channel to which this function is subscribed. This parameter is not particularly useful as it keeps the data as an array of bytes, but one of the other parameters actually contains exactly the same data already properly formatted in the format laid out in the uwbtalk.lcm file.

Second, you have channel, which is simply the character string identifying the channel to which this function is subscribed. In this case, the channel would be “P3”. This parameter is also not particularly useful as you will have been the one to subscribe this function to this channel in the first place, so you will already know which channel it is.

Third, you have pos3. This is a pointer to the incoming data after it has been properly formatted into the structure laid out in the uwbtalk.lcm file. In this particular case, it is the uwbtalk_pos3_t structure, which has the following format:
struct pos3_t
{
int64_t serial_num;
int16_t id;
float x;
float y;
float z;
float quality;
int16_t battery_voltage;
}

The pos3 parameter will probably be the most useful piece of data in this function as it is the structure actually containing all the data that was sent over our multicast UDP connection.

Finally, you have the user, which is intended to function as the pointer to the class that actually performs the computations on the data in the third parameter. This pointer is actually provided by the user whenever they
subscribe to the channel. In our example in the uwbtalk.howto.txt, you can see that we assign the pointer to “this” to be our “user”:
uwbtalk_pos3_t_subscribe(mLcm, UWBTALK_CHAN_POS3, POS3_receive_callback, this);

The POS3_rpt_receive_callback has the same basic parameters as the POS3_receive_callback. The only difference is that instead of receiving data in the uwbtalk_pos3_t format, the POS3_rpt_receive_callback function receives data that is parsed into the uwbtalk_pos3_rpt_t structure, which has the following format:
struct pos3_rpt_t
{
int64_t timestamp;
int16_t id;
int16_t num_targets;
pos3_t target[num_targets];
}

As you can see, the uwbtalk_pos3_rpt_t structure is basically just an array of uwbtalk_pos3_t structures.

Can you provide a simple Java code or C code to retrieve the position and anchor data through lcm.?? Or any code structure which is more self explanatory than the uwbtalk_howto.txt file.??

The following is the code from a sample main.c file that prints out the fields in the position data coming over LCM:

 --- BEGINNING OF CODE ---

 #include < stdio.h>
 #include < string.h> 
 #include < inttypes.h>
 #include < lcm/lcm.h>
 #include "uwbtalk/uwbtalk.h"

 #define DEFAULT_LCM_PORT    7667

void POS3_receive_callback(const lcm_recv_buf_t *rbuf, const char *channel, const uwbtalk_pos3_t *pos3, void *user);
void POS3_rpt_receive_callback(const lcm_recv_buf_t *rbuf, const char *channel, const uwbtalk_pos3_rpt_t *pos3_rpt, void *user);

int main(int n_args, char* args[]) {
    int port = 0;

    // If the user supplies a port number, use it
    if (n_args == 2) {
        port = atoi(args[1]);
    }

    // If the port is 0, use the default lcm port
    if (port == 0) {
        port = DEFAULT_LCM_PORT;
    }


    lcm_t           *mLcm;
    uwbtalk_pos3_t_subscription_t       *mPos3Sub;
    uwbtalk_pos3_rpt_t_subscription_t   *mPos3RptSub;

    // Format the lcm url
    char _lcm_url[100];
    sprintf(_lcm_url, "udpm://239.255.76.67:%d?ttl=1", port);

    //STARTUP:
    mLcm = lcm_create(_lcm_url);
    if(!mLcm) {
        // error
        printf("Error initializing lcm!!\n");
        fflush(stdout);
    }

    // establish subscriptions:
    if (mLcm) {
        mPos3Sub =    uwbtalk_pos3_t_subscribe(mLcm, UWBTALK_CHAN_POS3, POS3_receive_callback, NULL);
        mPos3RptSub = uwbtalk_pos3_rpt_t_subscribe(mLcm, UWBTALK_CHAN_POS3_RPT,POS3_rpt_receive_callback, NULL);
        
        int busy = 1;
        
        // Handle lcm messages until we stop receiving
        do { 
             // handle messages over lcm with a 100 ms timeout
             busy = lcm_handle_timeout(mLcm, 100);
        } while (busy);
   
        // Shutdown LCM
        if (mPos3Sub) uwbtalk_pos3_t_unsubscribe(mLcm, mPos3Sub);
        if (mPos3RptSub) uwbtalk_pos3_rpt_t_unsubscribe(mLcm, mPos3RptSub);
        lcm_destroy(mLcm);
    }

    printf("Exiting...\n");
    return 0;
}

void POS3_receive_callback(const lcm_recv_buf_t *rbuf, const char *channel, const uwbtalk_pos3_t *pos3, void *user) {
    printf("Rx pos3 over lcm with id %hd...\n", pos3->id);    
    // process the received data....    
    printf("\t Serial: %08"PRIX64"\n", pos3->serial_num);
    printf("\t     ID: %hd\n", pos3->id);
    printf("\t      X: %f\n", pos3->x);
    printf("\t      Y: %f\n", pos3->y);
    printf("\t      Z: %f\n", pos3->z);
    printf("\tQuality: %f\n", pos3->quality);
    // Note: Battery voltage always reports 0, except on custom engineered boards
    printf("\tBattery: %hd\n", pos3->battery_voltage);
}

void POS3_rpt_receive_callback(const lcm_recv_buf_t *rbuf, const char *channel, const uwbtalk_pos3_rpt_t *pos3_rpt, void *user) {
        printf("Rx pos3 report over lcm with id %hd...\n", pos3_rpt->id);
        printf("\ttimestamp: %"PRId64"\n\tnum_targets: %hd\n", pos3_rpt->timestamp, pos3_rpt->num_targets);
        // process the received data....    
        int i;
        for (i = 0; i < pos3_rpt->num_targets; i++) {
            printf("\t\t Target Serial: %08"PRIX64"\n", pos3_rpt->target[i].serial_num);
            printf("\t\t     Target ID: %"PRId16"\n", pos3_rpt->target[i].id);
            printf("\t\t      Target X: %f\n", pos3_rpt->target[i].x);
            printf("\t\t      Target Y: %f\n", pos3_rpt->target[i].y);
            printf("\t\t      Target Z: %f\n", pos3_rpt->target[i].z);
            printf("\t\tTarget Quality: %f\n", pos3_rpt->target[i].quality);
            // Note: Battery voltage always reports 0, except on custom engineered boards
            printf("\t\tTarget Battery: %"PRId16"\n", pos3_rpt->target[i].battery_voltage);
        }
}

 --- END OF CODE ---

The make command you will need to compile this code sample is as follows:

gcc main.c uwbtalk/uwbtalk_pos3_t.c uwbtalk/uwbtalk_pos3_rpt_t.c -o pos3_example -pthread -L/usr/local/lib -llcm -lgthread-2.0 -lglib-2.0 -I./

Do note that the uwbtalk folder containing all of the uwbtalk definition files will need to be located in the same directory as your main.c file.

For more examples of using LCM, you can also refer to the LCM website, which is located at https://lcm-proj.github.io.

Can you a suggest a solution to my question in another topic (Setup of the RTLS system from scratch based on few constraints) on the class files of other channel datatypes??

Continuing the discussion from Setup of the RTLS system from scratch based on few constraints: