001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2014. 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.can.CANExceptionFactory;
014import edu.wpi.first.wpilibj.can.CANJNI;
015import edu.wpi.first.wpilibj.can.CANMessageNotFoundException;
016import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
017import edu.wpi.first.wpilibj.tables.ITable;
018import edu.wpi.first.wpilibj.tables.ITableListener;
019import edu.wpi.first.wpilibj.util.AllocationException;
020import edu.wpi.first.wpilibj.util.CheckedAllocationException;
021
022/**
023 * Texas Instruments Jaguar Speed Controller as a CAN device.
024 * @author Thomas Clark
025 */
026public class CANJaguar implements MotorSafety, PIDOutput, SpeedController, LiveWindowSendable {
027
028        public static final int kMaxMessageDataSize = 8;
029
030        // The internal PID control loop in the Jaguar runs at 1kHz.
031        public static final int kControllerRate = 1000;
032        public static final double kApproxBusVoltage = 12.0;
033
034        private MotorSafetyHelper m_safetyHelper;
035        private static final Resource allocated = new Resource(63);
036
037        private static final int kFullMessageIDMask = CANJNI.CAN_MSGID_API_M | CANJNI.CAN_MSGID_MFR_M | CANJNI.CAN_MSGID_DTYPE_M;
038        private static final int kSendMessagePeriod = 20;
039
040        // Control Mode tags
041        private static class EncoderTag {};
042        /** Sets an encoder as the speed reference only. <br> Passed as the "tag" when setting the control mode.*/
043        public final static EncoderTag kEncoder = new EncoderTag();
044
045        private static class QuadEncoderTag {};
046        /** Sets a quadrature encoder as the position and speed reference. <br> Passed as the "tag" when setting the control mode.*/
047        public final static QuadEncoderTag kQuadEncoder = new QuadEncoderTag();
048
049        private static class PotentiometerTag {};
050        /** Sets a potentiometer as the position reference only. <br> Passed as the "tag" when setting the control mode. */
051        public final static PotentiometerTag kPotentiometer = new PotentiometerTag();
052
053
054        /**
055        * Mode determines how the Jaguar is controlled, used internally.
056        */
057        public enum ControlMode {
058                PercentVbus, Current, Speed, Position, Voltage;
059        }
060
061        public static final int kCurrentFault = 1;
062        public static final int kTemperatureFault = 2;
063        public static final int kBusVoltageFault = 4;
064        public static final int kGateDriverFault = 8;
065
066        /**
067        * Limit switch masks
068        */
069        public static final int kForwardLimit = 1;
070        public static final int kReverseLimit = 2;
071
072    /**
073     * Determines how the Jaguar behaves when sending a zero signal.
074     */
075    public enum NeutralMode {
076                /** Use the NeutralMode that is set by the jumper wire on the CAN device */
077                Jumper((byte)0),
078                /** Stop the motor's rotation by applying a force. */
079                Brake((byte)1),
080                /** Do not attempt to stop the motor. Instead allow it to coast to a stop without applying resistance. */
081                Coast((byte)2);
082
083                public byte value;
084
085                public static NeutralMode valueOf(byte value) {
086                        for(NeutralMode mode : values()) {
087                                if(mode.value == value) {
088                                        return mode;
089                                }
090                        }
091
092                        return null;
093                }
094
095                private NeutralMode(byte value) {
096                        this.value = value;
097                }
098        }
099
100
101    /**
102     * Determines which sensor to use for position reference.
103     * Limit switches will always be used to limit the rotation. This can not be disabled.
104     */
105    public enum LimitMode {
106                /**
107                 * Disables the soft position limits and only uses the limit switches to limit rotation.
108                 * @see CANJaguar#getForwardLimitOK()
109                 * @see CANJaguar#getReverseLimitOK()
110                 */
111                SwitchInputsOnly((byte)0),
112                /**
113                 * Enables the soft position limits on the Jaguar.
114                 * These will be used in addition to the limit switches. This does not disable the behavior
115                 * of the limit switch input.
116                 * @see CANJaguar#configSoftPositionLimits(double, double)
117                 */
118                SoftPositionLimits((byte)1);
119
120                public byte value;
121
122                public static LimitMode valueOf(byte value) {
123                        for(LimitMode mode : values()) {
124                                if(mode.value == value) {
125                                        return mode;
126                                }
127                        }
128
129                        return null;
130                }
131
132                private LimitMode(byte value) {
133                        this.value = value;
134                }
135        }
136
137        /**
138         * Constructor for the CANJaguar device.<br>
139         * By default the device is configured in Percent mode.
140         * The control mode can be changed by calling one of the control modes listed below.
141         *
142         * @param deviceNumber The address of the Jaguar on the CAN bus.
143         * @see CANJaguar#setCurrentMode(double, double, double)
144         * @see CANJaguar#setCurrentMode(PotentiometerTag, double, double, double)
145         * @see CANJaguar#setCurrentMode(EncoderTag, int, double, double, double)
146         * @see CANJaguar#setCurrentMode(QuadEncoderTag, int, double, double, double)
147         * @see CANJaguar#setPercentMode()
148         * @see CANJaguar#setPercentMode(PotentiometerTag)
149         * @see CANJaguar#setPercentMode(EncoderTag, int)
150         * @see CANJaguar#setPercentMode(QuadEncoderTag, int)
151         * @see CANJaguar#setPositionMode(PotentiometerTag, double, double, double)
152         * @see CANJaguar#setPositionMode(QuadEncoderTag, int, double, double, double)
153         * @see CANJaguar#setSpeedMode(EncoderTag, int, double, double, double)
154         * @see CANJaguar#setSpeedMode(QuadEncoderTag, int, double, double, double)
155         * @see CANJaguar#setVoltageMode()
156         * @see CANJaguar#setVoltageMode(PotentiometerTag)
157         * @see CANJaguar#setVoltageMode(EncoderTag, int)
158         * @see CANJaguar#setVoltageMode(QuadEncoderTag, int)
159         */
160        public CANJaguar(int deviceNumber) {
161                try {
162                        allocated.allocate(deviceNumber-1);
163                } catch (CheckedAllocationException e1) {
164                        throw new AllocationException(
165                        "CANJaguar device " + e1.getMessage() + "(increment index by one)");
166                }
167
168                m_deviceNumber = (byte)deviceNumber;
169                m_controlMode = ControlMode.PercentVbus;
170
171                m_safetyHelper = new MotorSafetyHelper(this);
172
173                boolean receivedFirmwareVersion = false;
174                byte[] data = new byte[8];
175
176                // Request firmware and hardware version only once
177                requestMessage(CANJNI.CAN_IS_FRAME_REMOTE | CANJNI.CAN_MSGID_API_FIRMVER);
178                requestMessage(CANJNI.LM_API_HWVER);
179
180                // Wait until we've gotten all of the status data at least once.
181                for(int i = 0; i < kReceiveStatusAttempts; i++)
182                {
183                        Timer.delay(0.001);
184
185                        setupPeriodicStatus();
186                        updatePeriodicStatus();
187
188                        if(!receivedFirmwareVersion) {
189                                try {
190                                        getMessage(CANJNI.CAN_MSGID_API_FIRMVER, CANJNI.CAN_MSGID_FULL_M, data);
191                                        m_firmwareVersion = unpackINT32(data);
192                                        receivedFirmwareVersion = true;
193                                } catch(CANMessageNotFoundException e) {}
194                        }
195
196                        if(m_receivedStatusMessage0 &&
197                        m_receivedStatusMessage1 &&
198                        m_receivedStatusMessage2 &&
199                        receivedFirmwareVersion) {
200                                break;
201                        }
202                }
203
204                if(!m_receivedStatusMessage0 ||
205                !m_receivedStatusMessage1 ||
206                !m_receivedStatusMessage2 ||
207                !receivedFirmwareVersion) {
208                        /* Free the resource */
209                        free();
210                        throw new CANMessageNotFoundException();
211                }
212
213                try {
214                        getMessage(CANJNI.LM_API_HWVER, CANJNI.CAN_MSGID_FULL_M, data);
215                        m_hardwareVersion = data[0];
216                } catch(CANMessageNotFoundException e) {
217                        // Not all Jaguar firmware reports a hardware version.
218                        m_hardwareVersion = 0;
219                }
220                
221                // 3330 was the first shipping RDK firmware version for the Jaguar
222                if (m_firmwareVersion >= 3330 || m_firmwareVersion < 108)
223                {
224                        if (m_firmwareVersion < 3330)
225                        {
226                                DriverStation.reportError("Jag " + m_deviceNumber + " firmware " + m_firmwareVersion + " is too old (must be at least version 108 of the FIRST approved firmware)", false);
227                        }
228                        else
229                        {
230                                DriverStation.reportError("Jag" + m_deviceNumber + " firmware " + m_firmwareVersion +  " is not FIRST approved (must be at least version 108 of the FIRST approved firmware)", false);
231                        }
232                        return;
233                }
234        }
235
236        /**
237        * Cancel periodic messages to the Jaguar, effectively disabling it.
238        * No other methods should be called after this is called.
239        */
240        public void free() {
241                allocated.free(m_deviceNumber-1);
242                m_safetyHelper = null;
243
244                ByteBuffer status = ByteBuffer.allocateDirect(4);
245                status.order(ByteOrder.LITTLE_ENDIAN);
246                status.asIntBuffer().put(0, 0);
247
248                int messageID ;
249
250                // Disable periodic setpoints
251                switch(m_controlMode) {
252                case PercentVbus:
253                        messageID = m_deviceNumber | CANJNI.LM_API_VOLT_T_SET;
254                        break;
255
256                case Speed:
257                        messageID = m_deviceNumber | CANJNI.LM_API_SPD_T_SET;
258                        break;
259
260                case Position:
261                        messageID = m_deviceNumber | CANJNI.LM_API_POS_T_SET;
262                        break;
263
264                case Current:
265                        messageID = m_deviceNumber | CANJNI.LM_API_ICTRL_T_SET;
266                        break;
267
268                case Voltage:
269                        messageID = m_deviceNumber | CANJNI.LM_API_VCOMP_T_SET;
270                        break;
271
272                default:
273                        return;
274                }
275
276                CANJNI.FRCNetworkCommunicationCANSessionMuxSendMessage(messageID, null,
277                        CANJNI.CAN_SEND_PERIOD_STOP_REPEATING, status.asIntBuffer());
278
279                configMaxOutputVoltage(kApproxBusVoltage);
280        }
281
282        /**
283        * @return The CAN ID passed in the constructor
284        */
285        int getDeviceNumber()
286        {
287                return m_deviceNumber;
288        }
289
290        /**
291         * Get the recently set outputValue set point.
292         *
293         * The scale and the units depend on the mode the Jaguar is in.<br>
294         * In percentVbus mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar).<br>
295         * In voltage mode, the outputValue is in volts.<br>
296         * In current mode, the outputValue is in amps.<br>
297         * In speed mode, the outputValue is in rotations/minute.<br>
298         * In position mode, the outputValue is in rotations.<br>
299         *
300         * @return The most recently set outputValue set point.
301         */
302        @Override
303        public double get() {
304                return m_value;
305        }
306
307        /**
308         * Sets the output set-point value.
309         *
310         * The scale and the units depend on the mode the Jaguar is in.<br>
311         * In percentVbus Mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar).<br>
312         * In voltage Mode, the outputValue is in volts. <br>
313         * In current Mode, the outputValue is in amps.<br>
314         * In speed mode, the outputValue is in rotations/minute.<br>
315         * In position Mode, the outputValue is in rotations.
316         *
317         * @param outputValue The set-point to sent to the motor controller.
318         * @param syncGroup The update group to add this set() to, pending UpdateSyncGroup().  If 0, update immediately.
319         */
320        @Override
321        public void set(double outputValue, byte syncGroup) {
322                int messageID;
323                byte[] data = new byte[8];
324                byte dataSize;
325
326                if(m_controlEnabled) {
327                        switch(m_controlMode) {
328                        case PercentVbus:
329                                messageID = CANJNI.LM_API_VOLT_T_SET;
330                                dataSize = packPercentage(data, outputValue);
331                                break;
332
333                        case Speed:
334                                messageID = CANJNI.LM_API_SPD_T_SET;
335                                dataSize = packFXP16_16(data, outputValue);
336                                break;
337
338                        case Position:
339                                messageID = CANJNI.LM_API_POS_T_SET;
340                                dataSize = packFXP16_16(data, outputValue);
341                                break;
342
343                        case Current:
344                                messageID = CANJNI.LM_API_ICTRL_T_SET;
345                                dataSize = packFXP8_8(data, outputValue);
346                                break;
347
348
349                        case Voltage:
350                                messageID = CANJNI.LM_API_VCOMP_T_SET;
351                                dataSize = packFXP8_8(data, outputValue);
352                                break;
353
354                        default:
355                                return;
356                        }
357
358                        if(syncGroup != 0) {
359                                data[dataSize++] = syncGroup;
360                        }
361
362                        sendMessage(messageID, data, dataSize, kSendMessagePeriod);
363
364                        if(m_safetyHelper != null) m_safetyHelper.feed();
365                }
366
367                m_value = outputValue;
368
369                verify();
370        }
371
372        /**
373         * Sets the output set-point value.
374         *
375         * The scale and the units depend on the mode the Jaguar is in.<br>
376         * In percentVbus mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar).<br>
377         * In voltage mode, the outputValue is in volts. <br>
378         * In current mode, the outputValue is in amps. <br>
379         * In speed mode, the outputValue is in rotations/minute.<br>
380         * In position mode, the outputValue is in rotations.
381         *
382         * @param value
383         *            The set-point to sent to the motor controller.
384         */
385        @Override
386        public void set(double value) {
387                set(value, (byte)0);
388        }
389
390        /**
391        * Check all unverified params and make sure they're equal to their local
392        * cached versions. If a value isn't available, it gets requested.  If a value
393        * doesn't match up, it gets set again.
394        */
395        protected void verify() {
396                byte[] data = new byte[8];
397
398                // If the Jaguar lost power, everything should be considered unverified
399                try {
400                        getMessage(CANJNI.LM_API_STATUS_POWER, CANJNI.CAN_MSGID_FULL_M, data);
401                        boolean powerCycled = data[0] != 0;
402
403                        if(powerCycled) {
404                                // Clear the power cycled bit
405                                data[0] = 1;
406                                sendMessage(CANJNI.LM_API_STATUS_POWER, data, 1);
407
408                                // Mark everything as unverified
409                                m_controlModeVerified = false;
410                                m_speedRefVerified = false;
411                                m_posRefVerified = false;
412                                m_neutralModeVerified = false;
413                                m_encoderCodesPerRevVerified = false;
414                                m_potentiometerTurnsVerified = false;
415                                m_forwardLimitVerified = false;
416                                m_reverseLimitVerified = false;
417                                m_limitModeVerified = false;
418                                m_maxOutputVoltageVerified = false;
419                                m_faultTimeVerified = false;
420
421                                if(m_controlMode == ControlMode.PercentVbus || m_controlMode == ControlMode.Voltage) {
422                                        m_voltageRampRateVerified = false;
423                                }
424                                else {
425                                        m_pVerified = false;
426                                        m_iVerified = false;
427                                        m_dVerified = false;
428                                }
429
430                                // Verify periodic status messages again
431                                m_receivedStatusMessage0 = false;
432                                m_receivedStatusMessage1 = false;
433                                m_receivedStatusMessage2 = false;
434
435                                // Remove any old values from netcomms. Otherwise, parameters
436                                // are incorrectly marked as verified based on stale messages.
437                                int[] messages = new int[] {
438                                        CANJNI.LM_API_SPD_REF, CANJNI.LM_API_POS_REF,
439                                        CANJNI.LM_API_SPD_PC, CANJNI.LM_API_POS_PC,
440                                        CANJNI.LM_API_ICTRL_PC, CANJNI.LM_API_SPD_IC,
441                                        CANJNI.LM_API_POS_IC, CANJNI.LM_API_ICTRL_IC,
442                                        CANJNI.LM_API_SPD_DC, CANJNI.LM_API_POS_DC,
443                                        CANJNI.LM_API_ICTRL_DC, CANJNI.LM_API_CFG_ENC_LINES,
444                                        CANJNI.LM_API_CFG_POT_TURNS, CANJNI.LM_API_CFG_BRAKE_COAST,
445                                        CANJNI.LM_API_CFG_LIMIT_MODE, CANJNI.LM_API_CFG_LIMIT_REV,
446                                        CANJNI.LM_API_CFG_MAX_VOUT, CANJNI.LM_API_VOLT_SET_RAMP,
447                                        CANJNI.LM_API_VCOMP_COMP_RAMP, CANJNI.LM_API_CFG_FAULT_TIME,
448                                        CANJNI.LM_API_CFG_LIMIT_FWD
449                                };
450
451                                for(int message : messages) {
452                                        try {
453                                                getMessage(message, CANJNI.CAN_MSGID_FULL_M, data);
454                                        } catch (CANMessageNotFoundException e) {}
455                                }
456                        }
457                } catch(CANMessageNotFoundException e) {
458                        requestMessage(CANJNI.LM_API_STATUS_POWER);
459                }
460
461                // Verify that any recently set parameters are correct
462                if(!m_controlModeVerified && m_controlEnabled) {
463                        try {
464                                getMessage(CANJNI.LM_API_STATUS_CMODE, CANJNI.CAN_MSGID_FULL_M, data);
465
466                                ControlMode mode = ControlMode.values()[data[0]];
467
468                                if(m_controlMode == mode) {
469                                        m_controlModeVerified = true;
470                                } else {
471                                        // Enable control again to resend the control mode
472                                        enableControl();
473                                }
474                        } catch(CANMessageNotFoundException e) {
475                                // Verification is needed but not available - request it again.
476                                requestMessage(CANJNI.LM_API_STATUS_CMODE);
477                        }
478                }
479
480                if(!m_speedRefVerified) {
481                        try {
482                                getMessage(CANJNI.LM_API_SPD_REF, CANJNI.CAN_MSGID_FULL_M, data);
483
484                                int speedRef = data[0];
485
486                                if(m_speedReference == speedRef) {
487                                        m_speedRefVerified = true;
488                                } else {
489                                        // It's wrong - set it again
490                                        setSpeedReference(m_speedReference);
491                                }
492                        } catch(CANMessageNotFoundException e) {
493                                        // Verification is needed but not available - request it again.
494                                        requestMessage(CANJNI.LM_API_SPD_REF);
495                        }
496                }
497
498                if(!m_posRefVerified) {
499                        try {
500                                getMessage(CANJNI.LM_API_POS_REF, CANJNI.CAN_MSGID_FULL_M, data);
501
502                                int posRef = data[0];
503
504                                if(m_positionReference == posRef) {
505                                        m_posRefVerified = true;
506                                } else {
507                                        // It's wrong - set it again
508                                        setPositionReference(m_positionReference);
509                                }
510                        } catch(CANMessageNotFoundException e) {
511                                        // Verification is needed but not available - request it again.
512                                        requestMessage(CANJNI.LM_API_POS_REF);
513                        }
514                }
515
516                if(!m_pVerified) {
517                        int message = 0;
518
519                        switch(m_controlMode) {
520                                case Speed:
521                                        message = CANJNI.LM_API_SPD_PC;
522                                        break;
523
524                                case Position:
525                                        message = CANJNI.LM_API_POS_PC;
526                                        break;
527
528                                case Current:
529                                        message = CANJNI.LM_API_ICTRL_PC;
530                                        break;
531
532                                default:
533                                        break;
534                        }
535
536                        try {
537                                getMessage(message, CANJNI.CAN_MSGID_FULL_M, data);
538
539                                double p = unpackFXP16_16(data);
540
541                                if(FXP16_EQ(m_p, p)) {
542                                        m_pVerified = true;
543                                } else {
544                                        // It's wrong - set it again
545                                        setP(m_p);
546                                }
547                        } catch(CANMessageNotFoundException e) {
548                                        // Verification is needed but not available - request it again.
549                                        requestMessage(message);
550                        }
551                }
552
553                if(!m_iVerified) {
554                        int message = 0;
555
556                        switch(m_controlMode) {
557                                case Speed:
558                                        message = CANJNI.LM_API_SPD_IC;
559                                        break;
560
561                                case Position:
562                                        message = CANJNI.LM_API_POS_IC;
563                                        break;
564
565                                case Current:
566                                        message = CANJNI.LM_API_ICTRL_IC;
567                                        break;
568
569                                default:
570                                        break;
571                        }
572
573                        try {
574                                getMessage(message, CANJNI.CAN_MSGID_FULL_M, data);
575
576                                double i = unpackFXP16_16(data);
577
578                                if(FXP16_EQ(m_i, i)) {
579                                        m_iVerified = true;
580                                } else {
581                                        // It's wrong - set it again
582                                        setI(m_i);
583                                }
584                        } catch(CANMessageNotFoundException e) {
585                                        // Verification is needed but not available - request it again.
586                                        requestMessage(message);
587                        }
588                }
589
590                if(!m_dVerified) {
591                        int message = 0;
592
593                        switch(m_controlMode) {
594                                case Speed:
595                                        message = CANJNI.LM_API_SPD_DC;
596                                        break;
597
598                                case Position:
599                                        message = CANJNI.LM_API_POS_DC;
600                                        break;
601
602                                case Current:
603                                        message = CANJNI.LM_API_ICTRL_DC;
604                                        break;
605
606                                default:
607                                        break;
608                        }
609
610                        try {
611                                getMessage(message, CANJNI.CAN_MSGID_FULL_M, data);
612
613                                double d = unpackFXP16_16(data);
614
615                                if(FXP16_EQ(m_d, d)) {
616                                        m_dVerified = true;
617                                } else {
618                                        // It's wrong - set it again
619                                        setD(m_d);
620                                }
621                        } catch(CANMessageNotFoundException e) {
622                                        // Verification is needed but not available - request it again.
623                                        requestMessage(message);
624                        }
625                }
626
627                if(!m_neutralModeVerified) {
628                        try {
629                                getMessage(CANJNI.LM_API_CFG_BRAKE_COAST, CANJNI.CAN_MSGID_FULL_M, data);
630
631                                NeutralMode mode = NeutralMode.valueOf(data[0]);
632
633                                if(mode == m_neutralMode) {
634                                        m_neutralModeVerified = true;
635                                } else {
636                                        //It's wrong - set it again
637                                        configNeutralMode(m_neutralMode);
638                                }
639                        } catch(CANMessageNotFoundException e) {
640                                // Verification is needed but not available - request it again.
641                                requestMessage(CANJNI.LM_API_CFG_BRAKE_COAST);
642                        }
643                }
644
645                if(!m_encoderCodesPerRevVerified) {
646                        try {
647                                getMessage(CANJNI.LM_API_CFG_ENC_LINES, CANJNI.CAN_MSGID_FULL_M, data);
648
649                                short codes = unpackINT16(data);
650
651                                if(codes == m_encoderCodesPerRev) {
652                                        m_encoderCodesPerRevVerified = true;
653                                } else {
654                                        //It's wrong - set it again
655                                        configEncoderCodesPerRev(m_encoderCodesPerRev);
656                                }
657                        } catch(CANMessageNotFoundException e) {
658                                // Verification is needed but not available - request it again.
659                                requestMessage(CANJNI.LM_API_CFG_ENC_LINES);
660                        }
661                }
662
663                if(!m_potentiometerTurnsVerified) {
664                        try {
665                                getMessage(CANJNI.LM_API_CFG_POT_TURNS, CANJNI.CAN_MSGID_FULL_M, data);
666
667                                short turns = unpackINT16(data);
668
669                                if(turns == m_potentiometerTurns) {
670                                        m_potentiometerTurnsVerified = true;
671                                } else {
672                                        //It's wrong - set it again
673                                        configPotentiometerTurns(m_potentiometerTurns);
674                                }
675                        } catch(CANMessageNotFoundException e) {
676                                // Verification is needed but not available - request it again.
677                                requestMessage(CANJNI.LM_API_CFG_POT_TURNS);
678                        }
679                }
680
681                if(!m_limitModeVerified) {
682                        try {
683                                getMessage(CANJNI.LM_API_CFG_LIMIT_MODE, CANJNI.CAN_MSGID_FULL_M, data);
684
685                                LimitMode mode = LimitMode.valueOf(data[0]);
686
687                                if(mode == m_limitMode) {
688                                        m_limitModeVerified = true;
689                                } else {
690                                        //It's wrong - set it again
691                                        configLimitMode(m_limitMode);
692                                }
693                        } catch(CANMessageNotFoundException e) {
694                                // Verification is needed but not available - request it again.
695                                requestMessage(CANJNI.LM_API_CFG_LIMIT_MODE);
696                        }
697                }
698
699                if(!m_forwardLimitVerified) {
700                        try {
701                                getMessage(CANJNI.LM_API_CFG_LIMIT_FWD, CANJNI.CAN_MSGID_FULL_M, data);
702
703                                double limit = unpackFXP16_16(data);
704
705                                if(FXP16_EQ(limit, m_forwardLimit)) {
706                                        m_forwardLimitVerified = true;
707                                } else {
708                                        //It's wrong - set it again
709                                        configForwardLimit(m_forwardLimit);
710                                }
711                        } catch(CANMessageNotFoundException e) {
712                                // Verification is needed but not available - request it again.
713                                requestMessage(CANJNI.LM_API_CFG_LIMIT_FWD);
714                        }
715                }
716
717                if(!m_reverseLimitVerified) {
718                        try {
719                                getMessage(CANJNI.LM_API_CFG_LIMIT_REV, CANJNI.CAN_MSGID_FULL_M, data);
720
721                                double limit = unpackFXP16_16(data);
722
723                                if(FXP16_EQ(limit, m_reverseLimit)) {
724                                        m_reverseLimitVerified = true;
725                                } else {
726                                        //It's wrong - set it again
727                                        configReverseLimit(m_reverseLimit);
728                                }
729                        } catch(CANMessageNotFoundException e) {
730                                // Verification is needed but not available - request it again.
731                                requestMessage(CANJNI.LM_API_CFG_LIMIT_REV);
732                        }
733                }
734
735                if(!m_maxOutputVoltageVerified) {
736                        try {
737                                getMessage(CANJNI.LM_API_CFG_MAX_VOUT, CANJNI.CAN_MSGID_FULL_M, data);
738
739                                double voltage = unpackFXP8_8(data);
740
741                                // The returned max output voltage is sometimes slightly higher
742                                // or lower than what was sent.  This should not trigger
743                                // resending the message.
744                                if(Math.abs(voltage - m_maxOutputVoltage) < 0.1) {
745                                        m_maxOutputVoltageVerified = true;
746                                } else {
747                                        // It's wrong - set it again
748                                        configMaxOutputVoltage(m_maxOutputVoltage);
749                                }
750
751                        } catch(CANMessageNotFoundException e) {
752                                // Verification is needed but not available - request it again.
753                                requestMessage(CANJNI.LM_API_CFG_MAX_VOUT);
754                        }
755                }
756
757                if(!m_voltageRampRateVerified) {
758                        if(m_controlMode == ControlMode.PercentVbus) {
759                                try {
760                                        getMessage(CANJNI.LM_API_VOLT_SET_RAMP, CANJNI.CAN_MSGID_FULL_M, data);
761
762                                        double rate = unpackPercentage(data);
763
764                                        if(FXP16_EQ(rate, m_voltageRampRate)) {
765                                                m_voltageRampRateVerified = true;
766                                        } else {
767                                                // It's wrong - set it again
768                                                setVoltageRampRate(m_voltageRampRate);
769                                        }
770
771                                } catch(CANMessageNotFoundException e) {
772                                        // Verification is needed but not available - request it again.
773                                        requestMessage(CANJNI.LM_API_VOLT_SET_RAMP);
774                                }
775                        }
776                } else if(m_controlMode == ControlMode.Voltage) {
777                        try {
778                                getMessage(CANJNI.LM_API_VCOMP_COMP_RAMP, CANJNI.CAN_MSGID_FULL_M, data);
779
780                                double rate = unpackFXP8_8(data);
781
782                                if(FXP8_EQ(rate, m_voltageRampRate)) {
783                                        m_voltageRampRateVerified = true;
784                                } else {
785                                        // It's wrong - set it again
786                                        setVoltageRampRate(m_voltageRampRate);
787                                }
788
789                        } catch(CANMessageNotFoundException e) {
790                                // Verification is needed but not available - request it again.
791                                requestMessage(CANJNI.LM_API_VCOMP_COMP_RAMP);
792                        }
793                }
794
795                if(!m_faultTimeVerified) {
796                        try {
797                                getMessage(CANJNI.LM_API_CFG_FAULT_TIME, CANJNI.CAN_MSGID_FULL_M, data);
798
799                                int faultTime = unpackINT16(data);
800
801                                if((int)(m_faultTime * 1000.0) == faultTime) {
802                                        m_faultTimeVerified = true;
803                                } else {
804                                        //It's wrong - set it again
805                                        configFaultTime(m_faultTime);
806                                }
807                        } catch(CANMessageNotFoundException e) {
808                                // Verification is needed but not available - request it again.
809                                requestMessage(CANJNI.LM_API_CFG_FAULT_TIME);
810                        }
811                }
812
813                if(!m_receivedStatusMessage0 ||
814                                !m_receivedStatusMessage1 ||
815                                !m_receivedStatusMessage2) {
816                        // If the periodic status messages haven't been verified as received,
817                        // request periodic status messages again and attempt to unpack any
818                        // available ones.
819                        setupPeriodicStatus();
820                        getTemperature();
821                        getPosition();
822                        getFaults();
823                }
824        }
825
826        /**
827        * Common interface for disabling a motor.
828        *
829        * @deprecated Call {@link #disableControl()} instead.
830        */
831        @Deprecated
832        @Override
833        public void disable() {
834                disableControl();
835        }
836
837        // PIDOutput interface
838        @Override
839        public void pidWrite(double output) {
840                if (m_controlMode == ControlMode.PercentVbus) {
841                        set(output);
842                } else {
843                        throw new IllegalStateException("PID only supported in PercentVbus mode");
844                }
845        }
846
847        /**
848        * Set the reference source device for speed controller mode.
849        *
850        * Choose encoder as the source of speed feedback when in speed control mode.
851        *
852        * @param reference Specify a speed reference.
853        */
854        private void setSpeedReference(int reference) {
855                sendMessage(CANJNI.LM_API_SPD_REF, new byte[] { (byte)reference }, 1);
856
857                m_speedReference = reference;
858                m_speedRefVerified = false;
859        }
860
861        /**
862         * Set the reference source device for position controller mode.
863         *
864         * Choose between using and encoder and using a potentiometer
865         * as the source of position feedback when in position control mode.
866         *
867         * @param reference Specify a position reference.
868         */
869        private void setPositionReference(int reference) {
870                sendMessage(CANJNI.LM_API_POS_REF, new byte[] { (byte)reference }, 1);
871
872                m_positionReference = reference;
873                m_posRefVerified = false;
874        }
875
876        /**
877        * Set the P constant for the closed loop modes.
878        *
879        * @param p The proportional gain of the Jaguar's PID controller.
880        */
881        public void setP(double p) {
882                byte[] data = new byte[8];
883                byte dataSize = packFXP16_16(data, p);
884
885                switch(m_controlMode) {
886                case Speed:
887                        sendMessage(CANJNI.LM_API_SPD_PC, data, dataSize);
888                        break;
889
890                case Position:
891                        sendMessage(CANJNI.LM_API_POS_PC, data, dataSize);
892                        break;
893
894                case Current:
895                        sendMessage(CANJNI.LM_API_ICTRL_PC, data, dataSize);
896                        break;
897
898                default:
899                        throw new IllegalStateException("PID constants only apply in Speed, Position, and Current mode");
900                }
901
902                m_p = p;
903                m_pVerified = false;
904        }
905
906        /**
907        * Set the I constant for the closed loop modes.
908        *
909        * @param i The integral gain of the Jaguar's PID controller.
910        */
911        public void setI(double i) {
912                byte[] data = new byte[8];
913                byte dataSize = packFXP16_16(data, i);
914
915                switch(m_controlMode) {
916                case Speed:
917                        sendMessage(CANJNI.LM_API_SPD_IC, data, dataSize);
918                        break;
919
920                case Position:
921                        sendMessage(CANJNI.LM_API_POS_IC, data, dataSize);
922                        break;
923
924                case Current:
925                        sendMessage(CANJNI.LM_API_ICTRL_IC, data, dataSize);
926                        break;
927
928                default:
929                        throw new IllegalStateException("PID constants only apply in Speed, Position, and Current mode");
930                }
931
932                m_i = i;
933                m_iVerified = false;
934        }
935
936        /**
937        * Set the D constant for the closed loop modes.
938        *
939        * @param d The derivative gain of the Jaguar's PID controller.
940        */
941        public void setD(double d) {
942                byte[] data = new byte[8];
943                byte dataSize = packFXP16_16(data, d);
944
945                switch(m_controlMode) {
946                case Speed:
947                        sendMessage(CANJNI.LM_API_SPD_DC, data, dataSize);
948                        break;
949
950                case Position:
951                        sendMessage(CANJNI.LM_API_POS_DC, data, dataSize);
952                        break;
953
954                case Current:
955                        sendMessage(CANJNI.LM_API_ICTRL_DC, data, dataSize);
956                        break;
957
958                default:
959                        throw new IllegalStateException("PID constants only apply in Speed, Position, and Current mode");
960                }
961
962                m_d = d;
963                m_dVerified = false;
964        }
965
966        /**
967        * Set the P, I, and D constants for the closed loop modes.
968        *
969        * @param p The proportional gain of the Jaguar's PID controller.
970        * @param i The integral gain of the Jaguar's PID controller.
971        * @param d The differential gain of the Jaguar's PID controller.
972        */
973        public void setPID(double p, double i, double d) {
974                setP(p);
975                setI(i);
976                setD(d);
977        }
978
979        /**
980        * Get the Proportional gain of the controller.
981        *
982        * @return The proportional gain.
983        */
984        public double getP() {
985                if(m_controlMode.equals(ControlMode.PercentVbus) || m_controlMode.equals(ControlMode.Voltage)){
986                        throw new IllegalStateException("PID does not apply in Percent or Voltage control modes");
987                }
988                return m_p;
989        }
990
991        /**
992        * Get the Integral gain of the controller.
993        *
994        * @return The integral gain.
995        */
996        public double getI() {
997                if(m_controlMode.equals(ControlMode.PercentVbus) || m_controlMode.equals(ControlMode.Voltage)){
998                        throw new IllegalStateException("PID does not apply in Percent or Voltage control modes");
999                }
1000                return m_i;
1001        }
1002
1003        /**
1004        * Get the Derivative gain of the controller.
1005        *
1006        * @return The derivative gain.
1007        */
1008        public double getD() {
1009                if(m_controlMode.equals(ControlMode.PercentVbus) || m_controlMode.equals(ControlMode.Voltage)){
1010                        throw new IllegalStateException("PID does not apply in Percent or Voltage control modes");
1011                }
1012                return m_d;
1013        }
1014
1015        /**
1016        * Enable the closed loop controller.
1017        *
1018        * Start actually controlling the output based on the feedback.
1019        * If starting a position controller with an encoder reference,
1020        * use the encoderInitialPosition parameter to initialize the
1021        * encoder state.
1022        *
1023        * @param encoderInitialPosition Encoder position to set if position with encoder reference.  Ignored otherwise.
1024        */
1025        public void enableControl(double encoderInitialPosition) {
1026                switch(m_controlMode) {
1027                case PercentVbus:
1028                        sendMessage(CANJNI.LM_API_VOLT_T_EN, new byte[0], 0);
1029                        break;
1030
1031                case Speed:
1032                        sendMessage(CANJNI.LM_API_SPD_T_EN, new byte[0], 0);
1033                        break;
1034
1035                case Position:
1036                        byte[] data = new byte[8];
1037                        int dataSize = packFXP16_16(data, encoderInitialPosition);
1038                        sendMessage(CANJNI.LM_API_POS_T_EN, data, dataSize);
1039                        break;
1040
1041                case Current:
1042                        sendMessage(CANJNI.LM_API_ICTRL_T_EN, new byte[0], 0);
1043                        break;
1044
1045                case Voltage:
1046                        sendMessage(CANJNI.LM_API_VCOMP_T_EN, new byte[0], 0);
1047                        break;
1048                }
1049
1050                m_controlEnabled = true;
1051        }
1052
1053        /**
1054         * Enable the closed loop controller.
1055         *
1056         * Start actually controlling the output based on the feedback.
1057         * This is the same as calling <code>CANJaguar.enableControl(double encoderInitialPosition)</code>
1058         * with <code>encoderInitialPosition</code> set to <code>0.0</code>
1059         */
1060        public void enableControl() {
1061                enableControl(0.0);
1062        }
1063
1064        /**
1065        * Disable the closed loop controller.
1066        *
1067        * Stop driving the output based on the feedback.
1068        */
1069        public void disableControl() {
1070                // Disable all control modes.
1071                sendMessage(CANJNI.LM_API_VOLT_DIS, new byte[0], 0);
1072                sendMessage(CANJNI.LM_API_SPD_DIS, new byte[0], 0);
1073                sendMessage(CANJNI.LM_API_POS_DIS, new byte[0], 0);
1074                sendMessage(CANJNI.LM_API_ICTRL_DIS, new byte[0], 0);
1075                sendMessage(CANJNI.LM_API_VCOMP_DIS, new byte[0], 0);
1076
1077                // Stop all periodic setpoints
1078                sendMessage(CANJNI.LM_API_VOLT_T_SET, new byte[0], 0, CANJNI.CAN_SEND_PERIOD_STOP_REPEATING);
1079                sendMessage(CANJNI.LM_API_SPD_T_SET, new byte[0], 0, CANJNI.CAN_SEND_PERIOD_STOP_REPEATING);
1080                sendMessage(CANJNI.LM_API_POS_T_SET, new byte[0], 0, CANJNI.CAN_SEND_PERIOD_STOP_REPEATING);
1081                sendMessage(CANJNI.LM_API_ICTRL_T_SET, new byte[0], 0, CANJNI.CAN_SEND_PERIOD_STOP_REPEATING);
1082                sendMessage(CANJNI.LM_API_VCOMP_T_SET, new byte[0], 0, CANJNI.CAN_SEND_PERIOD_STOP_REPEATING);
1083
1084                m_controlEnabled = false;
1085        }
1086
1087        /**
1088         * Enable controlling the motor voltage as a percentage of the bus voltage
1089         * without any position or speed feedback.<br>
1090         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1091         */
1092        public void setPercentMode()
1093        {
1094                changeControlMode(ControlMode.PercentVbus);
1095                setPositionReference(CANJNI.LM_REF_NONE);
1096                setSpeedReference(CANJNI.LM_REF_NONE);
1097        }
1098
1099        /**
1100         * Enable controlling the motor voltage as a percentage of the bus voltage,
1101         * and enable speed sensing from a non-quadrature encoder.<br>
1102         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1103         *
1104         * @param tag The constant {@link CANJaguar#kEncoder}
1105         * @param codesPerRev The counts per revolution on the encoder
1106         */
1107        public void setPercentMode(EncoderTag tag, int codesPerRev)
1108        {
1109                changeControlMode(ControlMode.PercentVbus);
1110                setPositionReference(CANJNI.LM_REF_NONE);
1111                setSpeedReference(CANJNI.LM_REF_ENCODER);
1112                configEncoderCodesPerRev(codesPerRev);
1113        }
1114
1115        /**
1116         * Enable controlling the motor voltage as a percentage of the bus voltage,
1117         * and enable position and speed sensing from a quadrature encoder.<br>
1118         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1119         *
1120         * @param tag The constant {@link CANJaguar#kQuadEncoder}
1121         * @param codesPerRev The counts per revolution on the encoder
1122         */
1123        public void setPercentMode(QuadEncoderTag tag, int codesPerRev)
1124        {
1125                changeControlMode(ControlMode.PercentVbus);
1126                setPositionReference(CANJNI.LM_REF_ENCODER);
1127                setSpeedReference(CANJNI.LM_REF_QUAD_ENCODER);
1128                configEncoderCodesPerRev(codesPerRev);
1129        }
1130
1131        /**
1132         * Enable controlling the motor voltage as a percentage of the bus voltage,
1133         * and enable position sensing from a potentiometer and no speed feedback.<br>
1134         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1135         *
1136         * @param tag The constant {@link CANJaguar#kPotentiometer}
1137         */
1138        public void setPercentMode(PotentiometerTag tag)
1139        {
1140                changeControlMode(ControlMode.PercentVbus);
1141                setPositionReference(CANJNI.LM_REF_POT);
1142                setSpeedReference(CANJNI.LM_REF_NONE);
1143                configPotentiometerTurns(1);
1144        }
1145
1146        /**
1147         * Enable controlling the motor current with a PID loop.<br>
1148         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1149         *
1150         * @param p The proportional gain of the Jaguar's PID controller.
1151         * @param i The integral gain of the Jaguar's PID controller.
1152         * @param d The differential gain of the Jaguar's PID controller.
1153         */
1154        public void setCurrentMode(double p, double i, double d)
1155        {
1156                changeControlMode(ControlMode.Current);
1157                setPositionReference(CANJNI.LM_REF_NONE);
1158                setSpeedReference(CANJNI.LM_REF_NONE);
1159                setPID(p, i, d);
1160        }
1161
1162        /**
1163         * Enable controlling the motor current with a PID loop, and enable speed
1164         * sensing from a non-quadrature encoder.<br>
1165         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1166         *
1167         * @param tag The constant {@link CANJaguar#kEncoder}
1168         * @param p The proportional gain of the Jaguar's PID controller.
1169         * @param i The integral gain of the Jaguar's PID controller.
1170         * @param d The differential gain of the Jaguar's PID controller.
1171         */
1172        public void setCurrentMode(EncoderTag tag, int codesPerRev, double p, double i, double d)
1173        {
1174                changeControlMode(ControlMode.Current);
1175                setPositionReference(CANJNI.LM_REF_NONE);
1176                setSpeedReference(CANJNI.LM_REF_NONE);
1177                configEncoderCodesPerRev(codesPerRev);
1178                setPID(p, i, d);
1179        }
1180
1181        /**
1182         * Enable controlling the motor current with a PID loop, and enable speed and
1183         * position sensing from a quadrature encoder.<br>
1184         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1185         *
1186         * @param tag The constant {@link CANJaguar#kQuadEncoder}
1187         * @param p The proportional gain of the Jaguar's PID controller.
1188         * @param i The integral gain of the Jaguar's PID controller.
1189         * @param d The differential gain of the Jaguar's PID controller.
1190         */
1191        public void setCurrentMode(QuadEncoderTag tag, int codesPerRev, double p, double i, double d)
1192        {
1193                changeControlMode(ControlMode.Current);
1194                setPositionReference(CANJNI.LM_REF_ENCODER);
1195                setSpeedReference(CANJNI.LM_REF_QUAD_ENCODER);
1196                configEncoderCodesPerRev(codesPerRev);
1197                setPID(p, i, d);
1198        }
1199
1200        /**
1201         * Enable controlling the motor current with a PID loop, and enable position
1202         * sensing from a potentiometer.<br>
1203         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1204         *
1205         * @param tag The constant {@link CANJaguar#kPotentiometer}
1206         * @param p The proportional gain of the Jaguar's PID controller.
1207         * @param i The integral gain of the Jaguar's PID controller.
1208         * @param d The differential gain of the Jaguar's PID controller.
1209         */
1210        public void setCurrentMode(PotentiometerTag tag, double p, double i, double d)
1211        {
1212                changeControlMode(ControlMode.Current);
1213                setPositionReference(CANJNI.LM_REF_POT);
1214                setSpeedReference(CANJNI.LM_REF_NONE);
1215                configPotentiometerTurns(1);
1216                setPID(p, i, d);
1217        }
1218
1219        /**
1220         * Enable controlling the speed with a feedback loop from a non-quadrature
1221         * encoder.<br>
1222         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1223         *
1224         * @param tag The constant {@link CANJaguar#kEncoder}
1225         * @param codesPerRev The counts per revolution on the encoder
1226         * @param p The proportional gain of the Jaguar's PID controller.
1227         * @param i The integral gain of the Jaguar's PID controller.
1228         * @param d The differential gain of the Jaguar's PID controller.
1229         */
1230        public void setSpeedMode(EncoderTag tag, int codesPerRev, double p, double i, double d)
1231        {
1232                changeControlMode(ControlMode.Speed);
1233                setPositionReference(CANJNI.LM_REF_NONE);
1234                setSpeedReference(CANJNI.LM_REF_ENCODER);
1235                configEncoderCodesPerRev(codesPerRev);
1236                setPID(p, i, d);
1237        }
1238
1239        /**
1240         * Enable controlling the speed with a feedback loop from a quadrature encoder.<br>
1241         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1242         *
1243         * @param tag The constant {@link CANJaguar#kQuadEncoder}
1244         * @param codesPerRev The counts per revolution on the encoder
1245         * @param p The proportional gain of the Jaguar's PID controller.
1246         * @param i The integral gain of the Jaguar's PID controller.
1247         * @param d The differential gain of the Jaguar's PID controller.
1248         */
1249        public void setSpeedMode(QuadEncoderTag tag, int codesPerRev, double p, double i, double d)
1250        {
1251                changeControlMode(ControlMode.Speed);
1252                setPositionReference(CANJNI.LM_REF_ENCODER);
1253                setSpeedReference(CANJNI.LM_REF_QUAD_ENCODER);
1254                configEncoderCodesPerRev(codesPerRev);
1255                setPID(p, i, d);
1256        }
1257
1258        /**
1259         * Enable controlling the position with a feedback loop using an encoder.<br>
1260         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1261         *
1262         * @param tag The constant {@link CANJaguar#kQuadEncoder}
1263         * @param codesPerRev The counts per revolution on the encoder
1264         * @param p The proportional gain of the Jaguar's PID controller.
1265         * @param i The integral gain of the Jaguar's PID controller.
1266         * @param d The differential gain of the Jaguar's PID controller.
1267         *
1268         */
1269        public void setPositionMode(QuadEncoderTag tag, int codesPerRev, double p, double i, double d)
1270        {
1271                changeControlMode(ControlMode.Position);
1272                setPositionReference(CANJNI.LM_REF_ENCODER);
1273                configEncoderCodesPerRev(codesPerRev);
1274                setPID(p, i, d);
1275        }
1276
1277        /**
1278         * Enable controlling the position with a feedback loop using a potentiometer.<br>
1279         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1280         *
1281         * @param tag The constant {@link CANJaguar#kPotentiometer}
1282         * @param p The proportional gain of the Jaguar's PID controller.
1283         * @param i The integral gain of the Jaguar's PID controller.
1284         * @param d The differential gain of the Jaguar's PID controller.
1285         */
1286        public void setPositionMode(PotentiometerTag tag, double p, double i, double d)
1287        {
1288                changeControlMode(ControlMode.Position);
1289                setPositionReference(CANJNI.LM_REF_POT);
1290                configPotentiometerTurns(1);
1291                setPID(p, i, d);
1292        }
1293
1294        /**
1295         * Enable controlling the motor voltage without any position or speed feedback.<br>
1296         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1297         */
1298        public void setVoltageMode()
1299        {
1300                changeControlMode(ControlMode.Voltage);
1301                setPositionReference(CANJNI.LM_REF_NONE);
1302                setSpeedReference(CANJNI.LM_REF_NONE);
1303        }
1304
1305        /**
1306         * Enable controlling the motor voltage with speed feedback from a
1307         * non-quadrature encoder and no position feedback.<br>
1308         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1309         *
1310         * @param tag The constant {@link CANJaguar#kEncoder}
1311         * @param codesPerRev The counts per revolution on the encoder
1312         */
1313        public void setVoltageMode(EncoderTag tag, int codesPerRev)
1314        {
1315                changeControlMode(ControlMode.Voltage);
1316                setPositionReference(CANJNI.LM_REF_NONE);
1317                setSpeedReference(CANJNI.LM_REF_ENCODER);
1318                configEncoderCodesPerRev(codesPerRev);
1319        }
1320
1321        /**
1322         * Enable controlling the motor voltage with position and speed feedback from a
1323         * quadrature encoder.<br>
1324         * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device.
1325         *
1326         * @param tag The constant {@link CANJaguar#kQuadEncoder}
1327         * @param codesPerRev The counts per revolution on the encoder
1328         */
1329        public void setVoltageMode(QuadEncoderTag tag, int codesPerRev)
1330        {
1331                changeControlMode(ControlMode.Voltage);
1332                setPositionReference(CANJNI.LM_REF_ENCODER);
1333                setSpeedReference(CANJNI.LM_REF_QUAD_ENCODER);
1334                configEncoderCodesPerRev(codesPerRev);
1335        }
1336
1337        /**
1338         * Enable controlling the motor voltage with position feedback from a
1339         * potentiometer and no speed feedback.
1340         *
1341         * @param tag The constant {@link CANJaguar#kPotentiometer}
1342         */
1343        public void setVoltageMode(PotentiometerTag tag)
1344        {
1345                changeControlMode(ControlMode.Voltage);
1346                setPositionReference(CANJNI.LM_REF_POT);
1347                setSpeedReference(CANJNI.LM_REF_NONE);
1348                configPotentiometerTurns(1);
1349        }
1350
1351        /**
1352         * Used internally. In order to set the control mode see the methods listed below.
1353         *
1354         * Change the control mode of this Jaguar object.
1355         *
1356         * After changing modes, configure any PID constants or other settings needed
1357         * and then EnableControl() to actually change the mode on the Jaguar.
1358         *
1359         * @param controlMode The new mode.
1360         *
1361         * @see CANJaguar#setCurrentMode(double, double, double)
1362         * @see CANJaguar#setCurrentMode(PotentiometerTag, double, double, double)
1363         * @see CANJaguar#setCurrentMode(EncoderTag, int, double, double, double)
1364         * @see CANJaguar#setCurrentMode(QuadEncoderTag, int, double, double, double)
1365         * @see CANJaguar#setPercentMode()
1366         * @see CANJaguar#setPercentMode(PotentiometerTag)
1367         * @see CANJaguar#setPercentMode(EncoderTag, int)
1368         * @see CANJaguar#setPercentMode(QuadEncoderTag, int)
1369         * @see CANJaguar#setPositionMode(PotentiometerTag, double, double, double)
1370         * @see CANJaguar#setPositionMode(QuadEncoderTag, int, double, double, double)
1371         * @see CANJaguar#setSpeedMode(EncoderTag, int, double, double, double)
1372         * @see CANJaguar#setSpeedMode(QuadEncoderTag, int, double, double, double)
1373         * @see CANJaguar#setVoltageMode()
1374         * @see CANJaguar#setVoltageMode(PotentiometerTag)
1375         * @see CANJaguar#setVoltageMode(EncoderTag, int)
1376         * @see CANJaguar#setVoltageMode(QuadEncoderTag, int)
1377         */
1378        private void changeControlMode(ControlMode controlMode) {
1379                // Disable the previous mode
1380                disableControl();
1381
1382                // Update the local mode
1383                m_controlMode = controlMode;
1384                m_controlModeVerified = false;
1385        }
1386
1387        /**
1388        * Get the active control mode from the Jaguar.
1389        *
1390        * Ask the Jagaur what mode it is in.
1391        *
1392        * @return ControlMode that the Jag is in.
1393        */
1394        public ControlMode getControlMode() {
1395                return m_controlMode;
1396        }
1397
1398        /**
1399        * Get the voltage at the battery input terminals of the Jaguar.
1400        *
1401        * @return The bus voltage in Volts.
1402        */
1403        public double getBusVoltage() {
1404                updatePeriodicStatus();
1405
1406                return m_busVoltage;
1407        }
1408
1409        /**
1410        * Get the voltage being output from the motor terminals of the Jaguar.
1411        *
1412        * @return The output voltage in Volts.
1413        */
1414        public double getOutputVoltage() {
1415                updatePeriodicStatus();
1416
1417                return m_outputVoltage;
1418        }
1419
1420        /**
1421        * Get the current through the motor terminals of the Jaguar.
1422        *
1423        * @return The output current in Amps.
1424        */
1425        public double getOutputCurrent() {
1426                updatePeriodicStatus();
1427
1428                return m_outputCurrent;
1429        }
1430
1431        /**
1432        * Get the internal temperature of the Jaguar.
1433        *
1434        * @return The temperature of the Jaguar in degrees Celsius.
1435        */
1436        public double getTemperature() {
1437                updatePeriodicStatus();
1438
1439                return m_temperature;
1440        }
1441
1442        /**
1443         * Get the position of the encoder or potentiometer.
1444         *
1445         * @return The position of the motor in rotations based on the configured feedback.
1446         * @see CANJaguar#configPotentiometerTurns(int)
1447         * @see CANJaguar#configEncoderCodesPerRev(int)
1448         */
1449        public double getPosition() {
1450                updatePeriodicStatus();
1451
1452                return m_position;
1453        }
1454
1455        /**
1456        * Get the speed of the encoder.
1457        *
1458        * @return The speed of the motor in RPM based on the configured feedback.
1459        */
1460        public double getSpeed() {
1461                updatePeriodicStatus();
1462
1463                return m_speed;
1464        }
1465
1466        /**
1467         * Get the status of the forward limit switch.
1468         *
1469         * @return true if the motor is allowed to turn in the forward direction.
1470         */
1471        public boolean getForwardLimitOK() {
1472                updatePeriodicStatus();
1473
1474                return (m_limits & kForwardLimit) != 0;
1475        }
1476
1477        /**
1478         * Get the status of the reverse limit switch.
1479         *
1480         * @return true if the motor is allowed to turn in the reverse direction.
1481         */
1482        public boolean getReverseLimitOK() {
1483                updatePeriodicStatus();
1484
1485                return (m_limits & kReverseLimit) != 0;
1486        }
1487
1488        /**
1489         * Get the status of any faults the Jaguar has detected.
1490         *
1491         * @return A bit-mask of faults defined by the "Faults" constants.
1492         * @see #kCurrentFault
1493         * @see #kBusVoltageFault
1494         * @see #kTemperatureFault
1495         * @see #kGateDriverFault
1496         */
1497        public short getFaults() {
1498                updatePeriodicStatus();
1499
1500                return m_faults;
1501        }
1502
1503        /**
1504        * set the maximum voltage change rate.
1505        *
1506        * When in PercentVbus or Voltage output mode, the rate at which the voltage changes can
1507        * be limited to reduce current spikes.  set this to 0.0 to disable rate limiting.
1508        *
1509        * @param rampRate The maximum rate of voltage change in Percent Voltage mode in V/s.
1510        */
1511        public void setVoltageRampRate(double rampRate) {
1512                byte[] data = new byte[8];
1513                int dataSize;
1514                int message;
1515
1516                switch(m_controlMode) {
1517                case PercentVbus:
1518                        dataSize = packPercentage(data, rampRate / (m_maxOutputVoltage * kControllerRate));
1519                        message = CANJNI.LM_API_VOLT_SET_RAMP;
1520                        break;
1521                case Voltage:
1522                        dataSize = packFXP8_8(data, rampRate / kControllerRate);
1523                        message = CANJNI.LM_API_VCOMP_COMP_RAMP;
1524                        break;
1525                default:
1526                        throw new IllegalStateException("Voltage ramp rate only applies in Percentage and Voltage modes");
1527                }
1528
1529                sendMessage(message, data, dataSize);
1530        }
1531
1532        /**
1533        * Get the version of the firmware running on the Jaguar.
1534        *
1535        * @return The firmware version.  0 if the device did not respond.
1536        */
1537        public int getFirmwareVersion() {
1538                return m_firmwareVersion;
1539        }
1540
1541        /**
1542        * Get the version of the Jaguar hardware.
1543        *
1544        * @return The hardware version. 1: Jaguar,  2: Black Jaguar
1545        */
1546        public byte getHardwareVersion() {
1547                return m_hardwareVersion;
1548        }
1549
1550        /**
1551        * Configure what the controller does to the H-Bridge when neutral (not driving the output).
1552        *
1553        * This allows you to override the jumper configuration for brake or coast.
1554        *
1555        * @param mode Select to use the jumper setting or to override it to coast or brake.
1556        */
1557        public void configNeutralMode(NeutralMode mode) {
1558                sendMessage(CANJNI.LM_API_CFG_BRAKE_COAST, new byte[] { mode.value }, 1);
1559
1560                m_neutralMode = mode;
1561                m_neutralModeVerified = false;
1562        }
1563
1564        /**
1565        * Configure how many codes per revolution are generated by your encoder.
1566        *
1567        * @param codesPerRev The number of counts per revolution in 1X mode.
1568        */
1569        public void configEncoderCodesPerRev(int codesPerRev) {
1570                byte[] data = new byte[8];
1571
1572                int dataSize = packINT16(data, (short)codesPerRev);
1573                sendMessage(CANJNI.LM_API_CFG_ENC_LINES, data, dataSize);
1574
1575                m_encoderCodesPerRev = (short)codesPerRev;
1576                m_encoderCodesPerRevVerified = false;
1577        }
1578
1579        /**
1580        * Configure the number of turns on the potentiometer.
1581        *
1582        * There is no special support for continuous turn potentiometers.
1583        * Only integer numbers of turns are supported.
1584        *
1585        * @param turns The number of turns of the potentiometer
1586        */
1587        public void configPotentiometerTurns(int turns) {
1588                byte[] data = new byte[8];
1589
1590                int dataSize = packINT16(data, (short)turns);
1591                sendMessage(CANJNI.LM_API_CFG_POT_TURNS, data, dataSize);
1592
1593                m_potentiometerTurns = (short)turns;
1594                m_potentiometerTurnsVerified = false;
1595        }
1596
1597        /**
1598         * Configure Soft Position Limits when in Position Controller mode.<br>
1599         *
1600         * When controlling position, you can add additional limits on top of the limit switch inputs
1601         * that are based on the position feedback.  If the position limit is reached or the
1602         * switch is opened, that direction will be disabled.
1603         *
1604         * @param forwardLimitPosition The position that, if exceeded, will disable the forward direction.
1605         * @param reverseLimitPosition The position that, if exceeded, will disable the reverse direction.
1606         */
1607        public void configSoftPositionLimits(double forwardLimitPosition, double reverseLimitPosition) {
1608                configLimitMode(LimitMode.SoftPositionLimits);
1609                configForwardLimit(forwardLimitPosition);
1610                configReverseLimit(reverseLimitPosition);
1611        }
1612
1613        /**
1614         * Disable Soft Position Limits if previously enabled.<br>
1615         *
1616         * Soft Position Limits are disabled by default.
1617         */
1618        public void disableSoftPositionLimits() {
1619                configLimitMode(LimitMode.SwitchInputsOnly);
1620        }
1621
1622        /**
1623         * Set the limit mode for position control mode.<br>
1624         *
1625         * Use {@link #configSoftPositionLimits(double, double)} or {@link #disableSoftPositionLimits()} to set this
1626         * automatically.
1627         * @param mode The {@link LimitMode} to use to limit the rotation of the device.
1628         * @see LimitMode#SwitchInputsOnly
1629         * @see LimitMode#SoftPositionLimits
1630         */
1631        public void configLimitMode(LimitMode mode) {
1632                sendMessage(CANJNI.LM_API_CFG_LIMIT_MODE, new byte[] { mode.value }, 1);
1633        }
1634
1635        /**
1636         * Set the position that, if exceeded, will disable the forward direction.
1637         *
1638         * Use {@link #configSoftPositionLimits(double, double)} to set this and the {@link LimitMode} automatically.
1639         * @param forwardLimitPosition The position that, if exceeded, will disable the forward direction.
1640         */
1641        public void configForwardLimit(double forwardLimitPosition) {
1642                byte[] data = new byte[8];
1643
1644                int dataSize = packFXP16_16(data, forwardLimitPosition);
1645                data[dataSize++] = 1;
1646                sendMessage(CANJNI.LM_API_CFG_LIMIT_FWD, data, dataSize);
1647
1648                m_forwardLimit = forwardLimitPosition;
1649                m_forwardLimitVerified = false;
1650        }
1651
1652        /**
1653         * Set the position that, if exceeded, will disable the reverse direction.
1654         *
1655         * Use {@link #configSoftPositionLimits(double, double)} to set this and the {@link LimitMode} automatically.
1656         * @param reverseLimitPosition The position that, if exceeded, will disable the reverse direction.
1657         */
1658        public void configReverseLimit(double reverseLimitPosition) {
1659                byte[] data = new byte[8];
1660
1661                int dataSize = packFXP16_16(data, reverseLimitPosition);
1662                data[dataSize++] = 1;
1663                sendMessage(CANJNI.LM_API_CFG_LIMIT_REV, data, dataSize);
1664
1665                m_reverseLimit = reverseLimitPosition;
1666                m_reverseLimitVerified = false;
1667        }
1668
1669        /**
1670        * Configure the maximum voltage that the Jaguar will ever output.
1671        *
1672        * This can be used to limit the maximum output voltage in all modes so that
1673        * motors which cannot withstand full bus voltage can be used safely.
1674        *
1675        * @param voltage The maximum voltage output by the Jaguar.
1676        */
1677        public void configMaxOutputVoltage(double voltage) {
1678                byte[] data = new byte[8];
1679
1680                int dataSize = packFXP8_8(data, voltage);
1681                sendMessage(CANJNI.LM_API_CFG_MAX_VOUT, data, dataSize);
1682
1683                m_maxOutputVoltage = voltage;
1684                m_maxOutputVoltageVerified = false;
1685        }
1686
1687        /**
1688        * Configure how long the Jaguar waits in the case of a fault before resuming operation.
1689        *
1690        * Faults include over temerature, over current, and bus under voltage.
1691        * The default is 3.0 seconds, but can be reduced to as low as 0.5 seconds.
1692        *
1693        * @param faultTime The time to wait before resuming operation, in seconds.
1694        */
1695        public void configFaultTime(float faultTime) {
1696                byte[] data = new byte[8];
1697
1698                if(faultTime < 0.5f) faultTime = 0.5f;
1699                else if(faultTime > 3.0f) faultTime = 3.0f;
1700
1701                int dataSize = packINT16(data, (short)(faultTime * 1000.0));
1702                sendMessage(CANJNI.LM_API_CFG_FAULT_TIME, data, dataSize);
1703
1704                m_faultTime = faultTime;
1705                m_faultTimeVerified = false;
1706        }
1707
1708        byte m_deviceNumber;
1709        double m_value = 0.0f;
1710
1711        // Parameters/configuration
1712        ControlMode m_controlMode;
1713        int m_speedReference = CANJNI.LM_REF_NONE;
1714        int m_positionReference = CANJNI.LM_REF_NONE;
1715        double m_p = 0.0;
1716        double m_i = 0.0;
1717        double m_d = 0.0;
1718        NeutralMode m_neutralMode = NeutralMode.Jumper;
1719        short m_encoderCodesPerRev = 0;
1720        short m_potentiometerTurns = 0;
1721        LimitMode m_limitMode = LimitMode.SwitchInputsOnly;
1722        double m_forwardLimit = 0.0;
1723        double m_reverseLimit = 0.0;
1724        double m_maxOutputVoltage = kApproxBusVoltage;
1725        double m_voltageRampRate = 0.0;
1726        float m_faultTime = 0.0f;
1727
1728        // Which parameters have been verified since they were last set?
1729        boolean m_controlModeVerified = true;
1730        boolean m_speedRefVerified = true;
1731        boolean m_posRefVerified = true;
1732        boolean m_pVerified = true;
1733        boolean m_iVerified = true;
1734        boolean m_dVerified = true;
1735        boolean m_neutralModeVerified = true;
1736        boolean m_encoderCodesPerRevVerified = true;
1737        boolean m_potentiometerTurnsVerified = true;
1738        boolean m_forwardLimitVerified = true;
1739        boolean m_reverseLimitVerified = true;
1740        boolean m_limitModeVerified = true;
1741        boolean m_maxOutputVoltageVerified = true;
1742        boolean m_voltageRampRateVerified = true;
1743        boolean m_faultTimeVerified = true;
1744
1745        // Status data
1746        double m_busVoltage = 0.0f;
1747        double m_outputVoltage = 0.0f;
1748        double m_outputCurrent = 0.0f;
1749        double m_temperature = 0.0f;
1750        double m_position = 0.0;
1751        double m_speed = 0.0;
1752        byte m_limits = (byte)0;
1753        short m_faults = (short)0;
1754        int m_firmwareVersion = 0;
1755        byte m_hardwareVersion = (byte)0;
1756
1757        // Which periodic status messages have we received at least once?
1758        boolean m_receivedStatusMessage0 = false;
1759        boolean m_receivedStatusMessage1 = false;
1760        boolean m_receivedStatusMessage2 = false;
1761
1762        static final int kReceiveStatusAttempts = 50;
1763
1764        boolean m_controlEnabled = true;
1765
1766        static void sendMessageHelper(int messageID, byte[] data, int dataSize, int period) throws CANMessageNotFoundException {
1767                final int[] kTrustedMessages = {
1768                                CANJNI.LM_API_VOLT_T_EN, CANJNI.LM_API_VOLT_T_SET, CANJNI.LM_API_SPD_T_EN, CANJNI.LM_API_SPD_T_SET,
1769                                CANJNI.LM_API_VCOMP_T_EN, CANJNI.LM_API_VCOMP_T_SET, CANJNI.LM_API_POS_T_EN, CANJNI.LM_API_POS_T_SET,
1770                                CANJNI.LM_API_ICTRL_T_EN, CANJNI.LM_API_ICTRL_T_SET
1771                };
1772
1773                ByteBuffer status = ByteBuffer.allocateDirect(4);
1774                status.order(ByteOrder.LITTLE_ENDIAN);
1775                status.asIntBuffer().put(0, 0);
1776
1777                for(byte i = 0; i < kTrustedMessages.length; i++) {
1778                        if((kFullMessageIDMask & messageID) == kTrustedMessages[i])
1779                        {
1780                                // Make sure the data will still fit after adjusting for the token.
1781                                if (dataSize > kMaxMessageDataSize - 2) {
1782                                        throw new RuntimeException("CAN message has too much data.");
1783                                }
1784
1785                                ByteBuffer trustedBuffer = ByteBuffer.allocateDirect(dataSize+2);
1786                                trustedBuffer.put(0, (byte)0);
1787                                trustedBuffer.put(1, (byte)0);
1788
1789                                for(byte j = 0; j < dataSize; j++) {
1790                                        trustedBuffer.put(j+2, data[j]);
1791                                }
1792
1793                                CANJNI.FRCNetworkCommunicationCANSessionMuxSendMessage(messageID, trustedBuffer, period, status.asIntBuffer());
1794                                int statusCode = status.asIntBuffer().get(0);
1795                                if(statusCode < 0) {
1796                                        CANExceptionFactory.checkStatus(statusCode, messageID);
1797                                }
1798
1799                                return;
1800                        }
1801                }
1802
1803                // Use a null pointer for the data buffer if the given array is null
1804                ByteBuffer buffer;
1805                if(data != null) {
1806                        buffer = ByteBuffer.allocateDirect(dataSize);
1807                        for(byte i = 0; i < dataSize; i++) {
1808                                buffer.put(i, data[i]);
1809                        }
1810                } else {
1811                        buffer = null;
1812                }
1813
1814                CANJNI.FRCNetworkCommunicationCANSessionMuxSendMessage(messageID, buffer, period, status.asIntBuffer());
1815
1816                int statusCode = status.asIntBuffer().get(0);
1817                if(statusCode < 0) {
1818                        CANExceptionFactory.checkStatus(statusCode, messageID);
1819                }
1820        }
1821
1822        /**
1823        * Send a message to the Jaguar.
1824        *
1825        * @param messageID The messageID to be used on the CAN bus (device number
1826        *       is added internally)
1827        * @param data The up to 8 bytes of data to be sent with the message
1828        * @param dataSize Specify how much of the data in "data" to send
1829        * @param period If positive, tell Network Communications to send the
1830        *       message every "period" milliseconds.
1831        */
1832        protected void sendMessage(int messageID, byte[] data, int dataSize, int period) {
1833                sendMessageHelper(messageID | m_deviceNumber, data, dataSize, period);
1834        }
1835
1836        /**
1837        * Send a message to the Jaguar, non-periodically
1838        *
1839        * @param messageID The messageID to be used on the CAN bus (device number
1840        *       is added internally)
1841        * @param data The up to 8 bytes of data to be sent with the message
1842        * @param dataSize Specify how much of the data in "data" to send
1843        */
1844        protected void sendMessage(int messageID, byte[] data, int dataSize) {
1845                sendMessage(messageID, data, dataSize, CANJNI.CAN_SEND_PERIOD_NO_REPEAT);
1846        }
1847
1848        /**
1849        * Request a message from the Jaguar, but don't wait for it to arrive.
1850        *
1851        * @param messageID The message to request
1852        * @param period If positive, tell Network Communications to request the
1853        *       message every "period" milliseconds.
1854        */
1855        protected void requestMessage(int messageID, int period) {
1856                sendMessageHelper(messageID | m_deviceNumber, null, 0, period);
1857        }
1858
1859        /**
1860        * Request a message from the Jaguar, but don't wait for it to arrive.
1861        *
1862        * @param messageID The message to request
1863        */
1864        protected void requestMessage(int messageID) {
1865                requestMessage(messageID, CANJNI.CAN_SEND_PERIOD_NO_REPEAT);
1866        }
1867
1868        /**
1869        * Get a previously requested message.
1870        *
1871        * Jaguar always generates a message with the same message ID when replying.
1872        *
1873        * @param messageID The messageID to read from the CAN bus (device number is added internally)
1874        * @param data The up to 8 bytes of data that was received with the message
1875        *
1876        * @throws CANMessageNotFoundException if there's not new message available
1877        */
1878        protected void getMessage(int messageID, int messageMask, byte[] data) throws CANMessageNotFoundException {
1879                messageID |= m_deviceNumber;
1880                messageID &= CANJNI.CAN_MSGID_FULL_M;
1881
1882                ByteBuffer targetedMessageID = ByteBuffer.allocateDirect(4);
1883                targetedMessageID.order(ByteOrder.LITTLE_ENDIAN);
1884                targetedMessageID.asIntBuffer().put(0, messageID);
1885
1886                ByteBuffer timeStamp = ByteBuffer.allocateDirect(4);
1887
1888                ByteBuffer status = ByteBuffer.allocateDirect(4);
1889                status.order(ByteOrder.LITTLE_ENDIAN);
1890                status.asIntBuffer().put(0, 0);
1891
1892                // Get the data.
1893                ByteBuffer dataBuffer = CANJNI.FRCNetworkCommunicationCANSessionMuxReceiveMessage(
1894                        targetedMessageID.asIntBuffer(),
1895                        messageMask,
1896                        timeStamp,
1897                        status.asIntBuffer());
1898
1899                if(data != null) {
1900                        for(int i = 0; i < dataBuffer.capacity(); i++) {
1901                                data[i] = dataBuffer.get(i);
1902                        }
1903                }
1904
1905                int statusCode = status.asIntBuffer().get(0);
1906                if(statusCode < 0) {
1907                        CANExceptionFactory.checkStatus(statusCode, messageID);
1908                }
1909        }
1910
1911        /**
1912        * Enables periodic status updates from the Jaguar
1913        */
1914        protected void setupPeriodicStatus() {
1915                byte[] data = new byte[8];
1916                int dataSize;
1917
1918                // Message 0 returns bus voltage, output voltage, output current, and
1919                // temperature.
1920                final byte[] kMessage0Data = new byte[] {
1921                        CANJNI.LM_PSTAT_VOLTBUS_B0, CANJNI.LM_PSTAT_VOLTBUS_B1,
1922                        CANJNI.LM_PSTAT_VOLTOUT_B0, CANJNI.LM_PSTAT_VOLTOUT_B1,
1923                        CANJNI.LM_PSTAT_CURRENT_B0, CANJNI.LM_PSTAT_CURRENT_B1,
1924                        CANJNI.LM_PSTAT_TEMP_B0, CANJNI.LM_PSTAT_TEMP_B1
1925                };
1926
1927                // Message 1 returns position and speed
1928                final byte[] kMessage1Data = new byte[] {
1929                        CANJNI.LM_PSTAT_POS_B0, CANJNI.LM_PSTAT_POS_B1, CANJNI.LM_PSTAT_POS_B2, CANJNI.LM_PSTAT_POS_B3,
1930                        CANJNI.LM_PSTAT_SPD_B0, CANJNI.LM_PSTAT_SPD_B1, CANJNI.LM_PSTAT_SPD_B2, CANJNI.LM_PSTAT_SPD_B3
1931                };
1932
1933                // Message 2 returns limits and faults
1934                final byte[] kMessage2Data = new byte[] {
1935                        CANJNI.LM_PSTAT_LIMIT_CLR,
1936                        CANJNI.LM_PSTAT_FAULT,
1937                        CANJNI.LM_PSTAT_END,
1938                        (byte)0,
1939                        (byte)0,
1940                        (byte)0,
1941                        (byte)0,
1942                        (byte)0,
1943                };
1944
1945                dataSize = packINT16(data, (short)(kSendMessagePeriod));
1946                sendMessage(CANJNI.LM_API_PSTAT_PER_EN_S0, data, dataSize);
1947                sendMessage(CANJNI.LM_API_PSTAT_PER_EN_S1, data, dataSize);
1948                sendMessage(CANJNI.LM_API_PSTAT_PER_EN_S2, data, dataSize);
1949
1950                dataSize = 8;
1951                sendMessage(CANJNI.LM_API_PSTAT_CFG_S0, kMessage0Data, dataSize);
1952                sendMessage(CANJNI.LM_API_PSTAT_CFG_S1, kMessage1Data, dataSize);
1953                sendMessage(CANJNI.LM_API_PSTAT_CFG_S2, kMessage2Data, dataSize);
1954        }
1955
1956        /**
1957        * Check for new periodic status updates and unpack them into local variables.
1958        */
1959        protected void updatePeriodicStatus() {
1960                byte[] data = new byte[8];
1961                int dataSize;
1962
1963                // Check if a new bus voltage/output voltage/current/temperature message
1964                // has arrived and unpack the values into the cached member variables
1965                try {
1966                        getMessage(CANJNI.LM_API_PSTAT_DATA_S0, CANJNI.CAN_MSGID_FULL_M, data);
1967
1968                        m_busVoltage    = unpackFXP8_8(new byte[] { data[0], data[1] });
1969                        m_outputVoltage = unpackPercentage(new byte[] { data[2], data[3] }) * m_busVoltage;
1970                        m_outputCurrent = unpackFXP8_8(new byte[] { data[4], data[5] });
1971                        m_temperature   = unpackFXP8_8(new byte[] { data[6], data[7] });
1972
1973                        m_receivedStatusMessage0 = true;
1974                } catch(CANMessageNotFoundException e) {}
1975
1976                // Check if a new position/speed message has arrived and do the same
1977                try {
1978                        getMessage(CANJNI.LM_API_PSTAT_DATA_S1, CANJNI.CAN_MSGID_FULL_M, data);
1979
1980                        m_position = unpackFXP16_16(new byte[] { data[0], data[1], data[2], data[3] });
1981                        m_speed    = unpackFXP16_16(new byte[] { data[4], data[5], data[6], data[7] });
1982
1983                        m_receivedStatusMessage1 = true;
1984                } catch(CANMessageNotFoundException e) {}
1985
1986                // Check if a new limits/faults message has arrived and do the same
1987                try {
1988                        getMessage(CANJNI.LM_API_PSTAT_DATA_S2, CANJNI.CAN_MSGID_FULL_M, data);
1989                        m_limits = data[0];
1990                        m_faults = data[1];
1991
1992                        m_receivedStatusMessage2 = true;
1993                } catch(CANMessageNotFoundException e) {}
1994        }
1995
1996        /**
1997        * Update all the motors that have pending sets in the syncGroup.
1998        *
1999        * @param syncGroup A bitmask of groups to generate synchronous output.
2000        */
2001        public static void updateSyncGroup(byte syncGroup) {
2002                byte[] data = new byte[8];
2003
2004                data[0] = syncGroup;
2005
2006                sendMessageHelper(CANJNI.CAN_MSGID_API_SYNC, data, 1, CANJNI.CAN_SEND_PERIOD_NO_REPEAT);
2007        }
2008
2009        /* we are on ARM-LE now, not Freescale so no need to swap */
2010        private final static void swap16(int x, byte[] buffer) {
2011                buffer[0] = (byte)(x & 0xff);
2012                buffer[1] = (byte)((x>>8) & 0xff);
2013        }
2014
2015        private final static void swap32(int x, byte[] buffer) {
2016                buffer[0] = (byte)(x & 0xff);
2017                buffer[1] = (byte)((x>>8) & 0xff);
2018                buffer[2] = (byte)((x>>16) & 0xff);
2019                buffer[3] = (byte)((x>>24) & 0xff);
2020        }
2021
2022        private static final byte packPercentage(byte[] buffer, double value) {
2023                if(value < -1.0) value = -1.0;
2024                if(value > 1.0) value = 1.0;
2025                short intValue = (short) (value * 32767.0);
2026                swap16(intValue, buffer);
2027                return 2;
2028        }
2029
2030        private static final byte packFXP8_8(byte[] buffer, double value) {
2031                short intValue = (short) (value * 256.0);
2032                swap16(intValue, buffer);
2033                return 2;
2034        }
2035
2036        private static final byte packFXP16_16(byte[] buffer, double value) {
2037                int intValue = (int) (value * 65536.0);
2038                swap32(intValue, buffer);
2039                return 4;
2040        }
2041
2042        private static final byte packINT16(byte[] buffer, short value) {
2043                swap16(value, buffer);
2044                return 2;
2045        }
2046
2047        private static final byte packINT32(byte[] buffer, int value) {
2048                swap32(value, buffer);
2049                return 4;
2050        }
2051
2052        /**
2053        * Unpack 16-bit data from a buffer in little-endian byte order
2054        * @param buffer The buffer to unpack from
2055        * @param offset The offset into he buffer to unpack
2056        * @return The data that was unpacked
2057        */
2058        private static final short unpack16(byte[] buffer, int offset) {
2059                return (short) ((buffer[offset] & 0xFF) | (short) ((buffer[offset + 1] << 8)) & 0xFF00);
2060        }
2061
2062        /**
2063        * Unpack 32-bit data from a buffer in little-endian byte order
2064        * @param buffer The buffer to unpack from
2065        * @param offset The offset into he buffer to unpack
2066        * @return The data that was unpacked
2067        */
2068        private static final int unpack32(byte[] buffer, int offset) {
2069                return (buffer[offset] & 0xFF) | ((buffer[offset + 1] << 8) & 0xFF00) |
2070                        ((buffer[offset + 2] << 16) & 0xFF0000) | ((buffer[offset + 3] << 24) & 0xFF000000);
2071        }
2072
2073        private static final double unpackPercentage(byte[] buffer) {
2074                return unpack16(buffer,0) / 32767.0;
2075        }
2076
2077        private static final double unpackFXP8_8(byte[] buffer) {
2078                return unpack16(buffer,0) / 256.0;
2079        }
2080
2081        private static final double unpackFXP16_16(byte[] buffer) {
2082                return unpack32(buffer,0) / 65536.0;
2083        }
2084
2085        private static final short unpackINT16(byte[] buffer) {
2086                return unpack16(buffer,0);
2087        }
2088
2089        private static final int unpackINT32(byte[] buffer) {
2090                return unpack32(buffer,0);
2091        }
2092
2093        /* Compare floats for equality as fixed point numbers */
2094        public boolean FXP8_EQ(double a, double b) {
2095                return (int)(a * 256.0) == (int)(b * 256.0);
2096        }
2097
2098        /* Compare floats for equality as fixed point numbers */
2099        public boolean FXP16_EQ(double a, double b) {
2100                return (int)(a * 65536.0) == (int)(b * 65536.0);
2101        }
2102
2103        @Override
2104        public void setExpiration(double timeout) {
2105                m_safetyHelper.setExpiration(timeout);
2106        }
2107
2108        @Override
2109        public double getExpiration() {
2110                return m_safetyHelper.getExpiration();
2111        }
2112
2113        @Override
2114        public boolean isAlive() {
2115                return m_safetyHelper.isAlive();
2116        }
2117
2118        @Override
2119        public boolean isSafetyEnabled() {
2120                return m_safetyHelper.isSafetyEnabled();
2121        }
2122
2123        @Override
2124        public void setSafetyEnabled(boolean enabled) {
2125                m_safetyHelper.setSafetyEnabled(enabled);
2126        }
2127
2128        @Override
2129        public String getDescription() {
2130                return "CANJaguar ID "+m_deviceNumber;
2131        }
2132
2133        public int getDeviceID() {
2134                return (int)m_deviceNumber;
2135        }
2136
2137        /**
2138        * Common interface for stopping a motor.
2139        *
2140        * @deprecated Use disableControl instead.
2141        */
2142        @Override
2143        @Deprecated
2144        public void stopMotor() {
2145                disableControl();
2146        }
2147
2148        /*
2149        * Live Window code, only does anything if live window is activated.
2150        */
2151        @Override
2152        public String getSmartDashboardType() {
2153                return "Speed Controller";
2154        }
2155        private ITable m_table = null;
2156        private ITableListener m_table_listener = null;
2157
2158        /**
2159        * {@inheritDoc}
2160        */
2161        @Override
2162        public void initTable(ITable subtable) {
2163                m_table = subtable;
2164                updateTable();
2165        }
2166
2167        /**
2168        * {@inheritDoc}
2169        */
2170        @Override
2171        public void updateTable() {
2172                if (m_table != null) {
2173                        m_table.putNumber("Value", get());
2174                }
2175        }
2176
2177        /**
2178        * {@inheritDoc}
2179        */
2180        @Override
2181        public ITable getTable() {
2182                return m_table;
2183        }
2184
2185        /**
2186        * {@inheritDoc}
2187        */
2188        @Override
2189        public void startLiveWindowMode() {
2190                set(0); // Stop for safety
2191                m_table_listener = new ITableListener() {
2192                        @Override
2193                        public void valueChanged(ITable itable, String key, Object value, boolean bln) {
2194                                set(((Double) value).doubleValue());
2195                        }
2196                };
2197                m_table.addTableListener("Value", m_table_listener, true);
2198        }
2199
2200        /**
2201        * {@inheritDoc}
2202        */
2203        @Override
2204        public void stopLiveWindowMode() {
2205                set(0); // Stop for safety
2206                // TODO: Broken, should only remove the listener from "Value" only.
2207                m_table.removeTableListener(m_table_listener);
2208        }
2209}