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.
|
* @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.
|
||||||
|
@@ -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])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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")));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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; }
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user