first show lad
[rb-clock.git] / php / time2.php
1 <?php
2 session_start();
3 $please_wait = '';
4 $last = time();
5 if(isset($_SESSION['last'])) {
6 $last = $_SESSION['last'];
7 }
8 else {
9 $_SESSION['last'] = $last;
10 }
11 //wrap the whole thing in a test for the last hit-time on the page, to avoid abusing NTP servers
12 if (time() - $last < 1) {
13   $wait = time() - $last;
14   $please_wait = 'Request limit exceeded, please wait ' . (15 - $wait) . ' s.';
15   $server = $vn_response = $mode_response = $stratum_response = $remote_originate = $remote_received
16   = $remote_received = $remote_transmitted = $delay_ms = $ntp_time_formatted = $server_time_formatted = $please_wait;
17 }
18 else {
19   $_SESSION['last'] = time();
20
21   $bit_max = 4294967296;
22   $epoch_convert = 2208988800;
23   $vn = 3;
24   
25   $servers = array('localhost');
26   $server_count = count($servers);
27   
28   //see rfc5905, page 20
29   //first byte
30   //LI (leap indicator), a 2-bit integer. 00 for 'no warning'
31   $header = '00';
32   //VN (version number), a 3-bit integer.  011 for version 3
33   $header .= sprintf('%03d',decbin($vn));
34   //Mode (association mode), a 3-bit integer. 011 for 'client'
35   $header .= '011';
36   
37   //echo bindec($header);    
38       
39   //construct the packet header, byte 1
40   $request_packet = chr(bindec($header));
41   
42   //we'll use a for loop to try additional servers should one fail to respond
43   $i = 0;
44   for($i; $i < $server_count; $i++) {
45     $socket = @fsockopen('udp://'.$servers[$i], 123, $err_no, $err_str,1);
46     if ($socket) {
47       
48       //add nulls to position 11 (the transmit timestamp, later to be returned as originate)
49       //10 lots of 32 bits
50       for ($j=1; $j<40; $j++) {
51         $request_packet .= chr(0x0);
52       }
53       
54       //the time our packet is sent from our server (returns a string in the form 'msec sec')
55       $local_sent_explode = explode(' ',microtime());
56       $local_sent = $local_sent_explode[1] + $local_sent_explode[0];
57       
58       //add 70 years to convert unix to ntp epoch
59       $originate_seconds = $local_sent_explode[1] + $epoch_convert;
60         
61       //convert the float given by microtime to a fraction of 32 bits
62       $originate_fractional = round($local_sent_explode[0] * $bit_max);
63         
64       //pad fractional seconds to 32-bit length
65       $originate_fractional = sprintf('%010d',$originate_fractional);
66         
67       //pack to big endian binary string
68       $packed_seconds = pack('N', $originate_seconds);
69       $packed_fractional = pack("N", $originate_fractional);
70   
71       //add the packed transmit timestamp
72       $request_packet .= $packed_seconds;
73       $request_packet .= $packed_fractional;
74       
75       if (fwrite($socket, $request_packet)) {
76         $data = NULL;
77         stream_set_timeout($socket, 1);
78         
79         $response = fread($socket, 48);
80         
81         //the time the response was received
82         $local_received = microtime(true);
83         
84         //echo 'response was: '.strlen($response).$response;
85       }
86       fclose($socket);
87       
88       if (strlen($response) == 48) {
89         //the response was of the right length, assume it's valid and break out of the loop
90         break;
91       }
92       else {
93         if ($i == $server_count-1) {
94           //this was the last server on the list, so give up
95           die('unable to establish a connection');
96         }
97       }
98     }
99     else {
100       if ($i == $server_count-1) {
101         //this was the last server on the list, so give up
102         die('unable to establish a connection');
103       }
104     }
105   }
106   
107   //unpack the response to unsiged lonng for calculations
108   $unpack0 = unpack("N12", $response);
109   //print_r($unpack0);
110   
111   //present as a decimal number
112   $remote_originate_seconds = sprintf('%u', $unpack0[7])-$epoch_convert;
113   $remote_received_seconds = sprintf('%u', $unpack0[9])-$epoch_convert;
114   $remote_transmitted_seconds = sprintf('%u', $unpack0[11])-$epoch_convert;
115   
116   $remote_originate_fraction = sprintf('%u', $unpack0[8]) / $bit_max;
117   $remote_received_fraction = sprintf('%u', $unpack0[10]) / $bit_max;
118   $remote_transmitted_fraction = sprintf('%u', $unpack0[12]) / $bit_max;
119   
120   $remote_originate = $remote_originate_seconds + $remote_originate_fraction;
121   $remote_received = $remote_received_seconds + $remote_received_fraction;
122   $remote_transmitted = $remote_transmitted_seconds + $remote_transmitted_fraction;
123   
124   //unpack to ascii characters for the header response
125   $unpack1 = unpack("C12", $response);
126   //print_r($unpack1);
127   
128   //echo 'byte 1: ' . $unpack1[1] . ' | ';
129   
130   //the header response in binary (base 2)
131   $header_response =  base_convert($unpack1[1], 10, 2);
132   
133   //pad with zeros to 1 byte (8 bits)
134   $header_response = sprintf('%08d',$header_response);
135   
136   //Mode (the last 3 bits of the first byte), converting to decimal for humans;
137   $mode_response = bindec(substr($header_response, -3));
138   
139   //VN
140   $vn_response = bindec(substr($header_response, -6, 3));
141   
142   //the header stratum response in binary (base 2)
143   $stratum_response =  base_convert($unpack1[2], 10, 2);
144   $stratum_response = bindec($stratum_response);
145   //echo 'stratum: ' . bindec($stratum_response);
146   
147   //calculations assume a symmetrical delay, fixed point would give more accuracy
148   $delay = (($local_received - $local_sent) / 2)  - ($remote_transmitted - $remote_received);
149   $delay_ms = round($delay * 1000) . ' ms';
150   //echo 'delay: ' . $delay * 1000 . 'ms';
151   
152   $server = $servers[$i];
153   
154   $ntp_time =  $remote_transmitted - $delay;
155   $ntp_time_explode = explode('.',$ntp_time);
156   
157   $ntp_time_formatted = date('Y-m-d H:i:s', $ntp_time_explode[0]).'.'.$ntp_time_explode[1];
158   
159   //compare with the current server time
160   $server_time =  microtime();
161   $server_time_explode = explode(' ', $server_time);
162   $server_time_micro = round($server_time_explode[0],4);
163   
164   $server_time_formatted = date('Y-m-d H:i:s', time()) .'.'. substr($server_time_micro,2);
165
166 }
167 ?>
168
169 <!doctype html>
170 <html lang="en">
171 <head>
172 <meta charset="utf-8">
173 <title></title>
174 <meta name="description" content="">
175 <meta name="author" content="">
176 <style type="text/css">
177 td{
178     width: 160px; height: 20px;
179     padding: 4px;
180     border: 1px solid #000;
181     font-size: 12px;
182 }
183 .ntp_response{
184     width: 240px;
185 }
186 </style>
187 </head>
188 <body onload="update_time();">
189 <table border="0">
190 <tr>
191 <td>Server:
192 <td class="ntp_response"><?php echo $server;?></td>
193 </tr>
194 <tr>
195 <td>VN (version number):</td>
196 <td class="ntp_response"><?php echo $vn_response;?></td>
197 </tr>
198 <tr>
199 <td>Mode:</td>
200 <td class="ntp_response"><?php echo $mode_response;?></td>
201 </tr>
202 <tr>
203 <td>Stratum:</td>
204 <td class="ntp_response"><?php echo $stratum_response;?></td>
205 </tr>
206 <tr>
207 <td>Origin time:</td>
208 <td class="ntp_response"><?php echo $remote_originate;?></td>
209 </tr>
210 <td>Received:</td>
211 <td class="ntp_response"><?php echo $remote_received;?></td>
212 </tr>
213 <td>Transmitted:</td>
214 <td class="ntp_response"><?php echo $remote_transmitted;?></td>
215 </tr>
216 <td>Delay:</td>
217 <td class="ntp_response"><?php echo $delay_ms;?></td>
218 </tr>
219 <td>NTP time:</td>
220 <td class="ntp_response"><?php echo $ntp_time_formatted;?></td>
221 </tr>
222 <td>Server time:</td>
223 <td class="ntp_response"><?php echo $server_time_formatted;?></td>
224 </tr>
225 </table>
226
227 <div id="updatetime"></div>
228 <script type="text/javascript">
229 var utcSeconds = parseInt(<?php echo $remote_received;?>,10);
230 var date = new Date(0);
231 date.setUTCSeconds(utcSeconds);
232
233 function update_time() {
234   date.setSeconds(date.getSeconds() + 1);
235   document.getElementById("updatetime").innerHTML = (date);
236   setTimeout("update_time()", 1000);
237 }
238
239 </script>
240 </body>
241 </html>