mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Huawei: Research related to GPS routes for workouts.
This commit is contained in:
+12
-3
@@ -28,6 +28,7 @@ public class HuaweiGpsParser {
|
||||
public int timestamp;
|
||||
public double latitude;
|
||||
public double longitude;
|
||||
public boolean pause;
|
||||
public boolean altitudeSupported;
|
||||
public double altitude;
|
||||
|
||||
@@ -38,6 +39,7 @@ public class HuaweiGpsParser {
|
||||
"timestamp=" + timestamp +
|
||||
", longitude=" + longitude +
|
||||
", latitude=" + latitude +
|
||||
", pause=" + pause +
|
||||
", altitudeSupported=" + altitudeSupported +
|
||||
", altitude=" + altitude +
|
||||
'}';
|
||||
@@ -90,10 +92,12 @@ public class HuaweiGpsParser {
|
||||
ArrayList<GpsPoint> retv = new ArrayList<>(buffer.remaining() / data_size);
|
||||
while (buffer.remaining() > data_size) {
|
||||
short time_delta = buffer.getShort();
|
||||
buffer.getShort(); // Unknown value
|
||||
buffer.getShort(); // Unknown value, possible "bearing" (buffer.getShort() & 0xFFFF) * 0.01.
|
||||
float lon_delta = buffer.getFloat();
|
||||
float lat_delta = buffer.getFloat();
|
||||
buffer.get(); buffer.get(); buffer.get(); // Unknown values
|
||||
buffer.get(); // Unknown values, possible "accuracy"
|
||||
buffer.get(); // Unknown values, possible "velocity" (buffer.get() & 0xFF) * 0.1
|
||||
byte pause = buffer.get();
|
||||
|
||||
time = time + time_delta;
|
||||
lat = lat + lat_delta;
|
||||
@@ -101,12 +105,17 @@ public class HuaweiGpsParser {
|
||||
|
||||
GpsPoint point = new GpsPoint();
|
||||
point.timestamp = time;
|
||||
// NOTE: instead of 6383807.0d should be 6378245.0 (Krassovsky 1940 ellipsoid).
|
||||
// According to my research it provides better result. But I am not sure.
|
||||
// Additional research required.
|
||||
point.latitude = (lat / 6383807.0d + lat_start) / 0.017453292519943d;
|
||||
point.longitude = (lon / 6383807.0d / Math.cos(lat_start) + lon_start) / 0.017453292519943d;
|
||||
point.pause = pause == 1;
|
||||
point.altitudeSupported = alt_support;
|
||||
if (alt_support) {
|
||||
// NOTE: in the modern devices e.g Watch Gt6 Pro altitude values absent or completely broken.
|
||||
alt = buffer.getShort();
|
||||
buffer.getShort(); // Unknown values
|
||||
buffer.getShort(); // Unknown values related to altitude.
|
||||
point.altitude = alt;
|
||||
}
|
||||
retv.add(point);
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class HuaweiPdrParser {
|
||||
|
||||
public static final double DEG_TO_RAD = 0.017453292519943d;
|
||||
|
||||
public static class PdrPoint {
|
||||
public int timestamp;
|
||||
public double x;
|
||||
public double y;
|
||||
public double distance;
|
||||
public double speed;
|
||||
public double yaw;
|
||||
public byte quality;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PdrPoint{" + "timestamp=" + timestamp +
|
||||
", x=" + x +
|
||||
", y=" + y +
|
||||
", distance=" + distance +
|
||||
", speed=" + speed +
|
||||
", yaw=" + yaw +
|
||||
", quality=" + quality +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static PdrPoint[] parseHuaweiPdr(byte[] data) {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
// Skip trim
|
||||
buffer.position(0x20);
|
||||
|
||||
int timestamp = buffer.getInt();
|
||||
buffer.get();
|
||||
buffer.get();
|
||||
|
||||
int time = timestamp;
|
||||
|
||||
double dx = 0;
|
||||
double dy = 0;
|
||||
|
||||
ArrayList<PdrPoint> retv = new ArrayList<>(buffer.remaining() / 6);
|
||||
while (buffer.remaining() > 6) {
|
||||
short time_delta = buffer.getShort();
|
||||
short rawYaw = buffer.getShort();
|
||||
byte rawDistance = buffer.get();
|
||||
byte quality = buffer.get(); //not sure
|
||||
|
||||
time += time_delta;
|
||||
|
||||
double yaw = (double) (rawYaw & 0xFFFF) * 0.01 * DEG_TO_RAD;
|
||||
double distance = (double) (rawDistance & 0xFF) * 0.1;
|
||||
|
||||
double x = Math.cos(yaw) * distance;
|
||||
double y = Math.sin(yaw) * distance;
|
||||
|
||||
dx = x + dx;
|
||||
dy = y + dy;
|
||||
|
||||
double speed = distance / time_delta;
|
||||
|
||||
PdrPoint point = new PdrPoint();
|
||||
point.timestamp = time;
|
||||
point.x = dx;
|
||||
point.y = dy;
|
||||
point.distance = distance;
|
||||
point.speed = speed;
|
||||
point.yaw = yaw;
|
||||
point.quality = quality;
|
||||
|
||||
retv.add(point);
|
||||
}
|
||||
return retv.toArray(new PdrPoint[0]);
|
||||
}
|
||||
|
||||
}
|
||||
+3
@@ -35,6 +35,7 @@ public class FileDownloadService2C {
|
||||
SLEEP_DATA,
|
||||
RRI,
|
||||
GPS,
|
||||
PDR,
|
||||
SEQUENCE_DATA,
|
||||
ECG_ANALYSIS_DATA,
|
||||
UNKNOWN; // Never use this as input
|
||||
@@ -45,6 +46,7 @@ public class FileDownloadService2C {
|
||||
case SLEEP_DATA -> (byte) 0x0f;
|
||||
case RRI -> (byte) 0x10;
|
||||
case GPS -> (byte) 0x11;
|
||||
case PDR -> (byte)0x12;
|
||||
case SEQUENCE_DATA -> (byte) 0x16;
|
||||
case ECG_ANALYSIS_DATA -> (byte) 0x18;
|
||||
default -> throw new RuntimeException();
|
||||
@@ -57,6 +59,7 @@ public class FileDownloadService2C {
|
||||
case 0x0f -> FileType.SLEEP_DATA;
|
||||
case 0x10 -> FileType.RRI;
|
||||
case 0x11 -> FileType.GPS;
|
||||
case 0x12 -> FileType.PDR;
|
||||
case 0x16 -> FileType.SEQUENCE_DATA;
|
||||
case 0x18 -> FileType.ECG_ANALYSIS_DATA;
|
||||
default -> FileType.UNKNOWN;
|
||||
|
||||
+5
@@ -123,6 +123,7 @@ public class HuaweiFileDownloadManager {
|
||||
SLEEP_DATA,
|
||||
RRI,
|
||||
GPS,
|
||||
PDR,
|
||||
SEQUENCE_DATA,
|
||||
ECG_ANALYSIS_DATA,
|
||||
UNKNOWN // Never for input!
|
||||
@@ -233,6 +234,10 @@ public class HuaweiFileDownloadManager {
|
||||
return new FileRequest(null, FileType.GPS, false, workoutId, databaseId, fileDownloadCallback);
|
||||
}
|
||||
|
||||
public static FileRequest workoutPdrFileRequest(short workoutId, Long databaseId, FileDownloadCallback fileDownloadCallback) {
|
||||
return new FileRequest(String.format(Locale.getDefault(), "%d_pdr.bin", workoutId), FileType.PDR, true, workoutId, databaseId, fileDownloadCallback);
|
||||
}
|
||||
|
||||
// Retrieved
|
||||
|
||||
private int fileSize;
|
||||
|
||||
+30
-3
@@ -78,6 +78,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiEmotionsSampleP
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiGpsParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiHrvValueSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPdrParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiSequenceDataFileParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiSleepApneaSampleProvider;
|
||||
@@ -99,7 +100,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiActivitySampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgDataSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgDataSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiEcgSummarySample;
|
||||
@@ -109,7 +109,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiHrvValueSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepApneaSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepStageSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepStatsSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiSleepStatsSampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiStressSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiTemperatureSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutDataSample;
|
||||
@@ -3467,11 +3466,39 @@ public class HuaweiSupportProvider {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void endOfWorkoutSync() {
|
||||
this.syncState.stopWorkoutSync();
|
||||
}
|
||||
|
||||
public void downloadWorkoutPdrFiles(short workoutId, Long databaseId) {
|
||||
if(!getHuaweiCoordinator().isSupportsGpsNewSync())
|
||||
return;
|
||||
huaweiFileDownloadManager.addToQueue(HuaweiFileDownloadManager.FileRequest.workoutPdrFileRequest(
|
||||
workoutId,
|
||||
databaseId,
|
||||
new HuaweiFileDownloadManager.FileDownloadCallback() {
|
||||
@Override
|
||||
public void downloadComplete(HuaweiFileDownloadManager.FileRequest fileRequest) {
|
||||
if (fileRequest.getData().length == 0) {
|
||||
LOG.debug("PDR file empty");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.debug("Parsing PDR file");
|
||||
HuaweiPdrParser.PdrPoint[] points = HuaweiPdrParser.parseHuaweiPdr(fileRequest.getData());
|
||||
LOG.info("Points: ", points);
|
||||
//TODO: postprocess and combine with Gps data
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadException(HuaweiFileDownloadManager.HuaweiFileDownloadException e) {
|
||||
super.downloadException(e);
|
||||
LOG.debug("Error download PDR file");
|
||||
}
|
||||
}
|
||||
), true);
|
||||
}
|
||||
|
||||
public void downloadWorkoutGpsFiles(short workoutId, Long databaseId, Runnable extraCallbackAction) {
|
||||
syncState.startWorkoutGpsDownload();
|
||||
|
||||
|
||||
+2
@@ -57,6 +57,7 @@ public class GetFileDownloadInitRequest extends Request {
|
||||
case SLEEP_DATA -> FileDownloadService2C.FileType.SLEEP_DATA;
|
||||
case RRI -> FileDownloadService2C.FileType.RRI;
|
||||
case GPS -> FileDownloadService2C.FileType.GPS;
|
||||
case PDR -> FileDownloadService2C.FileType.PDR;
|
||||
case SEQUENCE_DATA -> FileDownloadService2C.FileType.SEQUENCE_DATA;
|
||||
case ECG_ANALYSIS_DATA -> FileDownloadService2C.FileType.ECG_ANALYSIS_DATA;
|
||||
default -> FileDownloadService2C.FileType.UNKNOWN;
|
||||
@@ -69,6 +70,7 @@ public class GetFileDownloadInitRequest extends Request {
|
||||
case SLEEP_DATA -> HuaweiFileDownloadManager.FileType.SLEEP_DATA;
|
||||
case RRI -> HuaweiFileDownloadManager.FileType.RRI;
|
||||
case GPS -> HuaweiFileDownloadManager.FileType.GPS;
|
||||
case PDR -> HuaweiFileDownloadManager.FileType.PDR;
|
||||
case SEQUENCE_DATA -> HuaweiFileDownloadManager.FileType.SEQUENCE_DATA;
|
||||
case ECG_ANALYSIS_DATA -> HuaweiFileDownloadManager.FileType.ECG_ANALYSIS_DATA;
|
||||
default -> HuaweiFileDownloadManager.FileType.UNKNOWN;
|
||||
|
||||
Reference in New Issue
Block a user