Huawei: Research related to GPS routes for workouts.

This commit is contained in:
Me7c7
2026-01-01 21:59:18 +01:00
committed by José Rebelo
parent a27d4f6de2
commit 305f1827e0
6 changed files with 138 additions and 6 deletions
@@ -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);
@@ -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]);
}
}
@@ -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;
@@ -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;
@@ -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();
@@ -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;