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.StatusCode;
010import com.ctre.phoenix6.controls.jni.ControlJNI;
011
012import edu.wpi.first.units.*;
013import edu.wpi.first.units.measure.*;
014import static edu.wpi.first.units.Units.*;
015
016import java.util.HashMap;
017import java.util.Map;
018
019/**
020 * Requests Motion Magic® to target a final position using an exponential motion
021 * profile.  Users can optionally provide a voltage feedforward.
022 * <p>
023 * Motion Magic® Expo produces a motion profile in real-time while attempting to honor the Cruise Velocity
024 * (optional) and the mechanism kV and kA, specified via the Motion Magic® configuration values.  Note that
025 * unlike the slot gains, the Expo_kV and Expo_kA configs are always in output units of Volts.
026 * <p>
027 * Setting Cruise Velocity to 0 will allow the profile to run to the max possible velocity based on Expo_kV. 
028 * This control mode does not use the Acceleration or Jerk configs.
029 * <p>
030 * Target position can be changed on-the-fly and Motion Magic® will do its best to adjust the profile.  This
031 * control mode is voltage-based, so relevant closed-loop gains will use Volts for the numerator.
032 */
033public class MotionMagicExpoVoltage extends ControlRequest implements Cloneable
034{
035    /**
036     * Position to drive toward in rotations.
037     * 
038     * <ul>
039     *   <li> Units: rotations
040     * </ul>
041     * 
042     */
043    public double Position;
044    /**
045     * Set to true to use FOC commutation (requires Phoenix Pro), which increases
046     * peak power by ~15%. Set to false to use trapezoidal commutation.
047     * <p>
048     * FOC improves motor performance by leveraging torque (current) control. 
049     * However, this may be inconvenient for applications that require specifying
050     * duty cycle or voltage.  CTR-Electronics has developed a hybrid method that
051     * combines the performances gains of FOC while still allowing applications to
052     * provide duty cycle or voltage demand.  This not to be confused with simple
053     * sinusoidal control or phase voltage control which lacks the performance
054     * gains.
055     */
056    public boolean EnableFOC = true;
057    /**
058     * Feedforward to apply in volts
059     * 
060     * <ul>
061     *   <li> Units: Volts
062     * </ul>
063     * 
064     */
065    public double FeedForward = 0.0;
066    /**
067     * Select which gains are applied by selecting the slot.  Use the configuration
068     * api to set the gain values for the selected slot before enabling this
069     * feature. Slot must be within [0,2].
070     */
071    public int Slot = 0;
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 delay applying this control request until a timesync boundary
105     * (requires Phoenix Pro and CANivore). This eliminates the impact of
106     * nondeterministic network delays in exchange for a larger but deterministic
107     * control latency.
108     * <p>
109     * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
110     * Additionally, when this is enabled, the UpdateFreqHz of this request should
111     * be set to 0 Hz.
112     */
113    public boolean UseTimesync = false;
114
115    /**
116     * The period at which this control will update at.
117     * This is designated in Hertz, with a minimum of 20 Hz
118     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
119     * <p>
120     * If this field is set to 0 Hz, the control request will
121     * be sent immediately as a one-shot frame. This may be useful
122     * for advanced applications that require outputs to be
123     * synchronized with data acquisition. In this case, we
124     * recommend not exceeding 50 ms between control calls.
125     */
126    public double UpdateFreqHz = 100;
127
128    /**
129     * Requests Motion Magic® to target a final position using an exponential motion
130     * profile.  Users can optionally provide a voltage feedforward.
131     * <p>
132     * Motion Magic® Expo produces a motion profile in real-time while attempting to
133     * honor the Cruise Velocity (optional) and the mechanism kV and kA, specified
134     * via the Motion Magic® configuration values.  Note that unlike the slot gains,
135     * the Expo_kV and Expo_kA configs are always in output units of Volts.
136     * <p>
137     * Setting Cruise Velocity to 0 will allow the profile to run to the max
138     * possible velocity based on Expo_kV.  This control mode does not use the
139     * Acceleration or Jerk configs.
140     * <p>
141     * Target position can be changed on-the-fly and Motion Magic® will do its best
142     * to adjust the profile.  This control mode is voltage-based, so relevant
143     * closed-loop gains will use Volts for the numerator.
144     * 
145     * @param Position    Position to drive toward in rotations.
146     */
147    public MotionMagicExpoVoltage(double Position)
148    {
149        super("MotionMagicExpoVoltage");
150        this.Position = Position;
151    }
152
153    /**
154     * Requests Motion Magic® to target a final position using an exponential motion
155     * profile.  Users can optionally provide a voltage feedforward.
156     * <p>
157     * Motion Magic® Expo produces a motion profile in real-time while attempting to
158     * honor the Cruise Velocity (optional) and the mechanism kV and kA, specified
159     * via the Motion Magic® configuration values.  Note that unlike the slot gains,
160     * the Expo_kV and Expo_kA configs are always in output units of Volts.
161     * <p>
162     * Setting Cruise Velocity to 0 will allow the profile to run to the max
163     * possible velocity based on Expo_kV.  This control mode does not use the
164     * Acceleration or Jerk configs.
165     * <p>
166     * Target position can be changed on-the-fly and Motion Magic® will do its best
167     * to adjust the profile.  This control mode is voltage-based, so relevant
168     * closed-loop gains will use Volts for the numerator.
169     * 
170     * @param Position    Position to drive toward in rotations.
171     */
172    public MotionMagicExpoVoltage(Angle Position)
173    {
174        this(Position.in(Rotations));
175    }
176
177    @Override
178    public String toString()
179    {
180        String ss = "Control: MotionMagicExpoVoltage\n";
181        ss += "    Position: " + Position + " rotations" + "\n";
182        ss += "    EnableFOC: " + EnableFOC + "\n";
183        ss += "    FeedForward: " + FeedForward + " Volts" + "\n";
184        ss += "    Slot: " + Slot + "\n";
185        ss += "    OverrideBrakeDurNeutral: " + OverrideBrakeDurNeutral + "\n";
186        ss += "    LimitForwardMotion: " + LimitForwardMotion + "\n";
187        ss += "    LimitReverseMotion: " + LimitReverseMotion + "\n";
188        ss += "    IgnoreHardwareLimits: " + IgnoreHardwareLimits + "\n";
189        ss += "    UseTimesync: " + UseTimesync + "\n";
190        return ss;
191    }
192
193    @Override
194    public StatusCode sendRequest(String network, int deviceHash)
195    {
196        return StatusCode.valueOf(ControlJNI.JNI_RequestControlMotionMagicExpoVoltage(
197                network, deviceHash, UpdateFreqHz, Position, EnableFOC, FeedForward, Slot, OverrideBrakeDurNeutral, LimitForwardMotion, LimitReverseMotion, IgnoreHardwareLimits, UseTimesync));
198    }
199
200    /**
201     * Gets information about this control request.
202     *
203     * @return Map of control parameter names and corresponding applied values
204     */
205    @Override
206    public Map<String, String> getControlInfo()
207    {
208        var controlInfo = new HashMap<String, String>();
209        controlInfo.put("Name", getName());
210        controlInfo.put("Position", String.valueOf(this.Position));
211        controlInfo.put("EnableFOC", String.valueOf(this.EnableFOC));
212        controlInfo.put("FeedForward", String.valueOf(this.FeedForward));
213        controlInfo.put("Slot", String.valueOf(this.Slot));
214        controlInfo.put("OverrideBrakeDurNeutral", String.valueOf(this.OverrideBrakeDurNeutral));
215        controlInfo.put("LimitForwardMotion", String.valueOf(this.LimitForwardMotion));
216        controlInfo.put("LimitReverseMotion", String.valueOf(this.LimitReverseMotion));
217        controlInfo.put("IgnoreHardwareLimits", String.valueOf(this.IgnoreHardwareLimits));
218        controlInfo.put("UseTimesync", String.valueOf(this.UseTimesync));
219        return controlInfo;
220    }
221    
222    /**
223     * Modifies this Control Request's Position parameter and returns itself for
224     * method-chaining and easier to use request API.
225     * <p>
226     * Position to drive toward in rotations.
227     * 
228     * <ul>
229     *   <li> Units: rotations
230     * </ul>
231     * 
232     *
233     * @param newPosition Parameter to modify
234     * @return Itself
235     */
236    public MotionMagicExpoVoltage withPosition(double newPosition)
237    {
238        Position = newPosition;
239        return this;
240    }
241    
242    /**
243     * Modifies this Control Request's Position parameter and returns itself for
244     * method-chaining and easier to use request API.
245     * <p>
246     * Position to drive toward in rotations.
247     * 
248     * <ul>
249     *   <li> Units: rotations
250     * </ul>
251     * 
252     *
253     * @param newPosition Parameter to modify
254     * @return Itself
255     */
256    public MotionMagicExpoVoltage withPosition(Angle newPosition)
257    {
258        Position = newPosition.in(Rotations);
259        return this;
260    }
261    
262    /**
263     * Helper method to get this Control Request's Position parameter converted
264     * to a unit type. If not using the Java units library, {@link #Position}
265     * can be accessed directly instead.
266     * <p>
267     * Position to drive toward in rotations.
268     * 
269     * <ul>
270     *   <li> Units: rotations
271     * </ul>
272     * 
273     *
274     * @return Position
275     */
276    public Angle getPositionMeasure()
277    {
278        return Rotations.of(Position);
279    }
280    
281    /**
282     * Modifies this Control Request's EnableFOC parameter and returns itself for
283     * method-chaining and easier to use request API.
284     * <p>
285     * Set to true to use FOC commutation (requires Phoenix Pro), which increases
286     * peak power by ~15%. Set to false to use trapezoidal commutation.
287     * <p>
288     * FOC improves motor performance by leveraging torque (current) control. 
289     * However, this may be inconvenient for applications that require specifying
290     * duty cycle or voltage.  CTR-Electronics has developed a hybrid method that
291     * combines the performances gains of FOC while still allowing applications to
292     * provide duty cycle or voltage demand.  This not to be confused with simple
293     * sinusoidal control or phase voltage control which lacks the performance
294     * gains.
295     *
296     * @param newEnableFOC Parameter to modify
297     * @return Itself
298     */
299    public MotionMagicExpoVoltage withEnableFOC(boolean newEnableFOC)
300    {
301        EnableFOC = newEnableFOC;
302        return this;
303    }
304    
305    /**
306     * Modifies this Control Request's FeedForward parameter and returns itself for
307     * method-chaining and easier to use request API.
308     * <p>
309     * Feedforward to apply in volts
310     * 
311     * <ul>
312     *   <li> Units: Volts
313     * </ul>
314     * 
315     *
316     * @param newFeedForward Parameter to modify
317     * @return Itself
318     */
319    public MotionMagicExpoVoltage withFeedForward(double newFeedForward)
320    {
321        FeedForward = newFeedForward;
322        return this;
323    }
324    
325    /**
326     * Modifies this Control Request's FeedForward parameter and returns itself for
327     * method-chaining and easier to use request API.
328     * <p>
329     * Feedforward to apply in volts
330     * 
331     * <ul>
332     *   <li> Units: Volts
333     * </ul>
334     * 
335     *
336     * @param newFeedForward Parameter to modify
337     * @return Itself
338     */
339    public MotionMagicExpoVoltage withFeedForward(Voltage newFeedForward)
340    {
341        FeedForward = newFeedForward.in(Volts);
342        return this;
343    }
344    
345    /**
346     * Helper method to get this Control Request's FeedForward parameter converted
347     * to a unit type. If not using the Java units library, {@link #FeedForward}
348     * can be accessed directly instead.
349     * <p>
350     * Feedforward to apply in volts
351     * 
352     * <ul>
353     *   <li> Units: Volts
354     * </ul>
355     * 
356     *
357     * @return FeedForward
358     */
359    public Voltage getFeedForwardMeasure()
360    {
361        return Volts.of(FeedForward);
362    }
363    
364    /**
365     * Modifies this Control Request's Slot parameter and returns itself for
366     * method-chaining and easier to use request API.
367     * <p>
368     * Select which gains are applied by selecting the slot.  Use the configuration
369     * api to set the gain values for the selected slot before enabling this
370     * feature. Slot must be within [0,2].
371     *
372     * @param newSlot Parameter to modify
373     * @return Itself
374     */
375    public MotionMagicExpoVoltage withSlot(int newSlot)
376    {
377        Slot = newSlot;
378        return this;
379    }
380    
381    /**
382     * Modifies this Control Request's OverrideBrakeDurNeutral parameter and returns itself for
383     * method-chaining and easier to use request API.
384     * <p>
385     * Set to true to static-brake the rotor when output is zero (or within
386     * deadband).  Set to false to use the NeutralMode configuration setting
387     * (default). This flag exists to provide the fundamental behavior of this
388     * control when output is zero, which is to provide 0V to the motor.
389     *
390     * @param newOverrideBrakeDurNeutral Parameter to modify
391     * @return Itself
392     */
393    public MotionMagicExpoVoltage withOverrideBrakeDurNeutral(boolean newOverrideBrakeDurNeutral)
394    {
395        OverrideBrakeDurNeutral = newOverrideBrakeDurNeutral;
396        return this;
397    }
398    
399    /**
400     * Modifies this Control Request's LimitForwardMotion parameter and returns itself for
401     * method-chaining and easier to use request API.
402     * <p>
403     * Set to true to force forward limiting.  This allows users to use other limit
404     * switch sensors connected to robot controller.  This also allows use of active
405     * sensors that require external power.
406     *
407     * @param newLimitForwardMotion Parameter to modify
408     * @return Itself
409     */
410    public MotionMagicExpoVoltage withLimitForwardMotion(boolean newLimitForwardMotion)
411    {
412        LimitForwardMotion = newLimitForwardMotion;
413        return this;
414    }
415    
416    /**
417     * Modifies this Control Request's LimitReverseMotion parameter and returns itself for
418     * method-chaining and easier to use request API.
419     * <p>
420     * Set to true to force reverse limiting.  This allows users to use other limit
421     * switch sensors connected to robot controller.  This also allows use of active
422     * sensors that require external power.
423     *
424     * @param newLimitReverseMotion Parameter to modify
425     * @return Itself
426     */
427    public MotionMagicExpoVoltage withLimitReverseMotion(boolean newLimitReverseMotion)
428    {
429        LimitReverseMotion = newLimitReverseMotion;
430        return this;
431    }
432    
433    /**
434     * Modifies this Control Request's IgnoreHardwareLimits parameter and returns itself for
435     * method-chaining and easier to use request API.
436     * <p>
437     * Set to true to ignore hardware limit switches and the LimitForwardMotion and
438     * LimitReverseMotion parameters, instead allowing motion.
439     * <p>
440     * This can be useful on mechanisms such as an intake/feeder, where a limit
441     * switch stops motion while intaking but should be ignored when feeding to a
442     * shooter.
443     * <p>
444     * The hardware limit faults and Forward/ReverseLimit signals will still report
445     * the values of the limit switches regardless of this parameter.
446     *
447     * @param newIgnoreHardwareLimits Parameter to modify
448     * @return Itself
449     */
450    public MotionMagicExpoVoltage withIgnoreHardwareLimits(boolean newIgnoreHardwareLimits)
451    {
452        IgnoreHardwareLimits = newIgnoreHardwareLimits;
453        return this;
454    }
455    
456    /**
457     * Modifies this Control Request's UseTimesync parameter and returns itself for
458     * method-chaining and easier to use request API.
459     * <p>
460     * Set to true to delay applying this control request until a timesync boundary
461     * (requires Phoenix Pro and CANivore). This eliminates the impact of
462     * nondeterministic network delays in exchange for a larger but deterministic
463     * control latency.
464     * <p>
465     * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
466     * Additionally, when this is enabled, the UpdateFreqHz of this request should
467     * be set to 0 Hz.
468     *
469     * @param newUseTimesync Parameter to modify
470     * @return Itself
471     */
472    public MotionMagicExpoVoltage withUseTimesync(boolean newUseTimesync)
473    {
474        UseTimesync = newUseTimesync;
475        return this;
476    }
477
478    /**
479     * Sets the period at which this control will update at.
480     * This is designated in Hertz, with a minimum of 20 Hz
481     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
482     * <p>
483     * If this field is set to 0 Hz, the control request will
484     * be sent immediately as a one-shot frame. This may be useful
485     * for advanced applications that require outputs to be
486     * synchronized with data acquisition. In this case, we
487     * recommend not exceeding 50 ms between control calls.
488     *
489     * @param newUpdateFreqHz Parameter to modify
490     * @return Itself
491     */
492    @Override
493    public MotionMagicExpoVoltage withUpdateFreqHz(double newUpdateFreqHz)
494    {
495        UpdateFreqHz = newUpdateFreqHz;
496        return this;
497    }
498
499    /**
500     * Sets the period at which this control will update at.
501     * This is designated in Hertz, with a minimum of 20 Hz
502     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
503     * <p>
504     * If this field is set to 0 Hz, the control request will
505     * be sent immediately as a one-shot frame. This may be useful
506     * for advanced applications that require outputs to be
507     * synchronized with data acquisition. In this case, we
508     * recommend not exceeding 50 ms between control calls.
509     *
510     * @param newUpdateFreqHz Parameter to modify
511     * @return Itself
512     */
513    public MotionMagicExpoVoltage withUpdateFreqHz(Frequency newUpdateFreqHz)
514    {
515        UpdateFreqHz = newUpdateFreqHz.in(Hertz);
516        return this;
517    }
518
519    @Override
520    public MotionMagicExpoVoltage clone()
521    {
522        try {
523            return (MotionMagicExpoVoltage)super.clone();
524        } catch (CloneNotSupportedException ex) {
525            /* this should never happen */
526            throw new RuntimeException(ex);
527        }
528    }
529}
530