Desktop forensic toolkit for recovering, decoding, validating, and visualizing UAV telemetry logs with chain-of-custody logging, SHA-256 hashing, PDF reports, and machine-readable JSON/CSV artifacts.
python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt python3 -m uav_forensic_toolkit
┌─────────────────────────────────────────────────────────────────┐
│ UAV FORENSIC TOOLKIT FLOW │
└─────────────────────────────────────────────────────────────────┘
1. INITIALIZATION
└─> User enters Operator ID + Case Directory
└─> Click "Initialize Case" button
└─> Creates: case_metadata.json, chain_of_custody.jsonl
└─> Code: uav_forensic_toolkit/gui/main_window.py:86-105
2. RECOVER TAB
└─> User selects Evidence Source Directory
└─> (Optional) Enable deep recovery + select forensic image
└─> Click "Run Recovery" button
└─> Function: recover_logs_from_directory()
└─> Code: uav_forensic_toolkit/core/recover.py
└─> Outputs:
• RecoveredLogs/ (copied files)
• CarvedLogs/ (if deep recovery enabled)
• metadata.json, metadata.csv
• hash_verification.csv
• recovery_report.pdf
3. DECODE TAB
└─> User selects Input Log File (.txt/.dat/.csv)
└─> (Optional) Provide dji-log Path
└─> Click "Decode to Unified CSV" button
└─> Function: cli_decode subprocess
└─> Code: uav_forensic_toolkit/cli_decode.py
└─> Outputs:
• Decoded/decoded_flightlog.csv (unified schema)
• Decoded/decoder_metadata.json
• Decoded/decoder_report.pdf
4. ANALYZE TAB
└─> User selects Decoded CSV
└─> (Optional) Provide Labels CSV + Label column name
└─> Click "Run Tampering Analysis" button
└─> Function: analyze_tampering()
└─> Code: uav_forensic_toolkit/core/analyze.py:44-330
└─> Process:
a) Load points from CSV (lines 62-94)
b) Physics checks (lines 98-167):
• Speed limit: 20.0 m/s
• Acceleration limit: 5.0 m/s²
• Time gap threshold: 5.0 seconds
c) GPS precision check (lines 169-179)
d) ML anomaly detection (lines 181-236):
• IsolationForest model
• Features: dt_s, distance_m, speed_mps, accel_mps2,
alt_rate_mps, delta_lat, delta_lon, bearing_deg,
bearing_change_deg
• Threshold: 70th percentile (default 0.12)
e) Evaluation metrics (if labels provided) (lines 243-282)
└─> Outputs:
• Analysis/anomaly_scores.csv
• Analysis/tampering_report.json
• Analysis/tampering_report.pdf
5. VISUALIZE TAB
└─> User selects Decoded CSV + Tampering Report JSON
└─> Click "Generate Flight Visualization" button
└─> Function: build_visualization_artifacts()
└─> Code: uav_forensic_toolkit/core/visualize.py:72-234
└─> Process:
a) Load points and tampering report (lines 91-96)
b) Build integrity segments (lines 99-118):
• Green = authenticated
• Red = tampered
• Orange = missing data
c) Generate KML (lines 125-126)
d) Generate interactive map with folium (lines 128-146)
e) Generate charts with matplotlib (lines 169-191):
• altitude_chart.png
• speed_chart.png
└─> Outputs:
• Visualization/flight_map.html (interactive map)
• Visualization/flight_map.kml
• Visualization/altitude_chart.png
• Visualization/speed_chart.png
• Visualization/flight_report.pdf
• Visualization/flight_summary.json
main_window.py:81-84main_window.py:86-105
case_metadata.json, chain_of_custody.jsonlrecover_tab.py:78-81recover_tab.py:83-86recover_logs_from_directory()
recover_tab.py:88-131
decode_tab.py:72-75decode_tab.py:77-80decode_tab.py:85-128
analyze_tab.py:63-66analyze_tab.py:68-71analyze_tampering()
analyze_tab.py:73-111
visualize_tab.py:89-92visualize_tab.py:94-97build_visualization_artifacts()
visualize_tab.py:99-132
visualize_tab.py:142-145uav_forensic_toolkit/core/analyze.py
analyze_tampering() (line 44)
uav_forensic_toolkit/core/visualize.py
build_visualization_artifacts() (line 72)
uav_forensic_toolkit/gui/main_window.py
gui/tabs/recover_tab.py
gui/tabs/decode_tab.py
gui/tabs/analyze_tab.py
gui/tabs/visualize_tab.pyuav_forensic_toolkit/core/train_if.py
train_isolation_forest_on_dataset() (line 117)
uav_forensic_toolkit/core/train_supervised.py
train_supervised_on_dataset() (line 117)
uav_forensic_toolkit/core/geo.py
uav_forensic_toolkit/core/custody.py
uav_forensic_toolkit/core/pdf.py
analyze.py:99
"impossible_speed"
analyze.py:98
"excessive_acceleration"
analyze.py:100
"suspicious_gap"
analyze.py:181-226
min(0.18, max(0.08, len(X) / 100000.0))
analyze.py:205
analyze.py:183
analyze.py:175
"gps_precision_inconsistency"
analyze.py:171-172
train_supervised.py:123
train_supervised.py:121
train_supervised.py:120
analyze.py:184-211dt_s - Time delta between pointsdistance_m - Haversine distancespeed_mps - Calculated speedaccel_mps2 - Accelerationalt_rate_mps - Altitude change ratedelta_lat - Latitude changedelta_lon - Longitude changebearing_deg - Direction of travelbearing_change_deg - Change in directiontrain_supervised.py:117-398train_if.py:117-411row_idx,label 12,1 13,1 14,0 15,0
1 or "1" or "true" or "tampered" or "anomaly" or "yes" = Tampered0 or "0" or "false" or "normal" or "ok" or "no" = NormalLocation: analyze.py:244-259
"label"analyze_tab.py:49-51case_id,row_idx,label,timestamp,latitude,longitude,altitude 1,0,0,2024-01-01T10:00:00+00:00,37.7749,-122.4194,100.0 1,1,1,2024-01-01T10:00:01+00:00,37.7750,-122.4195,100.0
Location: train_supervised.py:52-66
Location: analyze.py:266-274
visualize.py:166-183
altitude_chart.png
visualize.py:185-190
speed_chart.png
visualize.py:128-146
flight_map.html
train_if.py:250-264
train_if.py:266-277train_if.py:279-290train_if.py:292-303train_if.py:305-316train_if.py:318-328train_if.py:330-354train_supervised.py:292-306
train_supervised.py:322train_supervised.py:323train_supervised.py:324train_supervised.py:325train_supervised.py:327-337train_supervised.py:339-349Who is operating the tool. This value is recorded for chain-of-custody traceability.
Where outputs are written. The app creates subfolders for each component.
Creates the case folder structure and writes:
case_metadata.jsonchain_of_custody.jsonl (one JSON event per line)Each custody event includes timestamp_utc, operator_id, tool_version, action, and a details object.
Shows progress updates from each operation.
The main window starts at 1280×720 for easier review.
Goal: copy telemetry-related files into the case without modifying originals.
Select the SD root folder (or a forensic copy). The recovery code automatically searches for FlightRecord / Flight Records directories and prefers them if found.
Copies supported extensions into RecoveredLogs/ and computes SHA-256 hashes.
If the SD card contains deleted or corrupted logs that are not visible as normal files, you can enable deep recovery.
.img, .dd, .raw, .dmg).foremost to carve files from the image. Otherwise it uses a built-in printable-bytes fallback.CarvedLogs/ with a confidence score and SHA-256 hashes.RecoveredLogs/CarvedLogs/ (only if deep recovery is enabled)metadata.json, metadata.csvhash_verification.csvrecovery_report.pdfGoal: convert DJI logs into a unified CSV schema used by the rest of the pipeline.
Select a recovered log file (for DJI, typically DJIFlightRecord_YYYY-MM-DD_[HH-MM-SS].txt).
If binary DJI FlightRecord decoding is needed, the toolkit calls the external dji-log tool (from dji-log-parser). Provide the executable path here if it is not in your PATH.
Runs decoding in a separate subprocess and writes:
Decoded/decoded_flightlog.csv (unified schema)Decoded/decoder_metadata.jsonDecoded/decoder_report.pdftimestamp,latitude,longitude,altitude,speed,heading,source
source records the origin/decoder path (example: DJI_FLIGHTRECORD_BIN_DJI_LOG).
.DAT decoding is not implemented in this build.
Goal: detect suspicious segments using physics checks + timestamp checks + GPS precision + ML anomaly scoring.
dt_s = time difference in secondsdistance_m = Haversine distancespeed_mps = distance / dt (m/s)accel_mps2 = (speed - prev_speed) / dtbearing_deg = direction of travel (0-360°)bearing_change_deg = change in directionalt_rate_mps = altitude change ratedelta_lat = latitude changedelta_lon = longitude changedt_s ≤ 0 → "non_monotonic_timestamp"dt_s ≥ 5.0 → "suspicious_gap"speed_mps > 20.0 → "impossible_speed"|accel_mps2| > 5.0 → "excessive_acceleration""gps_precision_inconsistency" flag"ml_anomaly"Select Decoded/decoded_flightlog.csv.
If you have ground truth labels, provide them to compute metrics (accuracy/precision/recall/F1).
row_idx,label 12,1 13,1 14,0
Writes:
Analysis/anomaly_scores.csvAnalysis/tampering_report.jsonAnalysis/tampering_report.pdfGoal: build an integrity-aware flight path and outputs.
Select Decoded/decoded_flightlog.csv and Analysis/tampering_report.json.
Writes:
Visualization/flight_map.html (interactive)Visualization/flight_map.kmlVisualization/altitude_chart.png, Visualization/speed_chart.pngVisualization/flight_report.pdf, Visualization/flight_summary.jsonvisualize.py:134visualize.py:135visualize.py:135The app embeds flight_map.html if Qt WebEngine is available; otherwise use the "Open Map in Browser" button.
Code: visualize_tab.py:73-83 - Uses QWebEngineView if available
You can run analysis without the GUI:
source .venv/bin/activate
python3 -c "from pathlib import Path; from uav_forensic_toolkit.core.analyze import analyze_tampering; \
from uav_forensic_toolkit.core.custody import OperatorContext; \
print(analyze_tampering(Path('output/Decoded/decoded_flightlog.csv'), Path('output/Analysis'), OperatorContext('op','0.1.0'), Path('output/chain_of_custody.jsonl'), print))"
This project includes an optional local HTTP API to run analysis and return JSON. It is intended for local testing only.
source .venv/bin/activate python3 -m uav_forensic_toolkit.api_server
Then in another terminal:
curl -s http://127.0.0.1:8000/health
curl -s -X POST http://127.0.0.1:8000/analyze \ -F "decoded_csv=@output/Decoded/decoded_flightlog.csv" \ -F "operator_id=test_op" \ -o analysis_result.json
The response is a JSON object containing paths to generated artifacts and key summary fields.
The toolkit can train a supervised Random Forest model on a labeled CSV dataset and generate training reports and graphs.
The training script expects a CSV with at least these columns:
case_id,row_idx,label,timestamp,latitude,longitude,altitude
Extra columns are allowed and will be ignored by the trainer.
source .venv/bin/activate python3 -m uav_forensic_toolkit.cli_train_supervised \ --model random_forest \ --dataset-csv "path/to/labeled_dataset.csv" \ --out-dir "output/ModelTraining_supervised_rf" \ --max-segments 800000 \ --test-size 0.2 \ --seed 42 \ --threshold 0.5
training_report.json: metrics (accuracy, precision, recall, F1) + confusion matrix + artifact pathstraining_report.pdf: human-readable training summarysupervised_model.joblib: trained Random Forest model bundlemetrics_curve.png: accuracy/precision/recall/F1 vs threshold (trimmed to the chosen threshold)accuracy_curve.png, precision_curve.png, recall_curve.png, f1_curve.png: separate graphsprecision_recall_curve.png: precision–recall curveconfusion_matrix.png: confusion matrix heatmapThis toolkit produces both human-readable PDFs and machine-readable JSON/CSV artifacts.
recovery_report.pdf: Recovery summary (including deep recovery if enabled)metadata.json, metadata.csv: Recovered file list + metadatahash_verification.csv: SHA-256 hashes for verificationRecoveredLogs/: Copied filesCarvedLogs/: Carved files (only when deep recovery is enabled)Decoded/decoder_report.pdfDecoded/decoder_metadata.jsonDecoded/decoded_flightlog.csvAnalysis/tampering_report.pdfAnalysis/tampering_report.json: Includes per-row status and flagsAnalysis/anomaly_scores.csv: ML anomaly score per rowVisualization/flight_report.pdfVisualization/flight_summary.jsonVisualization/flight_map.html, Visualization/flight_map.kmlVisualization/altitude_chart.png, Visualization/speed_chart.pngchain_of_custody.jsonl: One JSON event per line for every operationcase_metadata.json: Case initialization metadataEverything from the GUI is stored under your Case Directory (example: output/).
RecoveredLogs/CarvedLogs/ (if deep recovery was enabled)Decoded/Analysis/Visualization/chain_of_custody.jsonldji-log binary from dji-log-parser.