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