cotlib

package module
v0.3.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2025 License: MIT Imports: 18 Imported by: 0

README

Cursor On Target

'…we want the target dead or saved…we gotta get away from platform centric thinking…and we gotta focus on this thing where the sum of the wisdom is a cursor over the target…and we're indifferent [to the source]' — Gen. John Jumper

CoT Library

Go Report Card CI

A comprehensive Go library for creating, validating, and working with Cursor-on-Target (CoT) events.

Features

  • High-performance processing: Sub-microsecond event creation, millions of validations/sec
  • Complete CoT event creation and manipulation
  • XML serialization and deserialization with security protections
  • Full CoT type catalog with metadata
  • Zero-allocation type lookups and optimized memory usage
  • How and relation value support with comprehensive validation
  • Coordinate and spatial data handling
  • Event relationship management
  • Type validation and registration
  • Secure logging with slog
  • Thread-safe operations
  • Detail extensions with round-trip preservation
  • GeoChat message and receipt support
  • Predicate-based event classification
  • Security-first design
  • Wildcard pattern support for types
  • Type search by description or full name

Installation

go get github.com/NERVsystems/cotlib

Note: Schema validation relies on the libxml2 library and requires CGO to be enabled when building. See MIGRATION.md for guidance when upgrading from older versions.

Usage

Creating and Managing CoT Events
package main

import (
    "fmt"
    "log/slog"
    "os"
    "github.com/NERVsystems/cotlib"
)

func main() {
    logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

    // Create a new CoT event
    event, err := cotlib.NewEvent("UNIT-123", "a-f-G", 37.422, -122.084, 0.0)
    if err != nil {
        logger.Error("Failed to create event", "error", err)
        return
    }

    // Add detail information
    event.Detail = &cotlib.Detail{
        Contact: &cotlib.Contact{
            Callsign: "ALPHA-7",
        },
        Group: &cotlib.Group{
            Name: "Team Blue",
            Role: "Infantry",
        },
    }

    // Add relationship link
    event.AddLink(&cotlib.Link{
        Uid:      "HQ-1",
        Type:     "a-f-G-U-C",
        Relation: "p-p",
    })

    // Convert to XML
    xmlData, err := event.ToXML()
    if err != nil {
        logger.Error("Failed to convert to XML", "error", err)
        return
    }

    fmt.Println(string(xmlData))
}
Building Events with EventBuilder
builder := cotlib.NewEventBuilder("B1", "a-f-G", 34.0, -117.0, 0).
    WithContact(&cotlib.Contact{Callsign: "ALPHA"}).
    WithGroup(&cotlib.Group{Name: "Team Blue", Role: "Infantry"}).
    WithStaleTime(time.Now().Add(10 * time.Second))
event, err := builder.Build()
if err != nil {
    log.Fatal(err)
}
_ = event
Parsing CoT XML
package main

import (
    "errors"
    "fmt"
    "github.com/NERVsystems/cotlib"
)

func main() {
    xmlData := `<?xml version="1.0" encoding="UTF-8"?>
<event version="2.0" uid="UNIT-123" type="a-f-G" time="2023-05-15T18:30:22Z"
       start="2023-05-15T18:30:22Z" stale="2023-05-15T18:30:32Z">
  <point lat="37.422000" lon="-122.084000" hae="0.0" ce="9999999.0" le="9999999.0"/>
  <detail>
    <contact callsign="ALPHA-7"/>
    <group name="Team Blue" role="Infantry"/>
  </detail>
</event>`

    // Parse XML into CoT event
    event, err := cotlib.UnmarshalXMLEvent(context.Background(), []byte(xmlData))
    if err != nil {
        fmt.Printf("Error parsing XML: %v\n", err)
        return
    }

    // Access event data
    fmt.Printf("Event Type: %s\n", event.Type)
    fmt.Printf("Location: %.6f, %.6f\n", event.Point.Lat, event.Point.Lon)
    fmt.Printf("Callsign: %s\n", event.Detail.Contact.Callsign)

    // Check event predicates
    if event.Is("friend") {
        fmt.Println("This is a friendly unit")
    }

    if event.Is("ground") {
        fmt.Println("This is a ground-based entity")
    }
}
Handling Detail Extensions

CoT events often include TAK-specific extensions inside the <detail> element. cotlib preserves many of these extensions and validates them using embedded TAKCoT schemas. These extensions go beyond canonical CoT and include elements such as:

  • __chat
  • __chatReceipt
  • __chatreceipt
  • __geofence
  • __serverdestination
  • __video
  • __group
  • archive
  • attachmentList
  • environment
  • fileshare
  • precisionlocation
  • takv
  • track
  • mission
  • status
  • shape
  • strokecolor
  • strokeweight
  • fillcolor
  • labelson
  • uid
  • bullseye
  • routeInfo
  • color
  • hierarchy
  • link
  • usericon
  • emergency
  • height
  • height_unit
  • remarks

The remarks extension now follows the MITRE CoT Remarks Schema and includes a <remarks> root element, enabling validation through the tak-details-remarks schema.

All of these known TAK extensions are validated against embedded schemas when decoding and during event validation. Invalid XML will result in an error. Chat messages produced by TAK clients often include a <chatgrp> element inside <__chat>. cotlib first validates against the standard chat schema and automatically falls back to the TAK-specific tak-details-__chat schema so these messages are accepted.

Example: adding a shape extension with a strokeColor attribute:

event.Detail = &cotlib.Detail{
    Shape: &cotlib.Shape{Raw: []byte(`<shape strokeColor="#00FF00"/>`)},
}

Any unknown elements are stored in Detail.Unknown and serialized back verbatim. Unknown extensions are not validated. Although cotlib enforces XML size and depth limits, the data may still contain unexpected or malicious content. Treat these elements as untrusted and validate them separately if needed.

xmlData := `<?xml version="1.0"?>
<event version="2.0" uid="EXT-1" type="t-x-c" time="2023-05-15T18:30:22Z" start="2023-05-15T18:30:22Z" stale="2023-05-15T18:30:32Z">
  <point lat="0" lon="0" ce="9999999.0" le="9999999.0"/>
  <detail>
    <__chat chatroom="room" groupOwner="false" senderCallsign="Alpha">
      <chatgrp id="room" uid0="u0"/>
    </__chat>
    <__video url="http://example/video"/>
  </detail>
</event>`

evt, _ := cotlib.UnmarshalXMLEvent(context.Background(), []byte(xmlData))
out, _ := evt.ToXML()
fmt.Println(string(out)) // prints the same XML

The id attribute on __chat and __chatreceipt elements is optional.

Chat now exposes additional fields such as Chatroom, GroupOwner, SenderCallsign, Parent, MessageID and a slice of ChatGrp entries representing group membership.

GeoChat Messaging

cotlib provides full support for GeoChat messages and receipts. The Chat structure models the __chat extension including optional <chatgrp> elements and any embedded hierarchy. Incoming chat events automatically populate Event.Message from the <remarks> element. The Marti type holds destination callsigns and Remarks exposes the message text along with the source, to, and time attributes.

Chat receipts are represented by the ChatReceipt structure which handles both __chatReceipt and TAK-specific __chatreceipt forms. Parsing falls back to the TAK schemas when required so messages from ATAK and WinTAK are accepted without extra handling.

Example of constructing and serializing a chat message:

