Updated error function calculation to be more efficient

This commit is contained in:
Luca Warmenhoven
2024-05-09 17:12:59 +02:00
parent 8bbf5750f6
commit a16bd22e99
6 changed files with 73 additions and 34 deletions

View File

@@ -44,14 +44,42 @@ public class GesturePath {
* @return The error offset between the path and the reference point.
*/
public double getError(Vector3 referencePoint) {
// If there are no vectors, return 0.
if ( vectors.length == 0)
return 0;
// If there's only one vector, return the distance to that vector.
if ( vectors.length == 1)
return vectors[0].distance(referencePoint);
double distance = Double.MAX_VALUE;
for (int i = 0; i < vectors.length - 1; i++) {
double error = getError(vectors[i], vectors[i + 1], referencePoint);
if (error < distance) {
distance = error;
double currentDistSq, nextDistSq;
int closestVectorIdx = 0;
// Acquire two closest points to the reference point.
for ( int i = 0; i < vectors.length - 1; i++) {
currentDistSq = vectors[i].distanceSq(referencePoint);
nextDistSq = vectors[i + 1].distanceSq(referencePoint);
if ( currentDistSq < distance) {
distance = currentDistSq;
closestVectorIdx = i;
} else if ( nextDistSq < distance) {
distance = nextDistSq;
closestVectorIdx = i + 1;
i++; // Skip the next iteration; this point is already closer.
}
}
return distance;
// Calculate the error between the two closest points.
Vector3 pointB = (closestVectorIdx == vectors.length - 1) ?
vectors[closestVectorIdx - 1] : // If the closest point is the last point, use the 1 to last one
(closestVectorIdx > 0 && // Find the closer point between the surrounding points.
(vectors[closestVectorIdx - 1].distanceSq(referencePoint) < vectors[closestVectorIdx + 1].distanceSq(referencePoint))) ?
vectors[closestVectorIdx - 1] :
vectors[closestVectorIdx + 1];
return getError(vectors[closestVectorIdx], pointB, referencePoint);
}
// Builder class for the GesturePath object.

View File

@@ -20,7 +20,7 @@ public class MotionData {
* @param rotationY The rotation in the Y axis in degrees.
* @param rotationZ The rotation in the Z axis in degrees.
*/
public MotionData(float accelerationX, float accelerationY, float accelerationZ, float rotationX, float rotationY, float rotationZ) {
public MotionData(double accelerationX, double accelerationY, double accelerationZ, double rotationX, double rotationY, double rotationZ) {
this.acceleration = new Vector3(accelerationX, accelerationY, accelerationZ);
this.rotation = new Vector3(rotationX, rotationY, rotationZ);
}
@@ -52,12 +52,12 @@ public class MotionData {
return null;
return new MotionData(
Float.parseFloat(parts[0]),
Float.parseFloat(parts[1]),
Float.parseFloat(parts[2]),
Float.parseFloat(parts[3]),
Float.parseFloat(parts[4]),
Float.parseFloat(parts[5])
Double.parseDouble(parts[0]),
Double.parseDouble(parts[1]),
Double.parseDouble(parts[2]),
Double.parseDouble(parts[3]),
Double.parseDouble(parts[4]),
Double.parseDouble(parts[5])
);
}
}

View File

@@ -83,12 +83,22 @@ public class MotionProcessor {
this.motionData.add(data);
}
/**
* Function for updating the relative path.
*
* @param relativePath The new relative path.
*/
public void setRelativePath(List<Vector3> relativePath) {
this.relativePath.clear();
this.relativePath.addAll(relativePath);
}
/**
* Function for calculating the 'absolute' path of the motion data.
* This converts the relative acceleration and rotation vectors and converts
* them to a position that is relative to the ZERO point.
*/
public void calculatePath() {
public void calculateRelativePath() {
relativePath.clear(); // Clear previous path
@@ -121,7 +131,8 @@ public class MotionProcessor {
// s = 1/2 * a * t^2
return motionData.acceleration
.rotate(motionData.rotation.negate())
.multiply(sampleRate * sampleRate / 6.0D);
.divide(2)
.multiply(sampleRate * sampleRate);
}
/**
@@ -135,7 +146,7 @@ public class MotionProcessor {
// If the relative path is empty, calculate it.
if ( relativePath.isEmpty())
calculatePath();
calculateRelativePath();
// Return the errors of the relative path compared to the reference path.
return relativePath
@@ -159,7 +170,14 @@ public class MotionProcessor {
.orElse(0.0D);
}
public void printData() {
public void logStatistics(GesturePath referencePath) {
Log.i("MotionProcessor", "Average path error: " + averageError(referencePath));
Log.i("MotionProcessor", "Path length: " + relativePath.size());
Log.i("MotionProcessor", "Sample rate: " + sampleRate);
Log.i("MotionProcessor", "Calibration point: " + ZERO.toString());
Log.i("MotionProcessor", "Path comparison:\n" +
relativePath.stream().map(Vector3::toString).collect(Collectors.joining(", \t")) +
"\n" + getErrors(referencePath).stream().map(Object::toString).collect(Collectors.joining(", \t")));
}
}

View File

@@ -84,7 +84,7 @@ public class WebSocket {
public static class Message {
// Enumerable representing message type (opcode).
public enum Type {
public enum Opcode {
CONTINUING((byte) 0x0),
TEXT((byte) 0x1),
@@ -96,7 +96,7 @@ public class WebSocket {
RES5((byte) 0xB), RES6((byte) 0xC), RES7((byte) 0xD), RES8((byte) 0xE), RES9((byte) 0xF);
byte opcode;
Type(final byte opcode) {
Opcode(final byte opcode) {
this.opcode = opcode;
}
@@ -105,8 +105,8 @@ public class WebSocket {
* @param opcode The opcode to decode.
* @return The message type.
*/
public Type decode(byte opcode) {
return Type.values()[opcode & 0xF];
public static Opcode decode(byte opcode) {
return Opcode.values()[opcode & 0xF];
}
// Returns the opcode of this message type.
public byte getOpcode() { return this.opcode; }

View File

@@ -1,7 +1,5 @@
package com.example.fitbot.util.server;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
import java.io.IOException;
@@ -31,7 +29,6 @@ public class WebSocketConnectionHandler implements Runnable {
this.theSocket = webSocket;
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void run() {
// Listen for new connections until the socket
@@ -65,7 +62,6 @@ public class WebSocketConnectionHandler implements Runnable {
* @param streamOut The OutputStream of the client socket connection.
* @return Whether or not the connection was successfully upgraded.
*/
@RequiresApi(api = Build.VERSION_CODES.O)
private boolean upgradeConnection(InputStream streamIn, OutputStream streamOut) {
Scanner scanner = new Scanner(streamIn, "UTF-8");
String data = scanner.useDelimiter("\\r\\n\\r\\n").next();
@@ -105,7 +101,7 @@ public class WebSocketConnectionHandler implements Runnable {
* @param streamIn The message stream to decode.
*/
private void applyMessageDecoder(InputStream streamIn) {
// TODO: Implement
}
/**
@@ -119,13 +115,11 @@ public class WebSocketConnectionHandler implements Runnable {
if (bytes.length <= 2 || (bytes[0] & 0b1110) != 0)
throw new IllegalArgumentException("Attempted to decode corrupted WebSocket frame data");
byte opcode = (byte) (bytes[0] & 0b11110000); // Opcode (4 bits)
WebSocket.Message.Opcode opcode = WebSocket.Message.Opcode.decode((byte) (bytes[0] & 0b11110000));
byte payloadLength = (byte) (bytes[1] & 0b01111111); // Payload size (7 bits)
boolean fin = (bytes[0] & 0b1) != 0; // Whether this is the whole message
boolean masked = (bytes[1] & 0b10000000) != 0; // Whether the 9th bit is masked
System.out.printf("Fin: %b, Opcode: %d, masked: %b\n", fin, opcode, masked);
long extendedPayloadLength = 0;
int byteOffset = 2;

View File

@@ -1,6 +1,8 @@
package com.example.fitbot;
import static org.junit.Assert.assertEquals;
import com.example.fitbot.util.server.WebSocketConnectionHandler;
import org.junit.Test;
@@ -11,6 +13,8 @@ import org.junit.Test;
*/
public class WebSocketMessageParsingTest {
@Test
public void parseWebSocketMessage() {
@@ -27,11 +31,6 @@ public class WebSocketMessageParsingTest {
} catch (Exception e) {
System.err.println("Error occurred whilst attempting to parse input" + e.getMessage());
}
System.out.println("Parsed output: '" + decoded + "'");
if (decoded.equals(reference)) {
System.out.println("Test passed");
} else {
System.out.println("Test failed");
}
assertEquals(reference, decoded);
}
}