001    /*----------------------------------------------------------------------------*/
002    /* Copyright (c) FIRST 2008-2012. 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    
008    package edu.wpi.first.wpilibj;
009    
010    import edu.wpi.first.wpilibj.communication.UsageReporting;
011    import edu.wpi.first.wpilibj.fpga.tAnalogTrigger;
012    import edu.wpi.first.wpilibj.parsing.IInputOutput;
013    
014    /**
015     * Class to represent a specific output from an analog trigger.
016     * This class is used to get the current output value and also as a DigitalSource
017     * to provide routing of an output to digital subsystems on the FPGA such as
018     * Counter, Encoder, and Interrupt.
019     *
020     * 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
022     * than the upper limit, then the output is true.  If the analog value is in between, then
023     * the trigger output state maintains its most recent value.
024     *
025     * The InWindow output indicates whether or not the analog signal is inside the range defined
026     * by the limits.
027     *
028     * 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
030     * condition of a sensor and can be routed to an up / down couter or to interrupts.  Because
031     * the outputs generate a pulse, they cannot be read directly.  To help ensure that a rollover
032     * condition is not missed, there is an average rejection filter available that operates on the
033     * upper 8 bits of a 12 bit number and selects the nearest outlyer of 3 samples.  This will reject
034     * a sample that is (due to averaging or sampling) errantly between the two limits.  This filter
035     * will fail if more than one sample in a row is errantly in between the two limits.  You may see
036     * this problem if attempting to use this feature with a mechanical rollover sensor, such as a
037     * 360 degree no-stop potentiometer without signal conditioning, because the rollover transition
038     * is not sharp / clean enough.  Using the averaging engine may help with this, but rotational speeds of
039     * the sensor will then be limited.
040     */
041    public class AnalogTriggerOutput extends DigitalSource implements IInputOutput{
042    
043            /**
044         * Exceptions dealing with improper operation of the Analog trigger output
045         */
046        public class AnalogTriggerOutputException extends RuntimeException {
047    
048            /**
049             * Create a new exception with the given message
050             * @param message the message to pass with the exception
051             */
052            public AnalogTriggerOutputException(String message) {
053                super(message);
054            }
055    
056        }
057    
058        /**
059         * Type determines under what state the analog trigger evaluates to true or
060         * false
061         */
062        public static class Type {
063    
064            /**
065             * The integer value representing this enumeration
066             */
067            public final int value;
068            static final int kInWindow_val = 0;
069            static final int kTypeState_val = 1;
070            static final int kRisingPulse_val = 2;
071            static final int kFallingPulse_val = 3;
072            /**
073             * outputType: true if in the trigger window
074             */
075            public static final Type kInWindow = new Type(kInWindow_val);
076            /**
077             * outputType: true if above the upper limit, false if below the lower limit
078             * maintain previous state otherwise
079             */
080            public static final Type kTypeState = new Type(kTypeState_val);
081            /**
082             * outputType: true on a rising edge
083             */
084            public static final Type kRisingPulse = new Type(kRisingPulse_val);
085            /**
086             * outputType: true on a falling edge
087             */
088            public static final Type kFallingPulse = new Type(kFallingPulse_val);
089    
090            private Type(int value) {
091                this.value = value;
092            }
093        }
094        private AnalogTrigger m_trigger;
095        private Type m_outputType;
096    
097        /**
098         * Create an object that represents one of the four outputs from an analog trigger.
099         *
100         * Because this class derives from DigitalSource, it can be passed into routing functions
101         * for Counter, Encoder, etc.
102         *
103         * @param trigger The trigger for which this is an output.
104         * @param outputType An enum that specifies the output on the trigger to represent.
105         */
106        public AnalogTriggerOutput(AnalogTrigger trigger, final Type outputType) {
107            if (trigger ==  null)
108                throw new NullPointerException("Analog Trigger given was null");
109            m_trigger = trigger;
110            m_outputType = outputType;
111    
112            UsageReporting.report(UsageReporting.kResourceType_AnalogTriggerOutput, trigger.getIndex(), outputType.value);
113        }
114    
115        public void free() {
116        }
117    
118        /**
119         * Get the state of the analog trigger output.
120         * @return The state of the analog trigger output.
121         */
122        public boolean get() {
123            switch (m_outputType.value) {
124                case Type.kInWindow_val:
125                    return tAnalogTrigger.readOutput_InHysteresis((byte) m_trigger.m_index);
126                case Type.kTypeState_val:
127                    return tAnalogTrigger.readOutput_OverLimit((byte) m_trigger.m_index);
128                case Type.kRisingPulse_val:
129                case Type.kFallingPulse_val:
130                    throw new AnalogTriggerOutputException("Cannot get the state of the analog trigger output if it is configured for a rising or falling edge");
131            }
132            // Should never get here.
133            return false;
134        }
135    
136        public int getChannelForRouting() {
137            return (m_trigger.m_index << 2) + m_outputType.value;
138        }
139    
140        public int getModuleForRouting() {
141            return m_trigger.m_index >> 2;
142        }
143    
144        public boolean getAnalogTriggerForRouting() {
145            return true;
146        }
147    
148        /**
149         * Request interrupts asynchronously on this digital input.
150         * @param handler the interrupt service routine
151         * @param param a parameter for the ISR
152         */
153    //    public void requestInterrupts(/*tInterruptHandler*/Object handler, Object param) {
154            //TODO: add interrupt support
155            //TODO: throw exception
156    //    }
157    
158        /**
159         * Request interrupts synchronously on this digital input.
160         */
161    //    public void requestInterrupts() {
162            //TODO: throw exception
163    //    }
164    }