001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2012. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj;
009
010import java.nio.ByteBuffer;
011import java.nio.ByteOrder;
012
013import edu.wpi.first.wpilibj.AnalogTriggerOutput.AnalogTriggerType;
014import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
015import edu.wpi.first.wpilibj.communication.UsageReporting;
016import edu.wpi.first.wpilibj.hal.CounterJNI;
017import edu.wpi.first.wpilibj.hal.HALUtil;
018import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
019import edu.wpi.first.wpilibj.tables.ITable;
020import edu.wpi.first.wpilibj.util.BoundaryException;
021
022/**
023 * Class for counting the number of ticks on a digital input channel. This is a
024 * general purpose class for counting repetitive events. It can return the
025 * number of counts, the period of the most recent cycle, and detect when the
026 * signal being counted has stopped by supplying a maximum cycle time.
027 *
028 * All counters will immediately start counting - reset() them if you need them
029 * to be zeroed before use.
030 */
031public class Counter extends SensorBase implements CounterBase,
032                LiveWindowSendable, PIDSource {
033
034        /**
035         * Mode determines how and what the counter counts
036         */
037        public static enum Mode {
038                /**
039                 * mode: two pulse
040                 */
041                kTwoPulse(0),
042                /**
043                 * mode: semi period
044                 */
045                kSemiperiod(1),
046                /**
047                 * mode: pulse length
048                 */
049                kPulseLength(2),
050                /**
051                 * mode: external direction
052                 */
053                kExternalDirection(3);
054
055                /**
056                 * The integer value representing this enumeration
057                 */
058                public final int value;
059                private Mode(int value) {
060                        this.value = value;
061                }
062        }
063
064        private DigitalSource m_upSource; // /< What makes the counter count up.
065        private DigitalSource m_downSource; // /< What makes the counter count down.
066        private boolean m_allocatedUpSource;
067        private boolean m_allocatedDownSource;
068        private ByteBuffer m_counter; // /< The FPGA counter object.
069        private int m_index; // /< The index of this counter.
070        private PIDSourceParameter m_pidSource;
071        private double m_distancePerPulse; // distance of travel for each tick
072
073        private void initCounter(final Mode mode) {
074                ByteBuffer status = ByteBuffer.allocateDirect(4);
075                // set the byte order
076                status.order(ByteOrder.LITTLE_ENDIAN);
077                ByteBuffer index = ByteBuffer.allocateDirect(4);
078                // set the byte order
079                index.order(ByteOrder.LITTLE_ENDIAN);
080                m_counter = CounterJNI.initializeCounter(mode.value, index.asIntBuffer(), status.asIntBuffer());
081                HALUtil.checkStatus(status.asIntBuffer());
082                m_index = index.asIntBuffer().get(0);
083
084                m_allocatedUpSource = false;
085                m_allocatedDownSource = false;
086                m_upSource = null;
087                m_downSource = null;
088
089                setMaxPeriod(.5);
090
091                UsageReporting.report(tResourceType.kResourceType_Counter, m_index,
092                                mode.value);
093        }
094
095        /**
096         * Create an instance of a counter where no sources are selected. Then they
097         * all must be selected by calling functions to specify the upsource and the
098         * downsource independently.
099         *
100         * The counter will start counting immediately.
101         */
102        public Counter() {
103                initCounter(Mode.kTwoPulse);
104        }
105
106        /**
107         * Create an instance of a counter from a Digital Input. This is used if an
108         * existing digital input is to be shared by multiple other objects such as
109         * encoders or if the Digital Source is not a DIO channel (such as an Analog Trigger)
110         *
111         * The counter will start counting immediately.
112         *
113         * @param source
114         *            the digital source to count
115         */
116        public Counter(DigitalSource source) {
117                if (source == null)
118                        throw new NullPointerException("Digital Source given was null");
119                initCounter(Mode.kTwoPulse);
120                setUpSource(source);
121        }
122
123        /**
124         * Create an instance of a Counter object. Create an up-Counter instance
125         * given a channel.
126         *
127         * The counter will start counting immediately.
128         *
129         * @param channel
130         *            the DIO channel to use as the up source. 0-9 are on-board, 10-25 are on the MXP
131         */
132        public Counter(int channel) {
133                initCounter(Mode.kTwoPulse);
134                setUpSource(channel);
135        }
136
137        /**
138         * Create an instance of a Counter object. Create an instance of a simple
139         * up-Counter given an analog trigger. Use the trigger state output from the
140         * analog trigger.
141         *
142         * The counter will start counting immediately.
143         *
144         * @param encodingType
145         *            which edges to count
146         * @param upSource
147         *            first source to count
148         * @param downSource
149         *            second source for direction
150         * @param inverted
151         *            true to invert the count
152         */
153        public Counter(EncodingType encodingType, DigitalSource upSource,
154                        DigitalSource downSource, boolean inverted) {
155                initCounter(Mode.kExternalDirection);
156                if (encodingType != EncodingType.k1X
157                                && encodingType != EncodingType.k2X) {
158                        throw new RuntimeException(
159                                        "Counters only support 1X and 2X quadreature decoding!");
160                }
161                if (upSource == null)
162                        throw new NullPointerException("Up Source given was null");
163                setUpSource(upSource);
164                if (downSource == null)
165                        throw new NullPointerException("Down Source given was null");
166                setDownSource(downSource);
167
168                if (encodingType == null)
169                        throw new NullPointerException("Encoding type given was null");
170
171                ByteBuffer status = ByteBuffer.allocateDirect(4);
172                if (encodingType == EncodingType.k1X) {
173                        setUpSourceEdge(true, false);
174                        CounterJNI.setCounterAverageSize(m_counter, 1, status.asIntBuffer());
175                } else {
176                        setUpSourceEdge(true, true);
177                        CounterJNI.setCounterAverageSize(m_counter, 2, status.asIntBuffer());
178                }
179
180                HALUtil.checkStatus(status.asIntBuffer());
181                setDownSourceEdge(inverted, true);
182        }
183
184        /**
185         * Create an instance of a Counter object. Create an instance of a simple
186         * up-Counter given an analog trigger. Use the trigger state output from the
187         * analog trigger.
188         *
189         * The counter will start counting immediately.
190         *
191         * @param trigger
192         *            the analog trigger to count
193         */
194        public Counter(AnalogTrigger trigger) {
195                if( trigger == null){
196                        throw new NullPointerException("The Analog Trigger given was null");
197                }
198                initCounter(Mode.kTwoPulse);
199                setUpSource(trigger.createOutput(AnalogTriggerType.kState));
200        }
201
202        @Override
203        public void free() {
204                setUpdateWhenEmpty(true);
205
206                clearUpSource();
207                clearDownSource();
208
209                ByteBuffer status = ByteBuffer.allocateDirect(4);
210                status.order(ByteOrder.LITTLE_ENDIAN);
211                CounterJNI.freeCounter(m_counter, status.asIntBuffer());
212                HALUtil.checkStatus(status.asIntBuffer());
213
214                m_upSource = null;
215                m_downSource = null;
216                m_counter = null;
217        }
218
219        /**
220         * @return the Counter's FPGA index
221         */
222        public int getFPGAIndex() {
223                return m_index;
224        }
225
226        /**
227         * Set the upsource for the counter as a digital input channel.
228         *
229         * @param channel
230         *            the DIO channel to count 0-9 are on-board, 10-25 are on the MXP
231         */
232        public void setUpSource(int channel) {
233                setUpSource(new DigitalInput(channel));
234                m_allocatedUpSource = true;
235        }
236
237        /**
238         * Set the source object that causes the counter to count up. Set the up
239         * counting DigitalSource.
240         *
241         * @param source
242         *            the digital source to count
243         */
244        public void setUpSource(DigitalSource source) {
245                if (m_upSource != null && m_allocatedUpSource) {
246                        m_upSource.free();
247                        m_allocatedUpSource = false;
248                }
249                m_upSource = source;
250                ByteBuffer status = ByteBuffer.allocateDirect(4);
251                status.order(ByteOrder.LITTLE_ENDIAN);
252                CounterJNI.setCounterUpSource(m_counter,
253                                source.getChannelForRouting(),
254                                (byte) (source.getAnalogTriggerForRouting() ? 1 : 0), status.asIntBuffer());
255                HALUtil.checkStatus(status.asIntBuffer());
256        }
257
258        /**
259         * Set the up counting source to be an analog trigger.
260         *
261         * @param analogTrigger
262         *            The analog trigger object that is used for the Up Source
263         * @param triggerType
264         *            The analog trigger output that will trigger the counter.
265         */
266        public void setUpSource(AnalogTrigger analogTrigger, AnalogTriggerType triggerType) {
267                if (analogTrigger == null){
268                        throw new NullPointerException("Analog Trigger given was null");
269                }
270                if (triggerType == null){
271                        throw new NullPointerException("Analog Trigger Type given was null");
272                }
273                setUpSource(analogTrigger.createOutput(triggerType));
274                m_allocatedUpSource = true;
275        }
276
277        /**
278         * Set the edge sensitivity on an up counting source. Set the up source to
279         * either detect rising edges or falling edges.
280         *
281         * @param risingEdge
282         *            true to count rising edge
283         * @param fallingEdge
284         *            true to count falling edge
285         */
286        public void setUpSourceEdge(boolean risingEdge, boolean fallingEdge) {
287                if (m_upSource == null)
288                        throw new RuntimeException(
289                                        "Up Source must be set before setting the edge!");
290                ByteBuffer status = ByteBuffer.allocateDirect(4);
291                status.order(ByteOrder.LITTLE_ENDIAN);
292                CounterJNI.setCounterUpSourceEdge(m_counter,
293                                (byte) (risingEdge ? 1 : 0), (byte) (fallingEdge ? 1 : 0),
294                                status.asIntBuffer());
295                HALUtil.checkStatus(status.asIntBuffer());
296        }
297
298        /**
299         * Disable the up counting source to the counter.
300         */
301        public void clearUpSource() {
302                if (m_upSource != null && m_allocatedUpSource) {
303                        m_upSource.free();
304                        m_allocatedUpSource = false;
305                }
306                m_upSource = null;
307
308                ByteBuffer status = ByteBuffer.allocateDirect(4);
309                status.order(ByteOrder.LITTLE_ENDIAN);
310                CounterJNI.clearCounterUpSource(m_counter, status.asIntBuffer());
311                HALUtil.checkStatus(status.asIntBuffer());
312        }
313
314        /**
315         * Set the down counting source to be a digital input channel.
316         *
317         * @param channel
318         *            the DIO channel to count 0-9 are on-board, 10-25 are on the MXP
319         */
320        public void setDownSource(int channel) {
321                setDownSource(new DigitalInput(channel));
322                m_allocatedDownSource = true;
323        }
324
325        /**
326         * Set the source object that causes the counter to count down. Set the down
327         * counting DigitalSource.
328         *
329         * @param source
330         *            the digital source to count
331         */
332        public void setDownSource(DigitalSource source) {
333                if(source == null){
334                        throw new NullPointerException("The Digital Source given was null");
335                }
336
337                if (m_downSource != null && m_allocatedDownSource) {
338                        m_downSource.free();
339                        m_allocatedDownSource = false;
340                }
341                ByteBuffer status = ByteBuffer.allocateDirect(4);
342                status.order(ByteOrder.LITTLE_ENDIAN);
343                CounterJNI.setCounterDownSource(m_counter,
344                                source.getChannelForRouting(),
345                                (byte) (source.getAnalogTriggerForRouting() ? 1 : 0), status.asIntBuffer());
346                if (status.asIntBuffer().get(0) == HALUtil.PARAMETER_OUT_OF_RANGE) {
347                        throw new IllegalArgumentException(
348                                        "Counter only supports DownSource in TwoPulse and ExternalDirection modes.");
349                }
350                HALUtil.checkStatus(status.asIntBuffer());
351                m_downSource = source;
352        }
353
354        /**
355         * Set the down counting source to be an analog trigger.
356         *
357         * @param analogTrigger
358         *            The analog trigger object that is used for the Down Source
359         * @param triggerType
360         *            The analog trigger output that will trigger the counter.
361         */
362        public void setDownSource(AnalogTrigger analogTrigger, AnalogTriggerType triggerType) {
363                if (analogTrigger == null){
364                        throw new NullPointerException("Analog Trigger given was null");
365                }
366                if (triggerType == null){
367                        throw new NullPointerException("Analog Trigger Type given was null");
368                }
369
370                setDownSource(analogTrigger.createOutput(triggerType));
371                m_allocatedDownSource = true;
372        }
373
374        /**
375         * Set the edge sensitivity on a down counting source. Set the down source
376         * to either detect rising edges or falling edges.
377         *
378         * @param risingEdge
379         *            true to count the rising edge
380         * @param fallingEdge
381         *            true to count the falling edge
382         */
383        public void setDownSourceEdge(boolean risingEdge, boolean fallingEdge) {
384                if (m_downSource == null)
385                        throw new RuntimeException(
386                                        " Down Source must be set before setting the edge!");
387                ByteBuffer status = ByteBuffer.allocateDirect(4);
388                status.order(ByteOrder.LITTLE_ENDIAN);
389                CounterJNI.setCounterDownSourceEdge(m_counter, (byte) (risingEdge ? 1
390                                : 0), (byte) (fallingEdge ? 1 : 0), status.asIntBuffer());
391                HALUtil.checkStatus(status.asIntBuffer());
392        }
393
394        /**
395         * Disable the down counting source to the counter.
396         */
397        public void clearDownSource() {
398                if (m_downSource != null && m_allocatedDownSource) {
399                        m_downSource.free();
400                        m_allocatedDownSource = false;
401                }
402                m_downSource = null;
403
404                ByteBuffer status = ByteBuffer.allocateDirect(4);
405                status.order(ByteOrder.LITTLE_ENDIAN);
406                CounterJNI.clearCounterDownSource(m_counter, status.asIntBuffer());
407                HALUtil.checkStatus(status.asIntBuffer());
408        }
409
410        /**
411         * Set standard up / down counting mode on this counter. Up and down counts
412         * are sourced independently from two inputs.
413         */
414        public void setUpDownCounterMode() {
415                ByteBuffer status = ByteBuffer.allocateDirect(4);
416                status.order(ByteOrder.LITTLE_ENDIAN);
417                CounterJNI.setCounterUpDownMode(m_counter, status.asIntBuffer());
418                HALUtil.checkStatus(status.asIntBuffer());
419        }
420
421        /**
422         * Set external direction mode on this counter. Counts are sourced on the Up
423         * counter input. The Down counter input represents the direction to count.
424         */
425        public void setExternalDirectionMode() {
426                ByteBuffer status = ByteBuffer.allocateDirect(4);
427                status.order(ByteOrder.LITTLE_ENDIAN);
428                CounterJNI.setCounterExternalDirectionMode(m_counter, status.asIntBuffer());
429                HALUtil.checkStatus(status.asIntBuffer());
430        }
431
432        /**
433         * Set Semi-period mode on this counter. Counts up on both rising and
434         * falling edges.
435         *
436         * @param highSemiPeriod
437         *            true to count up on both rising and falling
438         */
439        public void setSemiPeriodMode(boolean highSemiPeriod) {
440                ByteBuffer status = ByteBuffer.allocateDirect(4);
441                status.order(ByteOrder.LITTLE_ENDIAN);
442                CounterJNI.setCounterSemiPeriodMode(m_counter,
443                                (byte) (highSemiPeriod ? 1 : 0), status.asIntBuffer());
444                HALUtil.checkStatus(status.asIntBuffer());
445        }
446
447        /**
448         * Configure the counter to count in up or down based on the length of the
449         * input pulse. This mode is most useful for direction sensitive gear tooth
450         * sensors.
451         *
452         * @param threshold
453         *            The pulse length beyond which the counter counts the opposite
454         *            direction. Units are seconds.
455         */
456        public void setPulseLengthMode(double threshold) {
457                ByteBuffer status = ByteBuffer.allocateDirect(4);
458                status.order(ByteOrder.LITTLE_ENDIAN);
459                CounterJNI.setCounterPulseLengthMode(m_counter, threshold,
460                                status.asIntBuffer());
461                HALUtil.checkStatus(status.asIntBuffer());
462        }
463
464        /**
465         * Read the current counter value. Read the value at this instant. It may
466         * still be running, so it reflects the current value. Next time it is read,
467         * it might have a different value.
468         */
469        @Override
470        public int get() {
471                ByteBuffer status = ByteBuffer.allocateDirect(4);
472                status.order(ByteOrder.LITTLE_ENDIAN);
473                int value = CounterJNI.getCounter(m_counter, status.asIntBuffer());
474                HALUtil.checkStatus(status.asIntBuffer());
475                return value;
476        }
477
478        /**
479         * Read the current scaled counter value. Read the value at this instant,
480         * scaled by the distance per pulse (defaults to 1).
481         *
482         * @return The distance since the last reset
483         */
484        public double getDistance() {
485                return get() * m_distancePerPulse;
486        }
487
488        /**
489         * Reset the Counter to zero. Set the counter value to zero. This doesn't
490         * effect the running state of the counter, just sets the current value to
491         * zero.
492         */
493        @Override
494        public void reset() {
495                ByteBuffer status = ByteBuffer.allocateDirect(4);
496                status.order(ByteOrder.LITTLE_ENDIAN);
497                CounterJNI.resetCounter(m_counter, status.asIntBuffer());
498                HALUtil.checkStatus(status.asIntBuffer());
499        }
500
501        /**
502         * Set the maximum period where the device is still considered "moving".
503         * Sets the maximum period where the device is considered moving. This value
504         * is used to determine the "stopped" state of the counter using the
505         * GetStopped method.
506         *
507         * @param maxPeriod
508         *            The maximum period where the counted device is considered
509         *            moving in seconds.
510         */
511        @Override
512        public void setMaxPeriod(double maxPeriod) {
513                ByteBuffer status = ByteBuffer.allocateDirect(4);
514                status.order(ByteOrder.LITTLE_ENDIAN);
515                CounterJNI.setCounterMaxPeriod(m_counter, maxPeriod, status.asIntBuffer());
516                HALUtil.checkStatus(status.asIntBuffer());
517        }
518
519        /**
520         * Select whether you want to continue updating the event timer output when
521         * there are no samples captured. The output of the event timer has a buffer
522         * of periods that are averaged and posted to a register on the FPGA. When
523         * the timer detects that the event source has stopped (based on the
524         * MaxPeriod) the buffer of samples to be averaged is emptied. If you enable
525         * the update when empty, you will be notified of the stopped source and the
526         * event time will report 0 samples. If you disable update when empty, the
527         * most recent average will remain on the output until a new sample is
528         * acquired. You will never see 0 samples output (except when there have
529         * been no events since an FPGA reset) and you will likely not see the
530         * stopped bit become true (since it is updated at the end of an average and
531         * there are no samples to average).
532         *
533         * @param enabled
534         *            true to continue updating
535         */
536        public void setUpdateWhenEmpty(boolean enabled) {
537                ByteBuffer status = ByteBuffer.allocateDirect(4);
538                status.order(ByteOrder.LITTLE_ENDIAN);
539                CounterJNI.setCounterUpdateWhenEmpty(m_counter,
540                                (byte) (enabled ? 1 : 0), status.asIntBuffer());
541                HALUtil.checkStatus(status.asIntBuffer());
542        }
543
544        /**
545         * Determine if the clock is stopped. Determine if the clocked input is
546         * stopped based on the MaxPeriod value set using the SetMaxPeriod method.
547         * If the clock exceeds the MaxPeriod, then the device (and counter) are
548         * assumed to be stopped and it returns true.
549         *
550         * @return Returns true if the most recent counter period exceeds the
551         *         MaxPeriod value set by SetMaxPeriod.
552         */
553        @Override
554        public boolean getStopped() {
555                ByteBuffer status = ByteBuffer.allocateDirect(4);
556                status.order(ByteOrder.LITTLE_ENDIAN);
557                boolean value = CounterJNI.getCounterStopped(m_counter, status.asIntBuffer()) != 0;
558                HALUtil.checkStatus(status.asIntBuffer());
559                return value;
560        }
561
562        /**
563         * The last direction the counter value changed.
564         *
565         * @return The last direction the counter value changed.
566         */
567        @Override
568        public boolean getDirection() {
569                ByteBuffer status = ByteBuffer.allocateDirect(4);
570                status.order(ByteOrder.LITTLE_ENDIAN);
571                boolean value = CounterJNI.getCounterDirection(m_counter, status.asIntBuffer()) != 0;
572                HALUtil.checkStatus(status.asIntBuffer());
573                return value;
574        }
575
576        /**
577         * Set the Counter to return reversed sensing on the direction. This allows
578         * counters to change the direction they are counting in the case of 1X and
579         * 2X quadrature encoding only. Any other counter mode isn't supported.
580         *
581         * @param reverseDirection
582         *            true if the value counted should be negated.
583         */
584        public void setReverseDirection(boolean reverseDirection) {
585                ByteBuffer status = ByteBuffer.allocateDirect(4);
586                status.order(ByteOrder.LITTLE_ENDIAN);
587                CounterJNI.setCounterReverseDirection(m_counter,
588                                (byte) (reverseDirection ? 1 : 0), status.asIntBuffer());
589                HALUtil.checkStatus(status.asIntBuffer());
590        }
591
592        /**
593         * Get the Period of the most recent count. Returns the time interval of the
594         * most recent count. This can be used for velocity calculations to
595         * determine shaft speed.
596         *
597         * @return The period of the last two pulses in units of seconds.
598         */
599        @Override
600        public double getPeriod() {
601                ByteBuffer status = ByteBuffer.allocateDirect(4);
602                status.order(ByteOrder.LITTLE_ENDIAN);
603                double value = CounterJNI.getCounterPeriod(m_counter, status.asIntBuffer());
604                HALUtil.checkStatus(status.asIntBuffer());
605                return value;
606        }
607
608        /**
609         * Get the current rate of the Counter. Read the current rate of the counter
610         * accounting for the distance per pulse value. The default value for
611         * distance per pulse (1) yields units of pulses per second.
612         *
613         * @return The rate in units/sec
614         */
615        public double getRate() {
616                return m_distancePerPulse / getPeriod();
617        }
618
619        /**
620         * Set the Samples to Average which specifies the number of samples of the
621         * timer to average when calculating the period. Perform averaging to
622         * account for mechanical imperfections or as oversampling to increase
623         * resolution.
624         *
625         * @param samplesToAverage
626         *            The number of samples to average from 1 to 127.
627         */
628        public void setSamplesToAverage(int samplesToAverage) {
629                ByteBuffer status = ByteBuffer.allocateDirect(4);
630                status.order(ByteOrder.LITTLE_ENDIAN);
631                CounterJNI.setCounterSamplesToAverage(m_counter, samplesToAverage,
632                                status.asIntBuffer());
633                if (status.asIntBuffer().get(0) == HALUtil.PARAMETER_OUT_OF_RANGE) {
634                        throw new BoundaryException(BoundaryException.getMessage(
635                                        samplesToAverage, 1, 127));
636                }
637                HALUtil.checkStatus(status.asIntBuffer());
638        }
639
640        /**
641         * Get the Samples to Average which specifies the number of samples of the
642         * timer to average when calculating the period. Perform averaging to
643         * account for mechanical imperfections or as oversampling to increase
644         * resolution.
645         *
646         * @return SamplesToAverage The number of samples being averaged (from 1 to
647         *         127)
648         */
649        public int getSamplesToAverage() {
650                ByteBuffer status = ByteBuffer.allocateDirect(4);
651                status.order(ByteOrder.LITTLE_ENDIAN);
652                int value = CounterJNI.getCounterSamplesToAverage(m_counter, status.asIntBuffer());
653                HALUtil.checkStatus(status.asIntBuffer());
654                return value;
655        }
656
657        /**
658         * Set the distance per pulse for this counter. This sets the multiplier
659         * used to determine the distance driven based on the count value from the
660         * encoder. Set this value based on the Pulses per Revolution and factor in
661         * any gearing reductions. This distance can be in any units you like,
662         * linear or angular.
663         *
664         * @param distancePerPulse
665         *            The scale factor that will be used to convert pulses to useful
666         *            units.
667         */
668        public void setDistancePerPulse(double distancePerPulse) {
669                m_distancePerPulse = distancePerPulse;
670        }
671
672        /**
673         * Set which parameter of the encoder you are using as a process control
674         * variable. The counter class supports the rate and distance parameters.
675         *
676         * @param pidSource
677         *            An enum to select the parameter.
678         */
679        public void setPIDSourceParameter(PIDSourceParameter pidSource) {
680                if(pidSource == null){
681                        throw new NullPointerException("PID Source Parameter given was null");
682                }
683                BoundaryException.assertWithinBounds(pidSource.value, 0, 1);
684                m_pidSource = pidSource;
685        }
686
687        @Override
688        public double pidGet() {
689                switch (m_pidSource.value) {
690                case PIDSourceParameter.kDistance_val:
691                        return getDistance();
692                case PIDSourceParameter.kRate_val:
693                        return getRate();
694                default:
695                        return 0.0;
696                }
697        }
698
699        /**
700         * Live Window code, only does anything if live window is activated.
701         */
702        @Override
703        public String getSmartDashboardType() {
704                return "Counter";
705        }
706
707        private ITable m_table;
708
709        /**
710         * {@inheritDoc}
711         */
712        @Override
713        public void initTable(ITable subtable) {
714                m_table = subtable;
715                updateTable();
716        }
717
718        /**
719         * {@inheritDoc}
720         */
721        @Override
722        public ITable getTable() {
723                return m_table;
724        }
725
726        /**
727         * {@inheritDoc}
728         */
729        @Override
730        public void updateTable() {
731                if (m_table != null) {
732                        m_table.putNumber("Value", get());
733                }
734        }
735
736        /**
737         * {@inheritDoc}
738         */
739        @Override
740        public void startLiveWindowMode() {
741        }
742
743        /**
744         * {@inheritDoc}
745         */
746        @Override
747        public void stopLiveWindowMode() {
748        }
749}