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. * @return The error offset between the path and the reference point.
*/ */
public double getError(Vector3 referencePoint) { 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; double distance = Double.MAX_VALUE;
for (int i = 0; i < vectors.length - 1; i++) { double currentDistSq, nextDistSq;
double error = getError(vectors[i], vectors[i + 1], referencePoint); int closestVectorIdx = 0;
if (error < distance) {
distance = error; // 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. // 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 rotationY The rotation in the Y axis in degrees.
* @param rotationZ The rotation in the Z 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.acceleration = new Vector3(accelerationX, accelerationY, accelerationZ);
this.rotation = new Vector3(rotationX, rotationY, rotationZ); this.rotation = new Vector3(rotationX, rotationY, rotationZ);
} }
@@ -52,12 +52,12 @@ public class MotionData {
return null; return null;
return new MotionData( return new MotionData(
Float.parseFloat(parts[0]), Double.parseDouble(parts[0]),
Float.parseFloat(parts[1]), Double.parseDouble(parts[1]),
Float.parseFloat(parts[2]), Double.parseDouble(parts[2]),
Float.parseFloat(parts[3]), Double.parseDouble(parts[3]),
Float.parseFloat(parts[4]), Double.parseDouble(parts[4]),
Float.parseFloat(parts[5]) Double.parseDouble(parts[5])
); );
} }
} }

View File

@@ -83,12 +83,22 @@ public class MotionProcessor {
this.motionData.add(data); 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. * Function for calculating the 'absolute' path of the motion data.
* This converts the relative acceleration and rotation vectors and converts * This converts the relative acceleration and rotation vectors and converts
* them to a position that is relative to the ZERO point. * them to a position that is relative to the ZERO point.
*/ */
public void calculatePath() { public void calculateRelativePath() {
relativePath.clear(); // Clear previous path relativePath.clear(); // Clear previous path
@@ -121,7 +131,8 @@ public class MotionProcessor {
// s = 1/2 * a * t^2 // s = 1/2 * a * t^2
return motionData.acceleration return motionData.acceleration
.rotate(motionData.rotation.negate()) .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 the relative path is empty, calculate it.
if ( relativePath.isEmpty()) if ( relativePath.isEmpty())
calculatePath(); calculateRelativePath();
// Return the errors of the relative path compared to the reference path. // Return the errors of the relative path compared to the reference path.
return relativePath return relativePath
@@ -159,7 +170,14 @@ public class MotionProcessor {
.orElse(0.0D); .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 { public static class Message {
// Enumerable representing message type (opcode). // Enumerable representing message type (opcode).
public enum Type { public enum Opcode {
CONTINUING((byte) 0x0), CONTINUING((byte) 0x0),
TEXT((byte) 0x1), 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); RES5((byte) 0xB), RES6((byte) 0xC), RES7((byte) 0xD), RES8((byte) 0xE), RES9((byte) 0xF);
byte opcode; byte opcode;
Type(final byte opcode) { Opcode(final byte opcode) {
this.opcode = opcode; this.opcode = opcode;
} }
@@ -105,8 +105,8 @@ public class WebSocket {
* @param opcode The opcode to decode. * @param opcode The opcode to decode.
* @return The message type. * @return The message type.
*/ */
public Type decode(byte opcode) { public static Opcode decode(byte opcode) {
return Type.values()[opcode & 0xF]; return Opcode.values()[opcode & 0xF];
} }
// Returns the opcode of this message type. // Returns the opcode of this message type.
public byte getOpcode() { return this.opcode; } public byte getOpcode() { return this.opcode; }

View File

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

View File

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