Wednesday, February 17, 2016

Precision Time Measurement to read Remote Sensor


A scenario is like this --

You got a task to figure precise round trip time of a remote device or perhaps, need to measure how much time it required to receive the sensor data at requested side.

To measure accurate round trip time from client to sensor, two different technique is mainly popular. 

First, used timespec struct (sec & nano-sec) with CLOCK_MONOTONIC marco of kernel to measure time between sending client request and receive of result from server. 

Second, fill timespec struct (sec and nano-sec) variable with network driver packet timestamp macro SIOCGSTAMPNS for sending socket and receiving socket and calculate the difference.


Example Output =>

 SHELL>$ ./pngclnt_v2 192.168.178.100  
 0.000000s : 0.019165 sec  
 Output 1  
 0.000000s : 0.020459sec  
 Output 0  
 0.000000s : 0.019314sec  
 Output 1  

The idea is client -- server scenario, which means a server will run on sensor side and wait for client will request. Once receive the request, server script will perform the task and respond back to client.



In between the above mentioned two method will execute to measure upto nano-seconds.


A server sample code [pngsrv.c] for precision time calculation is alike 

 /**  
 * @file pngsrv.c  
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
 * This Program coded with notepad++,   
 * if you use gedit set TAB Width = 4.  
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
 * This is a server program which will   
 * accept a command send from its client  
 * program execute it at local system  
 * and send back the output to client  
 * program via socket.  
 */  
 #include <stdio.h>  
 #include <unistd.h>  
 #include <stdlib.h>  
 #include <string.h>  
 #include <sys/types.h>  
 #include <sys/socket.h>  
 #include <netinet/in.h>  
 #include <arpa/inet.h>  
 #include <ifaddrs.h>  
 #include <netdb.h>  
 int main(){  
 /// declare of datatype used for socket interface [sockaddr], in for Internet.  
      struct sockaddr_in srvsock, clntsock;        /**   
                                                                       * sockaddr_in is structure contain an internet address  
                                                                       * defined in in.h include file =>  
                                                                       * struct sockaddr_in {  
                                                                       * short int     sin_family; // Address family, AF_INET = IPv4  
                                                                       * unsigned short int sin_port;  // Port number  
                                                                       * struct in_addr   sin_addr;  // Internet address  
                                                                       * };  
                                                                       */  
      struct ifaddrs *ifaddr, *ifa;                              /**   
                                                                       * derived linked list type structure from getifaddrs()  
                                                                       * function  
                                                                       * defined in <sys/types.h> and ifaddrs.h   
                                                                       */  
      socklen_t clntlen = sizeof (clntsock);                    /// size of the address  
      int sock_d, s, z, y;  
      FILE *file;  
      char host[NI_MAXHOST];                                        /**   
                                                                       * NI_MAXHOST is a macro defined in netdb.h  
                                                                       * as 1025 used by getnameinfo () function  
                                                                       */  
      char cmd_buf[32];  
      sock_d = socket (AF_INET, SOCK_DGRAM, 0);               /**   
                                                                       * the socket system call is defined as =>  
                                                                       * int socket(int domain [IPv4,IPv6, etc],   
                                                                       * int type[stream / datagram, etc], int protocol[IP/TCP/UDP]);  
                                                                       * socket returns a descriptor.  
                                                                       */  
      if (sock_d == -1){  
           perror ("Error opening socket\n");  
           exit (1);  
      }  
      srvsock.sin_family = AF_INET;                              /**   
                                                                       * sin_family is an element of sockaddr_in datatype.  
                                                                       * AF_INET = IPv4 Internet Protocol  
                                                                       */  
      srvsock.sin_port = htons(4221);                              /**   
                                                                       * sin_port is an element of sockaddr_in datatype.   
                                                                       * host_to_network_short (htons) convert  
                                                                       * host byte order to network byte order. defined in arpa/inet.h  
                                                                       */  
      if (getifaddrs (&ifaddr) == -1){                         /**  
                                                                       * get interface address information  
                                                                       * and point to defined ifaddrs pointer  
                                                                       */  
           perror ("Failed to detect Interface.\n");  
           exit (1);  
      }                                          
 /**  
 * loop to search all available interface in given system  
 * match for given interface's [i.e. wlan0] IP address.  
 */  
      for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next){  
           if (ifa->ifa_addr == NULL)  
                     continue;  
                s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in),                 
                                         host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);            
                                                                            /**  
                                                                            * getnameinfo() is used for loop up host & service name for given   
                                                                            * sockaddr. defined in sys/socket.h, netdb.h. Macro NI_NUMERICHOST  
                                                                            * return numeric form of hostname.  
                                                                            */  
                if((strcmp(ifa->ifa_name,"eth0")==0)&&  
                               (ifa->ifa_addr->sa_family==AF_INET))   
                                                                            /// comparison of interface name[wlan0] & protocol type [AF_INET = IPv4]  
                {  
                srvsock.sin_addr.s_addr = inet_addr(host);            
                                                                            /**       
                                                                            * sin_addr is an struct type element of sockaddr_int datatype.  
                                                                            * sin_addr is defined as ->  
                                                                            * struct in_addr {  
                                                                            * uint32_t    s_addr;   // address in network byte order   
                                                                            * };  
                                                                            * sin_addr.s_addr assign IPv4 address to srvsock socket.   
                                                                            * Here, Internet Host address(IPv4) been converted using inet_addr()   
                                                                            * to binary datagram in network byte order.  
                                                                            */  
                }  
      }  
      if (bind (sock_d, (struct sockaddr *) &srvsock,   
                                         sizeof(srvsock)) == -1 ){      ///     bind() assign local socket address to socket descriptor.  
                perror ("Unable to bind IP & PORT\n");  
                exit (1);  
      }  
 /// infinite while loop, wait until CTRL+C pressed or explicitly passed kill signal to process  
      while (1){  
           bzero (cmd_buf, 64);                                        /// filling zero to string cmd_buf  
           z = recvfrom (sock_d, cmd_buf, 64, 0,   
                           (struct sockaddr*)&clntsock, &clntlen);     /**   
                                                                                 * recvfrom() allow to retrieve source   
                                                                                 * address from received data [cmd_buf].  
                                                                                 * in this case, source address copied to clntsock.   
                                                                                 * recvfrom() return length of the received message  
                                                                                 * while succeed.  
                                                                                 */  
           if (z == -1){  
                perror ("Error on receiving..\n");  
                exit (1);  
           }  
           printf ("Command: %s\n", cmd_buf);  
           file = popen (cmd_buf, "r");                                   /**       
                                                                                 * popen() [FILE *popen(const char *command, const char *type);]  
                                                                                 * defined in stdio.h, creates pipe for processes.  
                                                                                 * The command string is passed to shell with -c flag  
                                                                                 * [reading from string instead std_input].  
                                                                                 * Return of this function is FILE.  
                                                                                 */       
           bzero (cmd_buf, sizeof(cmd_buf));                              /// again filling cmd_buf to zero for reuse & avoid mem overflow  
           while (fgets (cmd_buf, sizeof(cmd_buf) -1, file) != NULL){  
                                                                                 /// fgets() reads from stream and store to string  
                y = sendto (sock_d, cmd_buf, 64, 0,   
                         (struct sockaddr *)&clntsock, clntlen);       
                                                                                 /**       
                                                                                 * sending the popen command output to socket's  
                                                                                 * requested source address, see   
                                                                                 * clntsock in recvfrom()  
                                                                                 */  
                if (y == -1){  
                     perror ("Failed to send on socket..\n");  
                     exit (1);  
                }  
           }  
           sleep (1);                                                            /// delay for 1 second  
      }  
      return 0;  
 }  


 Client 1 [pngclnt_v2.c]
 /**  
 * @file pngclnt_v2.c  
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
 * This Program coded with notepad++,   
 * if you use gedit set TAB Width = 4.  
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
 * This is a client program version-2   
 * which will send a command send from  
 * its server program. and receive command  
 * output at server system sent from  
 * server program.       
 */  
 #include <time.h>  
 #include <stdio.h>  
 #include <unistd.h>  
 #include <stdlib.h>  
 #include <string.h>  
 #include <sys/types.h>  
 #include <sys/socket.h>  
 #include <netinet/in.h>  
 #include <arpa/inet.h>  
 #include <ifaddrs.h>  
 #include <netdb.h>  
 /**   
 * tic() declare timespec[def. second & nanosec] structure.  
 * and assign CLOCK_MONOTONIC to ts_start. CLOCK_MONOTONIC  
 * works as time-source linearly increasing. for calculating  
 * accurate difference between two sampling.  
 */  
 #define tic() do { struct timespec ts_start, ts_end; clock_gettime(CLOCK_MONOTONIC, &ts_start)       
 /**   
 * toc() takes CLOCK_MONOTONIC to ts_end,  
 * calculate and print the time difference  
 */  
 #define toc() clock_gettime(CLOCK_MONOTONIC, &ts_end); \  
        printf("%lfs : %lfsec\n", (double)(ts_end.tv_sec - ts_start.tv_sec), \  
                                                (double)(ts_end.tv_nsec - ts_start.tv_nsec)/1e9); } \  
        while (0)  
 int main(int argc, char *argv[]){  
 /// declare of datatype used for socket interface [sockaddr], in for Internet.  
      struct sockaddr_in srvsock, clntsock;                                        /**   
                                                                                           * sockaddr_in is structure contain  
                                                                                           * an internet address defined in  
                                                                                           * in.h include file =>  
                                                                                           * struct sockaddr_in {  
                                                                                           * short int     sin_family; // Address family,   
                                                                                                                                    //AF_INET used for IPv4  
                                                                                           * unsigned short int sin_port;  // Port number  
                                                                                           * struct in_addr   sin_addr;  // Internet address  
                                                                                           * };  
                                                                                           */  
      char cmd_buf[64] = "cat /sys/class/gpio/gpio2/value";                                             /// command to be executed to remote machine  
      char cmd_send_buf[64];  
      int sock_d, n, z, y;  
      socklen_t solen = sizeof (struct sockaddr_in);                              /// size of the address  
      sock_d = socket (AF_INET, SOCK_DGRAM, 0);                                   /**   
                                                                                           * the socket system call is defined as =>  
                                                                                           * int socket(int domain [IPv4,IPv6, etc],  
                                                                                           *                int type[stream / datagram, etc],  
                                                                                           *                int protocol[IP/TCP/UDP]);  
                                                                                           * socket returns a descriptor.  
                                                                                           */  
      if (sock_d == -1){  
           perror ("Error opening socket\n");  
           exit (1);  
      }  
      srvsock.sin_family = AF_INET;                                                  /**  
                                                                                           * sin_family is an element of sockaddr_in datatype.  
                                                                                           * AF_INET = IPv4 Internet Protocol  
                                                                                           */  
      srvsock.sin_port = htons (4221);                                             /**  
                                                                                           * sin_port is an element of sockaddr_in datatype.   
                                                                                           * host_to_network_short (htons) convert host byte order   
                                                                                           * to network byte order. defined in arpa/inet.h.  
                                                                                           */  
      if (argc != 2){  
           fprintf (stderr, "Usage:\n %s IP\n", argv[0]);  
           exit (0);  
      }  
      srvsock.sin_addr.s_addr = inet_addr(argv[1]);                              /**       
                                                                                           * sin_addr is an struct type element of  
                                                                                           * sockaddr_int datatype. sin_addr is defined as ->  
                                                                                           * struct in_addr {  
                                                                                           * uint32_t    s_addr;   // address in network byte order   
                                                                                           * };  
                                                                                           * sin_addr.s_addr assign IPv4 address to  
                                                                                           * srvsock socket. Here, argv[1], i.e. Internet Host address(IPv4)  
                                                                                           * been converted using inet_addr() to binary datagram in  
                                                                                           * network byte order.  
                                                                                           */  
 /// infinite while loop, wait until CTRL+C pressed or explicitly passed kill signal to process  
      while (1){  
           bzero (cmd_send_buf, 32);                                                  /// filling zero to string cmd_buf  
           strncpy (cmd_send_buf, cmd_buf, 64);  
           tic ();                                                                           /**  
                                                                                           * tic() called; i.e. start of  
                                                                                           * CLOCK_MONOTONIC as ts_start  
                                                                                           */  
           z = sendto (sock_d, cmd_send_buf, 64, 0,   
                          (struct sockaddr *)&srvsock, solen);                    /**   
                                                                                           * sending cmd_send_buf which consist the  
                                                                                           * command, to socket, i.e. srvsock address.  
                                                                                           */  
           if (z == -1){  
                perror ("Failed to send on socket..\n");  
                exit (1);  
           }  
           bzero (cmd_send_buf, 64);                                                  /// again filling cmd_buf to zero.  
           y = recvfrom (sock_d, cmd_send_buf, 64, 0,   
                           (struct sockaddr *)&clntsock, &solen);               /**   
                                                                                           * recvfrom() allow to retrieve source address  
                                                                                           * from received data. In this case, source  
                                                                                           * address copied to clntsock. recvfrom() return  
                                                                                           * length of the received message while succeed.  
                                                                                           */  
           if (y == -1){  
                perror ("Error on Receiving..\n");  
                exit (1);  
           }  
           toc();                                                                           /**   
                                                                                           * toc() called. end of CLOCK_MONOTONIC as ts_end,  
                                                                                           * and print elapsed time in second and nanosec.  
                                                                                           */  
           printf ("Output %s\n", cmd_send_buf);                                   /// print received data from server.  
           sleep (1);                                                                      /// delay for 1 second  
      }  
      return 0;  
 }  

