001/*
002 * Copyright (c) 2018-2021 REV Robotics
003 *
004 * Redistribution and use in source and binary forms, with or without
005 * modification, are permitted provided that the following conditions are met:
006 *
007 * 1. Redistributions of source code must retain the above copyright notice,
008 *    this list of conditions and the following disclaimer.
009 * 2. Redistributions in binary form must reproduce the above copyright
010 *    notice, this list of conditions and the following disclaimer in the
011 *    documentation and/or other materials provided with the distribution.
012 * 3. Neither the name of REV Robotics nor the names of its
013 *    contributors may be used to endorse or promote products derived from
014 *    this software without specific prior written permission.
015 *
016 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
017 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
020 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
021 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
022 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
023 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
024 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
025 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
026 * POSSIBILITY OF SUCH DAMAGE.
027 */
028
029package com.revrobotics;
030
031import com.revrobotics.CANSparkMaxLowLevel.MotorType;
032import com.revrobotics.jni.CANSparkMaxJNI;
033
034/**
035 * Get an instance of this class by using {@link CANSparkMax#getEncoder()} or {@link
036 * CANSparkMax#getEncoder(Type, int)}.
037 */
038public class SparkMaxRelativeEncoder implements RelativeEncoder {
039  public enum Type {
040    kNoSensor(0),
041    kHallSensor(1),
042    kQuadrature(2);
043
044    @SuppressWarnings("MemberName")
045    public final int value;
046
047    Type(int value) {
048      this.value = value;
049    }
050
051    public static Type fromId(int id) {
052      switch (id) {
053        case 1:
054          return kHallSensor;
055        case 2:
056          return kQuadrature;
057        default:
058          return kNoSensor;
059      }
060    }
061  }
062
063  // package-private to the revrobotics package
064  final Type type;
065  final int countsPerRev;
066  final CANSparkMax sparkMax;
067
068  // package-private to the revrobotics package
069  SparkMaxRelativeEncoder(CANSparkMax sparkMax, Type type, int countsPerRev) {
070    this.sparkMax = sparkMax;
071    this.type = type;
072    this.countsPerRev = countsPerRev;
073
074    if (type == null) {
075      throw new IllegalArgumentException("encoderType must not be null");
076    }
077
078    boolean setCountsPerRevParameter = true;
079
080    if (type == Type.kHallSensor) {
081      // The CPR for a hall sensor is hardcoded in the SPARK MAX firmware as 42
082      setCountsPerRevParameter = false;
083
084      if (sparkMax.motorType == MotorType.kBrushed) {
085        throw new IllegalStateException("A hall sensor cannot be used with a brushed motor");
086      }
087
088      // Make sure that the correct CPR value was provided
089      if (countsPerRev != 42 && countsPerRev != 0) {
090        throw new IllegalArgumentException("countsPerRev must be 42 when using the hall sensor");
091      }
092    } else { // non-hall sensor
093      if (sparkMax.motorType == MotorType.kBrushless) {
094        throw new IllegalStateException(
095            "The encoder type must be kHallSensor when the SPARK MAX is configured in brushless mode.\n"
096                + "To use an external quadrature encoder with a brushless motor, you must wire it as an Alternate Encoder, and then call getAlternateEncoder() on the CANSparkMax object.");
097      }
098
099      if (countsPerRev < 1) {
100        throw new IllegalArgumentException("countsPerRev must be a positive number");
101      }
102    }
103
104    CANSparkMaxJNI.c_SparkMax_SetSensorType(sparkMax.sparkMaxHandle, type.value);
105    if (setCountsPerRevParameter) {
106      CANSparkMaxJNI.c_SparkMax_SetCountsPerRevolution(sparkMax.sparkMaxHandle, countsPerRev);
107    }
108  }
109
110  @Override
111  public double getPosition() {
112    sparkMax.throwIfClosed();
113    return CANSparkMaxJNI.c_SparkMax_GetEncoderPosition(sparkMax.sparkMaxHandle);
114  }
115
116  @Override
117  public double getVelocity() {
118    sparkMax.throwIfClosed();
119    return CANSparkMaxJNI.c_SparkMax_GetEncoderVelocity(sparkMax.sparkMaxHandle);
120  }
121
122  @Override
123  public REVLibError setPosition(double position) {
124    sparkMax.throwIfClosed();
125    return REVLibError.fromInt(
126        CANSparkMaxJNI.c_SparkMax_SetEncoderPosition(sparkMax.sparkMaxHandle, (float) position));
127  }
128
129  @Override
130  public REVLibError setPositionConversionFactor(double factor) {
131    sparkMax.throwIfClosed();
132    return REVLibError.fromInt(
133        CANSparkMaxJNI.c_SparkMax_SetPositionConversionFactor(
134            sparkMax.sparkMaxHandle, (float) factor));
135  }
136
137  @Override
138  public REVLibError setVelocityConversionFactor(double factor) {
139    sparkMax.throwIfClosed();
140    return REVLibError.fromInt(
141        CANSparkMaxJNI.c_SparkMax_SetVelocityConversionFactor(
142            sparkMax.sparkMaxHandle, (float) factor));
143  }
144
145  @Override
146  public double getPositionConversionFactor() {
147    sparkMax.throwIfClosed();
148    return CANSparkMaxJNI.c_SparkMax_GetPositionConversionFactor(sparkMax.sparkMaxHandle);
149  }
150
151  @Override
152  public double getVelocityConversionFactor() {
153    sparkMax.throwIfClosed();
154    return CANSparkMaxJNI.c_SparkMax_GetVelocityConversionFactor(sparkMax.sparkMaxHandle);
155  }
156
157  @Override
158  public REVLibError setAverageDepth(int depth) {
159    sparkMax.throwIfClosed();
160    return REVLibError.fromInt(
161        CANSparkMaxJNI.c_SparkMax_SetAverageDepth(sparkMax.sparkMaxHandle, depth));
162  }
163
164  @Override
165  public int getAverageDepth() {
166    sparkMax.throwIfClosed();
167    return CANSparkMaxJNI.c_SparkMax_GetAverageDepth(sparkMax.sparkMaxHandle);
168  }
169
170  @Override
171  public REVLibError setMeasurementPeriod(int period_us) {
172    sparkMax.throwIfClosed();
173    return REVLibError.fromInt(
174        CANSparkMaxJNI.c_SparkMax_SetMeasurementPeriod(sparkMax.sparkMaxHandle, period_us));
175  }
176
177  @Override
178  public int getMeasurementPeriod() {
179    sparkMax.throwIfClosed();
180    return CANSparkMaxJNI.c_SparkMax_GetMeasurementPeriod(sparkMax.sparkMaxHandle);
181  }
182
183  @Override
184  public int getCountsPerRevolution() {
185    sparkMax.throwIfClosed();
186    return CANSparkMaxJNI.c_SparkMax_GetCountsPerRevolution(sparkMax.sparkMaxHandle);
187  }
188
189  @Override
190  public REVLibError setInverted(boolean inverted) {
191    sparkMax.throwIfClosed();
192    if (sparkMax.getMotorType() == MotorType.kBrushless) {
193      throw new IllegalArgumentException(
194          "The encoder cannot be inverted separately from the motor in Brushless Mode");
195    }
196    return REVLibError.fromInt(
197        CANSparkMaxJNI.c_SparkMax_SetEncoderInverted(sparkMax.sparkMaxHandle, inverted));
198  }
199
200  @Override
201  public boolean getInverted() {
202    sparkMax.throwIfClosed();
203    return CANSparkMaxJNI.c_SparkMax_GetEncoderInverted(sparkMax.sparkMaxHandle);
204  }
205
206  // package-private to the revrobotics package
207  int getSparkMaxFeedbackDeviceId() {
208    return (sparkMax.getMotorType() == MotorType.kBrushless)
209        ? CANSparkMaxLowLevel.FeedbackSensorType.kHallSensor.value
210        : CANSparkMaxLowLevel.FeedbackSensorType.kQuadrature.value;
211  }
212}