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.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  public static class AnalogTriggerOutputException extends RuntimeException {
044    /**
045     * Create a new exception with the given message.
046     *
047     * @param message the message to pass with the exception
048     */
049    public AnalogTriggerOutputException(String message) {
050      super(message);
051    }
052  }
053
054  private final AnalogTrigger m_trigger;
055  private final AnalogTriggerType m_outputType;
056
057  /**
058   * Create an object that represents one of the four outputs from an analog trigger.
059   *
060   * <p>Because this class derives from DigitalSource, it can be passed into routing functions for
061   * Counter, Encoder, etc.
062   *
063   * @param trigger The trigger for which this is an output.
064   * @param outputType An enum that specifies the output on the trigger to represent.
065   */
066  public AnalogTriggerOutput(AnalogTrigger trigger, final AnalogTriggerType outputType) {
067    requireNonNullParam(trigger, "trigger", "AnalogTriggerOutput");
068    requireNonNullParam(outputType, "outputType", "AnalogTriggerOutput");
069
070    m_trigger = trigger;
071    m_outputType = outputType;
072    HAL.report(
073        tResourceType.kResourceType_AnalogTriggerOutput,
074        trigger.getIndex() + 1,
075        outputType.value + 1);
076  }
077
078  /**
079   * Get the state of the analog trigger output.
080   *
081   * @return The state of the analog trigger output.
082   */
083  public boolean get() {
084    return AnalogJNI.getAnalogTriggerOutput(m_trigger.m_port, m_outputType.value);
085  }
086
087  @Override
088  public int getPortHandleForRouting() {
089    return m_trigger.m_port;
090  }
091
092  @Override
093  public int getAnalogTriggerTypeForRouting() {
094    return m_outputType.value;
095  }
096
097  @Override
098  public int getChannel() {
099    return m_trigger.getIndex();
100  }
101
102  @Override
103  public boolean isAnalogTrigger() {
104    return true;
105  }
106
107  /** Defines the state in which the AnalogTrigger triggers. */
108  public enum AnalogTriggerType {
109    kInWindow(AnalogJNI.AnalogTriggerType.kInWindow),
110    kState(AnalogJNI.AnalogTriggerType.kState),
111    kRisingPulse(AnalogJNI.AnalogTriggerType.kRisingPulse),
112    kFallingPulse(AnalogJNI.AnalogTriggerType.kFallingPulse);
113
114    private final int value;
115
116    AnalogTriggerType(int value) {
117      this.value = value;
118    }
119  }
120
121  @Override
122  public void initSendable(SendableBuilder builder) {}
123}