jerrym | f157933 | 2013-02-07 01:56:28 +0000 | [diff] [blame] | 1 | /*----------------------------------------------------------------------------*/
|
| 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 | */
|
| 25 | void 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 | */
|
| 36 | double 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 | */
|
| 45 | double 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 | */
|
| 61 | Timer::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 |
|
| 73 | Timer::~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 | */
|
| 85 | double 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 | */
|
| 110 | void 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 | */
|
| 122 | void 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 | */
|
| 138 | void 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 | */
|
| 158 | bool 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 | */
|
| 179 | double 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.
|
| 187 | extern "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 | */
|
| 199 | double Timer::GetPPCTimestamp()
|
| 200 | {
|
| 201 | // PPC system clock is 33MHz
|
| 202 | return niTimestamp64() / 33.0e6;
|
| 203 | }
|