mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-07-03 14:06:34 +02:00
a04a261b80
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jamesarich <2199651+jamesarich@users.noreply.github.com> Co-authored-by: James Rich <james.a.rich@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
9.5 KiB
9.5 KiB
Data Model: TAK v2 Protocol Integration
Core Entities
CoTMessage (Central Domain Model)
The primary data model flowing between TAK clients, the TAK server, and the mesh network.
@Serializable
data class CoTMessage(
val uid: String, // Unique event ID (e.g., "ANDROID-device-uuid")
val type: String, // CoT type string (e.g., "a-f-G-U-C", "b-t-f")
val time: Instant, // Event creation time
val start: Instant, // Event validity start
val stale: Instant, // Event expiry time
val how: String, // How generated (e.g., "m-g", "h-e")
val latitude: Double, // WGS84 latitude
val longitude: Double, // WGS84 longitude
val hae: Double, // Height above ellipsoid (meters)
val ce: Double, // Circular error (meters)
val le: Double, // Linear error (meters)
val contact: CoTContact?, // Contact info (callsign, endpoint)
val group: CoTGroup?, // Team/role info
val status: CoTStatus?, // Battery status
val track: CoTTrack?, // Speed/course
val chat: CoTChat?, // Chat routing info
val remarks: String?, // Free-text remarks
val rawDetailXml: String?, // Preserved inner <detail> XML for raw_detail
val parsedDetailXml: CoTDetailXml?, // Parsed structured detail
val sourceEventXml: String?, // Full source XML for round-trip fidelity
)
Supporting Detail Models
@Serializable data class CoTContact(val callsign: String?, val endpoint: String?)
@Serializable data class CoTGroup(val name: String?, val role: String?)
@Serializable data class CoTStatus(val battery: Int?)
@Serializable data class CoTTrack(val speed: Double?, val course: Double?)
@Serializable data class CoTChat(val chatroom: String?, val id: String?, val senderCallsign: String?)
TAKClientInfo
Tracks connected TAK client state.
data class TAKClientInfo(
val uid: String?, // Client's self-reported UID
val callsign: String?, // Client's callsign (from first PLI)
val connectionTime: Instant, // When client connected
)
InboundCoTMessage
Wraps a CoT message with its source client info for routing decisions.
data class InboundCoTMessage(
val cotMessage: CoTMessage,
val clientInfo: TAKClientInfo?, // null if from mesh (not local TAK client)
)
Enum Mappings
CotType (23 mapped values + fallback)
| Enum Value | CoT String | Category |
|---|---|---|
| CotType_PLI_Friendly | "a-f-G-U-C" | Position (friendly ground) |
| CotType_PLI_Hostile | "a-h-G" | Position (hostile) |
| CotType_PLI_Unknown | "a-u-G" | Position (unknown) |
| CotType_PLI_Neutral | "a-n-G" | Position (neutral) |
| CotType_Aircraft_Friendly | "a-f-A" | Aircraft |
| CotType_Aircraft_Military | "a-f-A-M" | Aircraft (military) |
| CotType_Aircraft_Helicopter | "a-f-A-M-H" | Aircraft (helicopter) |
| CotType_Aircraft_FixedWing | "a-f-A-C-F" | Aircraft (fixed wing) |
| CotType_GeoChat | "b-t-f" | Chat |
| CotType_DrawnShape | "u-d-f" | Drawing |
| CotType_Route | "b-m-r" | Route/Navigation |
| CotType_Marker | "b-m-p-s-p-i" | Marker (spot) |
| CotType_Waypoint | "b-m-p-s-p-loc" | Waypoint |
| CotType_Delete | "t-x-d-d" | Control |
| CotType_Casevac | "b-r-f-h-c" | Medical |
| CotType_Emergency | "b-a-o-pan" | Emergency |
| CotType_Alert | "b-a-o-opn" | Alert |
| CotType_Task | "b-i-v" | Tasking |
| ... (+ additional variants) | ||
| CotType_Other | (any unrecognized) | Fallback |
CotHow (4 mapped values)
| Enum Value | String | Meaning |
|---|---|---|
| HOW_HUMAN_ENTERED | "h-e" | Human-entered |
| HOW_MACHINE_GENERATED | "m-g" | Machine-generated |
| HOW_HUMAN_OSINT | "h-g-i-g-o" | Human-generated OSINT |
| HOW_MACHINE_REPORTED | "m-r" | Machine-reported |
State Machines
TAK Server Lifecycle
┌──────────┐
│ STOPPED │
└────┬─────┘
│ start()
▼
┌──────────┐
│ STARTING │ (bind port 8089, load certs)
└────┬─────┘
│ success
▼
┌──────────┐ accept() ┌────────────────┐
│ RUNNING │◄────────────────────│ CLIENT_CONNECTED│
│(0 clients)│ │(n clients) │
└────┬─────┘ └───────┬─────────┘
│ stop() │ stop() / all disconnect
▼ ▼
┌──────────┐
│ STOPPED │
└──────────┘
TAK Client Connection Lifecycle
┌──────────────┐
│ CONNECTED │ (mTLS handshake complete)
└──────┬───────┘
│ first PLI received
▼
┌──────────────┐
│ IDENTIFIED │ (callsign + UID known)
└──────┬───────┘
│ socket error / timeout / client close
▼
┌──────────────┐
│ DISCONNECTED │ (scope cancelled, removed from list)
└──────────────┘
Protocol Version Selection (per-send)
┌─────────────────────────────┐
│ CoT message from TAK client │
└──────────────┬──────────────┘
│
┌─────▼──────┐
│ useTakV2()?│
└──┬─────┬───┘
yes │ │ no
▼ ▼
┌───────────┐ ┌───────────┐
│ V2 Path │ │ V1 Path │
│ (port 78) │ │ (port 72) │
└─────┬─────┘ └─────┬─────┘
│ │
┌─────▼─────┐ ┌─────▼─────┐
│All CoT │ │PLI + Chat │
│types OK │ │only; else │
│ │ │drop+warn │
└─────┬─────┘ └─────┬─────┘
│ │
┌─────▼─────┐ ┌─────▼─────┐
│Compress │ │Bare proto │
│(zstd+dict)│ │(no compr.)│
└─────┬─────┘ └─────┬─────┘
│ │
┌─────▼─────┐ ┌─────▼─────┐
│Check MTU │ │Send on │
│≤225 bytes │ │port 72 │
│else drop │ └───────────┘
└─────┬─────┘
│
┌─────▼─────┐
│Send on │
│port 78 │
└───────────┘
Validation Rules
| Field | Rule | Error Handling |
|---|---|---|
| Compressed payload size | ≤ 225 bytes | Drop packet, log warning |
| Decompressed payload size | ≤ MAX_DECOMPRESSED_SIZE (4096) | Reject packet (memory exhaustion prevention) |
| CoT XML structure | Valid <event> root with <point> |
Parse returns Result.failure |
| Latitude/Longitude | -90/90 and -180/180 respectively | Passed through (ATAK validates) |
| Stale time | Must be in future (or extended to MIN_MESH_STALE_TTL for static types) | Extended automatically for routes/shapes |
| Offline queue | ≤ 50 messages, ≤ 5 min TTL | Oldest evicted on overflow; expired purged on drain |
| Port 8089 binding | Must succeed | Server start returns Result.failure with user-visible error |
Wire Format
TAKPacketV2 (Port 78)
┌─────────┬────────────────────────────────┐
│ Flags │ Compressed TAKPacketV2 Protobuf │
│ (1 byte)│ (variable, ≤224 bytes) │
└─────────┴────────────────────────────────┘
Flags byte:
Bits 0-5: Dictionary ID (0=non-aircraft, 1=aircraft)
Bits 6-7: Reserved
0xFF: Uncompressed (raw protobuf follows)
TAKPacket (Port 72, Legacy)
┌──────────────────────────────┐
│ Raw TAKPacket Protobuf │
│ (no compression, no header) │
└──────────────────────────────┘
Key Constants
const val DEFAULT_TAK_PORT = 8089
const val MAX_TAK_WIRE_PAYLOAD_BYTES = 225
const val MAX_DECOMPRESSED_SIZE = 4096
const val TAK_COORDINATE_SCALE = 1e7 // lat/lon → int scaling
const val DICT_ID_NON_AIRCRAFT = 0
const val DICT_ID_AIRCRAFT = 1
const val DICT_ID_UNCOMPRESSED = 0xFF
val OFFLINE_QUEUE_TTL = 5.minutes
const val OFFLINE_QUEUE_MAX_SIZE = 50
val KEEPALIVE_INTERVAL = 10.seconds
val MIN_MESH_STALE_TTL = 15.minutes