blob: fd58088c67d0f19e547e57c23d4d14f3cb940e13 [file] [log] [blame]
jerrymf1579332013-02-07 01:56:28 +00001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
5/*----------------------------------------------------------------------------*/
6
7#include "Timer.h"
8
9#include <sysLib.h> // for sysClkRateGet
10#include <time.h>
11#include <usrLib.h> // for taskDelay
12
13#include "Synchronized.h"
14#include "Utility.h"
15
16/**
17 * Pause the task for a specified time.
18 *
19 * Pause the execution of the program for a specified period of time given in seconds.
20 * Motors will continue to run at their last assigned values, and sensors will continue to
21 * update. Only the task containing the wait will pause until the wait time is expired.
22 *
23 * @param seconds Length of time to pause, in seconds.
24 */
25void Wait(double seconds)
26{
27 if (seconds < 0.0) return;
28 taskDelay((INT32)((double)sysClkRateGet() * seconds));
29}
30
31/*
32 * Return the FPGA system clock time in seconds.
33 * This is deprecated and just forwards to Timer::GetFPGATimestamp().
34 * @returns Robot running time in seconds.
35 */
36double GetClock()
37{
38 return Timer::GetFPGATimestamp();
39}
40
41/**
42 * @brief Gives real-time clock system time with nanosecond resolution
43 * @return The time, just in case you want the robot to start autonomous at 8pm on Saturday.
44*/
45double GetTime()
46{
47 struct timespec tp;
48
49 clock_gettime(CLOCK_REALTIME,&tp);
50 double realTime = (double)tp.tv_sec + (double)((double)tp.tv_nsec*1e-9);
51
52 return (realTime);
53}
54
55/**
56 * Create a new timer object.
57 *
58 * Create a new timer object and reset the time to zero. The timer is initially not running and
59 * must be started.
60 */
61Timer::Timer()
62 : m_startTime (0.0)
63 , m_accumulatedTime (0.0)
64 , m_running (false)
65 , m_semaphore (0)
66{
67 //Creates a semaphore to control access to critical regions.
68 //Initially 'open'
69 m_semaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
70 Reset();
71}
72
73Timer::~Timer()
74{
75 semDelete(m_semaphore);
76}
77
78/**
79 * Get the current time from the timer. If the clock is running it is derived from
80 * the current system clock the start time stored in the timer class. If the clock
81 * is not running, then return the time when it was last stopped.
82 *
83 * @return unsigned Current time value for this timer in seconds
84 */
85double Timer::Get()
86{
87 double result;
88 double currentTime = GetFPGATimestamp();
89
90 Synchronized sync(m_semaphore);
91 if(m_running)
92 {
93 // This math won't work if the timer rolled over (71 minutes after boot).
94 // TODO: Check for it and compensate.
95 result = (currentTime - m_startTime) + m_accumulatedTime;
96 }
97 else
98 {
99 result = m_accumulatedTime;
100 }
101
102 return result;
103}
104
105/**
106 * Reset the timer by setting the time to 0.
107 *
108 * Make the timer startTime the current time so new requests will be relative to now
109 */
110void Timer::Reset()
111{
112 Synchronized sync(m_semaphore);
113 m_accumulatedTime = 0;
114 m_startTime = GetFPGATimestamp();
115}
116
117/**
118 * Start the timer running.
119 * Just set the running flag to true indicating that all time requests should be
120 * relative to the system clock.
121 */
122void Timer::Start()
123{
124 Synchronized sync(m_semaphore);
125 if (!m_running)
126 {
127 m_startTime = GetFPGATimestamp();
128 m_running = true;
129 }
130}
131
132/**
133 * Stop the timer.
134 * This computes the time as of now and clears the running flag, causing all
135 * subsequent time requests to be read from the accumulated time rather than
136 * looking at the system clock.
137 */
138void Timer::Stop()
139{
140 double temp = Get();
141
142 Synchronized sync(m_semaphore);
143 if (m_running)
144 {
145 m_accumulatedTime += temp;
146 m_running = false;
147 }
148}
149
150/**
151 * Check if the period specified has passed and if it has, advance the start
152 * time by that period. This is useful to decide if it's time to do periodic
153 * work without drifting later by the time it took to get around to checking.
154 *
155 * @param period The period to check for (in seconds).
156 * @return If the period has passed.
157 */
158bool Timer::HasPeriodPassed(double period)
159{
160 if (Get() > period)
161 {
162 Synchronized sync(m_semaphore);
163 // Advance the start time by the period.
164 // Don't set it to the current time... we want to avoid drift.
165 m_startTime += period;
166 return true;
167 }
168 return false;
169}
170
171/*
172 * Return the FPGA system clock time in seconds.
173 *
174 * Return the time from the FPGA hardware clock in seconds since the FPGA
175 * started.
176 * Rolls over after 71 minutes.
177 * @returns Robot running time in seconds.
178 */
179double Timer::GetFPGATimestamp()
180{
181 // FPGA returns the timestamp in microseconds
182 // Call the helper GetFPGATime() in Utility.cpp
183 return GetFPGATime() * 1.0e-6;
184}
185
186// Internal function that reads the PPC timestamp counter.
187extern "C"
188{
189 UINT32 niTimestamp32(void);
190 UINT64 niTimestamp64(void);
191}
192
193/*
194 * Return the PowerPC timestamp since boot in seconds.
195 *
196 * This is lower overhead than GetFPGATimestamp() but not synchronized with other FPGA timestamps.
197 * @returns Robot running time in seconds.
198 */
199double Timer::GetPPCTimestamp()
200{
201 // PPC system clock is 33MHz
202 return niTimestamp64() / 33.0e6;
203}