001package jmri.jmrix.bachrus.speedmatcher.speedStepScale;
002
003import javax.swing.JLabel;
004
005import jmri.jmrix.bachrus.Speed;
006import jmri.jmrix.bachrus.speedmatcher.SpeedMatcher;
007
008/**
009 * Abstract class defining the basic operations of a Speed Step Scale speed
010 * matcher (sets up the complex speed table such that the speed step equals the
011 * locomotive speed when using "128" speed step mode). All speed step scale
012 * speed matcher implementations must extend this class.
013 *
014 * @author Todd Wegter Copyright (C) 2024
015 */
016public abstract class SpeedStepScaleSpeedMatcher extends SpeedMatcher {
017
018    //<editor-fold defaultstate="collapsed" desc="Instance Variables">
019    protected final SpeedTableStepSpeed targetMaxSpeedStep;
020    protected final float targetMaxSpeedKPH;
021    protected final Speed.Unit speedUnit;
022    protected final JLabel actualMaxSpeedField;
023
024    protected float measuredMaxSpeedKPH = 0;
025    protected float speedMatchMaxSpeedKPH = 0;
026    //</editor-fold>
027
028    /**
029     * Constructs the abstract SpeedStepScaleSpeedMatcher at the core of any
030     * Speed Step Scale Speed Matcher
031     *
032     * @param config SpeedStepScaleSpeedMatcherConfig
033     */
034    public SpeedStepScaleSpeedMatcher(SpeedStepScaleSpeedMatcherConfig config) {
035        super(config);
036
037        this.actualMaxSpeedField = config.actualMaxSpeedField;
038        this.speedUnit = config.speedUnit;
039
040        this.targetMaxSpeedStep = config.targetMaxSpeedStep;
041        this.targetMaxSpeedKPH = config.speedUnit == Speed.Unit.MPH ? Speed.mphToKph(this.targetMaxSpeedStep.getSpeed()) : this.targetMaxSpeedStep.getSpeed();
042    }
043
044    //<editor-fold defaultstate="collapsed" desc="Protected APIs">
045    /**
046     * Validates the speed matcher's configuration
047     *
048     * @return true if the configuration is valid, false otherwise
049     */
050    @Override
051    protected boolean validate() {
052        if (dccLocoAddress.getNumber() <= 0) {
053            statusLabel.setText(Bundle.getMessage("StatInvalidDCCAddress"));
054            return false;
055        }
056
057        if (targetMaxSpeedStep == null) {
058            statusLabel.setText(Bundle.getMessage("StatInvalidMaxSpeed"));
059            return false;
060        }
061
062        return true;
063    }
064
065    /**
066     * Gets the speed in KPH for a given speed step for a speed step scale speed
067     * matcher
068     *
069     * @param speedStep the int speed step to get the speed for
070     * @return speed for the given speedStep in KPH
071     */
072    protected float getSpeedStepScaleSpeedInKPH(int speedStep) {
073        //speed = step in 128 speed step mode
074        float speedStepSpeed = getSpeedForSpeedTableStep(speedStep);
075
076        //convert MPH to KPH since Bachrus does everything in KPH
077        if (speedUnit == Speed.Unit.MPH) {
078            speedStepSpeed = Speed.mphToKph(speedStepSpeed);
079        }
080
081        //speed must be bounded by the target max speed
082        speedStepSpeed = Math.min(speedStepSpeed, speedMatchMaxSpeedKPH);
083
084        return speedStepSpeed;
085    }
086    
087    /**
088     * Gets the speed step value for a linear speed table
089     * @param speedStep the inst speed step to get the value for
090     * @return value for the speed step
091     */
092    protected int getSpeedStepLinearValue(int speedStep) {
093        return (int) (speedStep / 28f * 255f);
094    }
095
096    /**
097     * Gets the 128 speed step mode speed for a speed table step
098     *
099     * @param speedStep the int speed table step to get the 128 speed step mode
100     *                  speed for
101     * @return the 128 speed step mode speed for the given speedStep
102     */
103    public static float getSpeedForSpeedTableStep(int speedStep) {
104        //speed step 1 (28) = speed step 1 (128), 28 (28) = 126 (128), 
105        //so 27 speed steps (28) = 125 speed steps (128),
106        //so each step (28) = 4.6296 steps (128)
107        return (speedStep * 4.6296f) - 3.6296f;
108    }
109
110    /**
111     * Gets the next lowest speed table step for the given speed
112     *
113     * @param speed float speed in the user facing unit
114     * @return the next lowest int speed table step for the given speed
115     */
116    public static int getNextLowestSpeedTableStepForSpeed(float speed) {
117        return (int) Math.floor((speed + 3.6296f) / 4.6296f);
118    }
119    //</editor-fold>
120}