001/*
002 * Copyright (C) Cross The Road Electronics.  All rights reserved.
003 * License information can be found in CTRE_LICENSE.txt
004 * For support and suggestions contact support@ctr-electronics.com or file
005 * an issue tracker at https://github.com/CrossTheRoadElec/Phoenix-Releases
006 */
007package com.ctre.phoenix6.controls;
008
009import com.ctre.phoenix6.CANBus;
010import com.ctre.phoenix6.StatusCode;
011import com.ctre.phoenix6.controls.jni.ControlJNI;
012import com.ctre.phoenix6.hardware.traits.*;
013
014import edu.wpi.first.units.*;
015import edu.wpi.first.units.measure.*;
016import static edu.wpi.first.units.Units.*;
017
018import java.util.HashMap;
019import java.util.Map;
020
021/**
022 * Request PID to target position with a differential position setpoint.
023 * <p>
024 * This control mode will set the motor's position setpoint to the position specified by the user. It will
025 * also set the motor's differential position setpoint to the specified position.
026 */
027public final class DifferentialPositionDutyCycle implements ControlRequest, Cloneable {
028    /**
029     * Average position to drive toward in rotations.
030     * 
031     * <ul>
032     *   <li> Units: rotations
033     * </ul>
034     * 
035     */
036    public double AveragePosition;
037    /**
038     * Differential position to drive toward in rotations.
039     * 
040     * <ul>
041     *   <li> Units: rotations
042     * </ul>
043     * 
044     */
045    public double DifferentialPosition;
046    /**
047     * Set to true to use FOC commutation (requires Phoenix Pro), which increases
048     * peak power by ~15% on supported devices (see {@link SupportsFOC}). Set to
049     * false to use trapezoidal commutation.
050     * <p>
051     * FOC improves motor performance by leveraging torque (current) control. 
052     * However, this may be inconvenient for applications that require specifying
053     * duty cycle or voltage.  CTR-Electronics has developed a hybrid method that
054     * combines the performances gains of FOC while still allowing applications to
055     * provide duty cycle or voltage demand.  This not to be confused with simple
056     * sinusoidal control or phase voltage control which lacks the performance
057     * gains.
058     */
059    public boolean EnableFOC = true;
060    /**
061     * Select which gains are applied to the average controller by selecting the
062     * slot.  Use the configuration api to set the gain values for the selected slot
063     * before enabling this feature. Slot must be within [0,2].
064     */
065    public int AverageSlot = 0;
066    /**
067     * Select which gains are applied to the differential controller by selecting
068     * the slot.  Use the configuration api to set the gain values for the selected
069     * slot before enabling this feature. Slot must be within [0,2].
070     */
071    public int DifferentialSlot = 1;
072    /**
073     * Set to true to static-brake the rotor when output is zero (or within
074     * deadband).  Set to false to use the NeutralMode configuration setting
075     * (default). This flag exists to provide the fundamental behavior of this
076     * control when output is zero, which is to provide 0V to the motor.
077     */
078    public boolean OverrideBrakeDurNeutral = false;
079    /**
080     * Set to true to force forward limiting.  This allows users to use other limit
081     * switch sensors connected to robot controller.  This also allows use of active
082     * sensors that require external power.
083     */
084    public boolean LimitForwardMotion = false;
085    /**
086     * Set to true to force reverse limiting.  This allows users to use other limit
087     * switch sensors connected to robot controller.  This also allows use of active
088     * sensors that require external power.
089     */
090    public boolean LimitReverseMotion = false;
091    /**
092     * Set to true to ignore hardware limit switches and the LimitForwardMotion and
093     * LimitReverseMotion parameters, instead allowing motion.
094     * <p>
095     * This can be useful on mechanisms such as an intake/feeder, where a limit
096     * switch stops motion while intaking but should be ignored when feeding to a
097     * shooter.
098     * <p>
099     * The hardware limit faults and Forward/ReverseLimit signals will still report
100     * the values of the limit switches regardless of this parameter.
101     */
102    public boolean IgnoreHardwareLimits = false;
103    /**
104     * Set to true to ignore software limits, instead allowing motion.
105     * <p>
106     * This can be useful when calibrating the zero point of a mechanism such as an
107     * elevator.
108     * <p>
109     * The software limit faults will still report the values of the software limits
110     * regardless of this parameter.
111     */
112    public boolean IgnoreSoftwareLimits = false;
113    /**
114     * Set to true to delay applying this control request until a timesync boundary
115     * (requires Phoenix Pro and CANivore). This eliminates the impact of
116     * nondeterministic network delays in exchange for a larger but deterministic
117     * control latency.
118     * <p>
119     * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
120     * Additionally, when this is enabled, the UpdateFreqHz of this request should
121     * be set to 0 Hz.
122     */
123    public boolean UseTimesync = false;
124
125    /**
126     * The frequency at which this control will update.
127     * This is designated in Hertz, with a minimum of 20 Hz
128     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
129     * Some update frequencies are not supported and will be
130     * promoted up to the next highest supported frequency.
131     * <p>
132     * If this field is set to 0 Hz, the control request will
133     * be sent immediately as a one-shot frame. This may be useful
134     * for advanced applications that require outputs to be
135     * synchronized with data acquisition. In this case, we
136     * recommend not exceeding 50 ms between control calls.
137     */
138    public double UpdateFreqHz = 100;
139
140    /**
141     * Request PID to target position with a differential position setpoint.
142     * <p>
143     * This control mode will set the motor's position setpoint to the position
144     * specified by the user. It will also set the motor's differential position
145     * setpoint to the specified position.
146     * 
147     * @param AveragePosition Average position to drive toward in rotations.
148     * @param DifferentialPosition Differential position to drive toward in
149     *                             rotations.
150     */
151    public DifferentialPositionDutyCycle(double AveragePosition, double DifferentialPosition) {
152        this.AveragePosition = AveragePosition;
153        this.DifferentialPosition = DifferentialPosition;
154    }
155
156    /**
157     * Request PID to target position with a differential position setpoint.
158     * <p>
159     * This control mode will set the motor's position setpoint to the position
160     * specified by the user. It will also set the motor's differential position
161     * setpoint to the specified position.
162     * 
163     * @param AveragePosition Average position to drive toward in rotations.
164     * @param DifferentialPosition Differential position to drive toward in
165     *                             rotations.
166     */
167    public DifferentialPositionDutyCycle(Angle AveragePosition, Angle DifferentialPosition) {
168        this(AveragePosition.in(Rotations), DifferentialPosition.in(Rotations));
169    }
170
171    @Override
172    public String getName() {
173        return "DifferentialPositionDutyCycle";
174    }
175
176    @Override
177    public String toString() {
178        String ss = "Control: DifferentialPositionDutyCycle\n";
179        ss += "    AveragePosition: " + AveragePosition + " rotations" + "\n";
180        ss += "    DifferentialPosition: " + DifferentialPosition + " rotations" + "\n";
181        ss += "    EnableFOC: " + EnableFOC + "\n";
182        ss += "    AverageSlot: " + AverageSlot + "\n";
183        ss += "    DifferentialSlot: " + DifferentialSlot + "\n";
184        ss += "    OverrideBrakeDurNeutral: " + OverrideBrakeDurNeutral + "\n";
185        ss += "    LimitForwardMotion: " + LimitForwardMotion + "\n";
186        ss += "    LimitReverseMotion: " + LimitReverseMotion + "\n";
187        ss += "    IgnoreHardwareLimits: " + IgnoreHardwareLimits + "\n";
188        ss += "    IgnoreSoftwareLimits: " + IgnoreSoftwareLimits + "\n";
189        ss += "    UseTimesync: " + UseTimesync + "\n";
190        return ss;
191    }
192
193    @Override
194    public StatusCode sendRequest(CANBus network, int deviceHash) {
195        return StatusCode.valueOf(ControlJNI.JNI_RequestControlDifferentialPositionDutyCycle(
196                network.getNameUTF8Bytes(), deviceHash, UpdateFreqHz, AveragePosition, DifferentialPosition, EnableFOC, AverageSlot, DifferentialSlot, OverrideBrakeDurNeutral, LimitForwardMotion, LimitReverseMotion, IgnoreHardwareLimits, IgnoreSoftwareLimits, UseTimesync));
197    }
198
199    /**
200     * Gets information about this control request.
201     *
202     * @return Map of control parameter names and corresponding applied values
203     */
204    @Override
205    public Map<String, String> getControlInfo() {
206        var controlInfo = new HashMap<String, String>();
207        controlInfo.put("Name", getName());
208        controlInfo.put("AveragePosition", String.valueOf(this.AveragePosition));
209        controlInfo.put("DifferentialPosition", String.valueOf(this.DifferentialPosition));
210        controlInfo.put("EnableFOC", String.valueOf(this.EnableFOC));
211        controlInfo.put("AverageSlot", String.valueOf(this.AverageSlot));
212        controlInfo.put("DifferentialSlot", String.valueOf(this.DifferentialSlot));
213        controlInfo.put("OverrideBrakeDurNeutral", String.valueOf(this.OverrideBrakeDurNeutral));
214        controlInfo.put("LimitForwardMotion", String.valueOf(this.LimitForwardMotion));
215        controlInfo.put("LimitReverseMotion", String.valueOf(this.LimitReverseMotion));
216        controlInfo.put("IgnoreHardwareLimits", String.valueOf(this.IgnoreHardwareLimits));
217        controlInfo.put("IgnoreSoftwareLimits", String.valueOf(this.IgnoreSoftwareLimits));
218        controlInfo.put("UseTimesync", String.valueOf(this.UseTimesync));
219        return controlInfo;
220    }
221    
222    /**
223     * Modifies this Control Request's AveragePosition parameter and returns itself for
224     * method-chaining and easier to use request API.
225     * <p>
226     * Average position to drive toward in rotations.
227     * 
228     * <ul>
229     *   <li> Units: rotations
230     * </ul>
231     * 
232     *
233     * @param newAveragePosition Parameter to modify
234     * @return Itself
235     */
236    public DifferentialPositionDutyCycle withAveragePosition(double newAveragePosition) {
237        AveragePosition = newAveragePosition;
238        return this;
239    }
240    
241    /**
242     * Modifies this Control Request's AveragePosition parameter and returns itself for
243     * method-chaining and easier to use request API.
244     * <p>
245     * Average position to drive toward in rotations.
246     * 
247     * <ul>
248     *   <li> Units: rotations
249     * </ul>
250     * 
251     *
252     * @param newAveragePosition Parameter to modify
253     * @return Itself
254     */
255    public DifferentialPositionDutyCycle withAveragePosition(Angle newAveragePosition) {
256        AveragePosition = newAveragePosition.in(Rotations);
257        return this;
258    }
259    
260    /**
261     * Helper method to get this Control Request's AveragePosition parameter converted
262     * to a unit type. If not using the Java units library, {@link #AveragePosition}
263     * can be accessed directly instead.
264     * <p>
265     * Average position to drive toward in rotations.
266     * 
267     * <ul>
268     *   <li> Units: rotations
269     * </ul>
270     * 
271     *
272     * @return AveragePosition
273     */
274    public Angle getAveragePositionMeasure() {
275        return Rotations.of(AveragePosition);
276    }
277    
278    /**
279     * Modifies this Control Request's DifferentialPosition parameter and returns itself for
280     * method-chaining and easier to use request API.
281     * <p>
282     * Differential position to drive toward in rotations.
283     * 
284     * <ul>
285     *   <li> Units: rotations
286     * </ul>
287     * 
288     *
289     * @param newDifferentialPosition Parameter to modify
290     * @return Itself
291     */
292    public DifferentialPositionDutyCycle withDifferentialPosition(double newDifferentialPosition) {
293        DifferentialPosition = newDifferentialPosition;
294        return this;
295    }
296    
297    /**
298     * Modifies this Control Request's DifferentialPosition parameter and returns itself for
299     * method-chaining and easier to use request API.
300     * <p>
301     * Differential position to drive toward in rotations.
302     * 
303     * <ul>
304     *   <li> Units: rotations
305     * </ul>
306     * 
307     *
308     * @param newDifferentialPosition Parameter to modify
309     * @return Itself
310     */
311    public DifferentialPositionDutyCycle withDifferentialPosition(Angle newDifferentialPosition) {
312        DifferentialPosition = newDifferentialPosition.in(Rotations);
313        return this;
314    }
315    
316    /**
317     * Helper method to get this Control Request's DifferentialPosition parameter converted
318     * to a unit type. If not using the Java units library, {@link #DifferentialPosition}
319     * can be accessed directly instead.
320     * <p>
321     * Differential position to drive toward in rotations.
322     * 
323     * <ul>
324     *   <li> Units: rotations
325     * </ul>
326     * 
327     *
328     * @return DifferentialPosition
329     */
330    public Angle getDifferentialPositionMeasure() {
331        return Rotations.of(DifferentialPosition);
332    }
333    
334    /**
335     * Modifies this Control Request's EnableFOC parameter and returns itself for
336     * method-chaining and easier to use request API.
337     * <p>
338     * Set to true to use FOC commutation (requires Phoenix Pro), which increases
339     * peak power by ~15% on supported devices (see {@link SupportsFOC}). Set to
340     * false to use trapezoidal commutation.
341     * <p>
342     * FOC improves motor performance by leveraging torque (current) control. 
343     * However, this may be inconvenient for applications that require specifying
344     * duty cycle or voltage.  CTR-Electronics has developed a hybrid method that
345     * combines the performances gains of FOC while still allowing applications to
346     * provide duty cycle or voltage demand.  This not to be confused with simple
347     * sinusoidal control or phase voltage control which lacks the performance
348     * gains.
349     *
350     * @param newEnableFOC Parameter to modify
351     * @return Itself
352     */
353    public DifferentialPositionDutyCycle withEnableFOC(boolean newEnableFOC) {
354        EnableFOC = newEnableFOC;
355        return this;
356    }
357    
358    /**
359     * Modifies this Control Request's AverageSlot parameter and returns itself for
360     * method-chaining and easier to use request API.
361     * <p>
362     * Select which gains are applied to the average controller by selecting the
363     * slot.  Use the configuration api to set the gain values for the selected slot
364     * before enabling this feature. Slot must be within [0,2].
365     *
366     * @param newAverageSlot Parameter to modify
367     * @return Itself
368     */
369    public DifferentialPositionDutyCycle withAverageSlot(int newAverageSlot) {
370        AverageSlot = newAverageSlot;
371        return this;
372    }
373    
374    /**
375     * Modifies this Control Request's DifferentialSlot parameter and returns itself for
376     * method-chaining and easier to use request API.
377     * <p>
378     * Select which gains are applied to the differential controller by selecting
379     * the slot.  Use the configuration api to set the gain values for the selected
380     * slot before enabling this feature. Slot must be within [0,2].
381     *
382     * @param newDifferentialSlot Parameter to modify
383     * @return Itself
384     */
385    public DifferentialPositionDutyCycle withDifferentialSlot(int newDifferentialSlot) {
386        DifferentialSlot = newDifferentialSlot;
387        return this;
388    }
389    
390    /**
391     * Modifies this Control Request's OverrideBrakeDurNeutral parameter and returns itself for
392     * method-chaining and easier to use request API.
393     * <p>
394     * Set to true to static-brake the rotor when output is zero (or within
395     * deadband).  Set to false to use the NeutralMode configuration setting
396     * (default). This flag exists to provide the fundamental behavior of this
397     * control when output is zero, which is to provide 0V to the motor.
398     *
399     * @param newOverrideBrakeDurNeutral Parameter to modify
400     * @return Itself
401     */
402    public DifferentialPositionDutyCycle withOverrideBrakeDurNeutral(boolean newOverrideBrakeDurNeutral) {
403        OverrideBrakeDurNeutral = newOverrideBrakeDurNeutral;
404        return this;
405    }
406    
407    /**
408     * Modifies this Control Request's LimitForwardMotion parameter and returns itself for
409     * method-chaining and easier to use request API.
410     * <p>
411     * Set to true to force forward limiting.  This allows users to use other limit
412     * switch sensors connected to robot controller.  This also allows use of active
413     * sensors that require external power.
414     *
415     * @param newLimitForwardMotion Parameter to modify
416     * @return Itself
417     */
418    public DifferentialPositionDutyCycle withLimitForwardMotion(boolean newLimitForwardMotion) {
419        LimitForwardMotion = newLimitForwardMotion;
420        return this;
421    }
422    
423    /**
424     * Modifies this Control Request's LimitReverseMotion parameter and returns itself for
425     * method-chaining and easier to use request API.
426     * <p>
427     * Set to true to force reverse limiting.  This allows users to use other limit
428     * switch sensors connected to robot controller.  This also allows use of active
429     * sensors that require external power.
430     *
431     * @param newLimitReverseMotion Parameter to modify
432     * @return Itself
433     */
434    public DifferentialPositionDutyCycle withLimitReverseMotion(boolean newLimitReverseMotion) {
435        LimitReverseMotion = newLimitReverseMotion;
436        return this;
437    }
438    
439    /**
440     * Modifies this Control Request's IgnoreHardwareLimits parameter and returns itself for
441     * method-chaining and easier to use request API.
442     * <p>
443     * Set to true to ignore hardware limit switches and the LimitForwardMotion and
444     * LimitReverseMotion parameters, instead allowing motion.
445     * <p>
446     * This can be useful on mechanisms such as an intake/feeder, where a limit
447     * switch stops motion while intaking but should be ignored when feeding to a
448     * shooter.
449     * <p>
450     * The hardware limit faults and Forward/ReverseLimit signals will still report
451     * the values of the limit switches regardless of this parameter.
452     *
453     * @param newIgnoreHardwareLimits Parameter to modify
454     * @return Itself
455     */
456    public DifferentialPositionDutyCycle withIgnoreHardwareLimits(boolean newIgnoreHardwareLimits) {
457        IgnoreHardwareLimits = newIgnoreHardwareLimits;
458        return this;
459    }
460    
461    /**
462     * Modifies this Control Request's IgnoreSoftwareLimits parameter and returns itself for
463     * method-chaining and easier to use request API.
464     * <p>
465     * Set to true to ignore software limits, instead allowing motion.
466     * <p>
467     * This can be useful when calibrating the zero point of a mechanism such as an
468     * elevator.
469     * <p>
470     * The software limit faults will still report the values of the software limits
471     * regardless of this parameter.
472     *
473     * @param newIgnoreSoftwareLimits Parameter to modify
474     * @return Itself
475     */
476    public DifferentialPositionDutyCycle withIgnoreSoftwareLimits(boolean newIgnoreSoftwareLimits) {
477        IgnoreSoftwareLimits = newIgnoreSoftwareLimits;
478        return this;
479    }
480    
481    /**
482     * Modifies this Control Request's UseTimesync parameter and returns itself for
483     * method-chaining and easier to use request API.
484     * <p>
485     * Set to true to delay applying this control request until a timesync boundary
486     * (requires Phoenix Pro and CANivore). This eliminates the impact of
487     * nondeterministic network delays in exchange for a larger but deterministic
488     * control latency.
489     * <p>
490     * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
491     * Additionally, when this is enabled, the UpdateFreqHz of this request should
492     * be set to 0 Hz.
493     *
494     * @param newUseTimesync Parameter to modify
495     * @return Itself
496     */
497    public DifferentialPositionDutyCycle withUseTimesync(boolean newUseTimesync) {
498        UseTimesync = newUseTimesync;
499        return this;
500    }
501
502    /**
503     * Sets the frequency at which this control will update.
504     * This is designated in Hertz, with a minimum of 20 Hz
505     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
506     * Some update frequencies are not supported and will be
507     * promoted up to the next highest supported frequency.
508     * <p>
509     * If this field is set to 0 Hz, the control request will
510     * be sent immediately as a one-shot frame. This may be useful
511     * for advanced applications that require outputs to be
512     * synchronized with data acquisition. In this case, we
513     * recommend not exceeding 50 ms between control calls.
514     *
515     * @param newUpdateFreqHz Parameter to modify
516     * @return Itself
517     */
518    @Override
519    public DifferentialPositionDutyCycle withUpdateFreqHz(double newUpdateFreqHz) {
520        UpdateFreqHz = newUpdateFreqHz;
521        return this;
522    }
523
524    /**
525     * Sets the frequency at which this control will update.
526     * This is designated in Hertz, with a minimum of 20 Hz
527     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
528     * Some update frequencies are not supported and will be
529     * promoted up to the next highest supported frequency.
530     * <p>
531     * If this field is set to 0 Hz, the control request will
532     * be sent immediately as a one-shot frame. This may be useful
533     * for advanced applications that require outputs to be
534     * synchronized with data acquisition. In this case, we
535     * recommend not exceeding 50 ms between control calls.
536     *
537     * @param newUpdateFreqHz Parameter to modify
538     * @return Itself
539     */
540    @Override
541    public DifferentialPositionDutyCycle withUpdateFreqHz(Frequency newUpdateFreqHz) {
542        UpdateFreqHz = newUpdateFreqHz.in(Hertz);
543        return this;
544    }
545
546    @Override
547    public DifferentialPositionDutyCycle clone() {
548        try {
549            return (DifferentialPositionDutyCycle)super.clone();
550        } catch (CloneNotSupportedException ex) {
551            /* this should never happen */
552            throw new RuntimeException(ex);
553        }
554    }
555}
556