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