# TeslaMate Database Schema for Drives, Charges, and Positions: A Complete Technical Guide

> Explore the TeslaMate database schema for drives, charges, and positions. Understand the Ecto schemas and their relationships for vehicle telemetry data.

- Repository: [TeslaMate/teslamate](https://github.com/teslamate-org/teslamate)
- Tags: technical-guide
- Published: 2026-06-23

---

**TeslaMate stores vehicle telemetry in four core Ecto schemas—`drives`, `positions`, `charges`, and `charging_processes`—with foreign-key relationships linking drives to their start/end coordinates, charging sessions to location data, and individual charge readings to aggregated processes.**

TeslaMate, the popular open-source Tesla data logger built with Elixir and Phoenix, persists high-granularity vehicle telemetry using a carefully normalized PostgreSQL schema. Understanding the database schema for drives, charges, and positions is essential for custom analytics, third-party integrations, or troubleshooting data integrity issues. The schema definitions reside in the `teslamate-org/teslamate` repository and leverage Ecto's relational mapping to maintain referential integrity between vehicle sessions and geographic coordinates.

## Core Schema Architecture

TeslaMate organizes historical vehicle data into four primary tables defined in `lib/teslamate/log/`. Each schema uses Ecto's `schema/2` macro and establishes associations via `belongs_to/3` and `has_many/2`, with foreign-key constraints enforced at the changeset level.

The logical model separates **discrete events** (individual charge readings, GPS waypoints) from **aggregate sessions** (complete drives, charging processes), enabling efficient querying of both summary statistics and granular telemetry.

## The Drives Schema

The `drives` table captures complete vehicle trips with temporal, spatial, and efficiency metrics. Defined in [`lib/teslamate/log/drive.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/drive.ex), the schema tracks mileage deltas, energy consumption, and environmental conditions during travel.

**Key fields include**:
- Temporal: `start_date`, `end_date`, `duration_min`
- Spatial: `start_km`, `end_km`, `distance`, `ascent`, `descent`
- Environmental: Temperature averages, speed limits, power limits, and range columns

**Relationships** (as implemented in [`lib/teslamate/log/drive.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/drive.ex) lines 8-38):
- `belongs_to :car` → **cars**
- `belongs_to :start_position` → **positions**
- `belongs_to :end_position` → **positions**
- `belongs_to :start_address` → **addresses**
- `belongs_to :end_address` → **addresses**
- `belongs_to :start_geofence` → **geofences**
- `belongs_to :end_geofence` → **geofences**
- `has_many :positions` → all GPS waypoints belonging to the drive

## The Positions Schema

Individual telemetry points are stored in the `positions` table, defined in [`lib/teslamate/log/position.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/position.ex). These records represent the granular GPS and vehicle-state snapshots captured during drives.

**Primary fields**:
- Geographic: `latitude`, `longitude`, `elevation`
- Vehicle state: `speed`, `power`, `odometer`, battery level, range columns
- Climate: `outside_temp`, TPMS pressures, HVAC data

**Relationships**:
- `belongs_to :car` → **cars**
- `belongs_to :drive` → **drives** (nullable for positions captured outside of trips)

## Charges and Charging Processes

TeslaMate distinguishes between **individual charge measurements** and **aggregated charging sessions** using two separate schemas.

### The Charges Schema

Defined in [`lib/teslamate/log/charge.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/charge.ex), this schema stores discrete readings from the vehicle's charging port. It tracks instantaneous values such as `charger_current`, `charger_voltage`, `charger_power`, and `charger_phases`, alongside `battery_level`, `charge_energy_added`, and cable connection status.

**Relationship**:
- `belongs_to :charging_process` → **charging_processes**

### The ChargingProcess Schema

