Skip to content

Add synthetic detection mode with configurable noise for testing #14

Description

@jonnyspicer

Summary

Add a synthetic detection mode to adsb2dd that generates realistic radar detection data with configurable noise/imperfections for testing downstream components like retina-tracker and 3lips.

Motivation

Currently, adsb2dd provides perfect delay-Doppler truth data. To test tracking and geolocator algorithms, we need realistic synthetic radar detections that include:

  • Measurement noise (Gaussian on delay/Doppler)
  • Variable SNR
  • Missed detections (detection probability < 1.0)
  • False alarms (clutter detections with no ADS-B match)

This synthetic data will enable:

  • Testing retina-tracker ADS-B-assisted tracking (Doppler from ADS-B speed #1)
  • Validating 3lips geolocation algorithms
  • Benchmarking algorithm performance under various noise conditions
  • Repeatability (same input → same output with fixed seed)

Proposed Solution

New API Endpoint: /api/synthetic-detections

Add a new endpoint that converts adsb2dd's per-aircraft output into frame-based detection arrays with realistic imperfections:

Query Parameters:

/api/synthetic-detections?server=...&rx=...&tx=...&fc=...
  &noise_delay=0.5        # Delay noise std (km)
  &noise_doppler=2.0      # Doppler noise std (Hz)
  &snr_min=8              # Minimum SNR (dB)
  &snr_max=20             # Maximum SNR (dB)
  &detection_prob=0.95    # Probability of detecting aircraft [0-1]
  &false_alarm_rate=0.5   # False alarms per frame
  &frame_interval=500     # Frame interval (ms)
  &duration=10            # Total duration (seconds)
  &seed=42                # Random seed (optional, for repeatability)

Output Format

Extended .detection format compatible with blah2-arm#14 and retina-tracker:

{
  "timestamp": 1718747745000,
  "delay": [16.1, 22.3, 15.8],
  "doppler": [134.5, -50.2, 88.3],
  "snr": [15.2, 12.8, 18.5],
  "adsb": [
    {
      "hex": "a12345",
      "lat": 37.7749,
      "lon": -122.4194,
      "alt_baro": 5000,
      "gs": 250,
      "track": 45
    },
    {
      "hex": "b67890",
      "lat": 37.8123,
      "lon": -122.3456,
      "alt_baro": 8500,
      "gs": 300,
      "track": 120
    },
    null  // False alarm (clutter)
  ]
}

Key differences from current output:

  • Frame-based arrays (not per-aircraft dict)
  • Multiple aircraft per frame
  • Parallel arrays: delay, doppler, snr, adsb
  • adsb[i] corresponds to delay[i], or null for clutter
  • Sequential frames over time duration

Implementation Details

Core Logic

function generateSyntheticFrame(aircraftData, config, rng) {
  const detections = [];
  const adsb = [];
  
  // Process each aircraft
  for (const aircraft of aircraftData) {
    // Simulate missed detection
    if (rng.random() > config.detection_prob) {
      continue;
    }
    
    // Calculate true delay/Doppler
    const trueDelay = calculateBistaticDelay(aircraft, config.rx, config.tx);
    const trueDoppler = calculateBistaticDoppler(aircraft, config.rx, config.tx, config.fc);
    
    // Add Gaussian noise
    const delay = trueDelay + rng.gaussian(0, config.noise_delay);
    const doppler = trueDoppler + rng.gaussian(0, config.noise_doppler);
    
    // Generate realistic SNR
    const snr = rng.uniform(config.snr_min, config.snr_max);
    
    detections.push({ delay, doppler, snr });
    adsb.push({
      hex: aircraft.hex,
      lat: aircraft.lat,
      lon: aircraft.lon,
      alt_baro: aircraft.alt_baro,
      gs: aircraft.gs,
      track: aircraft.track
    });
  }
  
  // Add false alarms (clutter)
  const nFalseAlarms = rng.poisson(config.false_alarm_rate);
  for (let i = 0; i < nFalseAlarms; i++) {
    detections.push({
      delay: rng.uniform(config.delay_min, config.delay_max),
      doppler: rng.uniform(config.doppler_min, config.doppler_max),
      snr: rng.uniform(config.snr_min, config.snr_max * 0.8)  // Lower SNR for clutter
    });
    adsb.push(null);
  }
  
  return { detections, adsb };
}

Random Number Generation

Use a seedable PRNG (e.g., seedrandom) for reproducibility:

import seedrandom from 'seedrandom';

const rng = seedrandom(config.seed || Date.now());

Dependencies

  • seedrandom: Seedable PRNG
  • gaussian: For Box-Muller transform (or implement inline)

Files to Modify

  1. src/server.js: Add new /api/synthetic-detections endpoint
  2. src/node/synthetic.js (NEW): Synthetic detection generation logic
  3. package.json: Add seedrandom dependency
  4. README.md: Document new endpoint and usage examples

Testing

Create test/synthetic.test.js:

  • Verify output format matches extended .detection spec
  • Check noise statistics (mean ≈ 0, std ≈ configured value)
  • Validate detection probability
  • Test repeatability with fixed seed
  • Verify false alarm rate

Example Usage

# Generate 10 seconds of synthetic data with medium noise
curl "http://localhost:49155/api/synthetic-detections?\
  server=http://sfo1.retnode.com&\
  rx=37.7644,-122.3954,23&\
  tx=37.49917,-121.87222,783&\
  fc=503&\
  noise_delay=0.5&\
  noise_doppler=2.0&\
  snr_min=8&\
  snr_max=20&\
  detection_prob=0.95&\
  false_alarm_rate=0.5&\
  duration=10&\
  seed=42" > test_synthetic.detection

Then use with retina-tracker:

cd ../retina-tracker
python -m tracker.track_detections test_synthetic.detection -o tracks.json

Alternative: Extend Existing Endpoint

Instead of a new endpoint, could add synthetic=true parameter to /api/dd:

/api/dd?...&synthetic=true&noise_delay=0.5&...

Pros: Reuses existing logic
Cons: Conflates real-time and synthetic modes

Recommendation: Use new endpoint for clearer separation of concerns.

Future Enhancements

  • Batch generation: Generate entire datasets offline
  • Scenarios: Predefined noise profiles (low/medium/high)
  • Time-varying noise: SNR degradation over time
  • Multi-sensor support: Different noise for different receivers
  • Track-correlated noise: Aircraft-specific noise characteristics

Acceptance Criteria

  • New endpoint /api/synthetic-detections implemented
  • Output matches extended .detection format
  • Configurable Gaussian noise on delay/Doppler
  • Configurable SNR range
  • Missed detections via detection probability
  • False alarms (clutter) generation
  • Seedable PRNG for repeatability
  • Tests verify noise statistics and output format
  • README documentation with usage examples
  • Compatible with retina-tracker input parser

Dependencies

  • Requires blah2-arm#14 extended detection format as reference
  • Will be used by retina-tracker#1 for testing ADS-B features

Related Issues

  • blah2-arm#14: ADS-B association in detection output
  • retina-tracker#1: ADS-B-assisted track initialization
  • tar1090-node#5: Shared geometry library (future consolidation)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions