Updated error function calculation to be more efficient
This commit is contained in:
@@ -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.
|
||||
|
@@ -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])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -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")));
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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; }
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user