The `charging_processes` table (defined in [`lib/teslamate/log/charging_process.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/charging_process.ex)) aggregates multiple `charges` into a single session, calculating cumulative statistics like total `charge_energy_added`, `duration_min`, and average `outside_temp`.

**Key fields**: `start_date`, `end_date`, `cost`, battery level deltas, and range columns.

**Relationships**:
- `belongs_to :car` → **cars**
- `belongs_to :position` → **positions** (the GPS coordinates where charging began)
- `belongs_to :address` → **addresses**
- `belongs_to :geofence` → **geofences**
- `has_many :charges` → individual charge readings belonging to the session

## Entity Relationship Model

The schemas form a directed graph connecting temporal sessions to spatial data:

1. **Drives link to Positions** via `start_position_id` and `end_position_id` foreign keys, while also maintaining a `has_many` association to all intermediate waypoints.
2. **ChargingProcesses link to Positions** via `position_id`, capturing the geographic context of where charging occurred.
3. **Charges link to ChargingProcesses** via `charging_process_id`, creating a parent-child relationship between aggregated sessions and raw measurements.
4. **Both Drives and ChargingProcesses** reference **addresses** and **geofences**, enabling location-based filtering without expensive coordinate calculations.

Foreign-key constraints are enforced through Ecto changesets, ensuring referential integrity across the `priv/repo/migrations/` directory's migration files.

## Querying the Schema with Ecto

### Fetching a Drive with Full Geographic Context

To retrieve a drive complete with its start/end positions, addresses, and geofences:

```elixir
drive =
  TeslaMate.Repo.get!(TeslaMate.Log.Drive, drive_id)
  |> TeslaMate.Repo.preload([
    :start_position,
    :end_position,
    :start_address,
    :end_address,
    :start_geofence,
    :end_geofence,
    :car,
    :positions
  ])

```

### Retrieving Charging Session Details

To load a charging process with all individual measurements and location data:

```elixir
charging_process =
  TeslaMate.Repo.get!(TeslaMate.Log.ChargingProcess, cp_id)
  |> TeslaMate.Repo.preload([
    :car,
    :position,
    :address,
    :geofence,
    :charges
  ])

```

### Querying Positions for Map Rendering

To fetch all GPS waypoints for a specific drive, ordered chronologically:

```elixir
positions =
  TeslaMate.Repo.all(
    from p in TeslaMate.Log.Position,
      where: p.drive_id == ^drive_id,
      order_by: p.date,
      preload: [:car]
  )

```

### Creating Linked Records

When inserting a new drive with associated positions:

```elixir
%TeslaMate.Log.Drive{}
|> TeslaMate.Log.Drive.changeset(%{
     start_date: DateTime.utc_now(),
     car_id: car_id,
     start_position_id: start_pos_id,
     end_position_id: end_pos_id,
     start_address_id: start_addr_id,
     end_address_id: end_addr_id
   })
|> TeslaMate.Repo.insert()

```

## Summary

- **Four core schemas** power TeslaMate's data model: `drives`, `positions`, `charges`, and `charging_processes`, defined in `lib/teslamate/log/`.
- **Drives** maintain dual relationships to positions (start and end) and aggregate all intermediate waypoints via `has_many :positions`.
- **ChargingProcesses** serve as the aggregation point for `charges`, linking energy data to specific geographic coordinates via `belongs_to :position`.
- **Referential integrity** is enforced through Ecto changesets and PostgreSQL foreign-key constraints established in `priv/repo/migrations/`.
- **Geographic normalization** occurs through separate `addresses` and `geofences` tables, allowing drives and charging sessions to share location metadata.

## Frequently Asked Questions

### How does TeslaMate associate GPS coordinates with a specific drive?

Each drive record stores `start_position_id` and `end_position_id` foreign keys referencing the `positions` table, along with a `has_many :positions` association that captures all intermediate waypoints between start and end timestamps. This design allows the application to render complete trip paths while efficiently querying start and end locations without scanning all GPS points.

### What is the difference between the charges and charging_processes tables?

The `charges` table stores discrete, high-frequency measurements (voltage, current, power) captured during charging, while `charging_processes` aggregates these into session-level summaries with calculated totals for energy added, duration, and cost. A `charging_process` `has_many :charges`, and each charge `belongs_to :charging_process`, creating a one-to-many relationship between sessions and individual readings.

### Can I query all positions for a specific drive using Ecto?

Yes, you can query the `positions` table filtering by `drive_id` and ordering by `date`. The `Drive` schema also supports preloading all positions via `Repo.preload(drive, :positions)`, which executes a single query to fetch the drive and all associated GPS waypoints for map visualization or efficiency analysis.

### How are geofences linked to drives and charging sessions?

Both the `Drive` and `ChargingProcess` schemas include `belongs_to` associations for geofences. Drives track `start_geofence` and `end_geofence` separately, while charging processes link to a single `geofence` representing the charging location. These associations enable automatic location tagging and statistics aggregation by frequently visited locations without requiring expensive coordinate-based queries.