Skip to content

Repository files navigation

LogAnalyzer

A browser-native flight log analysis platform for DJI drones. Parses encrypted and unencrypted telemetry files, GNSS observation data, and photogrammetry survey markers entirely on-device. No data leaves the user's browser.


Table of Contents


Overview

LogAnalyzer ingests DJI flight logs (.txt, .dat, .bin), subtitle telemetry (.srt), RINEX GNSS observations (.obs, .nav), and survey geotag markers (.mrk) to produce interactive dashboards covering flight telemetry, battery health, anomaly detection, satellite visibility, and photogrammetric accuracy. Every computation runs inside the browser using Web Workers. Data is persisted locally in IndexedDB.


Architecture

The application follows a layered architecture with strict separation between parsing, storage, analysis, and presentation.

┌─────────────────────────────────────────────────────────────┐
│                        UI Layer                             │
│  Pages: Upload │ History │ Detail │ Compare │ Survey │ Fleet│
│  Components: MapPanel │ DataTable │ Toast │ Modal │ Sidebar │
├─────────────────────────────────────────────────────────────┤
│                     Analysis Engine                         │
│  flight-summary │ anomaly-detector │ battery-health         │
│  flight-comparator │ survey-accuracy                        │
├─────────────────────────────────────────────────────────────┤
│                    Persistence Layer                        │
│  Dexie.js (IndexedDB) │ schema.js │ flight-store.js        │
│  rinex-store.js │ query-engine.js                           │
├─────────────────────────────────────────────────────────────┤
│                     Parser Suite                            │
│  dji-txt.parser │ dji-dat.parser │ srt.parser               │
│  rinex.parser │ mrk.parser │ dji-txt.crypto                 │
├─────────────────────────────────────────────────────────────┤
│                   Runtime Environment                       │
│  Browser │ Web Workers │ Web Crypto API │ IndexedDB          │
└─────────────────────────────────────────────────────────────┘

Directory Structure

src/
├── main.js                   # Router, page lifecycle, error boundary
├── components/               # Reusable UI components
│   ├── nav-sidebar.js        # Navigation sidebar with hash-based routing
│   ├── map-panel.js          # Leaflet map with trajectory & geotag layers
│   ├── file-dropzone.js      # Drag-and-drop file upload zone
│   ├── toast.js              # Notification system
│   └── modal.js              # Confirmation dialogs
├── pages/                    # Route-level page controllers
│   ├── upload.js             # File ingestion and processing queue
│   ├── flight-history.js     # Flight list with grid/table views
│   ├── flight-detail.js      # Single-flight telemetry dashboard
│   ├── survey-analysis.js    # RINEX + MRK survey quality dashboard
│   └── settings.js           # API key, storage, export preferences
├── core/
│   ├── parsers/              # File format parsers (see below)
│   ├── database/             # IndexedDB schema and store modules
│   └── analysis/             # Offline analysis engines
├── styles/                   # Global CSS design system
└── utils/                    # Binary helpers, geo math, time formatting

Supported File Formats

Extension Format Source Content
.txt DJI TXT Flight Log DJI Fly / DJI GO / DJI Pilot app Full telemetry: GPS, altitude, speed, battery, gimbal, RC inputs, motor status, warnings
.dat / .bin DJI DAT Binary Log Drone internal storage / DJI Assistant High-resolution sensor data: raw OSD, battery cells, gimbal angles, RC channels
.srt DJI SRT Subtitle DJI camera video recordings Per-frame GPS, altitude, ISO, shutter speed, focal length, color temperature
.obs RINEX Observation GNSS receiver / Base station Satellite pseudorange, carrier phase, Doppler, signal strength per epoch
.nav RINEX Navigation GNSS receiver / Base station Satellite ephemeris and orbit parameters
.mrk DJI Survey Geotags DJI Phantom 4 RTK / Matrice 300 RTK / PPK solutions Per-image WGS84 coordinates, ellipsoidal height, attitude, positional standard deviations

Parsing Pipeline

DJI TXT Logs (v1–v12)

