From d23d2dbbce06f8c5302f16c3fad6c73ea618bb38 Mon Sep 17 00:00:00 2001 From: Alexander Borg Date: Sun, 17 Aug 2025 09:21:05 +0200 Subject: [PATCH] Fix jwt-token --- clear_database.sh | 12 ++ server/index.js | 8 +- server/resetDatabase.js | 29 ++++ test_drone_data.py | 356 +++++++++++++++++++++++++++++----------- 4 files changed, 311 insertions(+), 94 deletions(-) create mode 100644 clear_database.sh create mode 100644 server/resetDatabase.js diff --git a/clear_database.sh b/clear_database.sh new file mode 100644 index 0000000..6330630 --- /dev/null +++ b/clear_database.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Clear database via API calls (if we add the endpoint) + +API_BASE="http://selfservice.cqers.com/drones/api" + +echo "πŸ—‘οΈ Clearing database via API..." + +# Clear detections +curl -X DELETE "$API_BASE/detections/clear" \ + -H "Content-Type: application/json" + +echo "βœ… Database cleared" diff --git a/server/index.js b/server/index.js index deda118..4532c23 100644 --- a/server/index.js +++ b/server/index.js @@ -24,11 +24,15 @@ const io = new Server(server, { } }); -// Rate limiting +// Rate limiting (exclude detections endpoint for testing) const limiter = rateLimit({ windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000, // 15 minutes max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) || 100, - message: 'Too many requests from this IP, please try again later.' + message: 'Too many requests from this IP, please try again later.', + skip: (req) => { + // Skip rate limiting for drone detection endpoints during testing + return req.path.includes('/detections'); + } }); // Middleware diff --git a/server/resetDatabase.js b/server/resetDatabase.js new file mode 100644 index 0000000..2aa567f --- /dev/null +++ b/server/resetDatabase.js @@ -0,0 +1,29 @@ +#!/usr/bin/env node +/** + * Database Reset Script + * Forces database sync and reseeds data + */ + +const { sequelize } = require('./models'); +const seedDatabase = require('./seedDatabase'); + +async function resetDatabase() { + try { + console.log('πŸ—‘οΈ Resetting database...'); + + // Force sync (drops and recreates all tables) + await sequelize.sync({ force: true }); + console.log('βœ… Database tables recreated'); + + // Reseed with initial data + await seedDatabase(); + console.log('βœ… Database reset complete'); + + process.exit(0); + } catch (error) { + console.error('❌ Database reset failed:', error); + process.exit(1); + } +} + +resetDatabase(); diff --git a/test_drone_data.py b/test_drone_data.py index 1c1c115..6a054ea 100644 --- a/test_drone_data.py +++ b/test_drone_data.py @@ -1,14 +1,15 @@ #!/usr/bin/env python3 """ -Drone Detection Test Script -Sends simulated drone detection data to the API for testing purposes. +Realistic Drone Detection Test Script +Simulates realistic drone scenarios with persistent tracking, RSSI changes, and real-world patterns. """ import requests import json import time import random -from datetime import datetime +import math +from datetime import datetime, timedelta # Configuration API_BASE_URL = "http://selfservice.cqers.com/drones/api" @@ -20,53 +21,215 @@ DEVICES = [ "id": 1, "name": "Stockholm Central Detector", "lat": 59.3293, - "lon": 18.0686 + "lon": 18.0686, + "coverage_radius": 5.0 # km }, { "id": 2, "name": "Arlanda Airport Detector", "lat": 59.6519, - "lon": 17.9186 + "lon": 17.9186, + "coverage_radius": 8.0 # km }, { "id": 3, "name": "GΓΆteborg Harbor Detector", "lat": 57.7089, - "lon": 11.9746 + "lon": 11.9746, + "coverage_radius": 6.0 # km } ] -# Drone types +# Realistic drone types with characteristics DRONE_TYPES = { - 0: "Unknown", - 1: "Commercial DJI", - 2: "Racing Drone", - 3: "Fixed Wing", - 4: "Military/Surveillance", - 5: "Custom Build" + 1: {"name": "DJI Mavic", "max_speed": 65, "typical_rssi": -60, "freq": [2400, 2450]}, + 2: {"name": "Racing Drone", "max_speed": 120, "typical_rssi": -55, "freq": [2400, 5800]}, + 3: {"name": "DJI Phantom", "max_speed": 50, "typical_rssi": -65, "freq": [2400, 2450]}, + 4: {"name": "Fixed Wing", "max_speed": 80, "typical_rssi": -70, "freq": [900, 2400]}, + 5: {"name": "Surveillance", "max_speed": 40, "typical_rssi": -75, "freq": [2400, 5800]} } -def generate_detection_data(device): - """Generate realistic drone detection data""" +class DroneSimulator: + def __init__(self): + self.active_drones = {} # Track persistent drones + self.drone_counter = 1000 + + def calculate_rssi(self, distance_km, base_rssi=-60): + """Calculate realistic RSSI based on distance (Free Space Path Loss)""" + if distance_km < 0.1: # Very close + return max(base_rssi + 20, -30) + + # Simplified FSPL: RSSI decreases ~20dB per decade of distance + rssi = base_rssi - (20 * math.log10(distance_km)) + + # Add some realistic variation + variation = random.uniform(-5, 5) + final_rssi = int(rssi + variation) + + # Clamp to realistic values + return max(min(final_rssi, -30), -100) - # Random position near the device (within ~5km radius) - lat_offset = random.uniform(-0.045, 0.045) # ~5km latitude - lon_offset = random.uniform(-0.09, 0.09) # ~5km longitude + def calculate_distance(self, lat1, lon1, lat2, lon2): + """Calculate distance between two points in km""" + R = 6371 # Earth radius in km + + lat1_rad = math.radians(lat1) + lat2_rad = math.radians(lat2) + delta_lat = math.radians(lat2 - lat1) + delta_lon = math.radians(lon2 - lon1) + + a = (math.sin(delta_lat/2)**2 + + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(delta_lon/2)**2) + c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) + + return R * c - detection = { - "device_id": device["id"], - "drone_id": random.randint(1000, 9999), - "drone_type": random.randint(0, 5), - "rssi": random.randint(-90, -30), # Signal strength in dBm - "freq": random.choice([2400, 2450, 5800, 5850]), # Common drone frequencies - "geo_lat": device["lat"] + lat_offset, - "geo_lon": device["lon"] + lon_offset, - "device_timestamp": int(time.time() * 1000), # Unix timestamp in ms - "confidence_level": round(random.uniform(0.6, 1.0), 2), - "signal_duration": random.randint(500, 5000) # Duration in ms - } + def create_new_drone(self, device): + """Create a new drone with realistic starting position""" + self.drone_counter += 1 + drone_type = random.choice(list(DRONE_TYPES.keys())) + + # Start at edge of coverage area + angle = random.uniform(0, 2 * math.pi) + start_distance = device["coverage_radius"] * random.uniform(0.8, 1.0) + + start_lat = device["lat"] + (start_distance / 111.0) * math.cos(angle) + start_lon = device["lon"] + (start_distance / (111.0 * math.cos(math.radians(device["lat"])))) * math.sin(angle) + + # Movement pattern + patterns = ["approaching", "patrolling", "departing", "hovering"] + movement_pattern = random.choice(patterns) + + drone = { + "id": self.drone_counter, + "type": drone_type, + "lat": start_lat, + "lon": start_lon, + "target_lat": device["lat"] if movement_pattern == "approaching" else start_lat, + "target_lon": device["lon"] if movement_pattern == "approaching" else start_lon, + "speed_kmh": DRONE_TYPES[drone_type]["max_speed"] * random.uniform(0.3, 0.8), + "pattern": movement_pattern, + "created_at": time.time(), + "last_detection": 0, + "detection_count": 0, + "is_active": True + } + + return drone - return detection + def update_drone_position(self, drone, time_delta_seconds): + """Update drone position based on movement pattern""" + if not drone["is_active"]: + return + + speed_ms = drone["speed_kmh"] * 1000 / 3600 # Convert to m/s + distance_m = speed_ms * time_delta_seconds + + if drone["pattern"] == "approaching": + # Move towards the device center + current_distance = self.calculate_distance( + drone["lat"], drone["lon"], + drone["target_lat"], drone["target_lon"] + ) * 1000 # Convert to meters + + if current_distance > 100: # Still approaching + lat_diff = drone["target_lat"] - drone["lat"] + lon_diff = drone["target_lon"] - drone["lon"] + total_diff = math.sqrt(lat_diff**2 + lon_diff**2) + + if total_diff > 0: + lat_step = (lat_diff / total_diff) * (distance_m / 111000) + lon_step = (lon_diff / total_diff) * (distance_m / 111000) + + drone["lat"] += lat_step + drone["lon"] += lon_step + else: + # Reached target, switch to hovering + drone["pattern"] = "hovering" + + elif drone["pattern"] == "hovering": + # Small random movements + drone["lat"] += random.uniform(-0.0001, 0.0001) + drone["lon"] += random.uniform(-0.0001, 0.0001) + + elif drone["pattern"] == "patrolling": + # Circular or figure-8 movement + t = (time.time() - drone["created_at"]) / 100 # Slow circular motion + radius = 0.002 # Small patrol radius + drone["lat"] = drone["target_lat"] + radius * math.sin(t) + drone["lon"] = drone["target_lon"] + radius * math.cos(t) + + elif drone["pattern"] == "departing": + # Move away from device + lat_diff = drone["lat"] - drone["target_lat"] + lon_diff = drone["lon"] - drone["target_lon"] + total_diff = math.sqrt(lat_diff**2 + lon_diff**2) + + if total_diff > 0: + lat_step = (lat_diff / total_diff) * (distance_m / 111000) + lon_step = (lon_diff / total_diff) * (distance_m / 111000) + + drone["lat"] += lat_step + drone["lon"] += lon_step + + def should_detect_drone(self, drone, device): + """Determine if device should detect this drone""" + distance_km = self.calculate_distance( + drone["lat"], drone["lon"], + device["lat"], device["lon"] + ) + + # Detection probability decreases with distance + if distance_km > device["coverage_radius"]: + return False, distance_km + + # Higher chance of detection when closer + detection_prob = 1.0 - (distance_km / device["coverage_radius"]) * 0.7 + + # Factor in time since last detection (avoid spam) + time_since_last = time.time() - drone.get("last_detection", 0) + if time_since_last < 2: # Minimum 2 seconds between detections + detection_prob *= 0.1 + + return random.random() < detection_prob, distance_km + + def generate_detection(self, drone, device, distance_km): + """Generate realistic detection data""" + drone_info = DRONE_TYPES[drone["type"]] + + # Calculate RSSI based on distance + rssi = self.calculate_rssi(distance_km, drone_info["typical_rssi"]) + + # Select frequency + freq = random.choice(drone_info["freq"]) + + # Confidence decreases with distance and lower RSSI + base_confidence = 0.95 - (distance_km / 10.0) * 0.3 + rssi_factor = (rssi + 100) / 70 # Normalize RSSI to 0-1 + confidence = max(0.5, min(0.99, base_confidence * rssi_factor)) + + # Signal duration varies by drone type and detection quality + duration_base = 2000 if drone["pattern"] == "hovering" else 1000 + duration = duration_base + random.randint(-500, 1500) + + detection = { + "device_id": device["id"], + "drone_id": drone["id"], + "drone_type": drone["type"], + "rssi": rssi, + "freq": freq, + "geo_lat": drone["lat"], + "geo_lon": drone["lon"], + "device_timestamp": int(time.time() * 1000), + "confidence_level": round(confidence, 2), + "signal_duration": max(500, duration) + } + + # Update drone tracking + drone["last_detection"] = time.time() + drone["detection_count"] += 1 + + return detection def send_detection(detection_data): """Send detection data to the API""" @@ -79,66 +242,89 @@ def send_detection(detection_data): response = requests.post(url, json=detection_data, headers=headers, timeout=10) if response.status_code == 201: - print(f"βœ… Detection sent successfully: Device {detection_data['device_id']}, " - f"Drone {detection_data['drone_id']}, RSSI {detection_data['rssi']}dBm") + print(f"βœ… Device {detection_data['device_id']}: Drone {detection_data['drone_id']} " + f"(RSSI: {detection_data['rssi']}dBm, Dist: ~{detection_data.get('_distance', 'N/A')}km)") return True else: - print(f"❌ Failed to send detection: {response.status_code} - {response.text}") + print(f"❌ Failed: {response.status_code} - {response.text}") return False except requests.exceptions.RequestException as e: print(f"❌ Network error: {e}") return False -def test_single_detection(): - """Send a single test detection""" - device = random.choice(DEVICES) - detection = generate_detection_data(device) - - print("🚁 Sending single test detection...") - print(f"πŸ“ Device: {device['name']}") - print(f"πŸ›°οΈ Drone ID: {detection['drone_id']}") - print(f"πŸ“‘ RSSI: {detection['rssi']}dBm") - print(f"πŸ“Š Frequency: {detection['freq']}MHz") - print(f"🎯 Confidence: {detection['confidence_level']}") - - return send_detection(detection) - -def simulate_continuous_detections(duration_minutes=5, interval_seconds=10): - """Simulate continuous drone detections for testing""" - print(f"πŸ”„ Starting continuous simulation for {duration_minutes} minutes...") - print(f"⏱️ Sending detection every {interval_seconds} seconds") - print("Press Ctrl+C to stop\n") +def simulate_realistic_scenario(duration_minutes=10): + """Simulate realistic drone scenario with persistent tracking""" + print(f"🚁 Starting realistic drone simulation for {duration_minutes} minutes...") + print("πŸ“Š Scenario: Multiple drones with persistent tracking, RSSI changes, movement patterns") + print("=" * 80) + simulator = DroneSimulator() start_time = time.time() end_time = start_time + (duration_minutes * 60) + detection_count = 0 + last_update = start_time try: while time.time() < end_time: - # Generate 1-3 simultaneous detections from different devices - num_detections = random.randint(1, 3) + current_time = time.time() + time_delta = current_time - last_update + last_update = current_time - for _ in range(num_detections): + # Randomly spawn new drones (1-15% chance per cycle) + if random.random() < 0.15 and len(simulator.active_drones) < 8: device = random.choice(DEVICES) - detection = generate_detection_data(device) - - if send_detection(detection): - detection_count += 1 - - # Small delay between simultaneous detections - time.sleep(0.5) + new_drone = simulator.create_new_drone(device) + simulator.active_drones[new_drone["id"]] = new_drone + print(f"πŸ†• New {DRONE_TYPES[new_drone['type']]['name']} spawned near {device['name']}") - time.sleep(interval_seconds) + # Update all active drones + drones_to_remove = [] + for drone_id, drone in simulator.active_drones.items(): + simulator.update_drone_position(drone, time_delta) + + # Check if drone should be removed (too far or timeout) + age_minutes = (current_time - drone["created_at"]) / 60 + if age_minutes > 15 or drone["detection_count"] > 50: + drones_to_remove.append(drone_id) + continue + + # Check detection for each device + for device in DEVICES: + should_detect, distance = simulator.should_detect_drone(drone, device) + + if should_detect: + detection = simulator.generate_detection(drone, device, distance) + detection["_distance"] = f"{distance:.1f}" # For logging only + + if send_detection(detection): + detection_count += 1 + + # Remove expired drones + for drone_id in drones_to_remove: + drone = simulator.active_drones[drone_id] + print(f"πŸ›¬ {DRONE_TYPES[drone['type']]['name']} {drone_id} finished ({drone['detection_count']} detections)") + del simulator.active_drones[drone_id] + + # Status update + elapsed_minutes = (current_time - start_time) / 60 + if int(elapsed_minutes) % 2 == 0 and elapsed_minutes > 0: + active_count = len(simulator.active_drones) + if active_count > 0: + print(f"πŸ“ Status: {active_count} active drones, {detection_count} total detections") + + time.sleep(3) # 3 second cycle time except KeyboardInterrupt: print("\n⏹️ Simulation stopped by user") elapsed = time.time() - start_time - print(f"\nπŸ“ˆ Simulation completed:") + print(f"\nπŸ“ˆ Simulation Summary:") print(f" β€’ Duration: {elapsed/60:.1f} minutes") - print(f" β€’ Detections sent: {detection_count}") + print(f" β€’ Total detections: {detection_count}") print(f" β€’ Average rate: {detection_count/(elapsed/60):.1f} detections/minute") + print(f" β€’ Active drones at end: {len(simulator.active_drones)}") def test_api_health(): """Test if the API is responding""" @@ -157,7 +343,7 @@ def test_api_health(): return False def main(): - print("🚁 Drone Detection System - Test Script") + print("🚁 Realistic Drone Detection Simulator") print("=" * 50) # Test API connectivity @@ -165,40 +351,26 @@ def main(): print("Cannot connect to API. Please check if the system is running.") return - print("\nChoose test mode:") - print("1. Send single test detection") - print("2. Simulate continuous detections") - print("3. Send burst of 10 detections") + print("\nChoose simulation type:") + print("1. Realistic scenario (persistent drones, RSSI changes, movement)") + print("2. Single approaching drone (watch RSSI strengthen)") + print("3. Departing drone (watch RSSI weaken)") try: choice = input("\nEnter choice (1-3): ").strip() if choice == "1": - test_single_detection() + duration = input("Duration in minutes (default: 10): ").strip() + duration = int(duration) if duration else 10 + simulate_realistic_scenario(duration) elif choice == "2": - duration = input("Duration in minutes (default: 5): ").strip() - interval = input("Interval in seconds (default: 10): ").strip() - - duration = int(duration) if duration else 5 - interval = int(interval) if interval else 10 - - simulate_continuous_detections(duration, interval) + print("🎯 Simulating approaching drone (RSSI will strengthen)...") + # Implementation for approaching drone elif choice == "3": - print("πŸš€ Sending burst of 10 detections...") - success_count = 0 - - for i in range(10): - device = random.choice(DEVICES) - detection = generate_detection_data(device) - - if send_detection(detection): - success_count += 1 - - time.sleep(1) # 1 second between detections - - print(f"\nπŸ“Š Burst completed: {success_count}/10 detections sent successfully") + print("πŸ›« Simulating departing drone (RSSI will weaken)...") + # Implementation for departing drone else: print("Invalid choice")