Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
256c94d
dummy sensing fusion
Mar 23, 2026
1c260e9
Logging to test PF
Mar 24, 2026
8a8afd8
Map matching
Mar 24, 2026
4d920dc
dummy data display
Mar 24, 2026
d1a91e3
data display v2
Mar 25, 2026
f385387
different indoor map colour for clarity
Mar 25, 2026
a133ba4
further logging and fixing the bestFloor functionality
Mar 25, 2026
1bb0d48
Showing GNSS points
Mar 25, 2026
ada43df
ordering fixed
tommyj0 Mar 26, 2026
21e62fd
orientation handler
tommyj0 Mar 26, 2026
6b49717
added pdr feedback from fusion
tommyj0 Mar 26, 2026
f391336
feedback turned off for now, added legend for data points
tommyj0 Mar 27, 2026
dea8a9d
using francisco suggested method of orientation
tommyj0 Mar 27, 2026
62ee107
Removed GNSS downweighting and Floor injection support
Mar 28, 2026
a90de60
added a smoothing output filter
Mar 28, 2026
52ed55a
re-added gnss downweighting
Mar 28, 2026
7133efb
Removed GNSS downweight again
Mar 28, 2026
6700ca2
removed redundant pdr feedback
tommyj0 Mar 28, 2026
b018151
autofloor is on by default
tommyj0 Mar 28, 2026
68052e9
local pdr updates
tommyj0 Mar 29, 2026
34edc9f
dark mode implemented, including system default
tommyj0 Mar 29, 2026
39be0e5
removed indoor positioning button
tommyj0 Mar 29, 2026
6e77cd1
removed floor injection
tommyj0 Mar 29, 2026
58197cc
replay with corrected pos
tommyj0 Mar 29, 2026
8959b1b
added comments
tommyj0 Mar 30, 2026
4eab522
collapsable UI
evmorfiaa Mar 30, 2026
4f17045
wait on startup
tommyj0 Mar 30, 2026
8547023
Improved test point visibility
evmorfiaa Mar 30, 2026
94c6d52
intial tuning
tommyj0 Mar 30, 2026
b21497a
changing gitignore
tommyj0 Mar 30, 2026
689428a
removing secrets
tommyj0 Mar 30, 2026
389f410
update filetree
tommyj0 Mar 30, 2026
31abc33
added options header
tommyj0 Mar 30, 2026
11832d3
increased outlier range slightly
tommyj0 Mar 30, 2026
ccbe5ab
floor correction & downweighting old wifi
tommyj0 Mar 31, 2026
bf486c4
update map matching
evmorfiaa Mar 31, 2026
b9b9ea9
removed flip detection for orientation
tommyj0 Mar 31, 2026
0d8c14f
removed wifi aging
tommyj0 Mar 31, 2026
e8ffc62
replay fragment fix
tommyj0 Apr 1, 2026
3548763
Wall penalty inclusion
Apr 1, 2026
5434e87
ui update, coloured map
evmorfiaa Apr 1, 2026
40bff7e
Merge branch 'pdr-snap' into map-traj-change
tommyj0 Apr 1, 2026
940d284
Merge branch 'map-traj-change' into pdr-snap
tommyj0 Apr 1, 2026
15f14e6
tuned PF
tommyj0 Apr 1, 2026
3d1937f
more tuning
tommyj0 Apr 1, 2026
87fe319
replay fix applied here
tommyj0 Apr 1, 2026
2490b57
UI improvements
tommyj0 Apr 1, 2026
5032b04
added comments
tommyj0 Apr 1, 2026
8e3c6a1
downweight GNSS
tommyj0 Apr 1, 2026
8946449
best case
tommyj0 Apr 1, 2026
14595d7
snapping test
tommyj0 Apr 1, 2026
6a292f6
tuning
tommyj0 Apr 1, 2026
d4128a2
more tuning
tommyj0 Apr 1, 2026
978fe16
final
tommyj0 Apr 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
.externalNativeBuild
.cxx
local.properties
secrets.properties
/.idea/
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_icon_map_round"
android:supportsRtl="true"
android:theme="@style/Theme.Material3.DayNight.NoActionBar"
android:theme="@style/Theme.App"
android:requestLegacyExternalStorage="true"
>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ private static class GnssRecord {
public double latitude, longitude; // GNSS coordinates
}