Client 2 [pngclnt_v3.c]
 /**  
 * @file pngclnt_v3.c  
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
 * This Program coded with notepad++,   
 * if you use gedit set TAB Width = 4.  
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   
 * This is a client program version-3   
 * which will send a command send from its  
 * server program and receive command output  
 * at server system sent from server program.       
 *  
 */  
 #include <time.h>  
 #include <stdio.h>  
 #include <unistd.h>  
 #include <stdlib.h>  
 #include <string.h>  
 #include <sys/types.h>  
 #include <sys/socket.h>  
 #include <netinet/in.h>  
 #include <arpa/inet.h>  
 #include <ifaddrs.h>  
 #include <netdb.h>  
 int main(int argc, char *argv[]){  
 /// declare of datatype used for socket interface [sockaddr], in for Internet.  
 struct sockaddr_in srvsock, clntsock;                                        /**   
                                                                                      * sockaddr_in is structure contain  
                                                                                      * an internet address defined in  
                                                                                      * in.h include file =>  
                                                                                      * struct sockaddr_in {  
                                                                                      * short int sin_family; // Address family,   
                                                                                                                     //AF_INET used for IPv4  
                                                                                      * unsigned short int sin_port;  // Port number  
                                                                                      * struct in_addr   sin_addr;  // Internet address  
                                                                                      * };  
                                                                                      */  
      char cmd_buf[64] = "cat /sys/class/gpio/gpio2/value";                                        /// command to be executed to remote machine  
      char cmd_send_buf[64];  
      struct timespec ts_start, ts_end;                                        /**   
                                                                                      * time structure holding accurate difference  
                                                                                      * between two sampling  
                                                                                      */  
      int sock_d, n, z, y, err;  
      socklen_t solen = sizeof (struct sockaddr_in);                         /// size of the address  
      sock_d = socket (AF_INET, SOCK_DGRAM, 0);                              /**   
                                                                                      * the socket system call is defined as =>  
                                                                                      * int socket(int domain [IPv4,IPv6, etc],  
                                                                                      *                int type[stream / datagram, etc],  
                                                                                      *                int protocol[IP/TCP/UDP]);  
                                                                                      * socket returns a descriptor.  
                                                                                      */  
      if (sock_d == -1){  
           perror ("Error opening socket\n");  
           exit (1);  
      }  
      srvsock.sin_family = AF_INET;                                             /**  
                                                                                      * sin_family is an element of sockaddr_in datatype.  
                                                                                      * AF_INET = IPv4 Internet Protocol  
                                                                                      */  
      srvsock.sin_port = htons (4221);                                        /**  
                                                                                      * sin_port is an element of sockaddr_in datatype.   
                                                                                      * host_to_network_short (htons) convert host byte order   
                                                                                      * to network byte order. defined in arpa/inet.h.  
                                                                                      */  
      if (argc != 2){  
           fprintf (stderr, "Usage:\n %s IP\n", argv[0]);  
           exit (0);  
      }  
      srvsock.sin_addr.s_addr = inet_addr(argv[1]);                         /**       
                                                                                      * sin_addr is an struct type element of  
                                                                                      * sockaddr_int datatype. sin_addr is defined as ->  
                                                                                      * struct in_addr {  
                                                                                      * uint32_t    s_addr;   // address in network byte order   
                                                                                      * };  
                                                                                      * sin_addr.s_addr assign IPv4 address to  
                                                                                      * srvsock socket. Here, argv[1], i.e. Internet Host address(IPv4)  
                                                                                      * been converted using inet_addr() to binary datagram in  
                                                                                      * network byte order.  
                                                                                      */  
 /// infinite while loop, wait until CTRL+C pressed or explicitly passed kill signal to process  
      while (1){  
           bzero (cmd_send_buf, 64);                                             /// filling zero to string cmd_buf  
           strncpy (cmd_send_buf, cmd_buf, 64);  
           z = sendto (sock_d, cmd_send_buf, 64, 0,   
                          (struct sockaddr *)&srvsock, solen);               /**  
                                                                                      * sending cmd_send_buf which consist the command,  
                                                                                      * to socket, i.e. srvsock address.  
                                                                                      */  
           if (z == -1){  
                perror ("Failed to send on socket..\n");  
                exit (1);  
           }  
           err = ioctl (sock_d, SIOCGSTAMPNS, &ts_start);                    /**   
                                                                                      * ioctl() SIOCGSTAMPNS on packet's timestamp  
                                                                                      * [in nanosec] assigned by network driver and   
                                                                                      * point to address of ts_start struct.  
                                                                                      */  
           bzero (cmd_send_buf, 64);                                             /// again filling cmd_buf to zero.  
           y = recvfrom (sock_d, cmd_send_buf, 64, 0,   
                           (struct sockaddr *)&clntsock, &solen);          /**   
                                                                                      * recvfrom() allow to retrieve source address  
                                                                                      * from received data. In this case, source address  
                                                                                      * copied to clntsock. recvfrom() return length of  
                                                                                      * the received message while succeed.  
                                                                                      */  
           if (y == -1){  
                perror ("Error on Receiving..\n");  
                exit (1);  
           }  
           err = ioctl (sock_d, SIOCGSTAMPNS, &ts_end);                    /**  
                                                                                      * ioctl() SIOCGSTAMPNS on packet's timestamp  
                                                                                      * [in nanosec] assigned by network driver and  
                                                                                      * point to address of ts_end struct.  
                                                                                      */  
           printf("%lfs : %lfsec\n", (double)(ts_end.tv_sec - ts_start.tv_sec),  
                                          (double)(ts_end.tv_nsec - ts_start.tv_nsec)/1e9);   
                                                                                      /// print difference between ts_start and ts_end.  
           printf ("Output %s\n", cmd_send_buf);                              /// print received data from server.  
           sleep (1);                                                                 /// delay for 1 second  
      }  
      return 0;  
 }  




No comments:

Post a Comment