继续修复平滑

This commit is contained in:
sladro 2026-02-11 12:17:06 +08:00
parent 900ccdbb02
commit 7bb910892a
6 changed files with 192 additions and 56 deletions

View File

@ -18,7 +18,7 @@
(4)Z31020**&d1231=+33
4.测试平台-- 10.100.23.10
(1)root
(2)Dianxin@222
Dianxin@222
5.红绿灯测试平台-- 10.98.23.111 windows系统
(1)账密
①administrator

View File

@ -26,6 +26,11 @@ public class Aircraft extends MovingObject {
*/
private String flightNumber;
/**
* Upstream track identifier for source-track partitioning.
*/
private String trackNumber;
/**
* 进港跑道编号
*/

View File

@ -115,6 +115,7 @@ public class DataCollectorDao {
Aircraft aircraft = new Aircraft();
aircraft.setObjectId(rawData.getFlightNo());
aircraft.setObjectName(rawData.getFlightNo()); // 使用 flightNo 作为 objectName
aircraft.setTrackNumber(rawData.getTrackNumber());
aircraft.setCurrentPosition(geometryFactory.createPoint(new org.locationtech.jts.geom.Coordinate(rawData.getLongitude(), rawData.getLatitude())));
// 外部API中没有直接的速度和方向如果需要可以在DataCollectorService中计算或使用默认值
// 暂时设置为0.0DataCollectorService会处理
@ -207,6 +208,8 @@ public class DataCollectorDao {
private static class ExternalAircraftData {
@JsonProperty("flightNo")
private String flightNo;
@JsonProperty("trackNumber")
private String trackNumber;
@JsonProperty("longitude")
private Double longitude;
@JsonProperty("latitude")

View File

@ -130,6 +130,7 @@ public class DataCollectorService {
// 用于缓存所有活跃的MovingObject的最新状态
private final Map<String, MovingObject> activeMovingObjectsCache = new ConcurrentHashMap<>();
private final Map<String, String> selectedTrackByFlightNo = new ConcurrentHashMap<>();
// 记录每个对象最近一次更新的时间戳用于淘汰不活跃对象防止内存长期增长
private final Map<String, Long> activeMovingObjectsLastUpdatedAtMs = new ConcurrentHashMap<>();
@ -430,19 +431,30 @@ public class DataCollectorService {
return Collections.emptyList();
}
Map<String, Aircraft> latestByFlightNo = new HashMap<>();
Map<String, Map<String, Aircraft>> latestByFlightAndTrack = new HashMap<>();
for (Aircraft aircraft : aircrafts) {
if (aircraft == null || aircraft.getObjectId() == null || aircraft.getObjectId().isBlank()) {
continue;
}
Aircraft existing = latestByFlightNo.get(aircraft.getObjectId());
String flightNo = aircraft.getObjectId();
String trackKey = normalizeTrackKey(aircraft.getTrackNumber());
Map<String, Aircraft> byTrack = latestByFlightAndTrack.computeIfAbsent(flightNo, k -> new HashMap<>());
Aircraft existing = byTrack.get(trackKey);
if (existing == null || isCandidateNewer(existing, aircraft)) {
latestByFlightNo.put(aircraft.getObjectId(), aircraft);
byTrack.put(trackKey, aircraft);
}
}
return List.copyOf(latestByFlightNo.values());
List<Aircraft> selected = new java.util.ArrayList<>(latestByFlightAndTrack.size());
for (Map.Entry<String, Map<String, Aircraft>> entry : latestByFlightAndTrack.entrySet()) {
Aircraft selectedAircraft = selectAircraftTrackCandidate(entry.getKey(), entry.getValue());
if (selectedAircraft != null) {
selected.add(selectedAircraft);
}
}
return selected;
}
private boolean isCandidateNewer(Aircraft existing, Aircraft candidate) {
@ -461,6 +473,72 @@ public class DataCollectorService {
return candidateTs >= existingTs;
}
private Aircraft selectAircraftTrackCandidate(String flightNo, Map<String, Aircraft> byTrack) {
if (byTrack == null || byTrack.isEmpty()) {
return null;
}
if (byTrack.size() == 1) {
Aircraft only = byTrack.values().iterator().next();
selectedTrackByFlightNo.put(flightNo, normalizeTrackKey(only.getTrackNumber()));
return only;
}
String selectedTrack = selectedTrackByFlightNo.get(flightNo);
if (selectedTrack != null && byTrack.containsKey(selectedTrack)) {
return byTrack.get(selectedTrack);
}
MovingObject previous = activeMovingObjectsCache.get(flightNo);
Aircraft best = null;
double bestDistance = Double.MAX_VALUE;
if (previous != null && previous.getCurrentPosition() != null) {
for (Aircraft candidate : byTrack.values()) {
if (candidate == null || candidate.getCurrentPosition() == null) {
continue;
}
double d = calculateDistanceMeters(previous.getCurrentPosition(), candidate.getCurrentPosition());
if (d < bestDistance) {
best = candidate;
bestDistance = d;
}
}
}
if (best == null) {
for (Aircraft candidate : byTrack.values()) {
if (best == null || isCandidateNewer(best, candidate)) {
best = candidate;
}
}
}
if (best != null) {
selectedTrackByFlightNo.put(flightNo, normalizeTrackKey(best.getTrackNumber()));
}
return best;
}
private String normalizeTrackKey(String trackNumber) {
if (trackNumber == null || trackNumber.isBlank()) {
return "_DEFAULT_TRACK_";
}
return trackNumber.trim();
}
private double calculateDistanceMeters(Point from, Point to) {
double lat1 = Math.toRadians(from.getY());
double lon1 = Math.toRadians(from.getX());
double lat2 = Math.toRadians(to.getY());
double lon2 = Math.toRadians(to.getX());
double dLat = lat2 - lat1;
double dLon = lon2 - lon1;
double a = Math.sin(dLat / 2.0) * Math.sin(dLat / 2.0)
+ Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2.0) * Math.sin(dLon / 2.0);
double c = 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1.0 - a));
return 6371000.0 * c;
}
@Scheduled(fixedRateString = "${data.collector.interval}")
public void collectAircraftData() {
if (collectorDisabled) {
@ -1274,6 +1352,7 @@ public class DataCollectorService {
Long last = entry.getValue();
if (last == null || last < cutoff) {
activeMovingObjectsCache.remove(entry.getKey());
selectedTrackByFlightNo.remove(entry.getKey());
return true;
}
return false;

View File

@ -42,6 +42,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
@Component
public class WebSocketMessageBroadcaster {
private static final int REACQUIRE_AFTER_REJECTS = 3;
private static final double EARTH_RADIUS_METERS = 6371000.0;
private static final double MIN_DT_SECONDS = 0.05;
@ -116,9 +117,6 @@ public class WebSocketMessageBroadcaster {
@Value("${websocket.position.filter.state-ttl-ms:120000}")
private long positionTrackStateTtlMs;
@Value("${websocket.position.filter.reacquire-gap-ms:8000}")
private long reacquireGapMs;
private record CoalescedPositionUpdate(PositionUpdatePayload payload, long timestamp) {}
private static final class PositionTrackState {
@ -393,7 +391,7 @@ public class WebSocketMessageBroadcaster {
}
long payloadTimestamp = payload.getTimestamp() != null ? payload.getTimestamp() : System.currentTimeMillis();
long filterTimestampMs = System.currentTimeMillis();
long filterTimestampMs = payloadTimestamp;
PositionTrackState state = positionTrackStates.computeIfAbsent(payload.getObjectId(), k -> new PositionTrackState());
double filteredLatitude = latitude;
@ -404,8 +402,11 @@ public class WebSocketMessageBroadcaster {
initializeTrackState(state, latitude, longitude, filterTimestampMs);
} else {
if (filterTimestampMs <= state.lastPayloadTimestampMs) {
state.rejectedCount++;
return null;
// Use local monotonic time to avoid dropping legitimate points on equal/out-of-order source timestamps.
filterTimestampMs = System.currentTimeMillis();
if (filterTimestampMs <= state.lastPayloadTimestampMs) {
filterTimestampMs = state.lastPayloadTimestampMs + 1;
}
}
long dtMs = filterTimestampMs - state.lastPayloadTimestampMs;
@ -418,32 +419,33 @@ public class WebSocketMessageBroadcaster {
);
double rawSpeedMps = rawDistance / Math.max(dtSec, MIN_DT_SECONDS);
AdaptiveFilterParams adaptive = resolveAdaptiveFilterParams(payload.getObjectType(), rawSpeedMps);
boolean aircraft = isAircraftType(payload.getObjectType());
double maxAllowedJump = resolveMaxAllowedJumpMeters(adaptive.maxSpeedMps(), adaptive.jumpMarginMeter(), dtSec);
boolean isOutlierJump = rawDistance > maxAllowedJump;
if (isOutlierJump && !aircraft && state.rejectedCount < REACQUIRE_AFTER_REJECTS - 1) {
state.rejectedCount++;
log.debug("Drop outlier position update: objectId={}, jumpMeter={}, allowedMeter={}",
payload.getObjectId(), rawDistance, maxAllowedJump);
return null;
}
if (isOutlierJump) {
boolean longGapReacquire = dtMs >= Math.max(reacquireGapMs, 0L);
if (longGapReacquire) {
state.rejectedCount = 0;
filteredLatitude = latitude;
filteredLongitude = longitude;
} else {
state.rejectedCount++;
double[] clipped = clipPointByMaxDistance(
state.lastFilteredLatitude,
state.lastFilteredLongitude,
latitude,
longitude,
maxAllowedJump
);
filteredLatitude = clipped[0];
filteredLongitude = clipped[1];
log.debug("Clip outlier position update: objectId={}, jumpMeter={}, allowedMeter={}, dtMs={}",
payload.getObjectId(), rawDistance, maxAllowedJump, dtMs);
state.rejectedCount = 0;
if (aircraft) {
if (rawDistance <= adaptive.jitterMeter()) {
filteredLatitude = state.lastFilteredLatitude;
filteredLongitude = state.lastFilteredLongitude;
} else {
double alpha = resolveAircraftDynamicAlpha(adaptive.emaAlpha(), rawDistance, maxAllowedJump);
filteredLatitude = state.lastFilteredLatitude + alpha * (latitude - state.lastFilteredLatitude);
filteredLongitude = state.lastFilteredLongitude + alpha * (longitude - state.lastFilteredLongitude);
}
} else {
state.rejectedCount = 0;
if (rawDistance <= adaptive.jitterMeter()) {
filteredLatitude = state.lastFilteredLatitude;
filteredLongitude = state.lastFilteredLongitude;
@ -453,6 +455,13 @@ public class WebSocketMessageBroadcaster {
filteredLongitude = state.lastFilteredLongitude + alpha * (longitude - state.lastFilteredLongitude);
}
}
}
if (isOutlierJump && aircraft) {
// Aircraft points should not be dropped to avoid visual path gaps.
filteredLatitude = latitude;
filteredLongitude = longitude;
}
state.lastRawLatitude = latitude;
state.lastRawLongitude = longitude;
@ -478,6 +487,18 @@ public class WebSocketMessageBroadcaster {
.build();
}
private boolean isAircraftType(String objectType) {
return "AIRCRAFT".equals(objectType);
}
private double resolveAircraftDynamicAlpha(double baseAlpha, double rawDistance, double maxAllowedJump) {
double normalized = maxAllowedJump <= 0.0 ? 1.0 : clamp(rawDistance / maxAllowedJump, 0.0, 1.0);
// Keep smoothness at low speed, but reduce lag quickly when displacement grows.
double minAlpha = Math.max(0.35, baseAlpha);
double maxAlpha = 0.92;
return clamp(minAlpha + (maxAlpha - minAlpha) * normalized, 0.05, 1.0);
}
private void initializeTrackState(PositionTrackState state, double latitude, double longitude, long payloadTimestamp) {
state.initialized = true;
state.lastPayloadTimestampMs = payloadTimestamp;
@ -569,21 +590,4 @@ public class WebSocketMessageBroadcaster {
}
return Math.min(value, max);
}
private double[] clipPointByMaxDistance(
double startLatitude,
double startLongitude,
double targetLatitude,
double targetLongitude,
double maxDistanceMeters) {
double distanceMeters = calculateDistanceMeters(startLatitude, startLongitude, targetLatitude, targetLongitude);
if (distanceMeters <= maxDistanceMeters || distanceMeters <= 0.0 || maxDistanceMeters <= 0.0) {
return new double[] {targetLatitude, targetLongitude};
}
double ratio = maxDistanceMeters / distanceMeters;
double clippedLatitude = startLatitude + (targetLatitude - startLatitude) * ratio;
double clippedLongitude = startLongitude + (targetLongitude - startLongitude) * ratio;
return new double[] {clippedLatitude, clippedLongitude};
}
}

View File

@ -134,8 +134,6 @@ docker exec -it qaup-app sh -lc 'sha256sum /app.jar'
docker exec -it qaup-app rm /app.jar
docker cp qaup-app.jar qaup-app:/app.jar
docker restart qaup-app
curl -s http://10.64.58.228:8086/api/adxp/status
curl -s http://10.64.58.228:8086/api/adxp/health
curl -s -X POST http://10.64.58.228:8086/api/adxp/reconnect
docker logs -f qaup-app | grep "路由"
```
@ -305,3 +303,50 @@ for F in MU9692; do
fi
echo "$F | biz=$BIZ | flight=[$FR] | biz=[$BR]"
done
grep -oE 'TISFLIGHT' ./adxp-adapter/logs/adapter-logs.log | sort | uniq -c | sort -nr
```
TOKEN=$(echo "$LOGIN_RESP" | sed -n 's/.*"data":"\([^"]*\)".*/\1/p')
# B. 连续观察 QDA9846无 jq 版本)
for i in $(seq 1 20); do
TS=$(date '+%H:%M:%S')
RESP=$(curl -sS -H "Authorization: $TOKEN" "http://10.32.38.3:8090/openApi/getCurrentFlightPositions")
LINE=$(echo "$RESP" | tr '{' '\n' | grep '"flightNo":"FGCX253"' | head -n1)
if [ -z "$LINE" ]; then
echo "[$TS] NO_HIT"
else
echo "[$TS] $LINE"
fi
sleep 1
done
root@root:/home/project_20250804/qaup# for i in $(seq 1 20); do TS=$(date '+%H:%M:%S'); RESP=$(curl -sS -H "Authorization: $TOKEN" "http://10.32.38.3:8090/openApi/getCurrentFlightPositions"); LINE=$(echo "$RESP" | tr '{' '\n' | grep '"flightNo":"FGCX253"' | head -n1); if [ -z "$LINE" ]; then echo "[$TS] NO_HIT"; else echo "[$TS] $LINE"; fi; sleep 1; done
[05:59:44] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35046,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:45] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35046,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:46] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35046,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:47] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:48] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:49] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:51] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:52] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:53] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:54] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:55] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35045,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:56] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35045,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:57] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35045,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:58] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35045,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[05:59:59] "flightNo":"FGCX253","longitude":120.09372,"latitude":36.35048,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[06:00:00] "flightNo":"FGCX253","longitude":120.09372,"latitude":36.35048,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[06:00:01] "flightNo":"FGCX253","longitude":120.09372,"latitude":36.35048,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[06:00:02] "flightNo":"FGCX253","longitude":120.09396,"latitude":36.35043,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[06:00:03] "flightNo":"FGCX253","longitude":120.094,"latitude":36.35036,"time":1770760653475,"altitude":0.0,"trackNumber":0},
[06:00:04] "flightNo":"FGCX253","longitude":120.09401,"latitude":36.3503,"time":1770760653475,"altitude":0.0,"trackNumber":0},
```