Fix jwt-token

This commit is contained in:
2025-08-17 09:36:12 +02:00
parent b4b9472cf8
commit fb6c105591
6 changed files with 807 additions and 5 deletions

View File

@@ -0,0 +1,285 @@
const EventEmitter = require('events');
class DroneTrackingService extends EventEmitter {
constructor() {
super();
this.droneHistory = new Map(); // Map of drone_id -> detection history
this.droneProximityAlerts = new Map(); // Map of drone_id -> current status
this.proximityThresholds = {
VERY_CLOSE: -40, // < -40dBm
CLOSE: -50, // -40 to -50dBm
MEDIUM: -60, // -50 to -60dBm
FAR: -70, // -60 to -70dBm
VERY_FAR: -80 // -70 to -80dBm
};
// Clean up old history every 30 minutes
setInterval(() => this.cleanupOldHistory(), 30 * 60 * 1000);
}
processDetection(detection) {
const droneId = detection.drone_id;
const deviceId = detection.device_id;
const droneKey = `${droneId}_${deviceId}`;
// Get or create history for this drone-device pair
if (!this.droneHistory.has(droneKey)) {
this.droneHistory.set(droneKey, []);
}
const history = this.droneHistory.get(droneKey);
const now = Date.now();
// Add current detection to history
const detectionRecord = {
timestamp: now,
rssi: detection.rssi,
geo_lat: detection.geo_lat,
geo_lon: detection.geo_lon,
confidence: detection.confidence_level,
freq: detection.freq,
signal_duration: detection.signal_duration
};
history.push(detectionRecord);
// Keep only last 50 detections per drone-device pair
if (history.length > 50) {
history.splice(0, history.length - 50);
}
// Analyze movement and proximity trends
const analysis = this.analyzeMovementTrend(history, detection);
// Emit movement alerts if significant changes detected
if (analysis.alertLevel > 0) {
this.emit('movement_alert', {
droneId,
deviceId,
detection,
analysis,
history: history.slice(-5) // Last 5 detections for context
});
}
return analysis;
}
analyzeMovementTrend(history, currentDetection) {
if (history.length < 2) {
return this.createAnalysis('INITIAL', 0, 'First detection');
}
const recent = history.slice(-5); // Last 5 detections
const current = recent[recent.length - 1];
const previous = recent[recent.length - 2];
// Calculate RSSI trend
const rssiTrend = this.calculateRSSITrend(recent);
const proximityLevel = this.getProximityLevel(current.rssi);
const previousProximityLevel = this.getProximityLevel(previous.rssi);
// Calculate distance trend (if we have coordinates)
let distanceTrend = null;
if (recent.length >= 2 && this.hasValidCoordinates(recent)) {
distanceTrend = this.calculateDistanceTrend(recent);
}
// Calculate movement speed and direction
const movement = this.calculateMovementMetrics(recent);
// Determine alert level and type
let alertLevel = 0;
let alertType = 'MONITORING';
let description = 'Drone being tracked';
// High priority alerts
if (proximityLevel === 'VERY_CLOSE' && rssiTrend.trend === 'STRENGTHENING') {
alertLevel = 3;
alertType = 'CRITICAL_APPROACH';
description = `🚨 CRITICAL: Drone very close and approaching (${current.rssi}dBm)`;
} else if (proximityLevel === 'CLOSE' && rssiTrend.trend === 'STRENGTHENING') {
alertLevel = 2;
alertType = 'HIGH_APPROACH';
description = `⚠️ HIGH: Drone approaching detector (${current.rssi}dBm, trend: +${rssiTrend.change.toFixed(1)}dB)`;
} else if (rssiTrend.change > 10 && rssiTrend.trend === 'STRENGTHENING') {
alertLevel = 2;
alertType = 'RAPID_APPROACH';
description = `📈 RAPID: Drone rapidly approaching (+${rssiTrend.change.toFixed(1)}dB in ${rssiTrend.timeSpan}s)`;
}
// Medium priority alerts
else if (proximityLevel !== previousProximityLevel) {
alertLevel = 1;
alertType = 'PROXIMITY_CHANGE';
description = `📍 Drone moved from ${previousProximityLevel} to ${proximityLevel} range`;
} else if (Math.abs(rssiTrend.change) > 5) {
alertLevel = 1;
alertType = rssiTrend.trend === 'STRENGTHENING' ? 'APPROACHING' : 'DEPARTING';
description = `${rssiTrend.trend === 'STRENGTHENING' ? '🔴' : '🟢'} Drone ${rssiTrend.trend.toLowerCase()} (${rssiTrend.change > 0 ? '+' : ''}${rssiTrend.change.toFixed(1)}dB)`;
}
return this.createAnalysis(alertType, alertLevel, description, {
rssiTrend,
proximityLevel,
previousProximityLevel,
distanceTrend,
movement,
detectionCount: history.length,
timeTracked: (current.timestamp - history[0].timestamp) / 1000
});
}
calculateRSSITrend(detections) {
if (detections.length < 2) return { trend: 'STABLE', change: 0, timeSpan: 0 };
const latest = detections[detections.length - 1];
const oldest = detections[0];
const change = latest.rssi - oldest.rssi;
const timeSpan = (latest.timestamp - oldest.timestamp) / 1000;
let trend = 'STABLE';
if (change > 2) trend = 'STRENGTHENING';
else if (change < -2) trend = 'WEAKENING';
return { trend, change, timeSpan, rate: change / Math.max(timeSpan, 1) };
}
calculateDistanceTrend(detections) {
if (detections.length < 2) return null;
const distances = detections.map((d, i) => {
if (i === 0) return 0;
return this.calculateDistance(
detections[i-1].geo_lat, detections[i-1].geo_lon,
d.geo_lat, d.geo_lon
);
}).filter(d => d > 0);
if (distances.length === 0) return null;
const totalDistance = distances.reduce((sum, d) => sum + d, 0);
const avgSpeed = totalDistance / ((detections[detections.length - 1].timestamp - detections[0].timestamp) / 1000);
return { totalDistance, avgSpeed, movementPoints: distances.length };
}
calculateMovementMetrics(detections) {
if (detections.length < 3) return { speed: 0, direction: null, pattern: 'INSUFFICIENT_DATA' };
const movements = [];
for (let i = 1; i < detections.length; i++) {
const prev = detections[i - 1];
const curr = detections[i];
const timeDiff = (curr.timestamp - prev.timestamp) / 1000;
if (timeDiff > 0 && this.hasValidCoordinates([prev, curr])) {
const distance = this.calculateDistance(prev.geo_lat, prev.geo_lon, curr.geo_lat, curr.geo_lon);
const speed = (distance * 1000) / timeDiff; // m/s
movements.push({ distance, speed, timeDiff });
}
}
if (movements.length === 0) return { speed: 0, direction: null, pattern: 'STATIONARY' };
const avgSpeed = movements.reduce((sum, m) => sum + m.speed, 0) / movements.length;
const totalDistance = movements.reduce((sum, m) => sum + m.distance, 0);
// Determine movement pattern
let pattern = 'MOVING';
if (avgSpeed < 1) pattern = 'HOVERING';
else if (avgSpeed > 10) pattern = 'FAST_MOVING';
else if (totalDistance < 0.1) pattern = 'CIRCLING';
return { speed: avgSpeed, totalDistance, pattern, movements: movements.length };
}
getProximityLevel(rssi) {
if (rssi >= this.proximityThresholds.VERY_CLOSE) return 'VERY_CLOSE';
if (rssi >= this.proximityThresholds.CLOSE) return 'CLOSE';
if (rssi >= this.proximityThresholds.MEDIUM) return 'MEDIUM';
if (rssi >= this.proximityThresholds.FAR) return 'FAR';
return 'VERY_FAR';
}
calculateDistance(lat1, lon1, lat2, lon2) {
const R = 6371; // Earth radius in km
const dLat = this.toRad(lat2 - lat1);
const dLon = this.toRad(lon2 - lon1);
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2);
return 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)) * R;
}
toRad(deg) {
return deg * (Math.PI/180);
}
hasValidCoordinates(detections) {
return detections.every(d => d.geo_lat && d.geo_lon &&
Math.abs(d.geo_lat) <= 90 &&
Math.abs(d.geo_lon) <= 180);
}
createAnalysis(alertType, alertLevel, description, details = {}) {
return {
alertType,
alertLevel,
description,
timestamp: Date.now(),
...details
};
}
cleanupOldHistory() {
const cutoffTime = Date.now() - (2 * 60 * 60 * 1000); // 2 hours ago
for (const [key, history] of this.droneHistory.entries()) {
const filtered = history.filter(record => record.timestamp > cutoffTime);
if (filtered.length === 0) {
this.droneHistory.delete(key);
} else {
this.droneHistory.set(key, filtered);
}
}
}
getDroneStatus(droneId, deviceId) {
const droneKey = `${droneId}_${deviceId}`;
const history = this.droneHistory.get(droneKey) || [];
if (history.length === 0) return null;
const latest = history[history.length - 1];
const analysis = this.analyzeMovementTrend(history, { rssi: latest.rssi });
return {
droneId,
deviceId,
lastSeen: latest.timestamp,
currentRSSI: latest.rssi,
proximityLevel: this.getProximityLevel(latest.rssi),
detectionCount: history.length,
analysis,
recentHistory: history.slice(-10)
};
}
getAllActiveTracking() {
const activeTracking = [];
const fiveMinutesAgo = Date.now() - (5 * 60 * 1000);
for (const [key, history] of this.droneHistory.entries()) {
const latest = history[history.length - 1];
if (latest.timestamp > fiveMinutesAgo) {
const [droneId, deviceId] = key.split('_');
activeTracking.push(this.getDroneStatus(droneId, deviceId));
}
}
return activeTracking;
}
}
module.exports = DroneTrackingService;