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