evt, _ := cotlib.NewEvent("GeoChat.UID.Room.example", "b-t-f", 0, 0, 0)
evt.Detail = &cotlib.Detail{
    Chat: &cotlib.Chat{
        ID:             "Room",
        Chatroom:       "Room",
        SenderCallsign: "Alpha",
        ChatGrps: []cotlib.ChatGrp{
            {ID: "Room", UID0: "AlphaUID", UID1: "BravoUID"},
        },
    },
    Marti: &cotlib.Marti{Dest: []cotlib.MartiDest{{Callsign: "Bravo"}}},
    Remarks: &cotlib.Remarks{
        Source: "Example.Alpha",
        To:     "Room",
        Text:   "Hello team",
    },
}
out, _ := evt.ToXML()

Delivery or read receipts can be sent by populating Detail.ChatReceipt with the appropriate Ack, ID, and MessageID fields.

Validator Package

The optional validator subpackage provides schema checks for common detail extensions. validator.ValidateAgainstSchema validates XML against embedded XSD files. Event.Validate automatically checks extensions such as __chat, __chatReceipt, __group, __serverdestination, __video, attachment_list, usericon, and the drawing-related details using these schemas. All schemas in this repository's takcot/xsd directory are embedded and validated, including those like Route.xsd that reference other files.

Type Validation and Catalog

The library provides comprehensive type validation and catalog management:

package main

import (
    "errors"
    "fmt"
    "log"
    "github.com/NERVsystems/cotlib"
)

func main() {
    // Register a custom CoT type
    if err := cotlib.RegisterCoTType("a-f-G-U-C-F"); err != nil {
        log.Fatal(err)
    }

    // Validate a CoT type
    if err := cotlib.ValidateType("a-f-G-U-C-F"); err != nil {
        if errors.Is(err, cotlib.ErrInvalidType) {
            log.Fatal(err)
        }
    }

    // Look up type metadata
    fullName, err := cotlib.GetTypeFullName("a-f-G-E-X-N")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Full name: %s\n", fullName)
    // Output: Full name: Gnd/Equip/Nbc Equipment

    // Get type description
    desc, err := cotlib.GetTypeDescription("a-f-G-E-X-N")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Description: %s\n", desc)
    // Output: Description: NBC EQUIPMENT

    // Retrieve full type information
    info, err := cotlib.GetTypeInfo("a-f-G-E-X-N")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s - %s\n", info.FullName, info.Description)
    // Output: Gnd/Equip/Nbc Equipment - NBC EQUIPMENT

    // Batch lookup for multiple types
    infos, err := cotlib.GetTypeInfoBatch([]string{"a-f-G-E-X-N", "a-f-G-U-C"})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Batch size: %d\n", len(infos))

    // Search for types by description
    types := cotlib.FindTypesByDescription("NBC")
    for _, t := range types {
        fmt.Printf("Found type: %s (%s)\n", t.Name, t.Description)
    }

    // Search for types by full name
    types = cotlib.FindTypesByFullName("Equipment")
    for _, t := range types {
        fmt.Printf("Found type: %s (%s)\n", t.Name, t.FullName)
    }
}

catalog.Upsert precomputes upper-case versions of each type's FullName and Description. FindByDescription and FindByFullName reuse these cached strings so searches are allocation-free.

Type Validation

The library enforces strict validation of CoT types:

  • Basic syntax checking
  • Standard prefix validation
  • Length limits
  • Wildcard pattern validation
  • Type catalog verification
  • Automatic resolution of f, h, n, or u segments to catalog entries containing .
// Examples of different validation scenarios:
cotlib.ValidateType("a-f-G")             // Valid - Friendly Ground
cotlib.ValidateType("b-m-r")             // Valid - Route
cotlib.ValidateType("invalid")           // Error - Unknown type
How and Relation Values

The library provides full support for CoT how values (indicating position source) and relation values (for event relationships):

How Values

How values indicate the source or method of position determination:

package main

import (
    "errors"
    "fmt"
    "log"
    "github.com/NERVsystems/cotlib"
)

func main() {
    // Create an event
    event, _ := cotlib.NewEvent("UNIT-123", "a-f-G", 37.422, -122.084, 0.0)
    
    // Set how value using descriptor (recommended)
    err := cotlib.SetEventHowFromDescriptor(event, "gps")
    if err != nil {
        log.Fatal(err)
    }
    // This sets event.How to "h-g-i-g-o"
    
    // Or set directly if you know the code
    event.How = "h-e" // manually entered
    
    // Validate how value
    if err := cotlib.ValidateHow(event.How); err != nil {
        if errors.Is(err, cotlib.ErrInvalidHow) {
            log.Fatal(err)
        }
    }
    
    // Get human-readable description
    desc, _ := cotlib.GetHowDescriptor("h-g-i-g-o")
    fmt.Printf("How: %s\n", desc) // Output: How: gps
}
Relation Values

Relation values specify the relationship type in link elements:

// Add a validated link with parent-point relation
err := event.AddValidatedLink("HQ-1", "a-f-G-U-C", "p-p")
if err != nil {
    if errors.Is(err, cotlib.ErrInvalidRelation) {
        log.Fatal(err)
    }
}

// Or add manually (validation happens during event.Validate())
event.AddLink(&cotlib.Link{
    Uid:      "CHILD-1",
    Type:     "a-f-G",
    Relation: "p-c", // parent-child
})

// Validate relation value
if err := cotlib.ValidateRelation("p-c"); err != nil {
    if errors.Is(err, cotlib.ErrInvalidRelation) {
        log.Fatal(err)
    }
}

// Get relation description
desc, _ := cotlib.GetRelationDescription("p-p")
fmt.Printf("Relation: %s\n", desc) // Output: Relation: parent-point
Available Values

How values include:

  • h-e (manual entry)
  • h-g-i-g-o (GPS)
  • m-g (GPS - MITRE)
  • And many others from both MITRE and TAK specifications

Relation values include:

  • c (connected)
  • p-p (parent-point)
  • p-c (parent-child)
  • p (parent - MITRE)
  • And many others from both MITRE and TAK specifications
Validation

Event validation automatically checks how and relation values:

event.How = "invalid-how"
err := event.Validate() // Will fail

event.AddLink(&cotlib.Link{
    Uid:      "test",
    Type:     "a-f-G", 
    Relation: "invalid-relation",
})
err = event.Validate() // Will fail
Custom Types

You can register custom type codes that extend the standard prefixes:

// Register a custom type
cotlib.RegisterCoTType("a-f-G-E-V-custom")

// Validate the custom type
if err := cotlib.ValidateType("a-f-G-E-V-custom"); err != nil {
    log.Fatal(err)
}

ctx := cotlib.WithLogger(context.Background(), logger)

// Register types from a file
if err := cotlib.RegisterCoTTypesFromFile(ctx, "my-types.xml"); err != nil {
    log.Fatal(err)
}

// Register types from a string
xmlContent := `<types>
    <cot cot="a-f-G-custom"/>
    <cot cot="a-h-A-custom"/>
</types>`
if err := cotlib.RegisterCoTTypesFromXMLContent(ctx, xmlContent); err != nil {
    log.Fatal(err)
}
Generating Type Metadata (cotgen)

The cmd/cotgen utility expands the CoT XML definitions and writes the cottypes/generated_types.go file used by the library. Ensure the cot-types directory (or cottypes as a fallback) is present, then run:

go run ./cmd/cotgen
# or simply
go generate ./cottypes

Add your custom type entries to cottypes/CoTtypes.xml (or cot-types/CoTtypes.xml) before running the generator to embed them into the resulting Go code.

The test suite ensures generated_types.go is up to date. If it fails, regenerate the file with go generate ./cottypes and commit the result.

TAK Types and Extensions

The library supports both canonical MITRE CoT types and TAK-specific extensions. TAK types are maintained separately to ensure clear namespace separation and avoid conflicts with official MITRE specifications.

Adding New CoT Types

For MITRE/canonical types: Add entries to cottypes/CoTtypes.xml For TAK-specific types: Add entries to cottypes/TAKtypes.xml

