diff --git a/docs/instantel_protocol_reference.md b/docs/instantel_protocol_reference.md index ae6e856..8202c7a 100644 --- a/docs/instantel_protocol_reference.md +++ b/docs/instantel_protocol_reference.md @@ -63,6 +63,10 @@ | 2026-03-31 | §14.2 | **CORRECTED — Sierra Wireless RV50/RV55 sends `RING`/`CONNECT` over TCP to caller even with Quiet Mode enabled.** Quiet Mode suppresses these only on the serial port (protecting the MiniMate). TCP client still receives `\r\nRING\r\n\r\nCONNECT\r\n` prefixed before the first S3 frame bytes. Parser handles correctly by scanning for DLE+STX (`0x10 0x02`) and discarding prefix bytes. Previous note "no CONNECT string" described Raven X ENQ-disable behaviour; RV50/RV55 differ. | | 2026-03-31 | §7.3 | **NEW — Calibration date field confirmed** at Full Config (SUB FE) destuffed payload offsets 0x53–0x57. Two-unit comparison: BE18189 (calibrated 2023) has `07 E7` at 0x56–0x57; BE11529 (calibrated 2025) has `07 E9`. Bytes 0x56–0x57 = uint16 BE calibration year ✅ CONFIRMED. Adjacent bytes at 0x53–0x55 likely encode month/day (both units show `0x10` at offset 0x54 = BCD October; 0x53 and 0x55 differ between units). Full date layout 🔶 INFERRED — pending third-unit capture or recalibration diff. Resolves open question. | | 2026-03-31 | §9 | **CONFIRMED via Console cold-start capture** — `"Operating System"` (16 B: `4f 70 65 72 61 74 69 6e 67 20 53 79 73 74 65 6d`) arrives as first TCP bytes on cold-connect before unit enters DLE-framed mode. `TcpTransport` + retry logic handles gracefully: first attempt times out waiting for SUB A4; second connect (after unit fully booted) succeeds. | +| 2026-04-01 | §7.7.5, §8 | **CONFIRMED — Full waveform record (0C) timestamp layout** cross-referenced against Blastware event report for BE11529 thump event ("00:28:12 April 1, 2026"). 9-byte format at bytes[0–8]: `[day][sub_code][month][year:2 BE][unknown][hour][min][sec]`. All fields verified. Sub_code `0x10` = Waveform (continuous/single-shot). **Previous 7-byte format doc was wrong** — replaced with confirmed 9-byte layout. | +| 2026-04-01 | §7.7.5 | **CONFIRMED — Record type** encoded in byte[1] (sub_code), not as ASCII string. `0x10` = Waveform ✅. Histogram sub_code not yet captured. ASCII string search approach removed. | +| 2026-04-01 | §7.7.5, §14 | **CONFIRMED — Per-channel PPV** at label+6 (✅ all four channels), cross-referenced vs Blastware: Tran=0.420, Vert=3.870, Long=0.495 in/s. **CONFIRMED — Peak Vector Sum** at fixed offset 87 = 3.906 in/s ✅ matches Blastware "Peak Vector Sum". Is √(Tran²+Vert²+Long²) at max instantaneous vector moment, not vector sum of per-channel peaks. Open question "offset 87 purpose" closed. | +| 2026-04-01 | §8 | **RESOLVED — §8 unknown byte at offset 3.** Field is confirmed absent in the 9-byte waveform record format (no such field). The 6-byte event-index format has a separator byte at [3] whose purpose remains ❓ but is no longer actively blocking anything. | --- @@ -211,7 +215,7 @@ Step 4 — Device sends actual data payload: | `1C` | **TRIGGER CONFIG READ** | Requests trigger settings block (0x2C bytes). | ✅ CONFIRMED | | `1E` | **EVENT HEADER READ** | Gets the first waveform key (4-byte opaque record address). All-zero params; key returned at data[11:15]. | ✅ CONFIRMED 2026-03-31 | | `0A` | **WAVEFORM HEADER READ** | Checks record type for a given waveform key. Variable DATA_LENGTH: 0x30=full bin, 0x26=partial bin. Key at params[4..7]. | ✅ CONFIRMED 2026-03-31 | -| `0C` | **FULL WAVEFORM RECORD** | Downloads 210-byte waveform/histogram record. Contains record type, PPV floats (at channel label+6), project strings, 7-byte timestamp. Key at params[4..7], DATA_LENGTH=0xD2. | ✅ CONFIRMED 2026-03-31 | +| `0C` | **FULL WAVEFORM RECORD** | Downloads 210-byte waveform/histogram record. 9-byte timestamp at bytes[0–8]; record sub_code at byte[1] (0x10=Waveform); PPV floats at channel label+6; Peak Vector Sum float at offset 87; project strings. Key at params[4..7], DATA_LENGTH=0xD2. | ✅ CONFIRMED 2026-04-01 | | `1F` | **EVENT ADVANCE** | Advances to next waveform key. Token byte at params[6]: 0x00=browse (one step), 0xFE=download (skip partial bins). Returns next key at data[11:15]; zeros = no more events. | ✅ CONFIRMED 2026-03-31 | | `5A` | **BULK WAVEFORM STREAM** | Initiates bulk download of raw ADC sample data, keyed by waveform key. Large multi-page transfer. | ✅ CONFIRMED | | `24` | **WAVEFORM PAGE A?** | Paged waveform read, possibly channel group A. | 🔶 INFERRED | @@ -775,26 +779,46 @@ Actual data lengths: #### 7.7.5 Waveform Record Layout (210 bytes, SUB F3 → response F3) +> ✅ **Updated 2026-04-01** — Full timestamp layout confirmed against Blastware +> event report (BE11529 thump event, "00:28:12 April 1, 2026"). Record type +> encoding corrected (byte[1], not ASCII string search). Peak Vector Sum field +> confirmed at fixed offset 87. + The 210-byte record (`data_rsp.data[11:11+0xD2]`) contains: -**Record type string** (search at variable offset): -- `"Histogram"` — histogram mode recording -- `"Waveform"` — single-shot waveform recording - -**Timestamp** (7-byte format, confirmed from 3-31-26 capture): +**Header / Timestamp** (9 bytes at offsets 0–8, ✅ CONFIRMED 2026-04-01): ``` -byte 0: 0x09 (magic/type marker) -bytes 1–2: year (uint16 big-endian) -byte 3: 0x00 -byte 4: hour -byte 5: minute -byte 6: second +byte[0]: day (uint8) +byte[1]: sub_code 0x10 = Waveform (continuous/single-shot) ✅ + histogram code not yet captured ❓ +byte[2]: month (uint8) +bytes[3–4]: year (uint16 big-endian) +byte[5]: unknown (0x00 in all observed samples ❓) +byte[6]: hour (uint8) +byte[7]: minute (uint8) +byte[8]: second (uint8) ``` -> ❓ Month and day are not present in the waveform record timestamp. -> Month/day may appear in the event index (SUB F7) or a separate header -> field not yet confirmed. -**Peak particle velocity floats** (✅ CONFIRMED 2026-03-31): +Thump event raw bytes (2026-04-01 00:28:12): +``` +01 10 04 07 ea 00 00 1c 0c +↑ ↑ ↑ ↑──↑ ↑ ↑ ↑ ↑ +d=1 sub m=4 y=2026 ? h=0 m=28 s=12 +``` + +Cross-referenced against the `.MLG` file for the same event, which stores an +8-byte timestamp at two offsets (trigger time and end time): +``` +MLG format: [day:1][month:1][year:2 LE][?:1][hour:1][min:1][sec:1] +01 04 ea 07 00 00 1c 0c → trigger at April 1, 2026 00:28:12 +01 04 ea 07 00 00 1c 0f → end time April 1, 2026 00:28:15 (3.0 s record time ✅) +``` + +**Record type** — encoded in `byte[1]` (sub_code), NOT as an ASCII string: +- `0x10` → `"Waveform"` (continuous / single-shot mode) ✅ +- histogram sub_code: not yet confirmed — capture a histogram event with `debug=true` + +**Peak particle velocity floats** (✅ CONFIRMED 2026-03-31, re-confirmed 2026-04-01): Channel labels `"Tran"`, `"Vert"`, `"Long"`, `"MicL"` are embedded as ASCII strings at variable offsets within the record. The PPV float for @@ -803,26 +827,42 @@ each channel is at `label_offset + 6` (IEEE 754 big-endian float32). The floats are **NOT 4-byte aligned** — Tran, Long, and MicL all fall at non-aligned offsets. The previous heuristic step-4 scanner missed all three. -Example from 3-31-26 capture: +Confirmed offsets from thump event (2026-04-01, cross-referenced vs Blastware): ``` -"Tran" at offset N → float at N+6 = 0.0916 in/s -"Vert" at offset M → float at M+6 = 0.0907 in/s -"Long" at offset P → float at P+6 = 0.0605 in/s -"MicL" at offset Q → float at Q+6 = 0.000145 psi +"Tran" at offset 99 → float at 105 = 0x3ED70A2D = 0.420 in/s ✅ Blastware: 0.420 +"Vert" at offset 114 → float at 120 = 0x4077AE01 = 3.870 in/s ✅ Blastware: 3.870 +"Long" at offset 129 → float at 135 = 0x3EFD7090 = 0.495 in/s ✅ Blastware: 0.495 +"MicL" at offset 144 → float at 150 = 0x3985114E = 0.000254 psi ``` Channel labels are separated by inner-frame bytes `10 03` (DLE ETX), preserved as literal data by `S3FrameParser`. +**Peak Vector Sum** (✅ CONFIRMED 2026-04-01): +``` +Offset 87: IEEE 754 big-endian float32 + = √(Tran² + Vert² + Long²) at the sample instant of maximum + combined geo motion + NOT the vector sum of the three per-channel peaks (those may + occur at different sample times) + +Thump event: 0x4079F6C5 = 3.906 in/s ✅ matches Blastware "Peak Vector Sum: 3.906 in/s" +Near-ambient: 0x3C75C28F = 0.015 in/s (histogram event, near-zero ambient) +``` + **Project strings** — ASCII label-value pairs (search for label, read null-terminated value): ``` -"Project:" → project description -"Client:" → client name ✅ offset confirmed -"User Name:" → operator / user -"Seis Loc:" → sensor location +"Project:" → project description (present in 0C record ✅) +"Client:" → client name (NOT in 0C; comes from compliance config SUB 1A/E5 ❓) +"User Name:" → operator / user (NOT confirmed in 0C ❓) +"Seis Loc:" → sensor location (NOT confirmed in 0C ❓) "Extended Notes"→ notes field ``` +> ❓ **Clarification needed:** Only "Project:" has been confirmed in the 210-byte 0C record. +> "Client:", "User Name:", and "Seis Loc:" appear in the Blastware event report but their +> source in the protocol (0C vs SUB 1A/E5 compliance config) is not yet confirmed. + --- #### 7.7.6 Complete Download Loop (Python pseudocode) @@ -854,28 +894,54 @@ return events --- ## 8. Timestamp Format + +Two timestamp wire formats are used: + +### 8.1 6-byte format (event index / 1E header) > 🔶 **Updated 2026-02-26** — Year field resolved. Confidence upgraded. -Timestamps are 6-byte sequences appearing in event headers and waveform keys. +Appears in event index blocks. Time-of-day fields (hour/min/sec) are absent. **Observed example:** ``` 01 07 CB 00 06 1E ``` -**Decoded:** - | Byte(s) | Value | Meaning | Certainty | |---|---|---|---| | `01` | 1 | Record validity / type flag | 🔶 INFERRED | | `07 CB` | 1995 | Year — 16-bit big-endian integer | ✅ CONFIRMED — 2026-02-26 | -| `00` | 0 | Unknown — possibly hours, minutes, or padding | ❓ SPECULATIVE | +| `00` | 0 | Unknown separator | ❓ | | `06` | 6 | Month (June) | ✅ CONFIRMED | | `1E` | 30 | Day (0x1E = 30 decimal) | ✅ CONFIRMED | > ✅ **2026-02-26 — CONFIRMED:** The year 1995 is the **MiniMate Plus factory default RTC date**, which the device reverts to whenever the internal battery is disconnected or the real-time clock loses power. Any event timestamped around 1995 means the clock was not set. This is known device behavior, not an encoding anomaly. -> ❓ **Still unknown:** The `00` byte at offset 3. Likely encodes time-of-day (hours or minutes). Needs a capture with a precisely known event time to decode. +### 8.2 9-byte format (Full Waveform Record / SUB 0C, bytes 0–8) +> ✅ **CONFIRMED 2026-04-01** — Cross-referenced against Blastware event report +> for BE11529 thump event: "00:28:12 April 1, 2026". + +Full date + time, including a sub_code byte that encodes the recording mode. + +**Observed example (thump event, 2026-04-01):** +``` +01 10 04 07 ea 00 00 1c 0c +``` + +| Byte(s) | Value | Meaning | Certainty | +|---|---|---|---| +| `01` | 1 | Day | ✅ | +| `10` | 0x10 | Sub_code: `0x10` = Waveform (continuous mode) | ✅ / histogram code ❓ | +| `04` | 4 | Month (April) | ✅ | +| `07 ea` | 2026 | Year — 16-bit big-endian integer | ✅ | +| `00` | 0 | Unknown separator | ❓ | +| `00` | 0 | Hour | ✅ | +| `1c` | 28 | Minute | ✅ | +| `0c` | 12 | Second | ✅ | + +The `.MLG` file for the same event stores the timestamp in a different binary +representation (little-endian year, no sub_code byte), confirming the waveform +record and the saved file use distinct serialisation formats. --- @@ -1258,7 +1324,7 @@ The `.bin` files produced by `s3_bridge` are **not raw wire bytes**. The logger | Question | Priority | Added | Notes | |---|---|---|---| -| Byte at timestamp offset 3 — hours, minutes, or padding? | MEDIUM | 2026-02-26 | | +| Timestamp 6-byte format byte[3] — purpose of the separator `0x00` byte | LOW | 2026-02-26 | Not blocking; 9-byte waveform record format (§8.2) fully confirmed without this byte. | | `trail[0]` in serial number response — unit-specific byte, derivation unknown. `trail[1]` resolved as firmware minor version. | MEDIUM | 2026-02-26 | | | Full channel ID mapping in SUB `5A` stream (01/02/03/04 → which sensor?) | MEDIUM | 2026-02-26 | | | Exact byte boundaries of project string fields in SUB `71` write frame — padding rules unconfirmed | MEDIUM | 2026-02-26 | | diff --git a/minimateplus/client.py b/minimateplus/client.py index 7bc95f9..e4e13f8 100644 --- a/minimateplus/client.py +++ b/minimateplus/client.py @@ -394,25 +394,30 @@ def _decode_waveform_record_into(data: bytes, event: Event) -> None: The *data* argument is the raw record bytes returned by MiniMateProtocol.read_waveform_record() — i.e. data_rsp.data[11:11+0xD2]. - Extracts: - - record_type: "Histogram" or "Waveform" (string search) 🔶 - - peak_values: label-based float32 lookup (confirmed ✅) - - project_info: "Project:", "Client:", etc. string search ✅ - - Timestamp in the waveform record: - 7-byte format: [0x09][year:2 BE][0x00][hour][minute][second] - Month and day come from a separate source (not yet fully mapped ❓). - For now we leave event.timestamp as None. + Extracts (all ✅ confirmed 2026-04-01 against Blastware event report): + - timestamp: 9-byte format at bytes [0:9] + - record_type: sub_code at byte[1] (0x10 = "Waveform") + - peak_values: label-based float32 at label+6 for Tran/Vert/Long/MicL + - peak_vector_sum: IEEE 754 BE float at offset 87 + - project_info: "Project:", "Client:", etc. string search Modifies event in-place. """ + # ── Timestamp ───────────────────────────────────────────────────────────── + # 9-byte format: [day][sub_code][month][year:2 BE][unknown][hour][min][sec] + try: + event.timestamp = Timestamp.from_waveform_record(data) + except Exception as exc: + log.warning("waveform record timestamp decode failed: %s", exc) + # ── Record type ─────────────────────────────────────────────────────────── + # Decoded from byte[1] (sub_code), not from ASCII string search try: event.record_type = _extract_record_type(data) except Exception as exc: log.warning("waveform record type decode failed: %s", exc) - # ── Peak values ─────────────────────────────────────────────────────────── + # ── Peak values (per-channel PPV + Peak Vector Sum) ─────────────────────── try: peak_values = _extract_peak_floats(data) if peak_values: @@ -431,14 +436,24 @@ def _decode_waveform_record_into(data: bytes, event: Event) -> None: def _extract_record_type(data: bytes) -> Optional[str]: """ - Search the waveform record for a record-type indicator string. + Decode the recording mode from byte[1] of the 210-byte waveform record. - Confirmed types from 3-31-26 capture: "Histogram", "Waveform". - Returns the first match, or None if neither is found. + Byte[1] is the sub-record code that immediately follows the day byte in the + 9-byte timestamp header at the start of each waveform record: + [day:1] [sub_code:1] [month:1] [year:2 BE] ... + + Confirmed codes (✅ 2026-04-01): + 0x10 → "Waveform" (continuous / single-shot mode) + + Histogram mode code is not yet confirmed — a histogram event must be + captured with debug=true to identify it. Returns None for unknown codes. """ - for rtype in (b"Histogram", b"Waveform"): - if data.find(rtype) >= 0: - return rtype.decode() + if len(data) < 2: + return None + code = data[1] + if code == 0x10: + return "Waveform" + # TODO: add histogram sub_code once a histogram event is captured with debug=true return None @@ -486,11 +501,23 @@ def _extract_peak_floats(data: bytes) -> Optional[PeakValues]: if not vals: return None + # ── Peak Vector Sum — fixed offset 87 (✅ confirmed 2026-04-01) ─────────── + # = √(Tran² + Vert² + Long²) at the sample instant of maximum combined geo + # motion, NOT the vector sum of the three per-channel peak values (which may + # occur at different times). Matches Blastware "Peak Vector Sum" exactly. + pvs: Optional[float] = None + if len(data) > 91: + try: + pvs = struct.unpack_from(">f", data, 87)[0] + except struct.error: + pass + return PeakValues( tran=vals.get("tran"), vert=vals.get("vert"), long=vals.get("long_"), micl=vals.get("micl"), + peak_vector_sum=pvs, ) diff --git a/minimateplus/models.py b/minimateplus/models.py index 93d3a2c..0e78350 100644 --- a/minimateplus/models.py +++ b/minimateplus/models.py @@ -24,30 +24,52 @@ from typing import Optional @dataclass class Timestamp: """ - 6-byte event timestamp decoded from the MiniMate Plus wire format. + Event timestamp decoded from the MiniMate Plus wire format. - Wire layout: [flag:1] [year:2 BE] [unknown:1] [month:1] [day:1] + Two source formats exist: + + 1. 6-byte format (from event index / 1E header — not yet decoded in client): + [flag:1] [year:2 BE] [unknown:1] [month:1] [day:1] + Use Timestamp.from_bytes(). + + 2. 9-byte format (from Full Waveform Record / 0C, bytes 0–8) ✅ CONFIRMED: + [day:1] [sub_code:1] [month:1] [year:2 BE] [unknown:1] [hour:1] [min:1] [sec:1] + Use Timestamp.from_waveform_record(). + + Confirmed 2026-04-01 against Blastware event report (BE11529 thump event): + raw bytes: 01 10 04 07 ea 00 00 1c 0c + → day=1, sub_code=0x10 (Waveform mode), month=4, year=2026, + hour=0, minute=28, second=12 ← matches Blastware "00:28:12 April 1, 2026" + + The sub_code at byte[1] is the record-mode indicator: + 0x10 → Waveform (continuous / single-shot) ✅ + other → Histogram (code not yet captured ❓) The year 1995 is the device's factory-default RTC date — it appears whenever the battery has been disconnected. Treat 1995 as "clock not set". """ - raw: bytes # raw 6-byte sequence for round-tripping - flag: int # byte 0 — validity/type flag (usually 0x01) 🔶 - year: int # bytes 1–2 big-endian uint16 ✅ - unknown_byte: int # byte 3 — likely hours/minutes ❓ - month: int # byte 4 ✅ - day: int # byte 5 ✅ + raw: bytes # raw bytes for round-tripping + flag: int # byte 0 of 6-byte format, or sub_code from 9-byte format + year: int # ✅ + unknown_byte: int # separator byte (purpose unclear ❓) + month: int # ✅ + day: int # ✅ + + # Time fields — populated only from the 9-byte waveform-record format + hour: Optional[int] = None # ✅ (waveform record format) + minute: Optional[int] = None # ✅ (waveform record format) + second: Optional[int] = None # ✅ (waveform record format) @classmethod def from_bytes(cls, data: bytes) -> "Timestamp": """ - Decode a 6-byte timestamp sequence. + Decode a 6-byte timestamp (6-byte event-index format). Args: data: exactly 6 bytes from the device payload. Returns: - Decoded Timestamp. + Decoded Timestamp (no time fields). Raises: ValueError: if data is not exactly 6 bytes. @@ -68,6 +90,55 @@ class Timestamp: day=day, ) + @classmethod + def from_waveform_record(cls, data: bytes) -> "Timestamp": + """ + Decode a 9-byte timestamp from the first bytes of a 210-byte waveform + record (SUB 0C / Full Waveform Record response). + + Wire layout (✅ CONFIRMED 2026-04-01 against Blastware event report): + byte[0]: day (uint8) + byte[1]: sub_code / mode flag (0x10 = Waveform mode) 🔶 + byte[2]: month (uint8) + bytes[3–4]: year (big-endian uint16) + byte[5]: unknown (0x00 in all observed samples ❓) + byte[6]: hour (uint8) + byte[7]: minute (uint8) + byte[8]: second (uint8) + + Args: + data: at least 9 bytes; only the first 9 are consumed. + + Returns: + Decoded Timestamp with hour/minute/second populated. + + Raises: + ValueError: if data is fewer than 9 bytes. + """ + if len(data) < 9: + raise ValueError( + f"Waveform record timestamp requires at least 9 bytes, got {len(data)}" + ) + day = data[0] + sub_code = data[1] # 0x10 = Waveform; histogram code not yet confirmed + month = data[2] + year = struct.unpack_from(">H", data, 3)[0] + unknown_byte = data[5] + hour = data[6] + minute = data[7] + second = data[8] + return cls( + raw=bytes(data[:9]), + flag=sub_code, + year=year, + unknown_byte=unknown_byte, + month=month, + day=day, + hour=hour, + minute=minute, + second=second, + ) + @property def clock_set(self) -> bool: """False when year == 1995 (factory default / battery-lost state).""" @@ -76,7 +147,10 @@ class Timestamp: def __str__(self) -> str: if not self.clock_set: return f"CLOCK_NOT_SET ({self.year}-{self.month:02d}-{self.day:02d})" - return f"{self.year}-{self.month:02d}-{self.day:02d}" + date_str = f"{self.year}-{self.month:02d}-{self.day:02d}" + if self.hour is not None: + return f"{date_str} {self.hour:02d}:{self.minute:02d}:{self.second:02d}" + return date_str # ── Device identity ─────────────────────────────────────────────────────────── @@ -136,15 +210,28 @@ class ChannelConfig: @dataclass class PeakValues: """ - Per-channel peak particle velocity / pressure for a single event. + Per-channel peak particle velocity / pressure for a single event, plus the + scalar Peak Vector Sum. - Extracted from the Full Waveform Record (SUB F3), stored as IEEE 754 - big-endian floats in the device's native units (in/s / psi). + Extracted from the Full Waveform Record (SUB F3 / 0C response), stored as + IEEE 754 big-endian floats in the device's native units (in/s / psi). + + Per-channel PPV location (✅ CONFIRMED 2026-04-01): + Found by searching for the 4-byte channel label string ("Tran", "Vert", + "Long", "MicL") and reading the float at label_offset + 6. + + Peak Vector Sum (✅ CONFIRMED 2026-04-01): + Fixed offset 87 in the 210-byte record. + = √(Tran² + Vert² + Long²) at the sample instant of maximum combined + geo motion. NOT the vector sum of the three per-channel peak values + (those may occur at different times). + Matches Blastware's "Peak Vector Sum" display exactly. """ - tran: Optional[float] = None # Transverse PPV (in/s) ✅ - vert: Optional[float] = None # Vertical PPV (in/s) ✅ - long: Optional[float] = None # Longitudinal PPV (in/s) ✅ - micl: Optional[float] = None # Air overpressure (psi) 🔶 (units uncertain) + tran: Optional[float] = None # Transverse PPV (in/s) ✅ + vert: Optional[float] = None # Vertical PPV (in/s) ✅ + long: Optional[float] = None # Longitudinal PPV (in/s) ✅ + micl: Optional[float] = None # Air overpressure (psi) 🔶 (units uncertain) + peak_vector_sum: Optional[float] = None # Scalar geo PVS (in/s) ✅ # ── Project / operator metadata ─────────────────────────────────────────────── diff --git a/sfm/server.py b/sfm/server.py index 63d59fb..b111460 100644 --- a/sfm/server.py +++ b/sfm/server.py @@ -83,11 +83,14 @@ def _serialise_timestamp(ts: Optional[Timestamp]) -> Optional[dict]: if ts is None: return None return { - "year": ts.year, - "month": ts.month, - "day": ts.day, + "year": ts.year, + "month": ts.month, + "day": ts.day, + "hour": ts.hour, + "minute": ts.minute, + "second": ts.second, "clock_set": ts.clock_set, - "display": str(ts), + "display": str(ts), } @@ -95,10 +98,11 @@ def _serialise_peak_values(pv: Optional[PeakValues]) -> Optional[dict]: if pv is None: return None return { - "tran_in_s": pv.tran, - "vert_in_s": pv.vert, - "long_in_s": pv.long, - "micl_psi": pv.micl, + "tran_in_s": pv.tran, + "vert_in_s": pv.vert, + "long_in_s": pv.long, + "micl_psi": pv.micl, + "peak_vector_sum": pv.peak_vector_sum, }