ETB quality instrumentation #494
This commit is contained in:
parent
5bb016ef59
commit
a92702dd54
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
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 +
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue