ETB quality instrumentation #494

This commit is contained in:
rusefi 2019-04-23 20:49:06 -04:00
parent 7e7261d2cc
commit d9c31bd6bd
12 changed files with 329 additions and 177 deletions

View File

@ -0,0 +1,34 @@
package com.rusefi;
import com.rusefi.core.Sensor;
import com.rusefi.ui.GaugesGridElement;
import com.rusefi.ui.etb.CalibrationPanel;
import com.rusefi.ui.etb.CommandsPanel;
import com.rusefi.ui.util.UiUtils;
import javax.swing.*;
import java.awt.*;
/**
* Controls related to Electronic Throttle Body
*
* (c) Andrey Belomutskiy 2013-2019
*/
public class ETBPane {
private final JPanel content = new JPanel(new BorderLayout());
public ETBPane() {
JPanel centerPanel = new JPanel(new GridLayout(2, 1));
centerPanel.add(GaugesGridElement.create(Sensor.PPS));
centerPanel.add(GaugesGridElement.create(Sensor.TPS));
content.add(new CommandsPanel().getContent(), BorderLayout.WEST);
content.add(centerPanel, BorderLayout.CENTER);
content.add(new CalibrationPanel().getContent(), BorderLayout.EAST);
}
public JPanel getContent() {
return content;
}
}

View File

@ -1,4 +1,4 @@
package com.rusefi.ui.widgets;
package com.rusefi.etb;
import com.rusefi.CyclicBuffer;
import com.rusefi.DataBuffer;
@ -63,7 +63,7 @@ public class ClosedLoopControlQualityMetric {
errorsBuffer.clear();
}
private synchronized double getStandardDeviation() {
public synchronized double getStandardDeviation() {
return DataBuffer.getStandardDeviation(errorsBuffer.getValues());
}

View File

@ -0,0 +1,31 @@
package com.rusefi.etb;
import com.rusefi.io.CommandQueue;
public class EtbTarget extends TestSequenceStep {
/**
* 0-100 percent open
*/
private final double position;
private final Runnable onEachStep;
public EtbTarget(long duration, double position, Runnable onEachStep) {
super(duration);
this.position = position;
this.onEachStep = onEachStep;
}
protected void doJob() {
CommandQueue.getInstance().write("set mock_pedal_position " + position);
if (onEachStep != null)
onEachStep.run();
}
@Override
public String toString() {
return "EtbTarget{" +
"duration=" + duration +
", position=" + position +
'}';
}
}

View File

@ -0,0 +1,65 @@
package com.rusefi.etb;
import com.rusefi.core.Sensor;
import com.rusefi.core.SensorCentral;
import org.jetbrains.annotations.NotNull;
import static com.rusefi.Timeouts.SECOND;
public class StandardTestSequence {
public final static ClosedLoopControlQualityMetric metric = createMetric();
@NotNull
protected static ClosedLoopControlQualityMetric createMetric() {
return new ClosedLoopControlQualityMetric(
SensorCentral.getInstance().getValueSource(Sensor.PPS),
SensorCentral.getInstance().getValueSource(Sensor.TPS),
Sensor.ETB_CONTROL_QUALITY
);
}
public static TestSequenceStep addSequence(TestSequenceStep first_step, Runnable onEachStep) {
TestSequenceStep secondStep = new TestSequenceStep(SECOND) {
@Override
protected void doJob() {
metric.reset();
}
};
TestSequenceStep result = first_step.addNext(secondStep)
.addNext(10 * SECOND, 4 /*position*/, onEachStep)
.addNext(5 * SECOND, 6, /*position*/onEachStep)
.addNext(5 * SECOND, 8, /*position*/onEachStep)
.addNext(5 * SECOND, 6, /*position*/onEachStep)
.addNext(5 * SECOND, 4, /*position*/onEachStep)
.addNext(5 * SECOND, 2, /*position*/onEachStep)
.addNext(5 * SECOND, 0, /*position*/onEachStep)
.addNext(5 * SECOND, 10, /*position*/onEachStep)
.addNext(3 * SECOND, 0, /*position*/onEachStep)
.addNext(1 * SECOND, 6, /*position*/onEachStep)
.addNext(1 * SECOND, 8, /*position*/onEachStep)
.addNext(1 * SECOND, 6, /*position*/onEachStep)
.addNext(1 * SECOND, 4, /*position*/onEachStep)
.addNext(1 * SECOND, 2, /*position*/onEachStep)
.addNext(1 * SECOND, 0, /*position*/onEachStep)
.addNext(1 * SECOND, 10, /*position*/onEachStep)
.addNext(3 * SECOND, 7, /*position*/onEachStep)
.addNext(3 * SECOND, 6, /*position*/onEachStep)
.addNext(3 * SECOND, 5, /*position*/onEachStep)
.addNext(3 * SECOND, 4, /*position*/onEachStep)
.addNext(3 * SECOND, 3, /*position*/onEachStep)
.addNext(3 * SECOND, 2, /*position*/onEachStep)
.addNext(3 * SECOND, 1, /*position*/onEachStep)
.addNext(3 * SECOND, 0, /*position*/onEachStep)
.addNext(10 * SECOND, 6, /*position*/onEachStep)
.addNext(10 * SECOND, 30, /*position*/onEachStep)
.addNext(10 * SECOND, 50, /*position*/onEachStep)
.addNext(10 * SECOND, 70, /*position*/onEachStep)
.addNext(10 * SECOND, 95, /*position*/onEachStep)
.addNext(10 * SECOND, 50, /*position*/onEachStep)
.addNext(10 * SECOND, 70, /*position*/onEachStep)
.addNext(10 * SECOND, 4, /*position*/onEachStep)
.addNext(10 * SECOND, 0, /*position*/onEachStep)
;
return result;
}
}

View File

@ -0,0 +1,37 @@
package com.rusefi.etb;
import com.rusefi.FileLog;
import com.rusefi.core.MessagesCentral;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public abstract class TestSequenceStep {
protected final long duration;
private TestSequenceStep next;
public TestSequenceStep(long duration) {
this.duration = duration;
}
public void execute(ScheduledExecutorService executor) {
doJob();
if (next != null) {
FileLog.MAIN.logLine("Scheduling " + next + " with " + duration + "ms delay");
executor.schedule(() -> next.execute(executor), duration, TimeUnit.MILLISECONDS);
} else {
MessagesCentral.getInstance().postMessage(TestSequenceStep.class, "ETB test sequence done!");
}
}
protected abstract void doJob();
public TestSequenceStep addNext(long duration, double position, Runnable onEachStep) {
return addNext(new EtbTarget(duration, position, onEachStep));
}
public TestSequenceStep addNext(TestSequenceStep step) {
next = step;
return next;
}
}

View File

@ -27,11 +27,11 @@ public class GaugesGridElement {
this.config = config;
}
public GaugesGridElement create(Sensor sensor) {
return new GaugesGridElement(new Node()).create(sensor);
public static JComponent create(Sensor sensor) {
return new GaugesGridElement(new Node()).createGauge(sensor);
}
private Component createLiveBarElement(final Sensor defaultSensor) {
private JComponent createLiveBarElement(final Sensor defaultSensor) {
wrapper.setLayout(new GridLayout(2, 1));
JMenuItem switchToGauge = new JMenuItem("Switch to Gauge Mode");
@ -50,7 +50,7 @@ public class GaugesGridElement {
return wrapper;
}
private Component createGauge(final Sensor sensor) {
private JComponent createGauge(final Sensor sensor) {
SensorGauge.GaugeChangeListener gaugeChangeListener = new SensorGauge.GaugeChangeListener() {
@Override
public void onSensorChange(Sensor sensor) {
@ -75,7 +75,7 @@ public class GaugesGridElement {
return wrapper;
}
public static Component read(final Node config, Sensor defaultSensor) {
public static JComponent read(final Node config, Sensor defaultSensor) {
if (config.getBoolProperty(IS_LIVE_GRAPH)) {
return new GaugesGridElement(config).createLiveBarElement(defaultSensor);

View File

@ -0,0 +1,22 @@
package com.rusefi.ui.etb;
import org.putgemin.VerticalFlowLayout;
import javax.swing.*;
public class CalibrationPanel {
private final JPanel content = new JPanel(new VerticalFlowLayout());
public CalibrationPanel() {
content.setBorder(BorderFactory.createTitledBorder("Calibration"));
content.add(new JButton("Grab TPS#1 fully closed"));
content.add(new JButton("Grab TPS#1 Wide Open"));
content.add(new JButton("Grab Pedal Up"));
content.add(new JButton("Grab Pedal Down"));
}
public JPanel getContent() {
return content;
}
}

View File

@ -0,0 +1,39 @@
package com.rusefi.ui.etb;
import com.rusefi.ui.util.UiUtils;
import org.putgemin.VerticalFlowLayout;
import javax.swing.*;
public class CommandsPanel {
private final JPanel content = new JPanel(new VerticalFlowLayout());
public CommandsPanel() {
JPanel testParameters = new JPanel(new VerticalFlowLayout());
testParameters.setBorder(BorderFactory.createTitledBorder("Try PID settings"));
EtbTestSequence etbTestSequence = new EtbTestSequence();
testParameters.add(UiUtils.wrap(etbTestSequence.getButton()));
testParameters.add(etbTestSequence.getResult());
testParameters.add(new JLabel("To change setting use following commands:"));
testParameters.add(new JLabel("set etb_p X"));
testParameters.add(new JLabel("set etb_i X"));
testParameters.add(new JLabel("set etb_d X"));
testParameters.add(new JLabel("set etb_o X"));
testParameters.add(new JLabel("For example:"));
testParameters.add(new JLabel("set etb_p 1.1"));
content.setBorder(BorderFactory.createTitledBorder("Commands"));
content.add(testParameters);
content.add(UiUtils.wrap(new EtbMonteCarloSequence().getButton()));
content.add(UiUtils.wrap(new MagicSpotsFinder().getButton()));
}
public JPanel getContent() {
return content;
}
}

View File

@ -1,8 +1,11 @@
package com.rusefi.ui.widgets;
package com.rusefi.ui.etb;
import com.rusefi.core.MessagesCentral;
import com.rusefi.core.Sensor;
import com.rusefi.core.SensorCentral;
import com.rusefi.etb.EtbTarget;
import com.rusefi.etb.StandardTestSequence;
import com.rusefi.etb.TestSequenceStep;
import com.rusefi.io.CommandQueue;
import javax.swing.*;
@ -11,7 +14,7 @@ import java.util.Random;
import static com.rusefi.SensorLogger.getSecondsSinceFileStart;
import static com.rusefi.Timeouts.SECOND;
import static com.rusefi.ui.widgets.EtbTestSequence.*;
import static com.rusefi.ui.etb.EtbTestSequence.*;
/**
* 3/2/2019
@ -28,7 +31,7 @@ public class EtbMonteCarloSequence {
counter = 0;
// 3000 data points at 10Hz should be 300 seconds worth of data
metric.start(/* buffer size: */3000, /*period, ms: */ 100);
StandardTestSequence.metric.start(/* buffer size: */3000, /*period, ms: */ 100);
executor.execute(this::runRandomCycle);
});
@ -50,9 +53,9 @@ public class EtbMonteCarloSequence {
CommandQueue.getInstance().write("set etb_i " + iFactor);
CommandQueue.getInstance().write("set etb_d " + dFactor);
SequenceStep firstStep = new EtbTarget(10 * SECOND, 4 /*position*/);
SequenceStep last = addSequence(firstStep);
last.addNext(new SequenceStep(5 * SECOND) {
TestSequenceStep firstStep = new EtbTarget(10 * SECOND, 4, null);
TestSequenceStep last = StandardTestSequence.addSequence(firstStep, null);
last.addNext(new TestSequenceStep(5 * SECOND) {
@Override
protected void doJob() {
double result = SensorCentral.getInstance().getValue(Sensor.ETB_CONTROL_QUALITY);

View File

@ -0,0 +1,76 @@
package com.rusefi.ui.etb;
import com.rusefi.TimeBasedBuffer;
import com.rusefi.core.Sensor;
import com.rusefi.etb.EtbTarget;
import com.rusefi.etb.StandardTestSequence;
import com.rusefi.etb.TestSequenceStep;
import javax.swing.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import static com.rusefi.Timeouts.SECOND;
/**
* Here we initiate a sequence of commands which would change target electronic throttle position so that we can
* see how far the result would be how soon.
* <p>
* Error standard deviation is posted to {@link Sensor#ETB_CONTROL_QUALITY}
* <p>
* https://github.com/rusefi/rusefi/issues/494
* <p>
* 11/16/2017
* (c) Andrey Belomutskiy
*
* @see TimeBasedBuffer
*/
public class EtbTestSequence {
protected static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private static final String BUTTON_TEXT = "Measure Quality Control";
private final JButton button = new JButton(BUTTON_TEXT);
private final JLabel result = new JLabel("Result: N/A");
public EtbTestSequence() {
button.addActionListener(e -> {
button.setEnabled(false);
// 3000 data points at 10Hz should be 300 seconds worth of data
StandardTestSequence.metric.start(/* buffer size: */3000, /*period, ms: */ 100);
AtomicInteger stepCounter = new AtomicInteger();
TestSequenceStep lastStep = new TestSequenceStep(SECOND) {
@Override
protected void doJob() {
button.setEnabled(true);
button.setText(BUTTON_TEXT);
double value = StandardTestSequence.metric.getStandardDeviation();
result.setText(String.format("Result: %.3f", value));
}
};
Runnable onEachStep = new Runnable() {
@Override
public void run() {
SwingUtilities.invokeLater(() -> button.setText("Running " + stepCounter.incrementAndGet()));
}
};
TestSequenceStep firstStep = new EtbTarget(10 * SECOND, 4, /*position*/onEachStep);
TestSequenceStep result = StandardTestSequence.addSequence(firstStep, onEachStep);
result.addNext(lastStep);
firstStep.execute(executor);
});
}
public JButton getButton() {
return button;
}
public JLabel getResult() {
return result;
}
}

View File

@ -1,4 +1,4 @@
package com.rusefi.ui.widgets;
package com.rusefi.ui.etb;
import com.rusefi.core.MessagesCentral;
import com.rusefi.core.Sensor;
@ -11,12 +11,16 @@ import java.awt.event.ActionEvent;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import static com.rusefi.Timeouts.SECOND;
/**
* This tool finds interesting ETB duty cycles like the value when in starts to open or the value
* when it's completely open
* <p>
* (c) Andrey Belomutskiy
* 10/21/2018
*/
public class EtbResearch {
private static final int SECOND = 1000;
public class MagicSpotsFinder {
private static final long SLEEP = SECOND;
// how much do we want to change duty cycle on each iteration?
private static final double DUTY_CYCLE_STEP = 0.5;
@ -27,7 +31,7 @@ public class EtbResearch {
// private boolean isStarted;
State state;
private State state;
private double currentValue;
/**
* here we record at which duty cycle ETB would start moving
@ -149,7 +153,7 @@ public class EtbResearch {
backToZeroValue = 0;
}
public EtbResearch() {
public MagicSpotsFinder() {
button.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {

View File

@ -1,159 +0,0 @@
package com.rusefi.ui.widgets;
import com.rusefi.FileLog;
import com.rusefi.TimeBasedBuffer;
import com.rusefi.core.MessagesCentral;
import com.rusefi.core.Sensor;
import com.rusefi.core.SensorCentral;
import com.rusefi.io.CommandQueue;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Here we initiate a sequence of commands which would change target electronic throttle position so that we can
* see how far the result would be how soon.
* <p>
* Error standard deviation is posted to {@link Sensor#ETB_CONTROL_QUALITY}
* <p>
* https://github.com/rusefi/rusefi/issues/494
* <p>
* 11/16/2017
* (c) Andrey Belomutskiy
*
* @see TimeBasedBuffer
*/
public class EtbTestSequence {
protected static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
protected final static ClosedLoopControlQualityMetric metric = createMetric();
@NotNull
protected static ClosedLoopControlQualityMetric createMetric() {
return new ClosedLoopControlQualityMetric(
SensorCentral.getInstance().getValueSource(Sensor.PPS),
SensorCentral.getInstance().getValueSource(Sensor.TPS),
Sensor.ETB_CONTROL_QUALITY
);
}
private final static long SECOND = 1000;
private final JButton button = new JButton("ETB Test");
protected static SequenceStep addSequence(SequenceStep first_step) {
return first_step.addNext(new SequenceStep(SECOND) {
@Override
protected void doJob() {
metric.reset();
}
})
.addNext(10 * SECOND, 4 /*position*/)
.addNext(5 * SECOND, 6 /*position*/)
.addNext(5 * SECOND, 8 /*position*/)
.addNext(5 * SECOND, 6 /*position*/)
.addNext(5 * SECOND, 4 /*position*/)
.addNext(5 * SECOND, 2 /*position*/)
.addNext(5 * SECOND, 0 /*position*/)
.addNext(5 * SECOND, 10 /*position*/)
.addNext(3 * SECOND, 0 /*position*/)
.addNext(1 * SECOND, 6 /*position*/)
.addNext(1 * SECOND, 8 /*position*/)
.addNext(1 * SECOND, 6 /*position*/)
.addNext(1 * SECOND, 4 /*position*/)
.addNext(1 * SECOND, 2 /*position*/)
.addNext(1 * SECOND, 0 /*position*/)
.addNext(1 * SECOND, 10 /*position*/)
.addNext(3 * SECOND, 7 /*position*/)
.addNext(3 * SECOND, 6 /*position*/)
.addNext(3 * SECOND, 5 /*position*/)
.addNext(3 * SECOND, 4 /*position*/)
.addNext(3 * SECOND, 3 /*position*/)
.addNext(3 * SECOND, 2 /*position*/)
.addNext(3 * SECOND, 1 /*position*/)
.addNext(3 * SECOND, 0 /*position*/)
.addNext(10 * SECOND, 6 /*position*/)
.addNext(10 * SECOND, 30 /*position*/)
.addNext(10 * SECOND, 50 /*position*/)
.addNext(10 * SECOND, 70 /*position*/)
.addNext(10 * SECOND, 95 /*position*/)
.addNext(10 * SECOND, 50 /*position*/)
.addNext(10 * SECOND, 70 /*position*/)
.addNext(10 * SECOND, 4 /*position*/)
.addNext(10 * SECOND, 0 /*position*/)
;
}
public EtbTestSequence() {
button.addActionListener(e -> {
// 3000 data points at 10Hz should be 300 seconds worth of data
metric.start(/* buffer size: */3000, /*period, ms: */ 100);
SequenceStep firstStep = new EtbTarget(10 * SECOND, 4 /*position*/);
addSequence(firstStep);
firstStep.execute(executor);
});
}
public JButton getButton() {
return button;
}
static abstract class SequenceStep {
final long duration;
SequenceStep next;
public SequenceStep(long duration) {
this.duration = duration;
}
public void execute(ScheduledExecutorService executor) {
doJob();
if (next != null) {
FileLog.MAIN.logLine("Scheduling " + next + " with " + duration + "ms delay");
executor.schedule(() -> next.execute(executor), duration, TimeUnit.MILLISECONDS);
} else {
MessagesCentral.getInstance().postMessage(EtbTestSequence.class, "ETB test sequence done!");
}
}
protected abstract void doJob();
public SequenceStep addNext(long duration, double position) {
return addNext(new EtbTarget(duration, position));
}
public SequenceStep addNext(SequenceStep step) {
next = step;
return next;
}
}
static class EtbTarget extends SequenceStep {
/**
* 0-100 percent open
*/
final double position;
public EtbTarget(long duration, double position) {
super(duration);
this.position = position;
}
protected void doJob() {
CommandQueue.getInstance().write("set mock_pedal_position " + position);
}
@Override
public String toString() {
return "EtbTarget{" +
"duration=" + duration +
", position=" + position +
'}';
}
}
}