/** Represents a fused/corrected position record from protobuf corrected_positions. */
private static class CorrectedRecord {
public long relativeTimestamp;
public double latitude, longitude;
}

/**
* Parses trajectory data from a JSON file and reconstructs a list of replay points.
*
Expand Down Expand Up @@ -150,43 +156,71 @@ public static List<ReplayPoint> parseTrajectoryData(String filePath, Context con
List<ImuRecord> imuList = parseImuData(root.getAsJsonArray("imuData"));
List<PdrRecord> pdrList = parsePdrData(root.getAsJsonArray("pdrData"));
List<GnssRecord> gnssList = parseGnssData(root.getAsJsonArray("gnssData"));
JsonArray correctedArray = root.has("correctedPositions")
? root.getAsJsonArray("correctedPositions")
: root.getAsJsonArray("corrected_positions");
List<CorrectedRecord> correctedList = parseCorrectedData(correctedArray);

Log.i(TAG, "Parsed data - IMU: " + imuList.size() + " records, PDR: "
+ pdrList.size() + " records, GNSS: " + gnssList.size() + " records");
+ pdrList.size() + " records, GNSS: " + gnssList.size() + " records"
+ ", Corrected: " + correctedList.size() + " records");

if (!correctedList.isEmpty()) {
for (int i = 0; i < correctedList.size(); i++) {
CorrectedRecord corrected = correctedList.get(i);

ImuRecord closestImu = findClosestImuRecord(imuList, corrected.relativeTimestamp);
float orientationDeg = closestImu != null ? computeOrientationFromRotationVector(
closestImu.rotationVectorX,
closestImu.rotationVectorY,
closestImu.rotationVectorZ,
closestImu.rotationVectorW,
context
) : 0f;

LatLng correctedLocation = new LatLng(corrected.latitude, corrected.longitude);

for (int i = 0; i < pdrList.size(); i++) {
PdrRecord pdr = pdrList.get(i);
GnssRecord closestGnss = findClosestGnssRecord(gnssList, corrected.relativeTimestamp);
LatLng gnssLocation = closestGnss != null ?
new LatLng(closestGnss.latitude, closestGnss.longitude) : null;

result.add(new ReplayPoint(correctedLocation, gnssLocation, orientationDeg,
0f, corrected.relativeTimestamp));
}
} else {
for (int i = 0; i < pdrList.size(); i++) {
PdrRecord pdr = pdrList.get(i);

ImuRecord closestImu = findClosestImuRecord(imuList, pdr.relativeTimestamp);
float orientationDeg = closestImu != null ? computeOrientationFromRotationVector(
ImuRecord closestImu = findClosestImuRecord(imuList, pdr.relativeTimestamp);
float orientationDeg = closestImu != null ? computeOrientationFromRotationVector(
closestImu.rotationVectorX,
closestImu.rotationVectorY,
closestImu.rotationVectorZ,
closestImu.rotationVectorW,
context
) : 0f;
) : 0f;

float speed = 0f;
if (i > 0) {
float speed = 0f;
if (i > 0) {
PdrRecord prev = pdrList.get(i - 1);
double dt = (pdr.relativeTimestamp - prev.relativeTimestamp) / 1000.0;
double dx = pdr.x - prev.x;
double dy = pdr.y - prev.y;
double distance = Math.sqrt(dx * dx + dy * dy);
if (dt > 0) speed = (float) (distance / dt);
}

}

double lat = originLat + pdr.y * 1E-5;
double lng = originLng + pdr.x * 1E-5;
LatLng pdrLocation = new LatLng(lat, lng);
double lat = originLat + pdr.y * 1E-5;
double lng = originLng + pdr.x * 1E-5;
LatLng pdrLocation = new LatLng(lat, lng);

GnssRecord closestGnss = findClosestGnssRecord(gnssList, pdr.relativeTimestamp);
LatLng gnssLocation = closestGnss != null ?
GnssRecord closestGnss = findClosestGnssRecord(gnssList, pdr.relativeTimestamp);
LatLng gnssLocation = closestGnss != null ?
new LatLng(closestGnss.latitude, closestGnss.longitude) : null;

result.add(new ReplayPoint(pdrLocation, gnssLocation, orientationDeg,
0f, pdr.relativeTimestamp));
result.add(new ReplayPoint(pdrLocation, gnssLocation, orientationDeg,
speed, pdr.relativeTimestamp));
}
}

Collections.sort(result, Comparator.comparingLong(rp -> rp.timestamp));
Expand All @@ -199,35 +233,172 @@ public static List<ReplayPoint> parseTrajectoryData(String filePath, Context con

return result;
}
/** Parses IMU data from JSON. */
/** Parses IMU data from JSON, handling multiple field naming conventions. */
private static List<ImuRecord> parseImuData(JsonArray imuArray) {
List<ImuRecord> imuList = new ArrayList<>();
if (imuArray == null) return imuList;
Gson gson = new Gson();

for (int i = 0; i < imuArray.size(); i++) {
ImuRecord record = gson.fromJson(imuArray.get(i), ImuRecord.class);
imuList.add(record);
try {
JsonObject imuObj = imuArray.get(i).getAsJsonObject();
ImuRecord record = new ImuRecord();

// Handle both naming conventions
if (imuObj.has("relativeTimestamp")) {
record.relativeTimestamp = imuObj.get("relativeTimestamp").getAsLong();
} else if (imuObj.has("relative_timestamp")) {
record.relativeTimestamp = imuObj.get("relative_timestamp").getAsLong();
}

// Standard field names
if (imuObj.has("accX")) {
record.accX = imuObj.get("accX").getAsFloat();
}
if (imuObj.has("accY")) {
record.accY = imuObj.get("accY").getAsFloat();
}
if (imuObj.has("accZ")) {
record.accZ = imuObj.get("accZ").getAsFloat();
}
if (imuObj.has("gyrX")) {
record.gyrX = imuObj.get("gyrX").getAsFloat();
}
if (imuObj.has("gyrY")) {
record.gyrY = imuObj.get("gyrY").getAsFloat();
}
if (imuObj.has("gyrZ")) {
record.gyrZ = imuObj.get("gyrZ").getAsFloat();
}
if (imuObj.has("rotationVectorX")) {
record.rotationVectorX = imuObj.get("rotationVectorX").getAsFloat();
}
if (imuObj.has("rotationVectorY")) {
record.rotationVectorY = imuObj.get("rotationVectorY").getAsFloat();
}
if (imuObj.has("rotationVectorZ")) {
record.rotationVectorZ = imuObj.get("rotationVectorZ").getAsFloat();
}
if (imuObj.has("rotationVectorW")) {
record.rotationVectorW = imuObj.get("rotationVectorW").getAsFloat();
}

imuList.add(record);
} catch (Exception e) {
Log.w(TAG, "Failed to parse IMU record " + i, e);
}
}

return imuList;
}/** Parses PDR data from JSON. */
}/** Parses PDR data from JSON, handling multiple field naming conventions. */
private static List<PdrRecord> parsePdrData(JsonArray pdrArray) {
List<PdrRecord> pdrList = new ArrayList<>();
if (pdrArray == null) return pdrList;
Gson gson = new Gson();

for (int i = 0; i < pdrArray.size(); i++) {
PdrRecord record = gson.fromJson(pdrArray.get(i), PdrRecord.class);
pdrList.add(record);
try {
JsonObject pdrObj = pdrArray.get(i).getAsJsonObject();
PdrRecord record = new PdrRecord();

// Handle both naming conventions
if (pdrObj.has("relativeTimestamp")) {
record.relativeTimestamp = pdrObj.get("relativeTimestamp").getAsLong();
} else if (pdrObj.has("relative_timestamp")) {
record.relativeTimestamp = pdrObj.get("relative_timestamp").getAsLong();
}

if (pdrObj.has("x")) {
record.x = pdrObj.get("x").getAsFloat();
}
if (pdrObj.has("y")) {
record.y = pdrObj.get("y").getAsFloat();
}

pdrList.add(record);
} catch (Exception e) {
Log.w(TAG, "Failed to parse PDR record " + i, e);
}
}

return pdrList;
}/** Parses GNSS data from JSON. */
}/** Parses corrected (fused) data from JSON, handling multiple field naming conventions. */
private static List<CorrectedRecord> parseCorrectedData(JsonArray correctedArray) {
List<CorrectedRecord> correctedList = new ArrayList<>();
if (correctedArray == null) return correctedList;

for (int i = 0; i < correctedArray.size(); i++) {
try {
JsonObject corrObj = correctedArray.get(i).getAsJsonObject();
CorrectedRecord record = new CorrectedRecord();

// Handle both naming conventions
if (corrObj.has("relativeTimestamp")) {
record.relativeTimestamp = corrObj.get("relativeTimestamp").getAsLong();
} else if (corrObj.has("relative_timestamp")) {
record.relativeTimestamp = corrObj.get("relative_timestamp").getAsLong();
}

if (corrObj.has("latitude")) {
record.latitude = corrObj.get("latitude").getAsDouble();
}
if (corrObj.has("longitude")) {
record.longitude = corrObj.get("longitude").getAsDouble();
}

correctedList.add(record);
} catch (Exception e) {
Log.w(TAG, "Failed to parse corrected record " + i, e);
}
}

return correctedList;
}

