Debounce implemented

This commit is contained in:
wengki81 2025-06-16 14:05:00 +08:00
parent 801d00c6d0
commit 5a72d447cb
6 changed files with 49 additions and 1 deletions

View File

@ -4,6 +4,7 @@ import android.Manifest
import android.content.pm.PackageManager
import android.graphics.Color
import android.os.Build
import android.util.Log
import android.view.View
import android.view.WindowInsetsController
import android.view.WindowManager
@ -69,6 +70,7 @@ class DumonGeolocation : Plugin() {
// private val emitIntervalMs: Long = 500L
private val emitIntervalMs: Long = 1000L // hard debounce
// private val emitIntervalMs: Long = 500L
override fun load() {
gpsManager = GpsStatusManager(
@ -91,6 +93,7 @@ class DumonGeolocation : Plugin() {
onImuUpdate = {
latestImu = it
emitPositionUpdate()
fusionManager?.updateMotionEstimate(it.speed.toDouble(), it.directionRad.toDouble())
}
)
@ -265,6 +268,19 @@ class DumonGeolocation : Plugin() {
notifyListeners("onPositionUpdate", buildPositionData())
}
// Fallback prediksi jika tidak ada GNSS update > 1.5 detik
if (System.currentTimeMillis() - latestTimestamp > 1500 && latestImu != null) {
val (predLat, predLon) = fusionManager?.predictForwardPosition(1.0) ?: return
latestLatitude = predLat
latestLongitude = predLon
latestAccuracy = 10.0
latestSource = "PREDICTED"
latestTimestamp = System.currentTimeMillis()
Log.d("DUMON_PREDICTION", "Predicted position: $predLat, $predLon")
notifyListeners("onPositionUpdate", buildPositionData())
}
}
private fun degToRad(deg: Double): Double {
@ -300,6 +316,8 @@ class DumonGeolocation : Plugin() {
obj.put("directionRad", it.directionRad)
}
obj.put("predicted", latestSource == "PREDICTED")
// === Full Detail (commented out for future use) ===
/*
satelliteStatus?.let {

View File

@ -18,6 +18,9 @@ class SensorFusionManager(
private var isFirstUpdate = true
private var lastUpdateTimestamp: Long = 0L
private var lastSpeed = 0.0
private var lastHeadingRad = 0.0
fun updateGpsPosition(lat: Double, lon: Double) {
val currentTimestamp = System.currentTimeMillis()
val dtSeconds = if (lastUpdateTimestamp > 0) {
@ -50,6 +53,24 @@ class SensorFusionManager(
onFusedPositionUpdate(latEstimate, lonEstimate)
}
fun updateMotionEstimate(speed: Double, headingRad: Double) {
lastSpeed = speed
lastHeadingRad = headingRad
}
fun predictForwardPosition(secondsAhead: Double): Pair<Double, Double> {
val distance = lastSpeed * secondsAhead
val R = 6371000.0
val deltaLat = (distance * cos(lastHeadingRad)) / R
val deltaLon = (distance * sin(lastHeadingRad)) / (R * cos(Math.toRadians(latEstimate)))
val predictedLat = latEstimate + Math.toDegrees(deltaLat)
val predictedLon = lonEstimate + Math.toDegrees(deltaLon)
return Pair(predictedLat, predictedLon)
}
fun reset() {
latEstimate = 0.0
lonEstimate = 0.0

7
dist/docs.json vendored
View File

@ -162,6 +162,13 @@
"docs": "",
"complexTypes": [],
"type": "boolean"
},
{
"name": "predicted",
"tags": [],
"docs": "",
"complexTypes": [],
"type": "boolean | undefined"
}
]
},

View File

@ -9,6 +9,7 @@ export interface PositioningData {
acceleration: number;
directionRad: number;
isMocked: boolean;
predicted?: boolean;
}
export interface PermissionStatus {
location: 'granted' | 'denied';

View File

@ -1 +1 @@
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n// export interface SatelliteStatus {\n// satellitesInView: number;\n// usedInFix: number;\n// constellationCounts: { [key: string]: number };\n// }\n\n// export interface WifiAp {\n// ssid: string;\n// bssid: string;\n// rssi: number;\n// distance?: number;\n// }\n\n// export interface WifiScanResult {\n// apCount: number;\n// aps: WifiAp[];\n// }\n\n// export interface ImuData {\n// accelX: number;\n// accelY: number;\n// accelZ: number;\n// gyroX: number;\n// gyroY: number;\n// gyroZ: number;\n// speed?: number;\n// acceleration?: number;\n// directionRad?: number;\n// }\n\n// export interface GpsData {\n// latitude: number;\n// longitude: number;\n// accuracy: number;\n// satellitesInView?: number;\n// usedInFix?: number;\n// constellationCounts?: { [key: string]: number };\n// }\n\n// export interface PositioningData {\n// source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';\n// timestamp: number;\n// latitude: number;\n// longitude: number;\n// accuracy: number;\n\n// gnssData?: SatelliteStatus;\n// wifiData?: WifiAp[];\n// imuData?: ImuData;\n// }\n\nexport interface PositioningData {\n source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';\n timestamp: number;\n latitude: number;\n longitude: number;\n accuracy: number;\n speed: number;\n acceleration: number;\n directionRad: number;\n isMocked: boolean;\n}\n\nexport interface PermissionStatus {\n location: 'granted' | 'denied';\n wifi: 'granted' | 'denied';\n}\n\nexport interface DumonGeolocationPlugin {\n startPositioning(): Promise<void>;\n stopPositioning(): Promise<void>;\n getLatestPosition(): Promise<PositioningData>;\n checkAndRequestPermissions(): Promise<PermissionStatus>;\n\n configureEdgeToEdge(options: {\n bgColor: string;\n style: 'DARK' | 'LIGHT';\n overlay?: boolean;\n }): Promise<void>;\n\n addListener(\n eventName: 'onPositionUpdate',\n listenerFunc: (data: PositioningData) => void\n ): PluginListenerHandle;\n}"]}
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n// export interface SatelliteStatus {\n// satellitesInView: number;\n// usedInFix: number;\n// constellationCounts: { [key: string]: number };\n// }\n\n// export interface WifiAp {\n// ssid: string;\n// bssid: string;\n// rssi: number;\n// distance?: number;\n// }\n\n// export interface WifiScanResult {\n// apCount: number;\n// aps: WifiAp[];\n// }\n\n// export interface ImuData {\n// accelX: number;\n// accelY: number;\n// accelZ: number;\n// gyroX: number;\n// gyroY: number;\n// gyroZ: number;\n// speed?: number;\n// acceleration?: number;\n// directionRad?: number;\n// }\n\n// export interface GpsData {\n// latitude: number;\n// longitude: number;\n// accuracy: number;\n// satellitesInView?: number;\n// usedInFix?: number;\n// constellationCounts?: { [key: string]: number };\n// }\n\n// export interface PositioningData {\n// source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';\n// timestamp: number;\n// latitude: number;\n// longitude: number;\n// accuracy: number;\n\n// gnssData?: SatelliteStatus;\n// wifiData?: WifiAp[];\n// imuData?: ImuData;\n// }\n\nexport interface PositioningData {\n source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';\n timestamp: number;\n latitude: number;\n longitude: number;\n accuracy: number;\n speed: number;\n acceleration: number;\n directionRad: number;\n isMocked: boolean;\n predicted?: boolean;\n}\n\nexport interface PermissionStatus {\n location: 'granted' | 'denied';\n wifi: 'granted' | 'denied';\n}\n\nexport interface DumonGeolocationPlugin {\n startPositioning(): Promise<void>;\n stopPositioning(): Promise<void>;\n getLatestPosition(): Promise<PositioningData>;\n checkAndRequestPermissions(): Promise<PermissionStatus>;\n\n configureEdgeToEdge(options: {\n bgColor: string;\n style: 'DARK' | 'LIGHT';\n overlay?: boolean;\n }): Promise<void>;\n\n addListener(\n eventName: 'onPositionUpdate',\n listenerFunc: (data: PositioningData) => void\n ): PluginListenerHandle;\n}"]}

View File

@ -61,6 +61,7 @@ export interface PositioningData {
acceleration: number;
directionRad: number;
isMocked: boolean;
predicted?: boolean;
}
export interface PermissionStatus {