Skip to content

Commit dc58e45

Browse files
Add shift tracking (#105)
* Add shift tracking * Nominal velocity when active * Fix * Cleanup warnings * Fix again sorry
1 parent 3b7ea8c commit dc58e45

5 files changed

Lines changed: 260 additions & 14 deletions

File tree

src/main/java/frc/robot/RobotContainer.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import static edu.wpi.first.units.Units.Meters;
1010
import static edu.wpi.first.units.Units.MetersPerSecond;
1111
import static edu.wpi.first.units.Units.RadiansPerSecond;
12+
import static edu.wpi.first.units.Units.RotationsPerSecond;
1213

1314
import java.util.Arrays;
15+
import java.util.Optional;
1416

1517
import org.ironmaple.simulation.drivesims.COTS;
1618
import org.ironmaple.simulation.drivesims.configs.DriveTrainSimulationConfig;
@@ -22,6 +24,7 @@
2224
import edu.wpi.first.math.geometry.Pose2d;
2325
import edu.wpi.first.math.geometry.Rotation2d;
2426
import edu.wpi.first.math.system.plant.DCMotor;
27+
import edu.wpi.first.wpilibj.DriverStation;
2528
import edu.wpi.first.wpilibj2.command.Command;
2629
import edu.wpi.first.wpilibj2.command.Commands;
2730
import edu.wpi.first.wpilibj2.command.button.CommandXboxController;
@@ -63,6 +66,7 @@
6366
import frc.robot.subsystems.vision.CameraIOLimelight4;
6467
import frc.robot.subsystems.vision.CameraIOPhotonSim;
6568
import frc.robot.subsystems.vision.VisionConstants;
69+
import frc.robot.util.HubShiftUtil;
6670
import frc.robot.util.MapleSimUtil;
6771
import frc.robot.util.Mechanism3d;
6872

@@ -264,6 +268,14 @@ public RobotContainer() {
264268
testBindings_.addOption("Hood Calibration", shooter_.hoodCalibration());
265269
testBindings_.addOption("Startup Sequence Test", new StartupCmd(intake_, hopper_, shooter_)) ;
266270

271+
// Auto Win Override
272+
LoggedNetworkBoolean winAutoPractice = new LoggedNetworkBoolean("Win Auto (Practice)", true);
273+
274+
HubShiftUtil.setAllianceWinOverride(() -> {
275+
if (DriverStation.isFMSAttached()) return Optional.empty();
276+
return Optional.of(winAutoPractice.get());
277+
});
278+
267279
// Sets the selected test binding to be triggered when the A button is pressed in test mode.
268280
RobotModeTriggers.test().and(gamepad_.a()).toggleOnTrue(Commands.deferredProxy(testBindings_::get));
269281

@@ -291,7 +303,10 @@ private void configureBindings() {
291303
.whileTrue(RobotCommands.shoot(shooter_, hopper_, intake_, drivebase_));
292304

293305
// When the shooter isnt shooting, get it ready to shoot.
294-
shooter_.setDefaultCommand(shooter_.idleCommand());
306+
shooter_.setDefaultCommand(shooter_.spinUpVelocityCommand(() -> {
307+
var shift = HubShiftUtil.getOfficialShiftInfo();
308+
return shift.active() ? RotationsPerSecond.of(50) : RotationsPerSecond.zero();
309+
}));
295310

296311
//While the X button is held, the intake will run the eject sequence. If it the intake is stowed, it will also deploy it.
297312

src/main/java/frc/robot/RobotState.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import edu.wpi.first.wpilibj.DriverStation;
1515
import edu.wpi.first.wpilibj.DriverStation.Alliance;
1616
import frc.robot.subsystems.shooter.ShooterConstants;
17+
import frc.robot.util.HubShiftUtil;
1718
import frc.robot.util.LoggedTracer;
1819

1920
/**
@@ -42,6 +43,8 @@ public static void initialize(Supplier<Pose2d> poseSupplier) {
4243
*/
4344
public static void periodic() {
4445
LoggedTracer.reset();
46+
47+
// Calculations
4548

4649
Pose2d currentPose = pose.get();
4750

@@ -66,6 +69,10 @@ public static void periodic() {
6669

6770
inAllianceZone = currentPose.getMeasureX().minus(allianceWall).abs(Meters) < ShooterConstants.Positions.spinUpZone.in(Meters);
6871

72+
// Other logging
73+
74+
Logger.recordOutput("ShiftInfo", HubShiftUtil.getOfficialShiftInfo());
75+
6976
LoggedTracer.record("RobotState");
7077
}
7178

src/main/java/frc/robot/subsystems/drive/Drive.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@
1515

1616
import static edu.wpi.first.units.Units.Degrees;
1717
import static edu.wpi.first.units.Units.MetersPerSecond;
18-
import static edu.wpi.first.units.Units.Radians;
1918
import static edu.wpi.first.units.Units.Volts;
20-
import static edu.wpi.first.units.Units.derive;
2119

2220
import java.util.concurrent.locks.Lock;
2321
import java.util.concurrent.locks.ReentrantLock;
@@ -54,7 +52,6 @@
5452
import edu.wpi.first.math.numbers.N1;
5553
import edu.wpi.first.math.numbers.N3;
5654
import edu.wpi.first.math.system.plant.DCMotor;
57-
import edu.wpi.first.units.AngleUnit;
5855
import edu.wpi.first.units.measure.Angle;
5956
import edu.wpi.first.units.measure.AngularVelocity;
6057
import edu.wpi.first.units.measure.LinearVelocity;
@@ -63,7 +60,6 @@
6360
import edu.wpi.first.wpilibj.DriverStation;
6461
import edu.wpi.first.wpilibj.DriverStation.Alliance;
6562
import edu.wpi.first.wpilibj2.command.Command;
66-
import edu.wpi.first.wpilibj2.command.Commands;
6763
import edu.wpi.first.wpilibj2.command.SubsystemBase;
6864
import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine;
6965
import frc.robot.Constants;

src/main/java/frc/robot/subsystems/shooter/Shooter.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -327,15 +327,12 @@ public Command ejectUp() {
327327
return startEnd(() -> setShooterVelocity(ShooterConstants.ejectVelocity), this::stopShooter);
328328
}
329329

330-
/**
331-
* Idles the shooter at 0 velocity and the hood at the parked position.
332-
* @return
333-
*/
334-
public Command idleCommand() {
335-
return runToSetpointsCmd(
336-
RotationsPerSecond.of(0.0),
337-
ShooterConstants.hoodParkedAngle
338-
);
330+
public Command spinUpSetpointsCommand(Supplier<AngularVelocity> velocity, Angle hoodAngle) {
331+
return run(() -> setSetpoints(velocity.get(), hoodAngle));
332+
}
333+
334+
public Command spinUpVelocityCommand(Supplier<AngularVelocity> velocity) {
335+
return run(() -> setSetpoints(velocity.get(), ShooterConstants.hoodParkedAngle));
339336
}
340337

341338
/**
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
// Copyright (c) 2025-2026 Littleton Robotics
2+
// http://github.com/Mechanical-Advantage
3+
//
4+
5+
// MIT License
6+
7+
// Copyright (c) 2025-2026 Littleton Robotics
8+
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
16+
// The above copyright notice and this permission notice shall be included in all
17+
// copies or substantial portions of the Software.
18+
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
// SOFTWARE.
26+
27+
28+
package frc.robot.util;
29+
30+
import java.util.Optional;
31+
import java.util.function.Supplier;
32+
33+
import org.littletonrobotics.junction.AutoLogOutput;
34+
35+
import edu.wpi.first.wpilibj.DriverStation;
36+
import edu.wpi.first.wpilibj.DriverStation.Alliance;
37+
import edu.wpi.first.wpilibj.Timer;
38+
39+
public class HubShiftUtil {
40+
public enum ShiftEnum {
41+
TRANSITION,
42+
SHIFT1,
43+
SHIFT2,
44+
SHIFT3,
45+
SHIFT4,
46+
ENDGAME,
47+
AUTO,
48+
DISABLED;
49+
}
50+
51+
public record ShiftInfo(
52+
ShiftEnum currentShift, double elapsedTime, double remainingTime, boolean active) {}
53+
54+
private static Timer shiftTimer = new Timer();
55+
private static final ShiftEnum[] shiftsEnums = ShiftEnum.values();
56+
57+
private static final double[] shiftStartTimes = {0.0, 10.0, 35.0, 60.0, 85.0, 110.0};
58+
private static final double[] shiftEndTimes = {10.0, 35.0, 60.0, 85.0, 110.0, 140.0};
59+
60+
// private static final double minFuelCountDelay = 1.0;
61+
// private static final double maxFuelCountDelay = 2.0;
62+
// private static final double shiftEndFuelCountExtension = 3.0;
63+
64+
public static final double autoEndTime = 20.0;
65+
public static final double teleopDuration = 140.0;
66+
private static final boolean[] activeSchedule = {true, true, false, true, false, true};
67+
private static final boolean[] inactiveSchedule = {true, false, true, false, true, true};
68+
private static final double timeResetThreshold = 1.0;
69+
private static double shiftTimerOffset = 0.0;
70+
private static Supplier<Optional<Boolean>> allianceWinOverride = () -> Optional.empty();
71+
72+
public static Optional<Boolean> getAllianceWinOverride() {
73+
return allianceWinOverride.get();
74+
}
75+
76+
public static void setAllianceWinOverride(Supplier<Optional<Boolean>> win) {
77+
allianceWinOverride = win;
78+
}
79+
80+
public static Alliance getFirstActiveAlliance() {
81+
var alliance = DriverStation.getAlliance().orElse(Alliance.Blue);
82+
83+
// Return override value
84+
var winOverride = getAllianceWinOverride();
85+
if (!winOverride.isEmpty()) {
86+
return winOverride.get()
87+
? (alliance == Alliance.Blue ? Alliance.Red : Alliance.Blue)
88+
: (alliance == Alliance.Blue ? Alliance.Blue : Alliance.Red);
89+
}
90+
91+
// Return FMS value
92+
String message = DriverStation.getGameSpecificMessage();
93+
if (message.length() > 0) {
94+
char character = message.charAt(0);
95+
if (character == 'R') {
96+
return Alliance.Blue;
97+
} else if (character == 'B') {
98+
return Alliance.Red;
99+
}
100+
}
101+
102+
// Return default value
103+
return alliance == Alliance.Blue ? Alliance.Red : Alliance.Blue;
104+
}
105+
106+
/** Starts the timer at the begining of teleop. */
107+
public static void initialize() {
108+
shiftTimerOffset = 0;
109+
shiftTimer.restart();
110+
}
111+
112+
private static boolean[] getSchedule() {
113+
boolean[] currentSchedule;
114+
Alliance startAlliance = getFirstActiveAlliance();
115+
currentSchedule =
116+
startAlliance == DriverStation.getAlliance().orElse(Alliance.Blue)
117+
? activeSchedule
118+
: inactiveSchedule;
119+
return currentSchedule;
120+
}
121+
122+
@AutoLogOutput
123+
private static ShiftInfo getShiftInfo(
124+
boolean[] currentSchedule, double[] shiftStartTimes, double[] shiftEndTimes) {
125+
double timerValue = shiftTimer.get();
126+
double currentTime = timerValue - shiftTimerOffset;
127+
double stateTimeElapsed = currentTime;
128+
double stateTimeRemaining = 0.0;
129+
boolean active = false;
130+
ShiftEnum currentShift = ShiftEnum.DISABLED;
131+
double fieldTeleopTime = 140.0 - DriverStation.getMatchTime();
132+
133+
if (DriverStation.isAutonomousEnabled()) {
134+
stateTimeElapsed = currentTime;
135+
stateTimeRemaining = autoEndTime - currentTime;
136+
active = true;
137+
currentShift = ShiftEnum.AUTO;
138+
} else if (DriverStation.isEnabled()) {
139+
// Adjust the current offset if the time difference above the theshold
140+
if (Math.abs(fieldTeleopTime - currentTime) >= timeResetThreshold
141+
&& fieldTeleopTime <= 135
142+
&& DriverStation.isFMSAttached()) {
143+
// shiftTimerOffset += currentTime - fieldTeleopTime;
144+
// currentTime = timerValue + shiftTimerOffset;
145+
146+
shiftTimerOffset = timerValue - fieldTeleopTime;
147+
currentTime = fieldTeleopTime;
148+
}
149+
int currentShiftIndex = -1;
150+
for (int i = 0; i < shiftStartTimes.length; i++) {
151+
if (currentTime >= shiftStartTimes[i] && currentTime < shiftEndTimes[i]) {
152+
currentShiftIndex = i;
153+
break;
154+
}
155+
}
156+
if (currentShiftIndex < 0) {
157+
// After last shift, so assume endgame
158+
currentShiftIndex = shiftStartTimes.length - 1;
159+
}
160+
161+
// Calculate elapsed and remaining time in the current shift, ignoring combined shifts
162+
stateTimeElapsed = currentTime - shiftStartTimes[currentShiftIndex];
163+
stateTimeRemaining = shiftEndTimes[currentShiftIndex] - currentTime;
164+
165+
// If the state is the same as the last shift, combine the elapsed time
166+
if (currentShiftIndex > 0) {
167+
if (currentSchedule[currentShiftIndex] == currentSchedule[currentShiftIndex - 1]) {
168+
stateTimeElapsed = currentTime - shiftStartTimes[currentShiftIndex - 1];
169+
}
170+
}
171+
172+
// If the state is the same as the next shift, combine the remaining time
173+
if (currentShiftIndex < shiftEndTimes.length - 1) {
174+
if (currentSchedule[currentShiftIndex] == currentSchedule[currentShiftIndex + 1]) {
175+
stateTimeRemaining = shiftEndTimes[currentShiftIndex + 1] - currentTime;
176+
}
177+
}
178+
179+
active = currentSchedule[currentShiftIndex];
180+
currentShift = shiftsEnums[currentShiftIndex];
181+
}
182+
ShiftInfo shiftInfo = new ShiftInfo(currentShift, stateTimeElapsed, stateTimeRemaining, active);
183+
return shiftInfo;
184+
}
185+
186+
public static ShiftInfo getOfficialShiftInfo() {
187+
return getShiftInfo(getSchedule(), shiftStartTimes, shiftEndTimes);
188+
}
189+
190+
// public static ShiftInfo getShiftedShiftInfo() {
191+
// boolean[] shiftSchedule = getSchedule();
192+
// // Starting active
193+
// if (shiftSchedule[1] == true) {
194+
// double[] shiftedShiftStartTimes = {
195+
// 0.0,
196+
// 10.0,
197+
// 35.0 + endingActiveFudge,
198+
// 60.0 + approachingActiveFudge,
199+
// 85.0 + endingActiveFudge,
200+
// 110.0 + approachingActiveFudge
201+
// };
202+
// double[] shiftedShiftEndTimes = {
203+
// 10.0,
204+
// 35.0 + endingActiveFudge,
205+
// 60.0 + approachingActiveFudge,
206+
// 85.0 + endingActiveFudge,
207+
// 110.0 + approachingActiveFudge,
208+
// 140.0
209+
// };
210+
// return getShiftInfo(shiftSchedule, shiftedShiftStartTimes, shiftedShiftEndTimes);
211+
// }
212+
// double[] shiftedShiftStartTimes = {
213+
// 0.0,
214+
// 10.0 + endingActiveFudge,
215+
// 35.0 + approachingActiveFudge,
216+
// 60.0 + endingActiveFudge,
217+
// 85.0 + approachingActiveFudge,
218+
// 110.0
219+
// };
220+
// double[] shiftedShiftEndTimes = {
221+
// 10.0 + endingActiveFudge,
222+
// 35.0 + approachingActiveFudge,
223+
// 60.0 + endingActiveFudge,
224+
// 85.0 + approachingActiveFudge,
225+
// 110.0,
226+
// 140.0
227+
// };
228+
// return getShiftInfo(shiftSchedule, shiftedShiftStartTimes, shiftedShiftEndTimes);
229+
// // }
230+
// }
231+
}

0 commit comments

Comments
 (0)