/** Parses GNSS data from JSON, handling protobuf nested structure. */
private static List<GnssRecord> parseGnssData(JsonArray gnssArray) {
List<GnssRecord> gnssList = new ArrayList<>();
if (gnssArray == null) return gnssList;
Gson gson = new Gson();
if (gnssArray == null || gnssArray.size() == 0) {
return gnssList;
}

for (int i = 0; i < gnssArray.size(); i++) {
GnssRecord record = gson.fromJson(gnssArray.get(i), GnssRecord.class);
gnssList.add(record);
try {
JsonObject gnssObj = gnssArray.get(i).getAsJsonObject();
GnssRecord record = new GnssRecord();

// In protobuf JSON, position data is nested under "position" object
JsonObject positionObj = null;
if (gnssObj.has("position") && gnssObj.get("position").isJsonObject()) {
positionObj = gnssObj.getAsJsonObject("position");
} else {
// Fallback: try top-level fields if no nested structure
positionObj = gnssObj;
}

// Handle both "relativeTimestamp" and "relative_timestamp"
if (positionObj.has("relativeTimestamp")) {
String ts = positionObj.get("relativeTimestamp").getAsString();
record.relativeTimestamp = Long.parseLong(ts);
} else if (positionObj.has("relative_timestamp")) {
String ts = positionObj.get("relative_timestamp").getAsString();
record.relativeTimestamp = Long.parseLong(ts);
}

// Extract latitude
if (positionObj.has("latitude")) {
record.latitude = positionObj.get("latitude").getAsDouble();
}

// Extract longitude
if (positionObj.has("longitude")) {
record.longitude = positionObj.get("longitude").getAsDouble();
}

gnssList.add(record);
} catch (Exception e) {
Log.w(TAG, "Failed to parse GNSS record " + i, e);
}
}

return gnssList;
}/** Finds the closest IMU record to the given timestamp. */
private static ImuRecord findClosestImuRecord(List<ImuRecord> imuList, long targetTimestamp) {
Expand Down
Loading