00001 /* 00002 * Copyright (C) 2011 Google Inc. 00003 * 00004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 00005 * use this file except in compliance with the License. You may obtain a copy of 00006 * the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 00012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 00013 * License for the specific language governing permissions and limitations under 00014 * the License. 00015 */ 00016 00017 package org.ros.time; 00018 00019 import com.google.common.base.Preconditions; 00020 import com.google.common.collect.Lists; 00021 00022 import org.apache.commons.logging.Log; 00023 import org.apache.commons.logging.LogFactory; 00024 import org.apache.commons.net.ntp.NTPUDPClient; 00025 import org.apache.commons.net.ntp.TimeInfo; 00026 import org.ros.math.CollectionMath; 00027 import org.ros.message.Duration; 00028 import org.ros.message.Time; 00029 00030 import java.io.IOException; 00031 import java.net.InetAddress; 00032 import java.util.List; 00033 import java.util.concurrent.ScheduledExecutorService; 00034 import java.util.concurrent.ScheduledFuture; 00035 import java.util.concurrent.TimeUnit; 00036 00042 public class NtpTimeProvider implements TimeProvider { 00043 00044 private static final boolean DEBUG = false; 00045 private static final Log log = LogFactory.getLog(NtpTimeProvider.class); 00046 00047 private static final int SAMPLE_SIZE = 11; 00048 00049 private final InetAddress host; 00050 private final ScheduledExecutorService scheduledExecutorService; 00051 private final WallTimeProvider wallTimeProvider; 00052 private final NTPUDPClient ntpClient; 00053 00054 private long offset; 00055 private ScheduledFuture<?> scheduledFuture; 00056 00061 public NtpTimeProvider(InetAddress host, ScheduledExecutorService scheduledExecutorService) { 00062 this.host = host; 00063 this.scheduledExecutorService = scheduledExecutorService; 00064 wallTimeProvider = new WallTimeProvider(); 00065 ntpClient = new NTPUDPClient(); 00066 offset = 0; 00067 scheduledFuture = null; 00068 } 00069 00075 public void updateTime() throws IOException { 00076 List<Long> offsets = Lists.newArrayList(); 00077 for (int i = 0; i < SAMPLE_SIZE; i++) { 00078 offsets.add(computeOffset()); 00079 } 00080 offset = CollectionMath.median(offsets); 00081 log.info(String.format("NTP time offset: %d ms", offset)); 00082 } 00083 00084 private long computeOffset() throws IOException { 00085 if (DEBUG) { 00086 log.info("Updating time offset from NTP server: " + host.getHostName()); 00087 } 00088 TimeInfo time; 00089 try { 00090 time = ntpClient.getTime(host); 00091 } catch (IOException e) { 00092 log.error("Failed to read time from NTP server: " + host.getHostName(), e); 00093 throw e; 00094 } 00095 time.computeDetails(); 00096 return time.getOffset(); 00097 } 00098 00114 public void startPeriodicUpdates(long period, TimeUnit unit) { 00115 scheduledFuture = 00116 scheduledExecutorService.scheduleAtFixedRate(new Runnable() { 00117 @Override 00118 public void run() { 00119 try { 00120 updateTime(); 00121 } catch (IOException e) { 00122 log.error("Periodic NTP update failed.", e); 00123 } 00124 } 00125 }, 0, period, unit); 00126 } 00127 00131 public void stopPeriodicUpdates() { 00132 Preconditions.checkNotNull(scheduledFuture); 00133 scheduledFuture.cancel(true); 00134 scheduledFuture = null; 00135 } 00136 00137 @Override 00138 public Time getCurrentTime() { 00139 Time currentTime = wallTimeProvider.getCurrentTime(); 00140 return currentTime.add(Duration.fromMillis(offset)); 00141 } 00142 }