blob: 8d3d82d3d944501aa4b1777ee47496bfb0e5cc05 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2016-2017. 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 the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "HAL/AnalogGyro.h"
9
10#include <chrono>
11#include <thread>
12
13#include "AnalogInternal.h"
14#include "HAL/AnalogAccumulator.h"
15#include "HAL/AnalogInput.h"
16#include "HAL/handles/IndexedHandleResource.h"
17
18namespace {
19struct AnalogGyro {
20 HAL_AnalogInputHandle handle;
21 double voltsPerDegreePerSecond;
22 double offset;
23 int32_t center;
24};
25}
26
27static constexpr uint32_t kOversampleBits = 10;
28static constexpr uint32_t kAverageBits = 0;
29static constexpr double kSamplesPerSecond = 50.0;
30static constexpr double kCalibrationSampleTime = 5.0;
31static constexpr double kDefaultVoltsPerDegreePerSecond = 0.007;
32
33using namespace hal;
34
35static IndexedHandleResource<HAL_GyroHandle, AnalogGyro, kNumAccumulators,
36 HAL_HandleEnum::AnalogGyro>
37 analogGyroHandles;
38
39static void Wait(double seconds) {
40 if (seconds < 0.0) return;
41 std::this_thread::sleep_for(std::chrono::duration<double>(seconds));
42}
43
44extern "C" {
45HAL_GyroHandle HAL_InitializeAnalogGyro(HAL_AnalogInputHandle analogHandle,
46 int32_t* status) {
47 if (!HAL_IsAccumulatorChannel(analogHandle, status)) {
48 if (*status == 0) {
49 *status = HAL_INVALID_ACCUMULATOR_CHANNEL;
50 }
51 return HAL_kInvalidHandle;
52 }
53
54 // handle known to be correct, so no need to type check
55 int16_t channel = getHandleIndex(analogHandle);
56
57 auto handle = analogGyroHandles.Allocate(channel, status);
58
59 if (*status != 0)
60 return HAL_kInvalidHandle; // failed to allocate. Pass error back.
61
62 // Initialize port structure
63 auto gyro = analogGyroHandles.Get(handle);
64 if (gyro == nullptr) { // would only error on thread issue
65 *status = HAL_HANDLE_ERROR;
66 return HAL_kInvalidHandle;
67 }
68
69 gyro->handle = analogHandle;
70 gyro->voltsPerDegreePerSecond = 0;
71 gyro->offset = 0;
72 gyro->center = 0;
73
74 return handle;
75}
76
77void HAL_SetupAnalogGyro(HAL_GyroHandle handle, int32_t* status) {
78 auto gyro = analogGyroHandles.Get(handle);
79 if (gyro == nullptr) {
80 *status = HAL_HANDLE_ERROR;
81 return;
82 }
83
84 gyro->voltsPerDegreePerSecond = kDefaultVoltsPerDegreePerSecond;
85
86 HAL_SetAnalogAverageBits(gyro->handle, kAverageBits, status);
87 if (*status != 0) return;
88 HAL_SetAnalogOversampleBits(gyro->handle, kOversampleBits, status);
89 if (*status != 0) return;
90 double sampleRate =
91 kSamplesPerSecond * (1 << (kAverageBits + kOversampleBits));
92 HAL_SetAnalogSampleRate(sampleRate, status);
93 if (*status != 0) return;
94 Wait(0.1);
95
96 HAL_SetAnalogGyroDeadband(handle, 0.0, status);
97 if (*status != 0) return;
98}
99
100void HAL_FreeAnalogGyro(HAL_GyroHandle handle) {
101 analogGyroHandles.Free(handle);
102}
103
104void HAL_SetAnalogGyroParameters(HAL_GyroHandle handle,
105 double voltsPerDegreePerSecond, double offset,
106 int32_t center, int32_t* status) {
107 auto gyro = analogGyroHandles.Get(handle);
108 if (gyro == nullptr) {
109 *status = HAL_HANDLE_ERROR;
110 return;
111 }
112
113 gyro->voltsPerDegreePerSecond = voltsPerDegreePerSecond;
114 gyro->offset = offset;
115 gyro->center = center;
116 HAL_SetAccumulatorCenter(gyro->handle, center, status);
117}
118
119void HAL_SetAnalogGyroVoltsPerDegreePerSecond(HAL_GyroHandle handle,
120 double voltsPerDegreePerSecond,
121 int32_t* status) {
122 auto gyro = analogGyroHandles.Get(handle);
123 if (gyro == nullptr) {
124 *status = HAL_HANDLE_ERROR;
125 return;
126 }
127
128 gyro->voltsPerDegreePerSecond = voltsPerDegreePerSecond;
129}
130
131void HAL_ResetAnalogGyro(HAL_GyroHandle handle, int32_t* status) {
132 auto gyro = analogGyroHandles.Get(handle);
133 if (gyro == nullptr) {
134 *status = HAL_HANDLE_ERROR;
135 return;
136 }
137 HAL_ResetAccumulator(gyro->handle, status);
138 if (*status != 0) return;
139
140 const double sampleTime = 1.0 / HAL_GetAnalogSampleRate(status);
141 const double overSamples =
142 1 << HAL_GetAnalogOversampleBits(gyro->handle, status);
143 const double averageSamples =
144 1 << HAL_GetAnalogAverageBits(gyro->handle, status);
145 if (*status != 0) return;
146 Wait(sampleTime * overSamples * averageSamples);
147}
148
149void HAL_CalibrateAnalogGyro(HAL_GyroHandle handle, int32_t* status) {
150 auto gyro = analogGyroHandles.Get(handle);
151 if (gyro == nullptr) {
152 *status = HAL_HANDLE_ERROR;
153 return;
154 }
155
156 HAL_InitAccumulator(gyro->handle, status);
157 if (*status != 0) return;
158 Wait(kCalibrationSampleTime);
159
160 int64_t value;
161 int64_t count;
162 HAL_GetAccumulatorOutput(gyro->handle, &value, &count, status);
163 if (*status != 0) return;
164
165 gyro->center = static_cast<int32_t>(
166 static_cast<double>(value) / static_cast<double>(count) + .5);
167
168 gyro->offset = static_cast<double>(value) / static_cast<double>(count) -
169 static_cast<double>(gyro->center);
170 HAL_SetAccumulatorCenter(gyro->handle, gyro->center, status);
171 if (*status != 0) return;
172 HAL_ResetAnalogGyro(handle, status);
173}
174
175void HAL_SetAnalogGyroDeadband(HAL_GyroHandle handle, double volts,
176 int32_t* status) {
177 auto gyro = analogGyroHandles.Get(handle);
178 if (gyro == nullptr) {
179 *status = HAL_HANDLE_ERROR;
180 return;
181 }
182 int32_t deadband = static_cast<int32_t>(
183 volts * 1e9 / HAL_GetAnalogLSBWeight(gyro->handle, status) *
184 (1 << HAL_GetAnalogOversampleBits(gyro->handle, status)));
185 if (*status != 0) return;
186 HAL_SetAccumulatorDeadband(gyro->handle, deadband, status);
187}
188
189double HAL_GetAnalogGyroAngle(HAL_GyroHandle handle, int32_t* status) {
190 auto gyro = analogGyroHandles.Get(handle);
191 if (gyro == nullptr) {
192 *status = HAL_HANDLE_ERROR;
193 return 0;
194 }
195 int64_t rawValue = 0;
196 int64_t count = 0;
197 HAL_GetAccumulatorOutput(gyro->handle, &rawValue, &count, status);
198
199 int64_t value = rawValue - static_cast<int64_t>(static_cast<double>(count) *
200 gyro->offset);
201
202 double scaledValue =
203 value * 1e-9 *
204 static_cast<double>(HAL_GetAnalogLSBWeight(gyro->handle, status)) *
205 static_cast<double>(1 << HAL_GetAnalogAverageBits(gyro->handle, status)) /
206 (HAL_GetAnalogSampleRate(status) * gyro->voltsPerDegreePerSecond);
207
208 return scaledValue;
209}
210
211double HAL_GetAnalogGyroRate(HAL_GyroHandle handle, int32_t* status) {
212 auto gyro = analogGyroHandles.Get(handle);
213 if (gyro == nullptr) {
214 *status = HAL_HANDLE_ERROR;
215 return 0;
216 }
217
218 return (HAL_GetAnalogAverageValue(gyro->handle, status) -
219 (static_cast<double>(gyro->center) + gyro->offset)) *
220 1e-9 * HAL_GetAnalogLSBWeight(gyro->handle, status) /
221 ((1 << HAL_GetAnalogOversampleBits(gyro->handle, status)) *
222 gyro->voltsPerDegreePerSecond);
223}
224
225double HAL_GetAnalogGyroOffset(HAL_GyroHandle handle, int32_t* status) {
226 auto gyro = analogGyroHandles.Get(handle);
227 if (gyro == nullptr) {
228 *status = HAL_HANDLE_ERROR;
229 return 0;
230 }
231 return gyro->offset;
232}
233
234int32_t HAL_GetAnalogGyroCenter(HAL_GyroHandle handle, int32_t* status) {
235 auto gyro = analogGyroHandles.Get(handle);
236 if (gyro == nullptr) {
237 *status = HAL_HANDLE_ERROR;
238 return 0;
239 }
240 return gyro->center;
241}
242}