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}