int main(int argc, char **argv)
{

    /********************************************************************************************
     * ROS setup
     ********************************************************************************************/

    ros::init(argc, argv, "udp_server", ros::init_options::NoSigintHandler);
    signal(SIGINT, ctrl_C_Handler);
    ros::NodeHandle nh;
    ros::Rate loop_rate(1000); //dont forget to add looprate sleep in the end
    std::string map_frame;
    ros::param::param<std::string>("map_frame", map_frame, "map");


    /********************************************************************************************
     * YAML parsing robots
     ********************************************************************************************/
    // Sum a list of doubles from the parameter server
    std::vector<std::string> robot_list;
    nh.getParam("camera_servo_robot_list", robot_list);
    ROS_INFO_STREAM("Camera node robot number: " << robot_list.size());

    std::string robot_frame[robot_list.size()];
    int robot_id[robot_list.size()];

    for(int itr = 0; itr < robot_list.size(); itr++) {
        nh.getParam("camera_servo_params/"+robot_list[itr]+"/camera_id", robot_id[itr]);
        nh.getParam("camera_servo_params/"+robot_list[itr]+"/link_frame", robot_frame[itr]);
        ROS_INFO_STREAM(robot_frame[itr] << ", " << robot_id[itr]);
    }

    /********************************************************************************************
     * data
     ********************************************************************************************/

    tf::Transform map_to_base;
    tf::TransformBroadcaster broadcaster;
    short x, y, th, u, v, w, t;

    /********************************************************************************************
     * UDP setup
     ********************************************************************************************/

    int port;
    ros::param::param<int>("~udp_port", port, DEFAULT_PORT);
    ROS_INFO("listening to port: %d", port);

    struct sockaddr_in si_me, si_other;
     
    int s, i;
    uint slen = sizeof(si_other);
    ssize_t recv_len=0;
    char buf[BUFLEN]="";

    //create a UDP socket
    if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) // IP addressing, UDP protocol, 
    {
        ROS_ERROR("Can't create UDP socket");
    }    
    // zero out the structure
    memset((char *) &si_me, 0, sizeof(si_me));
     
    si_me.sin_family = AF_INET;
    si_me.sin_port = htons(port);
    si_me.sin_addr.s_addr = htonl(INADDR_ANY);
     
    //bind socket to port
    if( bind(s , (struct sockaddr*)&si_me, sizeof(si_me) ) == -1)
    {
        ROS_ERROR("Can't bind UDP socket");
    }


    /********************************************************************************************
     * THE LOOP
     ********************************************************************************************/

    while(!g_request_shutdown)
    {
        /**********************************************************************************
                                  BLOCKING BLOCKING BLOCKING BLOCKING                         
                                        wait to receive camera data                               
        ***********************************************************************************/
        if ((recv_len = recvfrom(s, &buf, BUFLEN, 0, (struct sockaddr *)&si_other, &slen)) == -1)
        {
            ROS_WARN("Can't receive from UDP socket");
        }

        /**********************************************************************************
                                  BLOCKING BLOCKING BLOCKING BLOCKING                         
                                        just received camera data                               
        ***********************************************************************************/
        
        ros::Time data_stamp = ros::Time::now(); //we have to transmitt a time stamp from camera not the delay
        

        //message parser
        if (buf[0]==0x7E & buf[16]==0x7E)//delimiters
        {
            for(int i=0; i<robot_list.size(); i++) {
                if (buf[1] == robot_id[i])
                {   
                    memcpy(&x,  &buf[2], 2);// X in 10^-4 meters
                    memcpy(&y,  &buf[4], 2);// Y
                    memcpy(&th, &buf[6], 2);// theta 10^-4 rad 
                    memcpy(&u,  &buf[8], 2);// Xdot in 10^-4 meters/sec
                    memcpy(&v, &buf[10], 2);// Ydot
                    memcpy(&w, &buf[12], 2);// thetaDot 10^-4 rad/sec
                    memcpy(&t, &buf[14], 2);// latency ms

                    ros::Duration data_delay( (double)t * 0.001 );
                    ros::Time camera_stamp = data_stamp - data_delay;

                    //transformation broadcast
                    map_to_base.setOrigin(tf::Vector3((double)(x *0.0001), (double)(y *0.0001), 0.0));
                    tf::Quaternion q;
                    q.setRPY(0, 0, (double)(th *0.0001));
                    map_to_base.setRotation(q);
                    broadcaster.sendTransform(tf::StampedTransform(map_to_base, camera_stamp, map_frame, robot_frame[i]));

                }
            }
        }

        //print details of the client/peer and the data received
        //ROS_INFO("Received packet from %s:%d", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
        //ROS_INFO("Receivied data: Rob_ID: %d x:%d, y:%d, th:%d, u:%d, v:%d, w:%d, t:%d\n" , buf[1], x, y, th, u, v, w, t);

        ros::spinOnce();
        //loop_rate.sleep();//consider removing as we have a blocking call in the loop       
    } 

    //will never run this because recvfrom is blocking functiuon
    close(s);
    ROS_WARN("udp port: %d of camera node Closed", port);
    return 0;
}
int main(int argc, char **argv)
{

    /********************************************************************************************
     * ROS setup
     ********************************************************************************************/

    ros::init(argc, argv, "udp_server", ros::init_options::NoSigintHandler);
    signal(SIGINT, ctrl_C_Handler);
    ros::NodeHandle nh;
    ros::Publisher pub_odom = nh.advertise<nav_msgs::Odometry>("camera/odom", 1000);
    ros::Rate loop_rate(1000); //dont forget to add looprate sleep in the end
    nav_msgs::Odometry odom;
    short x,y,th,u,v,w,t;
    int rob_id;
    ros::param::param<int>("~robot_id", rob_id, ROBOT_ID);
    ROS_INFO("Reading odometry for Robot with ID: %d", rob_id);
    std::string frame;
    ros::param::param<std::string>("~frame_id", frame, "base_link");

    //transforms
    tf::Transform map_to_base;
    tf::Transform map_to_odom;
    tf::StampedTransform base_to_odom;

    tf::TransformBroadcaster broadcaster;
    tf::TransformListener listener;

    /********************************************************************************************
     * UDP setup
     ********************************************************************************************/

    int port;
    ros::param::param<int>("~udp_port", port, DEFAULT_PORT);
    ROS_INFO("listening to port: %d", port);

    struct sockaddr_in si_me, si_other;
     
    int s, i;
    uint slen = sizeof(si_other);
    ssize_t recv_len=0;
    char buf[BUFLEN]="";

    //create a UDP socket
    if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) // IP addressing, UDP protocol, 
    {
        ROS_ERROR("Can't create UDP socket");
    }    
    // zero out the structure
    memset((char *) &si_me, 0, sizeof(si_me));
     
    si_me.sin_family = AF_INET;
    si_me.sin_port = htons(port);
    si_me.sin_addr.s_addr = htonl(INADDR_ANY);
     
    //bind socket to port
    if( bind(s , (struct sockaddr*)&si_me, sizeof(si_me) ) == -1)
    {
        ROS_ERROR("Can't bind UDP socket");
    }


    /********************************************************************************************
     * THE LOOP
     ********************************************************************************************/

    //wait for the first odom to base transformation up to 5sec
    try{
        ROS_WARN("Camera node wait for first odom to base_link Transformation");
        listener.waitForTransform(frame, "odom", ros::Time::now(), ros::Duration(5.0));
    }
    catch (tf::TransformException ex){
        ROS_ERROR("%s",ex.what());
        ros::Duration(5.0).sleep();
    }

    while(!g_request_shutdown)
    {
        /**********************************************************************************
                                  BLOCKING BLOCKING BLOCKING BLOCKING                         
                                        wait to receive camera data                               
        ***********************************************************************************/
        if ((recv_len = recvfrom(s, &buf, BUFLEN, 0, (struct sockaddr *)&si_other, &slen)) == -1)
        {
            ROS_WARN("Can't receive from UDP socket");
        }

        /**********************************************************************************
                                  BLOCKING BLOCKING BLOCKING BLOCKING                         
                                        just received camera data                               
        ***********************************************************************************/
        
        ros::Time data_stamp = ros::Time::now(); //we have to transmitt a time stamp from camera not the delay
        

        //message parser
        if (buf[0]==0x7E & buf[16]==0x7E)//delimiters
        {
            if (buf[1] == rob_id)
            {   
                memcpy(&x,  &buf[2], 2);// X in 10^-4 meters
                memcpy(&y,  &buf[4], 2);// Y
                memcpy(&th, &buf[6], 2);// theta 10^-4 rad 
                memcpy(&u,  &buf[8], 2);// Xdot in 10^-4 meters/sec
                memcpy(&v, &buf[10], 2);// Ydot
                memcpy(&w, &buf[12], 2);// thetaDot 10^-4 rad/sec
                memcpy(&t, &buf[14], 2);// latency ms

                ros::Duration data_delay( (double)t * 0.001 );
                ros::Time camera_stamp = data_stamp - data_delay;
        
                //read odom transformation at the time the foto is taken (past time)
                try{
                  listener.lookupTransform(frame, "odom", camera_stamp, base_to_odom);
                }
                catch (tf::TransformException ex){
                ROS_ERROR("%s",ex.what());
                //ros::Duration(1.0).sleep();
                }

            //Odometry broadcast
                //header
                odom.header.stamp = camera_stamp;//as soon as possible minus the latency
                odom.header.frame_id = "map";//reference frame for position
                odom.child_frame_id  = frame;//reference frame for velocity         
                //set position
                odom.pose.pose.position.x = (double)(x *0.0001);
                odom.pose.pose.position.y = (double)(y *0.0001);
                odom.pose.pose.position.z = 0.43;                
                //set orientation
                geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw((double)(th *0.0001));
                odom.pose.pose.orientation= odom_quat;
                //set velocitiy
                odom.twist.twist.linear.x = (double)(u *0.0001);
                odom.twist.twist.linear.y = (double)(v *0.0001);
                odom.twist.twist.angular.z= (double)(w *0.0001); 
                
                pub_odom.publish(odom);

                //transformation broadcast
                map_to_base.setOrigin(tf::Vector3((double)(x *0.0001), (double)(y *0.0001), 0.0));
                tf::Quaternion q;
                q.setRPY(0, 0, (double)(th *0.0001));
                map_to_base.setRotation(q);
                map_to_odom.mult(map_to_base, base_to_odom); 

                broadcaster.sendTransform(tf::StampedTransform(map_to_odom, camera_stamp, "map", "odom"));
            }
            // else
            // {
            //     ROS_INFO("different Robot id recieved. ID: %d", (int)buf[1]);
            // }
        }

        //print details of the client/peer and the data received
        //ROS_INFO("Received packet from %s:%d", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));
        //ROS_INFO("Receivied data: Rob_ID: %d x:%d, y:%d, th:%d, u:%d, v:%d, w:%d, t:%d\n" , buf[1], x, y, th, u, v, w, t);

        ros::spinOnce();
        //loop_rate.sleep();//consider removing as we have a blocking call in the loop       
    } 

    //will never run this because recvfrom is blocking functiuon
    close(s);
    ROS_WARN("udp port: %d of camera node Closed", port);
    return 0;
}