ETB quality instrumentation #494
This commit is contained in:
parent
5bb016ef59
commit
a92702dd54
|
@ -30,6 +30,10 @@ public class CyclicBuffer implements DataBuffer {
|
||||||
pointer = 0;
|
pointer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getPointer() {
|
||||||
|
return pointer;
|
||||||
|
}
|
||||||
|
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
@ -44,6 +48,10 @@ public class CyclicBuffer implements DataBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public double get(int i) {
|
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];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,6 +8,7 @@ import com.rusefi.core.SensorCentral;
|
||||||
import com.rusefi.core.SensorStats;
|
import com.rusefi.core.SensorStats;
|
||||||
import com.rusefi.core.ValueSource;
|
import com.rusefi.core.ValueSource;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -24,6 +25,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
*/
|
*/
|
||||||
public class ClosedLoopControlQualityMetric {
|
public class ClosedLoopControlQualityMetric {
|
||||||
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(ClosedLoopControlQualityMetric.class.getSimpleName()));
|
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(ClosedLoopControlQualityMetric.class.getSimpleName()));
|
||||||
|
private final int delayDepth;
|
||||||
|
|
||||||
private final ValueSource target;
|
private final ValueSource target;
|
||||||
private final ValueSource result;
|
private final ValueSource result;
|
||||||
|
@ -35,15 +37,18 @@ public class ClosedLoopControlQualityMetric {
|
||||||
* GuardedBy(this)
|
* GuardedBy(this)
|
||||||
*/
|
*/
|
||||||
private DataBuffer errorsBuffer;
|
private DataBuffer errorsBuffer;
|
||||||
|
private CyclicBuffer targetBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param target what value are we trying to achieve
|
* @param delayDepth
|
||||||
* @param result what value do we actually have
|
* @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.target = target;
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
|
this.delayDepth = delayDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(int bufferSize, int periodMs) {
|
public void start(int bufferSize, int periodMs) {
|
||||||
|
@ -51,14 +56,31 @@ public class ClosedLoopControlQualityMetric {
|
||||||
return;
|
return;
|
||||||
isStarted = true;
|
isStarted = true;
|
||||||
|
|
||||||
errorsBuffer = new CyclicBuffer(bufferSize);
|
create(bufferSize);
|
||||||
executor.scheduleAtFixedRate(() -> {
|
executor.scheduleAtFixedRate(() -> {
|
||||||
rememberCurrentError(target.getValue() - result.getValue());
|
add();
|
||||||
SensorCentral.getInstance().setValue(getStandardDeviation(), destination);
|
SensorCentral.getInstance().setValue(getStandardDeviation(), destination);
|
||||||
|
|
||||||
}, 0, periodMs, TimeUnit.MILLISECONDS);
|
}, 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() {
|
public synchronized void reset() {
|
||||||
errorsBuffer.clear();
|
errorsBuffer.clear();
|
||||||
}
|
}
|
||||||
|
@ -67,7 +89,9 @@ public class ClosedLoopControlQualityMetric {
|
||||||
return DataBuffer.getStandardDeviation(errorsBuffer.getValues());
|
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);
|
errorsBuffer.add(error);
|
||||||
|
targetBuffer.add(targetValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import com.rusefi.core.SensorCentral;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import static com.rusefi.Timeouts.SECOND;
|
import static com.rusefi.Timeouts.SECOND;
|
||||||
|
import static com.rusefi.ui.etb.EtbTestSequence.PAST_DEPTH;
|
||||||
|
|
||||||
public class StandardTestSequence {
|
public class StandardTestSequence {
|
||||||
public final static ClosedLoopControlQualityMetric metric = createMetric();
|
public final static ClosedLoopControlQualityMetric metric = createMetric();
|
||||||
|
@ -14,7 +15,8 @@ public class StandardTestSequence {
|
||||||
return new ClosedLoopControlQualityMetric(
|
return new ClosedLoopControlQualityMetric(
|
||||||
SensorCentral.getInstance().getValueSource(Sensor.PPS),
|
SensorCentral.getInstance().getValueSource(Sensor.PPS),
|
||||||
SensorCentral.getInstance().getValueSource(Sensor.TPS),
|
SensorCentral.getInstance().getValueSource(Sensor.TPS),
|
||||||
Sensor.ETB_CONTROL_QUALITY
|
Sensor.ETB_CONTROL_QUALITY,
|
||||||
|
PAST_DEPTH
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,14 +26,14 @@ import static com.rusefi.ui.etb.EtbTestSequence.*;
|
||||||
* (c) Andrey Belomutskiy
|
* (c) Andrey Belomutskiy
|
||||||
*/
|
*/
|
||||||
public class EtbMonteCarloSequence {
|
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 double DEFAULT_POSITION = 7;
|
||||||
private static final int CLT_THRESHOLD = 75;
|
private static final int CLT_THRESHOLD = 75;
|
||||||
private final JButton button = new JButton("ETB I feel lucky!");
|
private final JButton button = new JButton("ETB I feel lucky!");
|
||||||
private final static Random r = new Random();
|
private final static Random r = new Random();
|
||||||
private int counter;
|
private int counter;
|
||||||
|
|
||||||
private double bestResultSoFar = 75;
|
private double bestResultSoFar = 750;
|
||||||
|
|
||||||
public EtbMonteCarloSequence() {
|
public EtbMonteCarloSequence() {
|
||||||
button.addActionListener(e -> {
|
button.addActionListener(e -> {
|
||||||
|
@ -44,8 +44,6 @@ public class EtbMonteCarloSequence {
|
||||||
public void run() {
|
public void run() {
|
||||||
CommandQueue.getInstance().write(CANCEL_DIRECT_DRIVE_COMMAND);
|
CommandQueue.getInstance().write(CANCEL_DIRECT_DRIVE_COMMAND);
|
||||||
sleep(3 * SECOND);
|
sleep(3 * SECOND);
|
||||||
int durationSeconds = 300;
|
|
||||||
int frequencyHz = 100;
|
|
||||||
// 30000 data points at 100Hz should be 300 seconds worth of data
|
// 30000 data points at 100Hz should be 300 seconds worth of data
|
||||||
StandardTestSequence.metric.start(/* buffer size: */durationSeconds * frequencyHz, /*period, ms: */ 1000 / frequencyHz);
|
StandardTestSequence.metric.start(/* buffer size: */durationSeconds * frequencyHz, /*period, ms: */ 1000 / frequencyHz);
|
||||||
|
|
||||||
|
@ -58,9 +56,9 @@ public class EtbMonteCarloSequence {
|
||||||
|
|
||||||
private void runRandomCycle() {
|
private void runRandomCycle() {
|
||||||
final int offset = 0;//r.nextInt(100);
|
final int offset = 0;//r.nextInt(100);
|
||||||
final double pFactor = 1 + (r.nextInt(300) / 100.0);
|
final double pFactor = 6 + counter * 2;// + (r.nextInt(300) / 100.0);
|
||||||
final double iFactor = r.nextInt(30) / 100.0;
|
final double iFactor = 0;//r.nextInt(30) / 100.0;
|
||||||
final double dFactor = r.nextInt(30) / 100.0;
|
final double dFactor = 0;//r.nextInt(30) / 100.0;
|
||||||
String stats = "mcstats:offset:" + offset +
|
String stats = "mcstats:offset:" + offset +
|
||||||
":pFactor:" + pFactor +
|
":pFactor:" + pFactor +
|
||||||
":iFactor:" + iFactor +
|
":iFactor:" + iFactor +
|
||||||
|
|
|
@ -28,6 +28,9 @@ import static com.rusefi.Timeouts.SECOND;
|
||||||
*/
|
*/
|
||||||
public class EtbTestSequence {
|
public class EtbTestSequence {
|
||||||
protected static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
|
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";
|
private static final String BUTTON_TEXT = "Measure Control Quality";
|
||||||
|
|
||||||
|
@ -38,7 +41,8 @@ public class EtbTestSequence {
|
||||||
button.addActionListener(e -> {
|
button.addActionListener(e -> {
|
||||||
button.setEnabled(false);
|
button.setEnabled(false);
|
||||||
// 3000 data points at 10Hz should be 300 seconds worth of data
|
// 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 stepCounter = new AtomicInteger();
|
||||||
AtomicInteger totalSteps = new AtomicInteger();
|
AtomicInteger totalSteps = new AtomicInteger();
|
||||||
|
|
Loading…
Reference in New Issue