@@ -1498,6 +1498,141 @@ message CasevacReport {
14981498 * "Victor 6"). Capped tight in options.
14991499 */
15001500 string frequency = 15 ;
1501+
1502+ // --- v2.x medline extensions (tags 16–33) --------------------------------
1503+ //
1504+ // Fields 16+ cost a 2-byte tag instead of 1 byte, but they're usually
1505+ // sparse so the on-wire delta is modest when most stay unset. A fully
1506+ // populated CASEVAC with 13 free-text fields + 2 ZMIST entries can run
1507+ // 200-400 bytes compressed, i.e. potentially over the 237 B LoRa MTU.
1508+ // Callers that hit the MTU on the `compressWithRemarksFallback` path
1509+ // SHOULD strip the tier-2 situational fields (tags 28-32 + terrain_other_detail)
1510+ // before dropping the packet entirely. See README "CASEVAC tier-2 stripping".
1511+
1512+ /*
1513+ * Short title / MEDEVAC identifier (e.g. "EAGLE.15.181230"). Usually the
1514+ * same as the envelope callsign but ATAK sometimes carries a distinct
1515+ * ops-number here.
1516+ */
1517+ string title = 16 ;
1518+ /*
1519+ * Primary medline free-text — the single most clinically important line
1520+ * on a MEDLINE form (e.g. "2 urgent litter patients, smoke on approach").
1521+ * MUST be preserved under MTU pressure as long as any casevac is sent.
1522+ */
1523+ string medline_remarks = 17 ;
1524+
1525+ /*
1526+ * Line 3 (newer ATAK format): patient counts by precedence level.
1527+ * Coexists with the enum-style `precedence` field (tag 1) — older ATAK
1528+ * emits a single enum, newer ATAK emits these counts, and both can be
1529+ * set simultaneously. Senders populate whichever style(s) the source
1530+ * XML had; receivers prefer counts when non-zero.
1531+ */
1532+ uint32 urgent_count = 18 ;
1533+ uint32 urgent_surgical_count = 19 ;
1534+ uint32 priority_count = 20 ;
1535+ uint32 routine_count = 21 ;
1536+ uint32 convenience_count = 22 ;
1537+
1538+ /*
1539+ * Line 4 supplementary: free-text description of non-standard equipment
1540+ * (e.g. "Blood warmer"). Pairs with the `equipment_flags` bitfield.
1541+ */
1542+ string equipment_detail = 23 ;
1543+ /*
1544+ * Line 1 override: MGRS grid when distinct from the event anchor point
1545+ * (e.g. "34T CQ 12345 67890"). Event lat/lon/hae still carries the
1546+ * numeric location; this field preserves the exact MGRS string the
1547+ * medic entered.
1548+ */
1549+ string zone_protected_coord = 24 ;
1550+ /*
1551+ * Line 9 supplementary: slope direction (e.g. "N", "NE", "SSW") when
1552+ * `terrain_flags` bit 0 (slope) is set.
1553+ */
1554+ string terrain_slope_dir = 25 ;
1555+ /*
1556+ * Line 9 supplementary: free-text description of "other" terrain hazards
1557+ * (e.g. "Loose debris on west edge") when `terrain_flags` bit 5 (other)
1558+ * is set. Tier-2 strippable under MTU pressure.
1559+ */
1560+ string terrain_other_detail = 26 ;
1561+ /*
1562+ * Line 7 supplementary: how the zone is being marked right now
1563+ * (e.g. "Orange smoke", "VS-17 panel"). Complements the structured
1564+ * `hlz_marking` enum with a specific human-readable description.
1565+ */
1566+ string marked_by = 27 ;
1567+
1568+ // --- Tier-2 situational awareness (stripped first under MTU pressure) ---
1569+ // These fields are free-text context that helps the receiver plan the
1570+ // approach but aren't strictly required to evacuate the patient.
1571+
1572+ /*
1573+ * Nearby obstacles on the approach (e.g. "Power lines north of HLZ").
1574+ */
1575+ string obstacles = 28 ;
1576+ /*
1577+ * Wind direction and speed (e.g. "270 at 12 kts").
1578+ */
1579+ string winds_are_from = 29 ;
1580+ /*
1581+ * Friendly forces posture near the pickup zone
1582+ * (e.g. "Squad east of HLZ").
1583+ */
1584+ string friendlies = 30 ;
1585+ /*
1586+ * Known or suspected enemy positions near the pickup zone
1587+ * (e.g. "Possible enemy on south ridge").
1588+ */
1589+ string enemy = 31 ;
1590+ /*
1591+ * Free-text description of the HLZ itself
1592+ * (e.g. "Primary HLZ is soccer field").
1593+ */
1594+ string hlz_remarks = 32 ;
1595+
1596+ /*
1597+ * Per-patient clinical records. Each entry is one patient's ZMIST card
1598+ * (Zap number / Mechanism / Injuries / Signs / Treatment). Repeatable —
1599+ * a mass-casualty event can carry 1-6 entries in practice, limited by
1600+ * the 237 B LoRa MTU.
1601+ */
1602+ repeated ZMistEntry zmist = 33 ;
1603+ }
1604+
1605+ /*
1606+ * Per-patient clinical summary record — one entry per patient in a CASEVAC.
1607+ * Maps directly to ATAK's <zMist> child element inside <zMistsMap>.
1608+ * All fields are optional free-text; senders populate what they have.
1609+ */
1610+ message ZMistEntry {
1611+ /*
1612+ * Patient identifier / sequence label (e.g. "ZMIST-1", "ZMIST-2").
1613+ */
1614+ string title = 1 ;
1615+ /*
1616+ * Zap number — unique patient tracking ID (often a terse code like
1617+ * "Gunshot" or a serial).
1618+ */
1619+ string z = 2 ;
1620+ /*
1621+ * Mechanism of injury (e.g. "Penetrating trauma", "Blast injury").
1622+ */
1623+ string m = 3 ;
1624+ /*
1625+ * Injuries observed (e.g. "Left thigh", "Concussion").
1626+ */
1627+ string i = 4 ;
1628+ /*
1629+ * Signs / vital stats (e.g. "Stable", "Priority", "BP 110/70").
1630+ */
1631+ string s = 5 ;
1632+ /*
1633+ * Treatment given (e.g. "Tourniquet 1810Z", "O2 administered").
1634+ */
1635+ string t = 6 ;
15011636}
15021637
15031638/*
@@ -1587,6 +1722,114 @@ message TaskRequest {
15871722 string note = 6 ;
15881723}
15891724
1725+ /*
1726+ * Weather annotation from <environment> CoT detail element.
1727+ *
1728+ * Attaches to any TAKPacketV2 regardless of payload_variant — an Aircraft,
1729+ * PLI, or Marker can all carry observed conditions at the emitting station.
1730+ * ATAK-CIV ships an XSD for <environment> but no dedicated handler, so the
1731+ * element round-trips through the generic detail pipeline; this message
1732+ * promotes it to a first-class structured field.
1733+ *
1734+ * Target wire cost: ~6-8 bytes compressed with a fully populated instance.
1735+ *
1736+ * Named `TAKEnvironment` (not just `Environment`) because the bare name
1737+ * collides with `SwiftUI.Environment` — every SwiftUI view in a consuming
1738+ * iOS app uses the `@Environment` property wrapper, and importing the
1739+ * generated proto module would make `Environment` ambiguous in every one
1740+ * of those files. The `TAK` prefix matches the convention used by the
1741+ * outer `TAKPacketV2` wrapper and is unambiguous across all target
1742+ * languages (Swift, Kotlin, Python, TypeScript, C#).
1743+ */
1744+ message TAKEnvironment {
1745+ /*
1746+ * Temperature in deci-degrees Celsius. 225 = 22.5°C.
1747+ * Range covers -50°C to +50°C (-500 to +500) which spans every realistic
1748+ * outdoor TAK deployment. sint32 because negative temps are common in
1749+ * cold-weather ops.
1750+ */
1751+ sint32 temperature_c_x10 = 1 ;
1752+ /*
1753+ * Wind direction in whole degrees, 0-359. "Direction FROM" per
1754+ * meteorological convention (matches CoT / ATAK).
1755+ */
1756+ uint32 wind_direction_deg = 2 ;
1757+ /*
1758+ * Wind speed in cm/s. Matches the unit of TAKPacketV2.speed for
1759+ * consistency. 1200 = 12.00 m/s = ~27 mph.
1760+ */
1761+ uint32 wind_speed_cm_s = 3 ;
1762+ }
1763+
1764+ /*
1765+ * Sensor field-of-view cone from <sensor> CoT detail element.
1766+ *
1767+ * Encodes the 8 geometry attributes that ATAK-CIV's SensorDetailHandler
1768+ * reads from the wire; drops the 9 visual-styling attributes that are
1769+ * receiver-side render hints (fovAlpha, fovRed/Green/Blue, strokeColor,
1770+ * strokeWeight, displayMagneticReference, hideFov, fovLabels, rangeLines).
1771+ * The receiving ATAK client restores those from its own defaults, same as
1772+ * every other CoT carried over Meshtastic today.
1773+ *
1774+ * Attaches to any TAKPacketV2 — a PLI with a sensor on the operator's head,
1775+ * an Aircraft with a FLIR turret, a Marker dropped on a UAV.
1776+ * Target wire cost: ~7-14 bytes compressed (dominated by model string).
1777+ */
1778+ message SensorFov {
1779+ /*
1780+ * Coarse sensor category, inferred from `model` on parse when the source
1781+ * XML doesn't label it. Receivers that render differently per sensor
1782+ * class (thermal overlay vs daylight cone) use this.
1783+ */
1784+ enum SensorType {
1785+ SensorType_Unspecified = 0 ;
1786+ SensorType_Camera = 1 ; // daylight / general optical
1787+ SensorType_Thermal = 2 ; // FLIR, thermal imager
1788+ SensorType_Laser = 3 ; // rangefinder, LRF, designator
1789+ SensorType_Nvg = 4 ; // night vision goggles
1790+ SensorType_Rf = 5 ; // radio/radar direction-finding
1791+ SensorType_Other = 6 ;
1792+ }
1793+
1794+ SensorType type = 1 ;
1795+ /*
1796+ * Azimuth in whole degrees, 0-359. "Pointing direction" of the cone axis,
1797+ * measured clockwise from true north. Whole degrees match ATAK-CIV's
1798+ * SensorDetailHandler default (270°) and save varint bytes over centi-deg.
1799+ */
1800+ uint32 azimuth_deg = 2 ;
1801+ /*
1802+ * Maximum range of the cone in meters.
1803+ * Optional — if unset, receivers should use the ATAK-CIV default of 100m.
1804+ */
1805+ optional uint32 range_m = 3 ;
1806+ /*
1807+ * Horizontal field of view in whole degrees (cone's angular width).
1808+ * ATAK-CIV default is 45°.
1809+ */
1810+ uint32 fov_horizontal_deg = 4 ;
1811+ /*
1812+ * Vertical field of view in whole degrees. ATAK-CIV default is 45°.
1813+ * Optional — a value of 0 means "not set / use horizontal FOV".
1814+ */
1815+ uint32 fov_vertical_deg = 5 ;
1816+ /*
1817+ * Elevation angle in whole degrees. Positive = up, negative = down.
1818+ * Range -90 to +90. sint32 for varint efficiency on small negatives.
1819+ */
1820+ sint32 elevation_deg = 6 ;
1821+ /*
1822+ * Roll (camera tilt) in whole degrees, -180 to +180.
1823+ * Optional — use 0 if the sensor doesn't track roll.
1824+ */
1825+ sint32 roll_deg = 7 ;
1826+ /*
1827+ * Free-form device model identifier, e.g. "FLIR-Boson-640", "SEEK".
1828+ * Optional — empty string means "unknown model" (ATAK-CIV default).
1829+ */
1830+ string model = 8 ;
1831+ }
1832+
15901833/*
15911834 * ATAK v2 packet with expanded CoT field support and zstd dictionary compression.
15921835 * Sent on ATAK_PLUGIN_V2 port. The wire payload is:
@@ -1695,6 +1938,29 @@ message TAKPacketV2 {
16951938 * Empty string (proto3 default) means no remarks were present.
16961939 */
16971940 string remarks = 24 ;
1941+
1942+ // --- Sensor / environment annotations ----------------------------------
1943+ //
1944+ // Both fields are OPTIONAL and attach to any payload_variant. They
1945+ // describe observed conditions at the emitting station — a PLI with
1946+ // environment data, an Aircraft with a sensor cone, a Marker with both.
1947+ // Absent by default; presence is signaled by the message being non-null.
1948+
1949+ /*
1950+ * Observed weather conditions (temperature, wind). From <environment>.
1951+ * Type is `TAKEnvironment`, not `Environment`, to avoid colliding with
1952+ * SwiftUI's `@Environment` property wrapper in iOS consumers.
1953+ */
1954+ optional TAKEnvironment environment = 25 ;
1955+ /*
1956+ * Sensor field-of-view cone (camera, FLIR, laser, etc.). From <sensor>.
1957+ */
1958+ optional SensorFov sensor_fov = 26 ;
1959+
1960+ reserved 27 , 28 , 29 ;
1961+ // Tags 27, 28, 29 reserved for future top-level annotations before the
1962+ // payload_variant oneof resumes at 30.
1963+
16981964 /*
16991965 * The payload of the packet
17001966 */
0 commit comments