blob: 246df4bdc8c44fa910af2327e23d48a2a662146d [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 "Encoder.h"
8#include "DigitalInput.h"
9#include "NetworkCommunication/UsageReporting.h"
10#include "Resource.h"
11#include "WPIErrors.h"
12#include "LiveWindow/LiveWindow.h"
13
14static Resource *quadEncoders = NULL;
15
16/**
17 * Common initialization code for Encoders.
18 * This code allocates resources for Encoders and is common to all constructors.
19 * @param reverseDirection If true, counts down instead of up (this is all relative)
20 * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
21 * selected, then an encoder FPGA object is used and the returned counts will be 4x the encoder
22 * spec'd value since all rising and falling edges are counted. If 1X or 2X are selected then
23 * a counter object will be used and the returned value will either exactly match the spec'd count
24 * or be double (2x) the spec'd count.
25 */
26void Encoder::InitEncoder(bool reverseDirection, EncodingType encodingType)
27{
28 m_encodingType = encodingType;
29 tRioStatusCode localStatus = NiFpga_Status_Success;
30 switch (encodingType)
31 {
32 case k4X:
33 Resource::CreateResourceObject(&quadEncoders, tEncoder::kNumSystems);
34 UINT32 index = quadEncoders->Allocate("4X Encoder");
35 if (index == ~0ul)
36 {
37 CloneError(quadEncoders);
38 return;
39 }
40 if (m_aSource->StatusIsFatal())
41 {
42 CloneError(m_aSource);
43 return;
44 }
45 if (m_bSource->StatusIsFatal())
46 {
47 CloneError(m_bSource);
48 return;
49 }
50 m_index = index;
51 m_encoder = tEncoder::create(m_index, &localStatus);
52 m_encoder->writeConfig_ASource_Module(m_aSource->GetModuleForRouting(), &localStatus);
53 m_encoder->writeConfig_ASource_Channel(m_aSource->GetChannelForRouting(), &localStatus);
54 m_encoder->writeConfig_ASource_AnalogTrigger(m_aSource->GetAnalogTriggerForRouting(), &localStatus);
55 m_encoder->writeConfig_BSource_Module(m_bSource->GetModuleForRouting(), &localStatus);
56 m_encoder->writeConfig_BSource_Channel(m_bSource->GetChannelForRouting(), &localStatus);
57 m_encoder->writeConfig_BSource_AnalogTrigger(m_bSource->GetAnalogTriggerForRouting(), &localStatus);
58 m_encoder->strobeReset(&localStatus);
59 m_encoder->writeConfig_Reverse(reverseDirection, &localStatus);
60 m_encoder->writeTimerConfig_AverageSize(4, &localStatus);
61 m_counter = NULL;
62 break;
63 case k1X:
64 case k2X:
65 m_counter = new Counter(m_encodingType, m_aSource, m_bSource, reverseDirection);
66 m_index = m_counter->GetIndex();
67 break;
68 }
69 m_distancePerPulse = 1.0;
70 m_pidSource = kDistance;
71 wpi_setError(localStatus);
72
73 nUsageReporting::report(nUsageReporting::kResourceType_Encoder, m_index, encodingType);
74 LiveWindow::GetInstance()->AddSensor("Encoder", m_aSource->GetModuleForRouting(), m_aSource->GetChannelForRouting(), this);
75}
76
77/**
78 * Encoder constructor.
79 * Construct a Encoder given a and b modules and channels fully specified.
80 * @param aModuleNumber The a channel digital input module.
81 * @param aChannel The a channel digital input channel.
82 * @param bModuleNumber The b channel digital input module.
83 * @param bChannel The b channel digital input channel.
84 * @param reverseDirection represents the orientation of the encoder and inverts the output values
85 * if necessary so forward represents positive values.
86 * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
87 * selected, then an encoder FPGA object is used and the returned counts will be 4x the encoder
88 * spec'd value since all rising and falling edges are counted. If 1X or 2X are selected then
89 * a counter object will be used and the returned value will either exactly match the spec'd count
90 * or be double (2x) the spec'd count.
91 */
92Encoder::Encoder(UINT8 aModuleNumber, UINT32 aChannel,
93 UINT8 bModuleNumber, UINT32 bChannel,
94 bool reverseDirection, EncodingType encodingType) :
95 m_encoder(NULL),
96 m_counter(NULL)
97{
98 m_aSource = new DigitalInput(aModuleNumber, aChannel);
99 m_bSource = new DigitalInput(bModuleNumber, bChannel);
100 InitEncoder(reverseDirection, encodingType);
101 m_allocatedASource = true;
102 m_allocatedBSource = true;
103}
104
105/**
106 * Encoder constructor.
107 * Construct a Encoder given a and b channels assuming the default module.
108 * @param aChannel The a channel digital input channel.
109 * @param bChannel The b channel digital input channel.
110 * @param reverseDirection represents the orientation of the encoder and inverts the output values
111 * if necessary so forward represents positive values.
112 * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
113 * selected, then an encoder FPGA object is used and the returned counts will be 4x the encoder
114 * spec'd value since all rising and falling edges are counted. If 1X or 2X are selected then
115 * a counter object will be used and the returned value will either exactly match the spec'd count
116 * or be double (2x) the spec'd count.
117 */
118Encoder::Encoder(UINT32 aChannel, UINT32 bChannel, bool reverseDirection, EncodingType encodingType) :
119 m_encoder(NULL),
120 m_counter(NULL)
121{
122 m_aSource = new DigitalInput(aChannel);
123 m_bSource = new DigitalInput(bChannel);
124 InitEncoder(reverseDirection, encodingType);
125 m_allocatedASource = true;
126 m_allocatedBSource = true;
127}
128
129/**
130 * Encoder constructor.
131 * Construct a Encoder given a and b channels as digital inputs. This is used in the case
132 * where the digital inputs are shared. The Encoder class will not allocate the digital inputs
133 * and assume that they already are counted.
134 * @param aSource The source that should be used for the a channel.
135 * @param bSource the source that should be used for the b channel.
136 * @param reverseDirection represents the orientation of the encoder and inverts the output values
137 * if necessary so forward represents positive values.
138 * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
139 * selected, then an encoder FPGA object is used and the returned counts will be 4x the encoder
140 * spec'd value since all rising and falling edges are counted. If 1X or 2X are selected then
141 * a counter object will be used and the returned value will either exactly match the spec'd count
142 * or be double (2x) the spec'd count.
143 */
144Encoder::Encoder(DigitalSource *aSource, DigitalSource *bSource, bool reverseDirection, EncodingType encodingType) :
145 m_encoder(NULL),
146 m_counter(NULL)
147{
148 m_aSource = aSource;
149 m_bSource = bSource;
150 m_allocatedASource = false;
151 m_allocatedBSource = false;
152 if (m_aSource == NULL || m_bSource == NULL)
153 wpi_setWPIError(NullParameter);
154 else
155 InitEncoder(reverseDirection, encodingType);
156}
157
158/**
159 * Encoder constructor.
160 * Construct a Encoder given a and b channels as digital inputs. This is used in the case
161 * where the digital inputs are shared. The Encoder class will not allocate the digital inputs
162 * and assume that they already are counted.
163 * @param aSource The source that should be used for the a channel.
164 * @param bSource the source that should be used for the b channel.
165 * @param reverseDirection represents the orientation of the encoder and inverts the output values
166 * if necessary so forward represents positive values.
167 * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
168 * selected, then an encoder FPGA object is used and the returned counts will be 4x the encoder
169 * spec'd value since all rising and falling edges are counted. If 1X or 2X are selected then
170 * a counter object will be used and the returned value will either exactly match the spec'd count
171 * or be double (2x) the spec'd count.
172 */
173Encoder::Encoder(DigitalSource &aSource, DigitalSource &bSource, bool reverseDirection, EncodingType encodingType) :
174 m_encoder(NULL),
175 m_counter(NULL)
176{
177 m_aSource = &aSource;
178 m_bSource = &bSource;
179 m_allocatedASource = false;
180 m_allocatedBSource = false;
181 InitEncoder(reverseDirection, encodingType);
182}
183
184/**
185 * Free the resources for an Encoder.
186 * Frees the FPGA resources associated with an Encoder.
187 */
188Encoder::~Encoder()
189{
190 if (m_allocatedASource) delete m_aSource;
191 if (m_allocatedBSource) delete m_bSource;
192 if (m_counter)
193 {
194 delete m_counter;
195 }
196 else
197 {
198 quadEncoders->Free(m_index);
199 delete m_encoder;
200 }
201}
202
203/**
204 * Start the Encoder.
205 * Starts counting pulses on the Encoder device.
206 */
207void Encoder::Start()
208{
209 if (StatusIsFatal()) return;
210 if (m_counter)
211 m_counter->Start();
212 else
213 {
214 tRioStatusCode localStatus = NiFpga_Status_Success;
215 m_encoder->writeConfig_Enable(1, &localStatus);
216 wpi_setError(localStatus);
217 }
218}
219
220/**
221 * Stops counting pulses on the Encoder device. The value is not changed.
222 */
223void Encoder::Stop()
224{
225 if (StatusIsFatal()) return;
226 if (m_counter)
227 m_counter->Stop();
228 else
229 {
230 tRioStatusCode localStatus = NiFpga_Status_Success;
231 m_encoder->writeConfig_Enable(0, &localStatus);
232 wpi_setError(localStatus);
233 }
234}
235
236/**
237 * Gets the raw value from the encoder.
238 * The raw value is the actual count unscaled by the 1x, 2x, or 4x scale
239 * factor.
240 * @return Current raw count from the encoder
241 */
242INT32 Encoder::GetRaw()
243{
244 if (StatusIsFatal()) return 0;
245 INT32 value;
246 if (m_counter)
247 value = m_counter->Get();
248 else
249 {
250 tRioStatusCode localStatus = NiFpga_Status_Success;
251 value = m_encoder->readOutput_Value(&localStatus);
252 wpi_setError(localStatus);
253 }
254 return value;
255}
256
257/**
258 * Gets the current count.
259 * Returns the current count on the Encoder.
260 * This method compensates for the decoding type.
261 *
262 * @return Current count from the Encoder adjusted for the 1x, 2x, or 4x scale factor.
263 */
264INT32 Encoder::Get()
265{
266 if (StatusIsFatal()) return 0;
267 return (INT32) (GetRaw() * DecodingScaleFactor());
268}
269
270/**
271 * Reset the Encoder distance to zero.
272 * Resets the current count to zero on the encoder.
273 */
274void Encoder::Reset()
275{
276 if (StatusIsFatal()) return;
277 if (m_counter)
278 m_counter->Reset();
279 else
280 {
281 tRioStatusCode localStatus = NiFpga_Status_Success;
282 m_encoder->strobeReset(&localStatus);
283 wpi_setError(localStatus);
284 }
285}
286
287/**
288 * Returns the period of the most recent pulse.
289 * Returns the period of the most recent Encoder pulse in seconds.
290 * This method compenstates for the decoding type.
291 *
292 * @deprecated Use GetRate() in favor of this method. This returns unscaled periods and GetRate() scales using value from SetDistancePerPulse().
293 *
294 * @return Period in seconds of the most recent pulse.
295 */
296double Encoder::GetPeriod()
297{
298 if (StatusIsFatal()) return 0.0;
299 double measuredPeriod;
300 if (m_counter)
301 {
302 measuredPeriod = m_counter->GetPeriod();
303 }
304 else
305 {
306 tRioStatusCode localStatus = NiFpga_Status_Success;
307 tEncoder::tTimerOutput output = m_encoder->readTimerOutput(&localStatus);
308 double value;
309 if (output.Stalled)
310 {
311 // Return infinity
312 double zero = 0.0;
313 value = 1.0 / zero;
314 }
315 else
316 {
317 // output.Period is a fixed point number that counts by 2 (24 bits, 25 integer bits)
318 value = (double)(output.Period << 1) / (double)output.Count;
319 }
320 wpi_setError(localStatus);
321 measuredPeriod = value * 1.0e-6;
322 }
323 return measuredPeriod / DecodingScaleFactor();
324}
325
326/**
327 * Sets the maximum period for stopped detection.
328 * Sets the value that represents the maximum period of the Encoder before it will assume
329 * that the attached device is stopped. This timeout allows users to determine if the wheels or
330 * other shaft has stopped rotating.
331 * This method compensates for the decoding type.
332 *
333 * @deprecated Use SetMinRate() in favor of this method. This takes unscaled periods and SetMinRate() scales using value from SetDistancePerPulse().
334 *
335 * @param maxPeriod The maximum time between rising and falling edges before the FPGA will
336 * report the device stopped. This is expressed in seconds.
337 */
338void Encoder::SetMaxPeriod(double maxPeriod)
339{
340 if (StatusIsFatal()) return;
341 if (m_counter)
342 {
343 m_counter->SetMaxPeriod(maxPeriod * DecodingScaleFactor());
344 }
345 else
346 {
347 tRioStatusCode localStatus = NiFpga_Status_Success;
348 m_encoder->writeTimerConfig_StallPeriod((UINT32)(maxPeriod * 1.0e6 * DecodingScaleFactor()), &localStatus);
349 wpi_setError(localStatus);
350 }
351}
352
353/**
354 * Determine if the encoder is stopped.
355 * Using the MaxPeriod value, a boolean is returned that is true if the encoder is considered
356 * stopped and false if it is still moving. A stopped encoder is one where the most recent pulse
357 * width exceeds the MaxPeriod.
358 * @return True if the encoder is considered stopped.
359 */
360bool Encoder::GetStopped()
361{
362 if (StatusIsFatal()) return true;
363 if (m_counter)
364 {
365 return m_counter->GetStopped();
366 }
367 else
368 {
369 tRioStatusCode localStatus = NiFpga_Status_Success;
370 bool value = m_encoder->readTimerOutput_Stalled(&localStatus) != 0;
371 wpi_setError(localStatus);
372 return value;
373 }
374}
375
376/**
377 * The last direction the encoder value changed.
378 * @return The last direction the encoder value changed.
379 */
380bool Encoder::GetDirection()
381{
382 if (StatusIsFatal()) return false;
383 if (m_counter)
384 {
385 return m_counter->GetDirection();
386 }
387 else
388 {
389 tRioStatusCode localStatus = NiFpga_Status_Success;
390 bool value = m_encoder->readOutput_Direction(&localStatus);
391 wpi_setError(localStatus);
392 return value;
393 }
394}
395
396/**
397 * The scale needed to convert a raw counter value into a number of encoder pulses.
398 */
399double Encoder::DecodingScaleFactor()
400{
401 if (StatusIsFatal()) return 0.0;
402 switch (m_encodingType)
403 {
404 case k1X:
405 return 1.0;
406 case k2X:
407 return 0.5;
408 case k4X:
409 return 0.25;
410 default:
411 return 0.0;
412 }
413}
414
415/**
416 * Get the distance the robot has driven since the last reset.
417 *
418 * @return The distance driven since the last reset as scaled by the value from SetDistancePerPulse().
419 */
420double Encoder::GetDistance()
421{
422 if (StatusIsFatal()) return 0.0;
423 return GetRaw() * DecodingScaleFactor() * m_distancePerPulse;
424}
425
426/**
427 * Get the current rate of the encoder.
428 * Units are distance per second as scaled by the value from SetDistancePerPulse().
429 *
430 * @return The current rate of the encoder.
431 */
432double Encoder::GetRate()
433{
434 if (StatusIsFatal()) return 0.0;
435 return (m_distancePerPulse / GetPeriod());
436}
437
438/**
439 * Set the minimum rate of the device before the hardware reports it stopped.
440 *
441 * @param minRate The minimum rate. The units are in distance per second as scaled by the value from SetDistancePerPulse().
442 */
443void Encoder::SetMinRate(double minRate)
444{
445 if (StatusIsFatal()) return;
446 SetMaxPeriod(m_distancePerPulse / minRate);
447}
448
449/**
450 * Set the distance per pulse for this encoder.
451 * This sets the multiplier used to determine the distance driven based on the count value
452 * from the encoder.
453 * Do not include the decoding type in this scale. The library already compensates for the decoding type.
454 * Set this value based on the encoder's rated Pulses per Revolution and
455 * factor in gearing reductions following the encoder shaft.
456 * This distance can be in any units you like, linear or angular.
457 *
458 * @param distancePerPulse The scale factor that will be used to convert pulses to useful units.
459 */
460void Encoder::SetDistancePerPulse(double distancePerPulse)
461{
462 if (StatusIsFatal()) return;
463 m_distancePerPulse = distancePerPulse;
464}
465
466/**
467 * Set the direction sensing for this encoder.
468 * This sets the direction sensing on the encoder so that it could count in the correct
469 * software direction regardless of the mounting.
470 * @param reverseDirection true if the encoder direction should be reversed
471 */
472void Encoder::SetReverseDirection(bool reverseDirection)
473{
474 if (StatusIsFatal()) return;
475 if (m_counter)
476 {
477 m_counter->SetReverseDirection(reverseDirection);
478 }
479 else
480 {
481 tRioStatusCode localStatus = NiFpga_Status_Success;
482 m_encoder->writeConfig_Reverse(reverseDirection, &localStatus);
483 wpi_setError(localStatus);
484 }
485}
486
487/**
488 * Set which parameter of the encoder you are using as a process control variable.
489 *
490 * @param pidSource An enum to select the parameter.
491 */
492void Encoder::SetPIDSourceParameter(PIDSourceParameter pidSource)
493{
494 if (StatusIsFatal()) return;
495 m_pidSource = pidSource;
496}
497
498/**
499 * Implement the PIDSource interface.
500 *
501 * @return The current value of the selected source parameter.
502 */
503double Encoder::PIDGet()
504{
505 if (StatusIsFatal()) return 0.0;
506 switch (m_pidSource)
507 {
508 case kDistance:
509 return GetDistance();
510 case kRate:
511 return GetRate();
512 default:
513 return 0.0;
514 }
515}
516
517void Encoder::UpdateTable() {
518 if (m_table != NULL) {
519 m_table->PutNumber("Speed", GetRate());
520 m_table->PutNumber("Distance", GetDistance());
521 m_table->PutNumber("Distance per Tick", m_distancePerPulse);
522 }
523}
524
525void Encoder::StartLiveWindowMode() {
526
527}
528
529void Encoder::StopLiveWindowMode() {
530
531}
532
533std::string Encoder::GetSmartDashboardType() {
534 if (m_encodingType == k4X)
535 return "Quadrature Encoder";
536 else
537 return "Encoder";
538}
539
540void Encoder::InitTable(ITable *subTable) {
541 m_table = subTable;
542 UpdateTable();
543}
544
545ITable * Encoder::GetTable() {
546 return m_table;
547}
548