The generator automatically discovers and processes all *.xml files in the cot-types/ directory (falling back to cottypes/ if needed).

TAK Namespace

All TAK-specific types use the TAK/ namespace prefix in their full attribute to distinguish them from MITRE types:

<!-- TAK-specific types in cottypes/TAKtypes.xml -->
<cot cot="b-t-f" full="TAK/Bits/File" desc="File Transfer" />
<cot cot="u-d-f" full="TAK/Drawing/FreeForm" desc="Free Form Drawing" />
<cot cot="t-x-c" full="TAK/Chat/Message" desc="Chat Message" />
Working with TAK Types
// Check if a type is TAK-specific
typ, err := cottypes.GetCatalog().GetType("b-t-f")
if err != nil {
    log.Fatal(err)
}

if cottypes.IsTAK(typ) {
    fmt.Printf("%s is a TAK type: %s\n", typ.Name, typ.FullName)
    // Output: b-t-f is a TAK type: TAK/Bits/File
}

// Search for TAK types specifically
takTypes := cottypes.GetCatalog().FindByFullName("TAK/")
fmt.Printf("Found %d TAK types\n", len(takTypes))

// Validate TAK types
if err := cotlib.ValidateType("b-t-f"); err != nil {
    log.Fatal(err) // TAK types are fully validated
}
Generator Workflow
  1. The generator scans cot-types/*.xml (or cottypes/*.xml) for type definitions
  2. Parses each XML file into the standard <types><cot> structure
  3. Validates TAK namespace integrity (no a- prefixes with TAK/ full names)
  4. Expands MITRE wildcards (a-.-) but leaves TAK types unchanged
  5. Generates cottypes/generated_types.go with all types
Adding New Types

To add new CoT types to the catalog:

  1. For MITRE types: Edit cottypes/CoTtypes.xml (or cot-types/CoTtypes.xml)
  2. For TAK extensions: Edit cottypes/TAKtypes.xml (or cot-types/TAKtypes.xml)
  3. For new categories: Create a new XML file in cottypes/ or cot-types/
  4. Run go generate ./cottypes to regenerate the catalog
  5. Verify with tests: go test ./cottypes -v -run TestTAK

Example TAK type entry:

<cot cot="b-m-p-c-z" full="TAK/Map/Zone" desc="Map Zone" />

Important: TAK types should never use the a- prefix (reserved for MITRE affiliation-based types) and must always use the TAK/ namespace prefix.

Event Predicates

The library provides convenient type classification with the Is() method:

// Create a friendly ground unit event
event, _ := cotlib.NewEvent("test123", "a-f-G", 30.0, -85.0, 0.0)

// Check various predicates
fmt.Printf("Is friendly: %v\n", event.Is("friend"))  // true
fmt.Printf("Is hostile: %v\n", event.Is("hostile")) // false
fmt.Printf("Is ground: %v\n", event.Is("ground"))   // true
fmt.Printf("Is air: %v\n", event.Is("air"))         // false
Thread Safety

All operations in the library are thread-safe. The type catalog uses internal synchronization to ensure safe concurrent access.

Security Features

The library implements several security measures:

  • XML parsing restrictions to prevent XXE attacks
  • Input validation on all fields
  • Coordinate range enforcement
  • Time field validation to prevent time-based attacks
  • Maximum value length controls
  • Configurable parser limits
  • UID values are limited to 64 characters and may not contain whitespace
  • Secure logging practices
// Set maximum allowed length for XML attribute values
// This protects against memory exhaustion attacks
cotlib.SetMaxValueLen(500 * 1024) // 500KB limit
cotlib.SetMaxXMLSize(2 << 20)    // 2MB overall XML size
cotlib.SetMaxElementDepth(32)    // nesting depth limit
cotlib.SetMaxElementCount(10000) // total element limit
cotlib.SetMaxTokenLen(1024)      // single token size
Logging

The library uses slog for structured logging:

  • Debug level for detailed operations
  • Info level for normal events
  • Warn level for recoverable issues
  • Error level for critical problems
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
    Level: slog.LevelDebug,
}))

ctx := cotlib.WithLogger(context.Background(), logger)
log := cotlib.LoggerFromContext(ctx)
log.Info("logger ready")
Event Pooling

UnmarshalXMLEvent reuses Event objects from an internal pool to reduce allocations. When you are done with an event, return it to the pool:

evt, _ := cotlib.UnmarshalXMLEvent(context.Background(), data)
defer cotlib.ReleaseEvent(evt)

Build Tags

The optional novalidator build tag disables XML schema validation. When this tag is provided, ValidateAgainstSchema becomes a no-op and always returns nil, so any XML is parsed without verification, including malformed or malicious content.

Only use this tag when performance is critical and the input XML is already trusted. Do not use it when processing untrusted or potentially invalid CoT, as skipping schema validation may expose your application to malformed data.

go build -tags novalidator

Benchmarks

Run benchmarks with the standard Go tooling:

go test -bench=. ./...

This executes any Benchmark... functions across the module, allowing you to profile serialization, validation, or other operations.

Performance

This library is optimized for high-performance CoT processing with minimal memory allocations:

Core Operations (Apple M4)
Operation Speed Allocations Memory Throughput
Event Creation 157.4 ns/op 1 alloc 288 B ~6.4M events/sec
XML Generation 583.2 ns/op 4 allocs 360 B ~1.7M events/sec
XML Parsing 5.08 μs/op 73 allocs 3.48 KB ~197K events/sec
XML Decode w/ Limits 2.31 μs/op 49 allocs 2.62 KB ~433K events/sec
Type Validation
Type Pattern Speed Allocations Throughput
Simple Types (a-f-G) 21.9 ns/op 0 allocs ~45.7M validations/sec
Complex Types (a-f-G-E-X-N) 22.3 ns/op 0 allocs ~44.8M validations/sec
Wildcards (a-f-G-*) 53.7 ns/op 0 allocs ~18.6M validations/sec
Atomic Wildcards (a-.-X) 32.3 ns/op 0 allocs ~31.0M validations/sec
Catalog Operations
Operation Speed Allocations Throughput
Type Lookup 18.9 ns/op 0 allocs ~52.9M lookups/sec
Search by Description 67.4 μs/op 0 allocs ~14.8K searches/sec
Search by Full Name 104.4 μs/op 0 allocs ~9.6K searches/sec
XML Schema Validation
Metric Performance Allocations Throughput Range
Average 2.89 μs/op 0 allocs ~346K validations/sec
Fastest 2.25 μs/op 0 allocs ~444K validations/sec
Slowest 5.25 μs/op 0 allocs ~190K validations/sec

Validation performance across 13 schema types (Contact, Track, Color, Environment, Precision Location, Shape, Event Point, Status, Video, Mission, TAK Version, Bullseye, Route Info)

Key Performance Features
  • Zero-allocation lookups: Type catalog operations don't allocate memory
  • Object pooling: XML parsing reuses event objects to minimize GC pressure
  • Optimized validation: Fast-path validation for common type patterns
  • Efficient searching: Pre-computed uppercase strings for case-insensitive search
  • Minimal serialization overhead: Direct byte buffer manipulation for XML generation
Real-World Scenarios

High-frequency tracking: Process 200,000+ position updates per second Bulk operations: Validate millions of type codes with zero GC impact
Memory-constrained environments: Minimal allocation footprint Low-latency systems: Sub-microsecond event processing

Benchmarks run on Apple M4 with Go 1.21. Your mileage may vary by platform.

Documentation

For detailed documentation and examples, see:

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Project History

Originally created by @pdfinn. All core functionality and initial versions developed prior to organisational transfer.

Documentation

Overview

Package cotlib implements the Cursor on Target (CoT) protocol for Go.

The package provides data structures and utilities for parsing and generating CoT messages, as well as a comprehensive type catalog system for working with CoT type codes.

Type Catalog

The type catalog system provides a way to work with CoT type codes and their metadata. Each type code (e.g., "a-f-G-E-X-N") has associated metadata:

  • Full Name: A hierarchical name (e.g., "Gnd/Equip/Nbc Equipment")
  • Description: A human-readable description (e.g., "NBC EQUIPMENT")

The catalog supports several operations:

  • Looking up metadata for a specific type code
  • Searching for types by description or full name
  • Validating type codes
  • Registering custom type codes

Example usage:

// Look up type metadata
fullName, err := cotlib.GetTypeFullName("a-f-G-E-X-N")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Full name: %s\n", fullName)

// Search for types
types := cotlib.FindTypesByDescription("NBC")
for _, t := range types {
    fmt.Printf("Found type: %s (%s)\n", t.Name, t.Description)
}

Thread Safety

All operations on the type catalog are thread-safe. The catalog uses internal synchronization to ensure safe concurrent access.

Custom Types

Applications can register custom type codes using RegisterCoTType. These custom types must follow the standard CoT type format and will be validated before registration.

For more information about CoT types and their format, see: https://www.mitre.org/sites/default/files/pdf/09_4937.pdf

Security features include:

  • XML parsing restrictions to prevent XXE attacks
  • Input validation on all fields
  • Strict coordinate range enforcement
  • Time field validation to prevent time-based attacks
  • Secure logging practices
  • Detail extension isolation

For more information about CoT, see:

The package follows these design principles:

  • High cohesion: focused on CoT event parsing and serialization
  • Low coupling: separated concerns for expansions and transport
  • Composition over inheritance: nested sub-structures for detail fields
  • Full schema coverage: implements Event.xsd with example extensions
  • Secure by design: validates inputs and prevents common attacks
Example (RoundTripStrokeColorUsericon)
package main

import (
	"bytes"
	"context"
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	evt, _ := cotlib.NewEvent("EX-UI", "a-f-G", 0, 0, 0)
	evt.StrokeColor = "ff0000ff"
	evt.UserIcon = "icon.png"

	xmlData, _ := evt.ToXML()
	cotlib.ReleaseEvent(evt)

	out, _ := cotlib.UnmarshalXMLEvent(context.Background(), xmlData)
	fmt.Printf("strokeColor: %s\n", out.StrokeColor)
	fmt.Printf("usericon: %s\n", out.UserIcon)
	outXML, _ := out.ToXML()
	fmt.Printf("round-trip equal: %v\n", bytes.Equal(xmlData, outXML))
	cotlib.ReleaseEvent(out)
}
Output:

strokeColor: ff0000ff
usericon: icon.png
round-trip equal: true
Example (TypePredicates)

Example_typePredicates demonstrates using type predicates

package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	// Create some example events
	events := []*cotlib.Event{
		{Type: "a-f-G-U-C"}, // Friendly ground combat unit
		{Type: "a-h-A-M-F"}, // Hostile fixed wing aircraft
		{Type: "b-d-c-n-r"}, // NBC radiation detection
		{Type: "t-s-i-e"},   // ISR EO tasking
	}

	// Test various predicates
	predicates := []string{"atom", "friend", "hostile", "ground", "air"}

	for _, evt := range events {
		fmt.Printf("\nEvent type: %s\n", evt.Type)
		for _, pred := range predicates {
			if evt.Is(pred) {
				fmt.Printf("  Matches predicate: %s\n", pred)
			}
		}
	}

}
Output:

Event type: a-f-G-U-C
  Matches predicate: atom
  Matches predicate: friend
  Matches predicate: ground

Event type: a-h-A-M-F
  Matches predicate: atom
  Matches predicate: hostile
  Matches predicate: air

Event type: b-d-c-n-r
  Matches predicate: atom

Event type: t-s-i-e
  Matches predicate: atom

Index

Examples

Constants

View Source
const (

	// CotTimeFormat is the standard time format for CoT messages (Zulu time, no offset)
	// Format: "2006-01-02T15:04:05Z" (UTC without timezone offset)
	CotTimeFormat = "2006-01-02T15:04:05Z"
)

Security limits for XML parsing and validation

Variables

View Source
var (
	ErrInvalidInput     = fmt.Errorf("invalid input")
	ErrInvalidLatitude  = fmt.Errorf("invalid latitude")
	ErrInvalidLongitude = fmt.Errorf("invalid longitude")
	ErrInvalidUID       = fmt.Errorf("invalid UID")
	// ErrInvalidType is returned when a CoT type fails validation.
	ErrInvalidType = fmt.Errorf("invalid type")
	// ErrInvalidHow is returned when a how value is not recognised.
	ErrInvalidHow = fmt.Errorf("invalid how")
	// ErrInvalidRelation is returned when a relation value is not recognised.
	ErrInvalidRelation = fmt.Errorf("invalid relation")
)

Error sentinels for validation

Functions

func FindTypes

func FindTypes(query string) []cottypes.Type

FindTypes returns all types matching the given query

func FindTypesByDescription

func FindTypesByDescription(desc string) []cottypes.Type

FindTypesByDescription searches for types matching the given description. The search is case-insensitive and matches partial descriptions.

For example:

  • "NBC" finds all types containing "NBC" in their description
  • "EQUIPMENT" finds all equipment-related types
  • "COMBAT" finds all combat-related types

This is useful for building search interfaces and type discovery tools. Returns an empty slice if no matches are found.

Example
package main

import (
	"fmt"
	"sort"

	"github.com/NERVsystems/cotlib"
)

func main() {
	types := cotlib.FindTypesByDescription("NBC EQUIPMENT")
	// Sort by name for consistent output
	sort.Slice(types, func(i, j int) bool {
		return types[i].Name < types[j].Name
	})
	for _, t := range types {
		fmt.Printf("Found type: %s (%s)\n", t.Name, t.Description)
	}
}
Output:

Found type: a-f-G-E-X-N (NBC EQUIPMENT)
Found type: a-h-G-E-X-N (NBC EQUIPMENT)
Found type: a-n-G-E-X-N (NBC EQUIPMENT)
Found type: a-u-G-E-X-N (NBC EQUIPMENT)

func FindTypesByFullName

func FindTypesByFullName(name string) []cottypes.Type

FindTypesByFullName searches for types matching the given full name. The search is case-insensitive and matches partial names.

For example:

  • "Nbc Equipment" finds all NBC equipment types
  • "Ground" finds all ground-based types
  • "Vehicle" finds all vehicle types

This is useful for finding types based on their hierarchical classification. Returns an empty slice if no matches are found.

Example
package main

import (
	"fmt"
	"sort"

	"github.com/NERVsystems/cotlib"
)

func main() {
	types := cotlib.FindTypesByFullName("Gnd/Equip/Nbc Equipment")
	// Sort by name for consistent output
	sort.Slice(types, func(i, j int) bool {
		return types[i].Name < types[j].Name
	})
	for _, t := range types {
		fmt.Printf("Found type: %s (%s)\n", t.Name, t.FullName)
	}
}
Output:

Found type: a-f-G-E-X-N (Gnd/Equip/Nbc Equipment)
Found type: a-h-G-E-X-N (Gnd/Equip/Nbc Equipment)
Found type: a-n-G-E-X-N (Gnd/Equip/Nbc Equipment)
Found type: a-u-G-E-X-N (Gnd/Equip/Nbc Equipment)

func GetHowDescriptor added in v0.2.7

func GetHowDescriptor(how string) (string, error)

GetHowDescriptor returns a human-readable description of the how value. For example: "h-g-i-g-o" returns "gps".

func GetRelationDescription added in v0.2.7

func GetRelationDescription(relation string) (string, error)

GetRelationDescription returns a human-readable description of the relation value. For example: "p-p" returns "parent-point".

func GetTypeDescription

func GetTypeDescription(name string) (string, error)

GetTypeDescription returns the human-readable description for a CoT type. For example, "a-f-G-E-X-N" returns "NBC EQUIPMENT".

The description is a concise explanation of what the type represents, suitable for display in user interfaces and logs.

Returns an error if the type is not registered in the catalog.

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	desc, err := cotlib.GetTypeDescription("a-f-G-E-X-N")
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Printf("Description: %s\n", desc)
}
Output:

Description: NBC EQUIPMENT

func GetTypeFullName

func GetTypeFullName(name string) (string, error)

GetTypeFullName returns the full hierarchical name for a CoT type. For example, "a-f-G-E-X-N" returns "Gnd/Equip/Nbc Equipment".

The full name represents the type's position in the CoT type hierarchy, making it useful for building user interfaces and documentation.

Returns an error if the type is not registered in the catalog.

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	fullName, err := cotlib.GetTypeFullName("a-f-G-E-X-N")
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Printf("Full name: %s\n", fullName)
}
Output:

Full name: Gnd/Equip/Nbc Equipment

func GetTypeInfo added in v0.3.7

func GetTypeInfo(name string) (cottypes.Type, error)

GetTypeInfo returns the metadata for a CoT type. It includes the type code, full hierarchical name, and description.

Returns an error if the type is not registered in the catalog.

func GetTypeInfoBatch added in v0.3.7

func GetTypeInfoBatch(names []string) ([]cottypes.Type, error)

GetTypeInfoBatch returns metadata for all provided type names. If any lookup fails, the function returns an error.

func LoadCoTTypesFromFile

func LoadCoTTypesFromFile(ctx context.Context, path string) error

LoadCoTTypesFromFile loads CoT types from a file

func LoggerFromContext added in v0.2.7

func LoggerFromContext(ctx context.Context) *slog.Logger

LoggerFromContext retrieves the logger from context or returns slog.Default.

func LookupType

func LookupType(name string) (cottypes.Type, bool)

LookupType returns the Type for the given name if it exists LookupType returns the Type for the given name if it exists. If the exact type is not found, the function attempts wildcard resolution by substituting affiliation segments (f/h/n/u) with '.' and retrying the lookup. This mirrors ValidateType's wildcard handling to ensure lookups succeed for types that only exist in their wildcard form in the catalog.

func RegisterAllCoTTypes

func RegisterAllCoTTypes() error

RegisterAllCoTTypes is a no-op since XML is already embedded

func RegisterCoTType

func RegisterCoTType(name string)

RegisterCoTType adds a specific CoT type to the valid types registry It does not log individual type registrations to avoid log spam

func RegisterCoTTypesFromFile

func RegisterCoTTypesFromFile(ctx context.Context, filename string) error

RegisterCoTTypesFromFile loads and registers CoT types from an XML file

func RegisterCoTTypesFromReader

func RegisterCoTTypesFromReader(ctx context.Context, r io.Reader) error

RegisterCoTTypesFromReader loads and registers CoT types from an XML reader

func RegisterCoTTypesFromXMLContent

func RegisterCoTTypesFromXMLContent(ctx context.Context, xmlContent string) error

RegisterCoTTypesFromXMLContent registers CoT types from the given XML content string This is particularly useful for embedding the CoTtypes.xml content directly in code

func ReleaseEvent added in v0.2.6

func ReleaseEvent(e *Event)

ReleaseEvent returns an Event to the internal pool after resetting all fields.

The provided pointer should no longer be used after calling this function.

func SetEventHowFromDescriptor added in v0.2.7

func SetEventHowFromDescriptor(event *Event, descriptor string) error

SetEventHowFromDescriptor sets the how field on an event using a descriptor. For example: SetEventHowFromDescriptor(event, "gps") sets how to "h-g-i-g-o". It returns an error if event is nil or the descriptor is invalid.

func SetLogger

func SetLogger(l *slog.Logger)

SetLogger sets the package-level logger

func SetMaxElementCount added in v0.2.7

func SetMaxElementCount(max int64)

SetMaxElementCount sets the maximum allowed number of XML elements

func SetMaxElementDepth added in v0.2.7

func SetMaxElementDepth(max int64)

SetMaxElementDepth sets the maximum depth of XML elements

func SetMaxTokenLen added in v0.2.7

func SetMaxTokenLen(max int64)

SetMaxTokenLen sets the maximum length for any single XML token

func SetMaxValueLen

func SetMaxValueLen(max int64)

SetMaxValueLen sets the maximum allowed length for XML attribute values and character data This is used to prevent memory exhaustion attacks via large XML payloads

func SetMaxXMLSize added in v0.2.7

func SetMaxXMLSize(max int64)

SetMaxXMLSize sets the maximum allowed size for XML input

func ValidateAgainstSchema added in v0.3.0

func ValidateAgainstSchema(data []byte) error

ValidateAgainstSchema validates the given CoT event XML against the point schema.

func ValidateHow added in v0.2.7

func ValidateHow(how string) error

ValidateHow checks if a how value is valid according to the CoT catalog. How values indicate the source or method of position determination.

func ValidateLatLon

func ValidateLatLon(lat, lon float64) error

ValidateLatLon checks if latitude and longitude are within valid ranges

func ValidateRelation added in v0.2.7

func ValidateRelation(relation string) error

ValidateRelation checks if a relation value is valid according to the CoT catalog. Relation values indicate the relationship type in link elements.

func ValidateType

func ValidateType(typ string) error

ValidateType checks if a CoT type is valid

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	// Test various CoT types
	types := []string{
		"a-f-G",      // Friendly ground
		"a-h-A",      // Hostile air
		"b-d",        // Detection
		"t-x-takp-v", // TAK presence
		"invalid",    // Invalid type
	}

	for _, typ := range types {
		err := cotlib.ValidateType(typ)
		fmt.Printf("Type %s: %v\n", typ, err == nil)
	}

}
Output:

Type a-f-G: true
Type a-h-A: true
Type b-d: true
Type t-x-takp-v: true
Type invalid: false

func ValidateUID

func ValidateUID(uid string) error

ValidateUID checks if a UID is valid. It rejects empty values, leading hyphens, double dots, whitespace, and UIDs longer than 64 characters.

func WithLogger

func WithLogger(ctx context.Context, l *slog.Logger) context.Context

WithLogger adds a logger to the context

Types

type Archive added in v0.2.8

type Archive struct {
	Raw RawMessage
}

Archive represents the TAK archive extension.

func (Archive) MarshalXML added in v0.2.8

func (a Archive) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Archive) UnmarshalXML added in v0.2.8

func (a *Archive) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type AttachmentList added in v0.2.8

type AttachmentList struct {
	Raw RawMessage
}

AttachmentList represents the TAK attachmentList extension.

func (AttachmentList) MarshalXML added in v0.2.8

func (a AttachmentList) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*AttachmentList) UnmarshalXML added in v0.2.8

func (a *AttachmentList) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Bullseye added in v0.2.8

type Bullseye struct {
	Raw RawMessage
}

Bullseye represents the TAK bullseye extension.

func (Bullseye) MarshalXML added in v0.2.8

func (b Bullseye) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Bullseye) UnmarshalXML added in v0.2.8

func (b *Bullseye) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Chat added in v0.2.8

type Chat struct {
	XMLName        xml.Name   `xml:"__chat"`
	ID             string     `xml:"id,attr,omitempty"`
	Message        string     `xml:"message,attr,omitempty"`
	Sender         string     `xml:"sender,attr,omitempty"`
	Chatroom       string     `xml:"chatroom,attr,omitempty"`
	GroupOwner     string     `xml:"groupOwner,attr,omitempty"`
	SenderCallsign string     `xml:"senderCallsign,attr,omitempty"`
	Parent         string     `xml:"parent,attr,omitempty"`
	MessageID      string     `xml:"messageId,attr,omitempty"`
	DeleteChild    string     `xml:"deleteChild,attr,omitempty"`
	ChatGrps       []ChatGrp  `xml:"chatgrp,omitempty"`
	Hierarchy      *Hierarchy `xml:"hierarchy,omitempty"`
	Raw            RawMessage `xml:"-"`
}

Chat represents the TAK __chat extension including group information.

func (*Chat) IsGroupChat added in v0.3.7

func (c *Chat) IsGroupChat() bool

IsGroupChat reports whether the chat message targets a group. A chat is considered a group chat when the Chatroom or GroupOwner fields are non-empty or when any ChatGrp elements are present.

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	direct := &cotlib.Chat{Message: "hi", Sender: "A"}
	fmt.Println(direct.IsGroupChat())

	group := &cotlib.Chat{Chatroom: "room", SenderCallsign: "A"}
	fmt.Println(group.IsGroupChat())
}
Output:

false
true

func (Chat) MarshalXML added in v0.2.8

func (c Chat) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Chat) UnmarshalXML added in v0.2.8

func (c *Chat) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type ChatGrp added in v0.3.6

type ChatGrp struct {
	XMLName xml.Name `xml:"chatgrp"`
	ID      string   `xml:"id,attr,omitempty"`
	UID0    string   `xml:"uid0,attr,omitempty"`
	UID1    string   `xml:"uid1,attr,omitempty"`
	UID2    string   `xml:"uid2,attr,omitempty"`
}

ChatGrp represents a chat group entry within a chat message.

type ChatReceipt added in v0.2.8

type ChatReceipt struct {
	XMLName        xml.Name   `xml:""`
	Ack            string     `xml:"ack,attr,omitempty"`
	ID             string     `xml:"id,attr,omitempty"`
	Chatroom       string     `xml:"chatroom,attr,omitempty"`
	GroupOwner     string     `xml:"groupOwner,attr,omitempty"`
	SenderCallsign string     `xml:"senderCallsign,attr,omitempty"`
	MessageID      string     `xml:"messageId,attr,omitempty"`
	Parent         string     `xml:"parent,attr,omitempty"`
	ChatGrp        *ChatGrp   `xml:"chatgrp,omitempty"`
	Raw            RawMessage `xml:"-"`
}

ChatReceipt represents the TAK chat receipt extensions.

func (ChatReceipt) MarshalXML added in v0.2.8

func (c ChatReceipt) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*ChatReceipt) UnmarshalXML added in v0.2.8

func (c *ChatReceipt) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type CoTTime

type CoTTime time.Time

CoTTime represents a time in CoT format (UTC without timezone offset)

func (CoTTime) MarshalXML

func (t CoTTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error

MarshalXML implements xml.Marshaler

func (CoTTime) MarshalXMLAttr

func (t CoTTime) MarshalXMLAttr(name xml.Name) (xml.Attr, error)

MarshalXMLAttr implements xml.MarshalerAttr

func (CoTTime) Time

func (t CoTTime) Time() time.Time

Time returns the underlying time.Time value

func (*CoTTime) UnmarshalXML

func (t *CoTTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

UnmarshalXML implements xml.Unmarshaler

func (*CoTTime) UnmarshalXMLAttr

func (t *CoTTime) UnmarshalXMLAttr(attr xml.Attr) error

UnmarshalXMLAttr implements xml.UnmarshalerAttr

type ColorExtension added in v0.2.8

type ColorExtension struct {
	Raw RawMessage
}

ColorExtension represents the TAK color extension.

func (ColorExtension) MarshalXML added in v0.2.8

func (c ColorExtension) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*ColorExtension) UnmarshalXML added in v0.2.8

func (c *ColorExtension) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Contact

type Contact struct {
	XMLName  xml.Name `xml:"contact"`
	Callsign string   `xml:"callsign,attr,omitempty"`
}

Contact represents contact information

type Detail

type Detail struct {
	Group             *Group             `xml:"group,omitempty"`
	Contact           *Contact           `xml:"contact,omitempty"`
	Chat              *Chat              `xml:"__chat,omitempty"`
	ChatReceipt       *ChatReceipt       `xml:"__chatReceipt,omitempty"`
	Emergency         *Emergency         `xml:"emergency,omitempty"`
	Geofence          *Geofence          `xml:"__geofence,omitempty"`
	ServerDestination *ServerDestination `xml:"__serverdestination,omitempty"`
	Video             *Video             `xml:"__video,omitempty"`
	GroupExtension    *GroupExtension    `xml:"__group,omitempty"`
	Archive           *Archive           `xml:"archive,omitempty"`
	AttachmentList    *AttachmentList    `xml:"attachmentList,omitempty"`
	Environment       *Environment       `xml:"environment,omitempty"`
	FileShare         *FileShare         `xml:"fileshare,omitempty"`
	PrecisionLocation *PrecisionLocation `xml:"precisionlocation,omitempty"`
	Takv              *Takv              `xml:"takv,omitempty"`
	Track             *Track             `xml:"track,omitempty"`
	Mission           *Mission           `xml:"mission,omitempty"`
	Status            *Status            `xml:"status,omitempty"`
	Shape             *Shape             `xml:"shape,omitempty"`
	StrokeColor       *StrokeColor       `xml:"strokecolor,omitempty"`
	StrokeWeight      *StrokeWeight      `xml:"strokeweight,omitempty"`
	FillColor         *FillColor         `xml:"fillcolor,omitempty"`
	Height            *Height            `xml:"height,omitempty"`
	HeightUnit        *HeightUnit        `xml:"height_unit,omitempty"`
	LabelsOn          *LabelsOn          `xml:"labelson,omitempty"`
	ColorExtension    *ColorExtension    `xml:"color,omitempty"`
	Hierarchy         *Hierarchy         `xml:"hierarchy,omitempty"`
	LinkDetail        *DetailLink        `xml:"link,omitempty"`
	UserIcon          *UserIcon          `xml:"usericon,omitempty"`
	UID               *UID               `xml:"uid,omitempty"`
	Bullseye          *Bullseye          `xml:"bullseye,omitempty"`
	RouteInfo         *RouteInfo         `xml:"routeInfo,omitempty"`
	Marti             *Marti             `xml:"marti,omitempty"`
	Remarks           *Remarks           `xml:"remarks,omitempty"`
	Unknown           []RawMessage       `xml:"-"`
}

Detail contains additional information about an event

func (*Detail) MarshalXML added in v0.2.8

func (d *Detail) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

MarshalXML implements xml.Marshaler for Detail.

func (*Detail) UnmarshalXML added in v0.2.8

func (d *Detail) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

UnmarshalXML implements xml.Unmarshaler for Detail.

type DetailLink struct {
	Raw RawMessage
}

DetailLink represents the TAK link detail extension.

func (DetailLink) MarshalXML added in v0.3.1

func (dl DetailLink) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*DetailLink) UnmarshalXML added in v0.3.1

func (dl *DetailLink) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Emergency added in v0.3.1

type Emergency struct {
	Raw RawMessage
}

Emergency represents the TAK emergency extension.

func (Emergency) MarshalXML added in v0.3.1

func (e Emergency) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Emergency) UnmarshalXML added in v0.3.1

func (e *Emergency) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Environment added in v0.2.8

type Environment struct {
	Raw RawMessage
}

Environment represents the TAK environment extension.

func (Environment) MarshalXML added in v0.2.8

func (e Environment) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Environment) UnmarshalXML added in v0.2.8

func (e *Environment) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Event

type Event struct {
	XMLName xml.Name `xml:"event"`
	Version string   `xml:"version,attr"`
	Uid     string   `xml:"uid,attr"`
	Type    string   `xml:"type,attr"`
	How     string   `xml:"how,attr,omitempty"`
	Time    CoTTime  `xml:"time,attr"`
	Start   CoTTime  `xml:"start,attr"`
	Stale   CoTTime  `xml:"stale,attr"`
	// UnknownAttrs captures attributes that are not explicitly mapped to
	// struct fields when unmarshalling.
	UnknownAttrs []xml.Attr `xml:"-"`
	Point        Point      `xml:"point"`
	Detail       *Detail    `xml:"detail,omitempty"`
	Links        []Link     `xml:"link,omitempty"`
	// Message is populated for GeoChat events from the <remarks> element.
	Message string `xml:"-"`
	// StrokeColor is an ARGB hex color used for drawing events.
	StrokeColor string `xml:"strokeColor,attr,omitempty"`
	// UserIcon specifies a custom icon URL or resource for the event.
	UserIcon string `xml:"usericon,attr,omitempty"`
}

Event represents a CoT event message

func NewEvent

func NewEvent(uid, typ string, lat, lon, hae float64) (*Event, error)

NewEvent creates a new CoT event with the given parameters

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	// Create a new event with a friendly ground unit
	event, err := cotlib.NewEvent("test123", "a-f-G", 30.0, -85.0, 0.0)
	if err != nil {
		fmt.Printf("Error creating event: %v\n", err)
		return
	}

	// Add some details
	event.Detail = &cotlib.Detail{
		Contact: &cotlib.Contact{
			Callsign: "TEST-1",
		},
	}

	// Print event details
	fmt.Printf("Event Type: %s\n", event.Type)
	fmt.Printf("Location: %.2f, %.2f\n", event.Point.Lat, event.Point.Lon)
	fmt.Printf("Callsign: %s\n", event.Detail.Contact.Callsign)

}
Output:

Event Type: a-f-G
Location: 30.00, -85.00
Callsign: TEST-1

func NewPresenceEvent

func NewPresenceEvent(uid string, lat, lon, hae float64) (*Event, error)

NewPresenceEvent creates a new presence event (t-x-takp-v)

func UnmarshalXMLEvent

func UnmarshalXMLEvent(ctx context.Context, data []byte) (*Event, error)

UnmarshalXMLEvent parses an XML byte slice into an Event. The returned Event is obtained from an internal pool; callers should release it with ReleaseEvent when finished. The function uses the standard library's encoding/xml Decoder under the hood.

func UnmarshalXMLEventCtx added in v0.2.8

func UnmarshalXMLEventCtx(ctx context.Context, data []byte) (*Event, error)

UnmarshalXMLEventCtx parses an XML byte slice into an Event using the provided context for logging. The returned Event is obtained from an internal pool and must be released with ReleaseEvent when finished.

func (e *Event) AddLink(link *Link)

AddLink adds a link to the event

func (e *Event) AddValidatedLink(uid, linkType, relation string) error

AddValidatedLink adds a link to the event after validating the relation and type. It returns an error if called on a nil Event.

func (*Event) InjectIdentity

func (e *Event) InjectIdentity(selfUid, groupName, groupRole string)

InjectIdentity adds identity information to the event

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	// Create a new event
	event, err := cotlib.NewEvent("test123", "a-f-G", 30.0, -85.0, 0.0)
	if err != nil {
		fmt.Printf("Error creating event: %v\n", err)
		return
	}

	// Inject identity information
	event.InjectIdentity("self123", "Blue", "HQ")

	// Print identity details
	if event.Detail != nil && event.Detail.Group != nil {
		fmt.Printf("Group: %s\n", event.Detail.Group.Name)
		fmt.Printf("Role: %s\n", event.Detail.Group.Role)
	}

}
Output:

Group: Blue
Role: HQ

func (*Event) Is

func (e *Event) Is(pred string) bool

Is checks if the event matches a predicate

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	// Create a friendly ground unit event
	event, err := cotlib.NewEvent("test123", "a-f-G", 30.0, -85.0, 0.0)
	if err != nil {
		fmt.Printf("Error creating event: %v\n", err)
		return
	}

	// Check various predicates
	fmt.Printf("Is friendly: %v\n", event.Is("friend"))
	fmt.Printf("Is hostile: %v\n", event.Is("hostile"))
	fmt.Printf("Is ground: %v\n", event.Is("ground"))
	fmt.Printf("Is air: %v\n", event.Is("air"))

}
Output:

Is friendly: true
Is hostile: false
Is ground: true
Is air: false

func (*Event) ToXML

func (e *Event) ToXML() ([]byte, error)

ToXML serialises an Event to CoT-compliant XML. Attribute values are escaped to prevent XML-injection. The <point> element is always emitted so that the zero coordinate (0° N 0° E) is representable.

func (*Event) UnmarshalXML added in v0.3.8

func (e *Event) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

UnmarshalXML implements xml.Unmarshaler for Event. It preserves any attributes that are not recognised by storing them in UnknownAttrs.

func (*Event) Validate

func (e *Event) Validate() error

Validate checks if the event is valid

func (*Event) ValidateAt added in v0.2.6

func (e *Event) ValidateAt(now time.Time) error

ValidateAt checks if the event is valid using the provided reference time

type EventBuilder added in v0.3.7

type EventBuilder struct {
	// contains filtered or unexported fields
}

EventBuilder is a helper for constructing Event objects.

func NewEventBuilder added in v0.3.7

func NewEventBuilder(uid, typ string, lat, lon, hae float64) *EventBuilder

NewEventBuilder creates a new EventBuilder with the basic event fields set.

func (*EventBuilder) Build added in v0.3.7

func (b *EventBuilder) Build() (*Event, error)

Build validates and returns the constructed Event.

func (*EventBuilder) WithContact added in v0.3.7

func (b *EventBuilder) WithContact(c *Contact) *EventBuilder

WithContact sets the contact detail on the event.

func (*EventBuilder) WithGroup added in v0.3.7

func (b *EventBuilder) WithGroup(g *Group) *EventBuilder

WithGroup sets the group detail on the event.

func (*EventBuilder) WithStaleTime added in v0.3.7

func (b *EventBuilder) WithStaleTime(t time.Time) *EventBuilder

WithStaleTime sets a custom stale time for the event.

type FileShare added in v0.2.8

type FileShare struct {
	Raw RawMessage
}

FileShare represents the TAK fileshare extension.

func (FileShare) MarshalXML added in v0.2.8

func (f FileShare) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*FileShare) UnmarshalXML added in v0.2.8

func (f *FileShare) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type FillColor added in v0.2.8

type FillColor struct {
	Raw RawMessage
}

FillColor represents the TAK fillcolor extension.

func (FillColor) MarshalXML added in v0.2.8

func (fc FillColor) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*FillColor) UnmarshalXML added in v0.2.8

func (fc *FillColor) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Geofence added in v0.2.8

type Geofence struct {
	Raw RawMessage
}

Geofence represents the TAK __geofence extension.

func (Geofence) MarshalXML added in v0.2.8

func (g Geofence) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Geofence) UnmarshalXML added in v0.2.8

func (g *Geofence) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Group

type Group struct {
	Name string `xml:"name,attr"`
	Role string `xml:"role,attr"`
}

Group represents a group affiliation

type GroupExtension added in v0.2.8

type GroupExtension struct {
	Raw RawMessage
}

GroupExtension represents the TAK __group extension.

func (GroupExtension) MarshalXML added in v0.2.8

func (g GroupExtension) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*GroupExtension) UnmarshalXML added in v0.2.8

func (g *GroupExtension) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Height added in v0.3.1

type Height struct {
	Raw RawMessage
}

Height represents the TAK height extension.

func (Height) MarshalXML added in v0.3.1

func (h Height) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Height) UnmarshalXML added in v0.3.1

func (h *Height) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type HeightUnit added in v0.3.1

type HeightUnit struct {
	Raw RawMessage
}

HeightUnit represents the TAK height_unit extension.

func (HeightUnit) MarshalXML added in v0.3.1

func (hu HeightUnit) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*HeightUnit) UnmarshalXML added in v0.3.1

func (hu *HeightUnit) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Hierarchy added in v0.3.1

type Hierarchy struct {
	Raw RawMessage
}

Hierarchy represents the TAK hierarchy extension.

func (Hierarchy) MarshalXML added in v0.3.1

func (h Hierarchy) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Hierarchy) UnmarshalXML added in v0.3.1

func (h *Hierarchy) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type LabelsOn added in v0.2.8

type LabelsOn struct {
	Raw RawMessage
}

LabelsOn represents the TAK labelson extension.

func (LabelsOn) MarshalXML added in v0.2.8

func (lo LabelsOn) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*LabelsOn) UnmarshalXML added in v0.2.8

func (lo *LabelsOn) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error
type Link struct {
	Uid      string `xml:"uid,attr"`
	Type     string `xml:"type,attr"`
	Relation string `xml:"relation,attr"`
}

Link represents a relationship to another event

type Marti added in v0.3.6

type Marti struct {
	XMLName xml.Name    `xml:"marti"`
	Dest    []MartiDest `xml:"dest"`
}

Marti represents the TAK marti extension containing destination callsigns.

type MartiDest added in v0.3.6

type MartiDest struct {
	Callsign string `xml:"callsign,attr,omitempty"`
}

MartiDest represents a destination callsign within a Marti extension.

type Mission added in v0.2.8

type Mission struct {
	Raw RawMessage
}

Mission represents the TAK mission extension.

func (Mission) MarshalXML added in v0.2.8

func (m Mission) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Mission) UnmarshalXML added in v0.2.8

func (m *Mission) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Point

type Point struct {
	Lat float64 `xml:"lat,attr"` // Latitude in degrees
	Lon float64 `xml:"lon,attr"` // Longitude in degrees
	Hae float64 `xml:"hae,attr"` // Height above ellipsoid in meters
	Ce  float64 `xml:"ce,attr"`  // Circular error in meters
	Le  float64 `xml:"le,attr"`  // Linear error in meters
}

Point represents a location in 3D space with error estimates

func (*Point) Validate

func (p *Point) Validate() error

Validate checks if the point coordinates and errors are valid

type PrecisionLocation added in v0.2.8

type PrecisionLocation struct {
	Raw RawMessage
}

PrecisionLocation represents the TAK precisionlocation extension.

func (PrecisionLocation) MarshalXML added in v0.2.8

func (p PrecisionLocation) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*PrecisionLocation) UnmarshalXML added in v0.2.8

func (p *PrecisionLocation) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type RawMessage added in v0.2.8

type RawMessage []byte

RawMessage represents raw XML data preserved during decoding.

type Remarks added in v0.2.8

type Remarks struct {
	XMLName  xml.Name   `xml:"remarks"`
	Source   string     `xml:"source,attr,omitempty"`
	SourceID string     `xml:"sourceID,attr,omitempty"`
	To       string     `xml:"to,attr,omitempty"`
	Time     CoTTime    `xml:"time,attr,omitempty"`
	Text     string     `xml:",chardata"`
	Raw      RawMessage `xml:"-"`
}

Remarks represents the TAK remarks extension. Remarks represents the TAK remarks extension. It preserves the original XML while also allowing convenient access to common attributes and the text payload.

func (Remarks) MarshalXML added in v0.2.8

func (r Remarks) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Remarks) Parse added in v0.3.6

func (r *Remarks) Parse() error

Parse fills the Remarks fields from Raw if present. It is safe to call multiple times.

func (*Remarks) UnmarshalXML added in v0.2.8

func (r *Remarks) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type RouteInfo added in v0.2.8

type RouteInfo struct {
	Raw RawMessage
}

RouteInfo represents the TAK routeInfo extension.

func (RouteInfo) MarshalXML added in v0.2.8

func (ri RouteInfo) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*RouteInfo) UnmarshalXML added in v0.2.8

func (ri *RouteInfo) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type ServerDestination added in v0.2.8

type ServerDestination struct {
	Raw RawMessage
}

ServerDestination represents the TAK __serverdestination extension.

func (ServerDestination) MarshalXML added in v0.2.8

func (sd ServerDestination) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*ServerDestination) UnmarshalXML added in v0.2.8

func (sd *ServerDestination) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Shape added in v0.2.8

type Shape struct {
	Raw RawMessage
}

Shape represents the TAK shape extension.

func (Shape) MarshalXML added in v0.2.8

func (s Shape) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Shape) UnmarshalXML added in v0.2.8

func (s *Shape) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Status added in v0.2.8

type Status struct {
	Raw RawMessage
}

Status represents the TAK status extension.

func (Status) MarshalXML added in v0.2.8

func (s Status) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Status) UnmarshalXML added in v0.2.8

func (s *Status) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type StrokeColor added in v0.2.8

type StrokeColor struct {
	Raw RawMessage
}

StrokeColor represents the TAK strokecolor extension.

func (StrokeColor) MarshalXML added in v0.2.8

func (sc StrokeColor) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*StrokeColor) UnmarshalXML added in v0.2.8

func (sc *StrokeColor) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type StrokeWeight added in v0.2.8

type StrokeWeight struct {
	Raw RawMessage
}

StrokeWeight represents the TAK strokeweight extension.

func (StrokeWeight) MarshalXML added in v0.2.8

func (sw StrokeWeight) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*StrokeWeight) UnmarshalXML added in v0.2.8

func (sw *StrokeWeight) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Takv added in v0.2.8

type Takv struct {
	Raw RawMessage
}

Takv represents the TAK takv extension.

func (Takv) MarshalXML added in v0.2.8

func (t Takv) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Takv) UnmarshalXML added in v0.2.8

func (t *Takv) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Track added in v0.2.8

type Track struct {
	Raw RawMessage
}

Track represents the TAK track extension.

func (Track) MarshalXML added in v0.2.8

func (t Track) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Track) UnmarshalXML added in v0.2.8

func (t *Track) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type UID added in v0.3.1

type UID struct {
	Raw RawMessage
}

UID represents the TAK uid extension.

func (UID) MarshalXML added in v0.3.1

func (u UID) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*UID) UnmarshalXML added in v0.3.1

func (u *UID) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type UserIcon added in v0.2.8

type UserIcon struct {
	Raw RawMessage
}

UserIcon represents the TAK usericon extension.

func (UserIcon) MarshalXML added in v0.2.8

func (ui UserIcon) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*UserIcon) UnmarshalXML added in v0.2.8

func (ui *UserIcon) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

type Video added in v0.2.8

type Video struct {
	Raw RawMessage
}

Video represents the TAK __video extension.

func (Video) MarshalXML added in v0.2.8

func (v Video) MarshalXML(enc *xml.Encoder, start xml.StartElement) error

func (*Video) UnmarshalXML added in v0.2.8

func (v *Video) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error

Directories

Path Synopsis
cmd
cotgen command
logcheck command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL