From a92702dd548396d70a0752f724ddd835d6f91b22 Mon Sep 17 00:00:00 2001 From: rusEfi Date: Wed, 26 Jun 2019 22:25:30 -0400 Subject: [PATCH] ETB quality instrumentation #494 --- .../models/src/com/rusefi/CyclicBuffer.java | 10 +++++- .../etb/ClosedLoopControlQualityMetric.java | 36 +++++++++++++++---- .../com/rusefi/etb/StandardTestSequence.java | 4 ++- .../ClosedLoopControlQualityMetricTest.java | 33 +++++++++++++++++ .../rusefi/ui/etb/EtbMonteCarloSequence.java | 12 +++---- .../com/rusefi/ui/etb/EtbTestSequence.java | 6 +++- 6 files changed, 85 insertions(+), 16 deletions(-) create mode 100644 java_console/ui/src/com/rusefi/etb/test/ClosedLoopControlQualityMetricTest.java diff --git a/java_console/models/src/com/rusefi/CyclicBuffer.java b/java_console/models/src/com/rusefi/CyclicBuffer.java index e36600ab16..127eee3606 100644 --- a/java_console/models/src/com/rusefi/CyclicBuffer.java +++ b/java_console/models/src/com/rusefi/CyclicBuffer.java @@ -30,6 +30,10 @@ public class CyclicBuffer implements DataBuffer { pointer = 0; } + public int getPointer() { + return pointer; + } + public int getSize() { return size; } @@ -44,6 +48,10 @@ public class CyclicBuffer implements DataBuffer { } public double get(int i) { - return values[i]; + if (size < 1) + throw new IllegalStateException("Size " + size); + while (i < 0) + i += size; + return values[i % size]; } } \ No newline at end of file diff --git a/java_console/ui/src/com/rusefi/etb/ClosedLoopControlQualityMetric.java b/java_console/ui/src/com/rusefi/etb/ClosedLoopControlQualityMetric.java index 9ccf8435dc..fa28db98f4 100644 --- a/java_console/ui/src/com/rusefi/etb/ClosedLoopControlQualityMetric.java +++ b/java_console/ui/src/com/rusefi/etb/ClosedLoopControlQualityMetric.java @@ -8,6 +8,7 @@ import com.rusefi.core.SensorCentral; import com.rusefi.core.SensorStats; import com.rusefi.core.ValueSource; +import java.util.Objects; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -24,6 +25,7 @@ import java.util.concurrent.TimeUnit; */ public class ClosedLoopControlQualityMetric { private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(ClosedLoopControlQualityMetric.class.getSimpleName())); + private final int delayDepth; private final ValueSource target; private final ValueSource result; @@ -35,15 +37,18 @@ public class ClosedLoopControlQualityMetric { * GuardedBy(this) */ private DataBuffer errorsBuffer; + private CyclicBuffer targetBuffer; /** - * @param target what value are we trying to achieve - * @param result what value do we actually have + * @param delayDepth + * @param target what value are we trying to achieve + * @param result what value do we actually have */ - public ClosedLoopControlQualityMetric(ValueSource target, ValueSource result, Sensor destination) { + public ClosedLoopControlQualityMetric(ValueSource target, ValueSource result, Sensor destination, int delayDepth) { this.target = target; this.result = result; this.destination = destination; + this.delayDepth = delayDepth; } public void start(int bufferSize, int periodMs) { @@ -51,14 +56,31 @@ public class ClosedLoopControlQualityMetric { return; isStarted = true; - errorsBuffer = new CyclicBuffer(bufferSize); + create(bufferSize); executor.scheduleAtFixedRate(() -> { - rememberCurrentError(target.getValue() - result.getValue()); + add(); SensorCentral.getInstance().setValue(getStandardDeviation(), destination); }, 0, periodMs, TimeUnit.MILLISECONDS); } + public void add() { + double targetValue = target.getValue(); + double resultValue = result.getValue(); + double error = Math.abs(targetValue - resultValue); + int pointer = targetBuffer.getPointer(); + for (int i = 0; i < Math.min(delayDepth - 1, targetBuffer.getSize()); i++) { + double thisError = Math.abs(targetBuffer.get(pointer - i) - resultValue); + error = Math.min(error, thisError); + } + rememberCurrentError(error, targetValue); + } + + public void create(int bufferSize) { + errorsBuffer = new CyclicBuffer(bufferSize); + targetBuffer = new CyclicBuffer(delayDepth); + } + public synchronized void reset() { errorsBuffer.clear(); } @@ -67,7 +89,9 @@ public class ClosedLoopControlQualityMetric { return DataBuffer.getStandardDeviation(errorsBuffer.getValues()); } - private synchronized void rememberCurrentError(double error) { + private synchronized void rememberCurrentError(double error, double targetValue) { + Objects.requireNonNull(errorsBuffer, "errorsBuffer"); errorsBuffer.add(error); + targetBuffer.add(targetValue); } } diff --git a/java_console/ui/src/com/rusefi/etb/StandardTestSequence.java b/java_console/ui/src/com/rusefi/etb/StandardTestSequence.java index ddcc0b8765..429f99c8ed 100644 --- a/java_console/ui/src/com/rusefi/etb/StandardTestSequence.java +++ b/java_console/ui/src/com/rusefi/etb/StandardTestSequence.java @@ -5,6 +5,7 @@ import com.rusefi.core.SensorCentral; import org.jetbrains.annotations.NotNull; import static com.rusefi.Timeouts.SECOND; +import static com.rusefi.ui.etb.EtbTestSequence.PAST_DEPTH; public class StandardTestSequence { public final static ClosedLoopControlQualityMetric metric = createMetric(); @@ -14,7 +15,8 @@ public class StandardTestSequence { return new ClosedLoopControlQualityMetric( SensorCentral.getInstance().getValueSource(Sensor.PPS), SensorCentral.getInstance().getValueSource(Sensor.TPS), - Sensor.ETB_CONTROL_QUALITY + Sensor.ETB_CONTROL_QUALITY, + PAST_DEPTH ); } diff --git a/java_console/ui/src/com/rusefi/etb/test/ClosedLoopControlQualityMetricTest.java b/java_console/ui/src/com/rusefi/etb/test/ClosedLoopControlQualityMetricTest.java new file mode 100644 index 0000000000..ecdb2e2905 --- /dev/null +++ b/java_console/ui/src/com/rusefi/etb/test/ClosedLoopControlQualityMetricTest.java @@ -0,0 +1,33 @@ +package com.rusefi.etb.test; + +import com.rusefi.core.Sensor; +import com.rusefi.etb.ClosedLoopControlQualityMetric; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; + +public class ClosedLoopControlQualityMetricTest { + private static final double EPS = 0; + + @Test + public void testPastDepth() { + AtomicInteger targetValue = new AtomicInteger(); + AtomicInteger resultValue = new AtomicInteger(); + + ClosedLoopControlQualityMetric m = new ClosedLoopControlQualityMetric( + targetValue::doubleValue, + resultValue::doubleValue, + Sensor.ETB_CONTROL_QUALITY, 3); + m.create(1000); + + m.add(); + assertEquals(0, m.getStandardDeviation(), EPS); + + targetValue.set(10); + // result same 0 + m.add(); + assertEquals(0, m.getStandardDeviation(), EPS); + } +} diff --git a/java_console/ui/src/com/rusefi/ui/etb/EtbMonteCarloSequence.java b/java_console/ui/src/com/rusefi/ui/etb/EtbMonteCarloSequence.java index cbfbe01054..f48a068dce 100644 --- a/java_console/ui/src/com/rusefi/ui/etb/EtbMonteCarloSequence.java +++ b/java_console/ui/src/com/rusefi/ui/etb/EtbMonteCarloSequence.java @@ -26,14 +26,14 @@ import static com.rusefi.ui.etb.EtbTestSequence.*; * (c) Andrey Belomutskiy */ public class EtbMonteCarloSequence { - private static final int TOTAL_CYCLES_COUNT = 300; + private static final int TOTAL_CYCLES_COUNT = 15; private static final double DEFAULT_POSITION = 7; private static final int CLT_THRESHOLD = 75; private final JButton button = new JButton("ETB I feel lucky!"); private final static Random r = new Random(); private int counter; - private double bestResultSoFar = 75; + private double bestResultSoFar = 750; public EtbMonteCarloSequence() { button.addActionListener(e -> { @@ -44,8 +44,6 @@ public class EtbMonteCarloSequence { public void run() { CommandQueue.getInstance().write(CANCEL_DIRECT_DRIVE_COMMAND); sleep(3 * SECOND); - int durationSeconds = 300; - int frequencyHz = 100; // 30000 data points at 100Hz should be 300 seconds worth of data StandardTestSequence.metric.start(/* buffer size: */durationSeconds * frequencyHz, /*period, ms: */ 1000 / frequencyHz); @@ -58,9 +56,9 @@ public class EtbMonteCarloSequence { private void runRandomCycle() { final int offset = 0;//r.nextInt(100); - final double pFactor = 1 + (r.nextInt(300) / 100.0); - final double iFactor = r.nextInt(30) / 100.0; - final double dFactor = r.nextInt(30) / 100.0; + final double pFactor = 6 + counter * 2;// + (r.nextInt(300) / 100.0); + final double iFactor = 0;//r.nextInt(30) / 100.0; + final double dFactor = 0;//r.nextInt(30) / 100.0; String stats = "mcstats:offset:" + offset + ":pFactor:" + pFactor + ":iFactor:" + iFactor + diff --git a/java_console/ui/src/com/rusefi/ui/etb/EtbTestSequence.java b/java_console/ui/src/com/rusefi/ui/etb/EtbTestSequence.java index 03eaee4e9c..fbe89cc0a4 100644 --- a/java_console/ui/src/com/rusefi/ui/etb/EtbTestSequence.java +++ b/java_console/ui/src/com/rusefi/ui/etb/EtbTestSequence.java @@ -28,6 +28,9 @@ import static com.rusefi.Timeouts.SECOND; */ public class EtbTestSequence { protected static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + public static final int durationSeconds = 300; + public static final int frequencyHz = 100; + public static final int PAST_DEPTH = 200 / (1000 / frequencyHz); private static final String BUTTON_TEXT = "Measure Control Quality"; @@ -38,7 +41,8 @@ public class 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); + StandardTestSequence.metric.start(/* buffer size: */durationSeconds * frequencyHz, + /*period, ms: */ 1000 / frequencyHz); AtomicInteger stepCounter = new AtomicInteger(); AtomicInteger totalSteps = new AtomicInteger();