Fix jwt-token
This commit is contained in:
12
clear_database.sh
Normal file
12
clear_database.sh
Normal file
@@ -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"
|
||||
@@ -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
|
||||
|
||||
29
server/resetDatabase.js
Normal file
29
server/resetDatabase.js
Normal file
@@ -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();
|
||||
@@ -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,52 +21,214 @@ 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
|
||||
|
||||
# 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_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)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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": 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
|
||||
"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):
|
||||
@@ -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)
|
||||
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']}")
|
||||
|
||||
# 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
|
||||
|
||||
# Small delay between simultaneous detections
|
||||
time.sleep(0.5)
|
||||
# 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]
|
||||
|
||||
time.sleep(interval_seconds)
|
||||
# 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")
|
||||
|
||||
Reference in New Issue
Block a user