diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java index 7d0438e..e5b143d 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java @@ -152,7 +152,7 @@ public class PersonalMotionPreviewElement extends View { ); timePassed = (System.nanoTime() - startingTime) / 1E9D; - this.exerciseProgress = timePassed*.4 % 1.0d; + this.exerciseProgress = Math.min(1, this.motionProcessor.getAverageError() / 10); this.invalidate(); // Causes a redraw. } diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java index 198f136..c76d0cb 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java @@ -2,11 +2,11 @@ package com.example.fitbot.util.processing; import android.util.Log; -import com.aldebaran.qi.sdk.QiContext; -import com.example.fitbot.util.FitnessCycle; import com.example.fitbot.util.path.GesturePath; -import com.example.fitbot.util.server.IWebServerHandler; import com.example.fitbot.util.server.WebServer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import org.jetbrains.annotations.NotNull; import org.joml.Vector3f; @@ -20,11 +20,16 @@ public class MotionProcessor { public static final String DELIMITER = ";"; private final List preprocessedData = new ArrayList<>(); // Preprocessed motion data - private final List relativePath = new ArrayList<>(); // Relative path of the motion data + + private final List relativeLeftPath = new ArrayList<>(); // Relative path of the left motion data + private final List relativeRightPath = new ArrayList<>(); // Relative path of the motion data + private Vector3f ZERO = new Vector3f(0, 0, 0); - private float sampleRate = 1.0F; // samples/second - private IMotionDataConsumer motionDataConsumer = (p1, p2, p3, p4, p5) -> {}; + private final float sampleRate = 10.0F; // samples/second + + private IMotionDataConsumer motionDataConsumer = (p1, p2, p3, p4, p5) -> { + }; private GesturePath path; private WebServer server; @@ -65,30 +70,51 @@ public class MotionProcessor { /** * Function for parsing arbitrary packet data. + * * @param data The data to parse. */ public void parsePacket(@NotNull String data) { - // If the message starts with 'data', it's a data packet. - if ( data.startsWith("data")) { - Log.i("MotionProcessor", "Received data packet: " + data.split(" ")[1]); - MotionData parsedData = MotionData.decode(data.split(" ")[1]); - if (parsedData != null) { - addMotionData(parsedData); + try { + JsonElement json = JsonParser.parseString(data); + + if (!json.isJsonObject()) + return; + + JsonObject object = json.getAsJsonObject(); + + // Object must contain device identifier + if (!object.has("deviceId")) + return; + + String[] required = { + "rotationX", "rotationY", "rotationZ", + "accelerationX", "accelerationY", "accelerationZ", + "type", + "deviceId" + }; + + // Ensure all properties are present in the received JSON object + for (String s : required) { + if (!object.has(s)) + return; } - // Otherwise check if it starts with 'calibrate', this is the ZERO point. - } else if ( data.startsWith("zero")) { // message to calibrate device - String[] vectorData = data.split(" ")[1].split(DELIMITER); - ZERO = new Vector3f( - Float.parseFloat(vectorData[0]), - Float.parseFloat(vectorData[1]), - Float.parseFloat(vectorData[2]) - ); - Log.i("MotionProcessor", "Device calibrated at " + ZERO); - } else if ( data.startsWith("sampleRate")) { - this.sampleRate = Float.parseFloat(data.split(" ")[1]); - } else { - Log.i("MotionProcessor", "Received unknown packet: " + data); + + // Parse the data + Vector3f rotation = new Vector3f(object.get("rotationX").getAsFloat(), object.get("rotationY").getAsFloat(), object.get("rotationZ").getAsFloat()); + Vector3f acceleration = new Vector3f(object.get("accelerationX").getAsFloat(), object.get("accelerationY").getAsFloat(), object.get("accelerationZ").getAsFloat()); + int deviceId = object.get("deviceId").getAsInt(); + String type = object.get("type").getAsString(); + MotionData motionData = new MotionData(rotation, acceleration, deviceId); + + if (type.equals("calibrate")) { + ZERO = getRelativeVector(motionData); + return; + } + + addMotionData(motionData); + } catch (Exception e) { + // Don't do anything ... just ignore the exception } } @@ -108,28 +134,35 @@ public class MotionProcessor { */ public void addMotionData(MotionData data) { preprocessedData.add(data); - Vector3f previous = this.relativePath.isEmpty() ? ZERO : this.relativePath.get(this.relativePath.size() - 1); + List target; + if (data.sensorId == 0) + target = relativeLeftPath; + else target = relativeRightPath; + Vector3f previous = target.isEmpty() ? ZERO : target.get(target.size() - 1); Vector3f relativeVector = getRelativeVector(data).add(previous); - this.relativePath.add(relativeVector); - motionDataConsumer.accept(relativeVector, data, this.relativePath.size(), this.sampleRate, data.sensorId); + target.add(relativeVector); + motionDataConsumer.accept(relativeVector, data, target.size(), this.sampleRate, data.sensorId); } /** * Function for updating the relative path. * - * @param relativePath The new relative path. + * @param relativeRightPath The new relative path. */ - public void setRelativePath(List relativePath) { - this.relativePath.clear(); - this.relativePath.addAll(relativePath); + public void setRelativeRightPath(List relativeLeftPath, List relativeRightPath) { + this.relativeRightPath.clear(); + this.relativeLeftPath.clear(); + this.relativeLeftPath.addAll(relativeLeftPath); + this.relativeRightPath.addAll(relativeRightPath); } /** * Function for setting the motion data receiver. + * * @param consumer The consumer to set. */ public void setMotionDataEventHandler(IMotionDataConsumer consumer) { - if ( consumer != null) + if (consumer != null) this.motionDataConsumer = consumer; } @@ -164,11 +197,11 @@ public class MotionProcessor { */ public List getErrors(GesturePath referencePath) { - // Return the errors of the relative path compared to the reference path. - return relativePath - .stream() - .map(referencePath::getError) - .collect(Collectors.toList()); + List errors = new ArrayList<>(); + for (Vector3f vector : relativeRightPath) { + errors.add(referencePath.getError(vector)); + } + return errors; } /** @@ -176,10 +209,10 @@ public class MotionProcessor { * reference path. * * @return A list of error offsets of the motion data compared to the reference path. - * If no path is set, an empty list will be returned. + * If no path is set, an empty list will be returned. */ public List getErrors() { - if ( path == null) + if (path == null) return new ArrayList<>(); return getErrors(path); } @@ -187,12 +220,11 @@ public class MotionProcessor { /** * Function for getting the error of the motion data compared to the reference path. * - * @param path The path to compare the motion data to. + * @param path The path to compare the motion data to. * @param referencePoint The reference point to compare the motion data to. * @return The error of the motion data compared to the reference path. */ - public double getError(GesturePath path, Vector3f referencePoint) - { + public double getError(GesturePath path, Vector3f referencePoint) { return path.getError(referencePoint); } @@ -204,7 +236,7 @@ public class MotionProcessor { * @return The error of the motion data compared to the reference path. */ public double getError(Vector3f referencePoint) { - if ( path == null) + if (path == null) return 0; return path.getError(referencePoint); } @@ -217,11 +249,11 @@ public class MotionProcessor { * @return The average error of the motion data compared to the reference path. */ public double getAverageError(GesturePath referencePath) { - return getErrors(referencePath) - .stream() - .mapToDouble(Double::doubleValue) - .average() - .orElse(0.0D); + double error = 0; + for (Double e : getErrors(referencePath)) { + error += e; + } + return error / Math.max(1, relativeRightPath.size()); } /** @@ -231,7 +263,7 @@ public class MotionProcessor { * @return The average error of the motion data compared to the reference path. */ public double getAverageError() { - if ( path == null) + if (path == null) return 0; return getAverageError(path); } @@ -243,7 +275,7 @@ public class MotionProcessor { */ public void logStatistics(GesturePath referencePath) { Log.i("MotionProcessor", "Average path error: " + getAverageError(referencePath)); - Log.i("MotionProcessor", "Path length: " + relativePath.size()); + Log.i("MotionProcessor", "Path length: " + relativeRightPath.size()); Log.i("MotionProcessor", "Sample rate: " + sampleRate); Log.i("MotionProcessor", "Calibration point: " + ZERO.toString()); }