001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2008-2018 FIRST. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj;
009
010import edu.wpi.first.wpilibj.hal.AnalogJNI;
011import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
012import edu.wpi.first.wpilibj.hal.HAL;
013import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder;
014
015import static java.util.Objects.requireNonNull;
016
017/**
018 * Class to represent a specific output from an analog trigger. This class is used to get the
019 * current output value and also as a DigitalSource to provide routing of an output to digital
020 * subsystems on the FPGA such as Counter, Encoder, and Interrupt.
021 *
022 * <p>The TriggerState output indicates the primary output value of the trigger. If the analog
023 * signal is less than the lower limit, the output is false. If the analog value is greater than the
024 * upper limit, then the output is true. If the analog value is in between, then the trigger output
025 * state maintains its most recent value.
026 *
027 * <p>The InWindow output indicates whether or not the analog signal is inside the range defined by
028 * the limits.
029 *
030 * <p>The RisingPulse and FallingPulse outputs detect an instantaneous transition from above the
031 * upper limit to below the lower limit, and vise versa. These pulses represent a rollover condition
032 * of a sensor and can be routed to an up / down counter or to interrupts. Because the outputs
033 * generate a pulse, they cannot be read directly. To help ensure that a rollover condition is not
034 * missed, there is an average rejection filter available that operates on the upper 8 bits of a 12
035 * bit number and selects the nearest outlier of 3 samples. This will reject a sample that is (due
036 * to averaging or sampling) errantly between the two limits. This filter will fail if more than one
037 * sample in a row is errantly in between the two limits. You may see this problem if attempting to
038 * use this feature with a mechanical rollover sensor, such as a 360 degree no-stop potentiometer
039 * without signal conditioning, because the rollover transition is not sharp / clean enough. Using
040 * the averaging engine may help with this, but rotational speeds of the sensor will then be
041 * limited.
042 */
043public class AnalogTriggerOutput extends DigitalSource {
044  /**
045   * Exceptions dealing with improper operation of the Analog trigger output.
046   */
047  public class AnalogTriggerOutputException extends RuntimeException {
048    /**
049     * Create a new exception with the given message.
050     *
051     * @param message the message to pass with the exception
052     */
053    public AnalogTriggerOutputException(String message) {
054      super(message);
055    }
056  }
057
058  private final AnalogTrigger m_trigger;
059  private final AnalogTriggerType m_outputType;
060
061  /**
062   * Create an object that represents one of the four outputs from an analog trigger.
063   *
064   * <p>Because this class derives from DigitalSource, it can be passed into routing functions for
065   * Counter, Encoder, etc.
066   *
067   * @param trigger    The trigger for which this is an output.
068   * @param outputType An enum that specifies the output on the trigger to represent.
069   */
070  public AnalogTriggerOutput(AnalogTrigger trigger, final AnalogTriggerType outputType) {
071    requireNonNull(trigger, "Analog Trigger given was null");
072    requireNonNull(outputType, "Analog Trigger Type given was null");
073
074    m_trigger = trigger;
075    m_outputType = outputType;
076    HAL.report(tResourceType.kResourceType_AnalogTriggerOutput,
077        trigger.getIndex(), outputType.value);
078  }
079
080  /**
081   * Get the state of the analog trigger output.
082   *
083   * @return The state of the analog trigger output.
084   */
085  public boolean get() {
086    return AnalogJNI.getAnalogTriggerOutput(m_trigger.m_port, m_outputType.value);
087  }
088
089  @Override
090  public int getPortHandleForRouting() {
091    return m_trigger.m_port;
092  }
093
094  @Override
095  public int getAnalogTriggerTypeForRouting() {
096    return m_outputType.value;
097  }
098
099  @Override
100  public int getChannel() {
101    return m_trigger.m_index;
102  }
103
104  @Override
105  public boolean isAnalogTrigger() {
106    return true;
107  }
108
109  /**
110   * Defines the state in which the AnalogTrigger triggers.
111   */
112  public enum AnalogTriggerType {
113    kInWindow(AnalogJNI.AnalogTriggerType.kInWindow), kState(AnalogJNI.AnalogTriggerType.kState),
114    kRisingPulse(AnalogJNI.AnalogTriggerType.kRisingPulse),
115    kFallingPulse(AnalogJNI.AnalogTriggerType.kFallingPulse);
116
117    @SuppressWarnings("MemberName")
118    private final int value;
119
120    AnalogTriggerType(int value) {
121      this.value = value;
122    }
123  }
124
125  @Override
126  public void initSendable(SendableBuilder builder) {
127  }
128}