The TXT parser operates on raw ArrayBuffer input. The file layout is:

  1. Header (100 bytes): Contains the record area end offset, details area length, and a version byte at offset 10.
  2. Records Area (offset 100 to recordAreaEnd): Sequential binary records, each structured as [type:u8][length:u8][payload][0xFF].
  3. Details Area: JSON metadata appended after the records area.

Each record payload is XOR-descrambled using a CRC-64/ECMA-182 lookup table. The scramble seed is derived from payload[0] ^ recordType. An 8-byte key is generated from the CRC-64 table entry at that seed index, and every byte in the payload is XOR'd against the repeating key.

Parsed record types:

Type Code Category Fields Extracted
0x01 OSD lat, lng, altitude, height, speed, heading, satellites, fly state, flight action
0x02 Home home lat/lng/altitude
0x03 Gimbal pitch, roll, yaw
0x04 RC Input aileron, elevator, throttle, rudder
0x06 Battery voltage, current, capacity %, temperature, cell voltages
0x07 App Tip warning messages
0x0A Motor per-motor speed, status
0x0D App GPS app-reported GPS data

After parsing, timestamps are attached at 100ms intervals based on OSD record sequence position.

DJI TXT Logs (v13+ Encrypted)

Logs from firmware version 13 onward use AES-128-CBC encryption on the entire records area. The decryption workflow is:

  1. The parser detects version >= 13 from the header byte.
  2. The file's fingerprint (SHA-256 hash) is computed and sent to the DJI Open Platform API (https://openapi.dji.com/api/v1/flight-records/keychains).
  3. The API returns an AES key and initialization vector.
  4. The Web Crypto API decrypts the records area using AES-CBC.
  5. The decrypted buffer is reassembled (header + decrypted records + remainder) and fed back into the standard parser.

Keychains are cached locally in IndexedDB to avoid repeated API calls for the same file.

DJI DAT Binary Logs

DAT files use a simpler structure: length-prefixed records with 16-bit type identifiers and 16-bit length fields. The parser skips a 128-byte file header if the magic bytes match (0x0755 or 0x0306). Record types are:

  • 0x0001 — OSD (lat/lng stored as float64 radians, converted to degrees)
  • 0x0002 — Battery (voltage in centivolt units, current in centiamp units)
  • 0x0003 — Gimbal (angles in decidegrees)
  • 0x0004 — RC (raw stick values as int16)

SRT Subtitle Files

The SRT parser splits the file by double-newlines into subtitle blocks. Each block contains:

  1. A sequence index
  2. A timecode line (HH:MM:SS,mmm --> HH:MM:SS,mmm)
  3. One or more telemetry lines embedded in <font> tags

Telemetry is extracted using regex patterns for keys like latitude, longitude, altitude, iso, shutter, fnum, ev, ct, and focal_len.

RINEX Observation Files

The RINEX parser supports both v2.x and v3.x formats:

  • Header parsing: Extracts version, file type, marker name, antenna type, approximate position, observation types, and first observation time.
  • v2 epoch parsing: Reads epoch headers with 2-digit years (80+ = 1900s, <80 = 2000s), satellite lists packed 12 per line, and observation data at 5 values per line (16 characters each).
  • v3 epoch parsing: Reads epoch headers prefixed with >, 4-digit years, and per-satellite observation data on individual lines.
  • Derived metrics: countConstellations() classifies satellites by SV prefix (G=GPS, R=GLONASS, E=Galileo, C=BeiDou, J=QZSS). estimatePDOP() computes a simplified geometric dilution estimate.

MRK Geotag Files

The MRK parser handles two distinct formats:

  1. Standard DJI format: Space-delimited columns — index timestamp_gps longitude latitude altitude_ell altitude_baro roll pitch yaw filename [std_lng std_lat std_alt]
  2. PPK solution exports: Detected by the presence of ,Lat, ,Lon, and ,Ellh markers in the data. The parser searches for these text markers to locate coordinate fields regardless of column order.

Additional robustness features:

  • UTF-16 detection: Strips null bytes from improperly decoded files.
  • Header skipping: Ignores lines starting with #, //, photo, or index.
  • GPS time conversion: Converts GPS seconds-of-week to UTC using the current GPS week estimate and a leap-second offset of 18s.
  • CEP50 computation: Calculates horizontal position accuracy as 0.5887 * sqrt(sigma_lng^2 + sigma_lat^2).

Data Storage

All data is stored in the browser's IndexedDB via the Dexie.js ORM. The database is named LogAnalyzer and uses the following schema:

Table Primary Key Indexes Purpose
flights flight_id drone_sn, start_time, end_time, *tags Core flight records with summary statistics
telemetry auto-increment flight_id, timestamp_ms, source_type OSD telemetry samples (100ms intervals)
battery auto-increment flight_id, timestamp_ms Battery voltage, current, cell data
motors auto-increment flight_id, timestamp_ms Per-motor RPM and status
gimbal auto-increment flight_id, timestamp_ms Gimbal pitch/roll/yaw
rc_input auto-increment flight_id, timestamp_ms RC stick positions
warnings auto-increment flight_id, timestamp_ms App warnings and tips
survey_marks auto-increment flight_id, image_num MRK geotag positions and accuracy
rinex_files filename type, start_time, end_time, epoch_count RINEX file metadata and headers
gnss_epochs auto-increment flight_id, rinex_filename, time Per-epoch satellite counts, constellations, PDOP
analysis_cache flight_id analysis_type Cached analysis results
keychains log_hash Cached AES decryption keychains
drones serial_number model, firmware Fleet inventory

Storage Characteristics

  • Capacity: Browser-managed, typically 50–100 GB depending on available disk space (the UI displays a conservative 500 MB estimate).
  • Privacy: All data remains on-device. No telemetry, flight paths, or survey coordinates are transmitted to any server.
  • Deletion: The "Clear All Flight Data" function in Settings calls db.delete() which removes the entire IndexedDB database file, followed by db.open() to reinitialize the schema.
  • Storage estimate: Uses navigator.storage.estimate() to report actual disk usage.

Auto-Linking Engine

When flight logs and RINEX/survey files are uploaded independently, the system automatically correlates them using temporal overlap detection.

How It Works

Upload Flight Log (.txt)          Upload RINEX (.obs)
        │                                 │
        ▼                                 ▼
  Parse → Extract                   Parse → Extract
  start_time, end_time              start_time, end_time
        │                                 │
        ▼                                 ▼
  Store in `flights`               Store in `rinex_files`
  table                            + `gnss_epochs` tables
        │                                 │
        └──────────┬──────────────────────┘
                   ▼
         Auto-Link: Time Window Intersection
         WHERE flight.start_time < rinex.end_time
         AND   flight.end_time   > rinex.start_time
                   │
                   ▼
         Update gnss_epochs.flight_id
         for all epochs within the flight's time window

Bidirectional Linking

The auto-linker runs in both directions:

  1. Flight uploaded first (autoLinkFlightToRinex): Queries rinex_files for any files whose time window overlaps the flight. Updates matching gnss_epochs records with the flight_id.
  2. RINEX uploaded first (autoLinkRinexToFlights): Queries flights for any flights whose time window overlaps the RINEX file. Updates matching gnss_epochs records with the flight_id.

This means files can be uploaded in any order and the correlation is established automatically.


Analysis Modules

Flight Summary (flight-summary.js)

Computes aggregate statistics from parsed OSD and battery records:

  • Duration, total distance (Haversine), max altitude (MSL and AGL)
  • Max horizontal/vertical speed, max distance from home point
  • Battery start/end percentage, minimum voltage, consumption rate
  • Drone type identification from OSD record fields
  • Path point extraction for map visualization

Anomaly Detector (anomaly-detector.js)

Scans telemetry streams against configurable thresholds:

Anomaly Threshold Severity
Rapid voltage drop > 0.5V in 5 seconds Critical
Battery overtemperature > 50°C Warning
Cell imbalance > 200mV delta Warning
Sustained high current > 25A for 10+ seconds Warning
Low satellite count < 6 satellites Warning
Position jump > 10m between samples Warning
Altitude violation > 120m AGL Regulatory
Distance violation > 5000m from home Warning
Excessive descent rate > 5 m/s Warning

Battery Health (battery-health.js)

Evaluates long-term battery condition from voltage curves, cell balance, internal resistance estimates, and charge cycle patterns.

Flight Comparator (flight-comparator.js)

Side-by-side analysis of two flight logs — compares altitude profiles, speed patterns, battery consumption, and distance metrics.

Survey Accuracy (survey-accuracy.js)

Generates a quality report from MRK geotags and RINEX epochs:

  • Per-tag CEP50: Horizontal circular error probable at the 50th percentile.
  • Aggregate statistics: Mean, P95, min/max for both horizontal and vertical accuracy.
  • PDOP analysis: Min, max, mean, and P95 of position dilution of precision.
  • Constellation timeline: Time-series satellite counts broken down by GPS, GLONASS, Galileo, BeiDou, and QZSS.
  • Quality grading: A (CEP P95 <= 5cm), B (<= 10cm), C (single issue), D (multiple issues).

Survey Analysis Workflow

  1. Upload RINEX files (.obs / .nav) via the Upload page or the Survey page's "Load RINEX" button. Epochs are parsed, constellation counts and PDOP are computed per-epoch, and everything is stored in IndexedDB.

  2. Upload MRK file (.mrk) via the Upload page or the Survey page's "Load MRK File" button. Geotags are parsed (supporting both standard DJI and PPK solution formats), stored in the survey_marks table, and displayed on the map.

  3. View the Survey dashboard. The page loads all available RINEX files and survey marks from the database. Clicking a RINEX file loads its epochs and computes the full survey report:

    • KPI cards showing image count, quality grade, mean CEP50, and mean PDOP
    • Interactive map with auto-zoom to the mission area
    • Satellite count per constellation stacked area chart (D3.js)
    • PDOP over time line chart with a threshold reference line
    • Sortable geotag table with coordinates, altitude, and accuracy metrics
  4. Auto-zoom: The map automatically calculates the bounding box of all geotag markers and fits the view to the mission area using L.featureGroup().getBounds().


Security Model

  • Local-only data: All parsing, analysis, and storage happens in the browser. No flight data, GPS coordinates, or survey results are ever sent to a remote server.
  • DJI API key: The only external API call is to the DJI Open Platform for fetching AES decryption keychains (v13+ logs only). The API key can be configured in two ways:
    • Environment variable (VITE_DJI_API_KEY): Set at build time or in the deployment platform. The Settings page shows "Using Global System Key" and disables manual input.
    • Per-user (localStorage): Users can enter their own key in Settings. Stored in localStorage, never transmitted.

Getting Started

Prerequisites

  • Node.js 18+ with npm 9+

Setup

# Install dependencies
npm install

# Start development server
npm run dev

The app will be available at http://localhost:5173.

Environment Variables

Variable Required Description
VITE_DJI_API_KEY No DJI Open Platform API key for v13+ log decryption. If not set, users can enter their own key in Settings.

Create a .env file in the project root:

VITE_DJI_API_KEY=your_dji_open_api_key_here

Deployment

Vercel

  1. Push the repository to GitHub.
  2. Import the project in Vercel.
  3. Add VITE_DJI_API_KEY as an environment variable in the Vercel dashboard.
  4. Deploy. The build command (vite build) and output directory (dist/) are detected automatically.

Manual Build

npm run build      # Produces optimized bundle in dist/
npm run preview    # Serves the production build locally

Tech Stack

Layer Technology Purpose
Build Vite 5 Development server, HMR, production bundling
UI Vanilla JS Zero-framework SPA with hash-based routing
Charts Plotly.js Interactive telemetry time-series charts
Charts D3.js Satellite constellation stacked area charts
Maps Leaflet Interactive map with trajectory and geotag layers
Database Dexie.js IndexedDB ORM with transactions and indexing
Crypto Web Crypto API AES-128-CBC decryption for v13+ logs
Testing Vitest Unit test runner

Scripts

Command Description
npm run dev Start the Vite development server
npm run build Build the production bundle to dist/
npm run preview Serve the production build locally
npm run test Run the Vitest test suite

License

This project is proprietary. All rights reserved.

About

LogAnalyzer for analyzing DJI flight logs. Can combine multiple kinds of logs for one master feedback. A potential project which never made it to production.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages