feat: implement geophone sensitivity and recording mode settings in compliance config
This commit is contained in:
@@ -4,6 +4,34 @@ All notable changes to seismo-relay are documented here.
|
||||
|
||||
---
|
||||
|
||||
## v0.12.2 — 2026-04-20
|
||||
|
||||
### Added / Fixed
|
||||
|
||||
- **Geophone sensitivity / maximum range field confirmed** — 4-20-26 geo sensitivity
|
||||
captures (1.25 in/s vs 10 in/s) diffed across all three SUB 71 write chunks and both
|
||||
E5 read payloads. The `geo_range` uint8 field per channel is now fully confirmed:
|
||||
- E5 read offset: `channel_label + 33`; SUB 71 write offset: `channel_label + 29`
|
||||
- `0x00` = Normal 10.000 in/s (standard gain); `0x01` = Sensitive 1.250 in/s (high gain)
|
||||
- **Correction:** previous hypothesis (`channel_label+20`, `0x01`=Normal) was wrong.
|
||||
`channel_label+20` reads `0x01` on ALL captures regardless of range — not this field.
|
||||
- `_decode_compliance_config_into`: read offset corrected from `tran_pos+20` → `tran_pos+33`
|
||||
- `_encode_compliance_config`: added `geo_range` parameter; writes to Tran/Vert/Long at `+29`
|
||||
- `apply_config`: added `geo_range` parameter
|
||||
- `POST /device/config`: added `geo_range` to `DeviceConfigBody`
|
||||
- Web UI Config tab: added "Maximum Range — Geo" select (Normal / Sensitive)
|
||||
- Web UI Device tab: added "Max Range (geo)" row to compliance table
|
||||
|
||||
- **`recording_mode` + `histogram_interval_sec` confirmed and implemented** (4-20-26 captures)
|
||||
- `recording_mode`: uint8 at anchor−8 (E5 read) / anchor−7 (write); enum: 0x00=Single Shot,
|
||||
0x01=Continuous, 0x03=Histogram, 0x04=Histogram+Continuous
|
||||
- `histogram_interval_sec`: uint16 BE seconds at anchor−4; same offset in read & write;
|
||||
valid: 2, 5, 15, 60, 300, 900 (matching Blastware dropdown: 2s, 5s, 15s, 1m, 5m, 15m)
|
||||
- Both fields added to `ComplianceConfig`, `_decode_compliance_config_into`,
|
||||
`_encode_compliance_config`, `apply_config`, REST API body, and web UI
|
||||
|
||||
---
|
||||
|
||||
## v0.12.1 — 2026-04-16
|
||||
|
||||
### Added
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Ground-up Python replacement for **Blastware**, Instantel's Windows-only software for
|
||||
managing MiniMate Plus seismographs. Connects over direct RS-232 or cellular modem
|
||||
(Sierra Wireless RV50 / RV55). Current version: **v0.12.1**.
|
||||
(Sierra Wireless RV50 / RV55). Current version: **v0.12.2**.
|
||||
|
||||
When new information about the protocol is discovered, please update the instantel_protocol_reference.md with the findings in addition to this document
|
||||
|
||||
@@ -372,8 +372,8 @@ Do NOT use fixed absolute offsets for sample_rate or record_time.
|
||||
| record_time | float32 BE at anchor + 10 |
|
||||
| trigger_level_geo | float32 BE, located in channel block |
|
||||
| alarm_level_geo | float32 BE, adjacent to trigger_level_geo |
|
||||
| geo_hardware_constant (adc_scale_factor) | float32 BE at channel_label+28 — reads **6.206053** on BOTH tested units (BE11529 and BE18189); identical across all geo channels (Tran/Vert/Long) and all captures. **Confirmed 2026-04-17 from Interface Handbook §4.5**: this is the **ADC-to-velocity scale factor** = 1/sensitivity = (in/s per V). Firmware uses it as: `PPV (in/s) = ADC_voltage × 6.206053`. Cross-check: `1.61133 V (ADC full-scale) × 6.206053 = 10.000 in/s` (Normal range ✅). Stored field name: `max_range_geo`. Do NOT write this field — it is a hardware/firmware constant for the Instantel standard geophone. |
|
||||
| max_range_geo | **uint8 at channel_label+20** — reads `0x01` on all tested captures (both units, all geo channels). Hypothesis: `0x01` = Normal 10.000 in/s, `0x00` = Sensitive 1.25 in/s. Unconfirmed — need a capture with 1.25 in/s range to verify. |
|
||||
| geo_hardware_constant (adc_scale_factor) | float32 BE at **channel_label+28** in both read (E5) and write (SUB 71) payloads — reads **6.206053** on BOTH tested units (BE11529 and BE18189); identical across all geo channels (Tran/Vert/Long) and all captures. **Confirmed 2026-04-17 from Interface Handbook §4.5**: this is the **ADC-to-velocity scale factor** = 1/sensitivity = (in/s per V). Firmware uses it as: `PPV (in/s) = ADC_voltage × 6.206053`. Cross-check: `1.61133 V (ADC full-scale) × 6.206053 = 10.000 in/s` (Normal range ✅). Do NOT write this field — it is a hardware/firmware constant. |
|
||||
| geo_range (sensitivity selector) | **uint8 at channel_label+33** in both read (E5) and write (SUB 71) payloads — **CONFIRMED 2026-04-20** from 4-20-26 geo sensitivity captures: `0x00` = Normal 10.000 in/s (standard gain), `0x01` = Sensitive 1.250 in/s (high gain). Present in all three geo channel blocks (Tran, Vert, Long). **NOTE: `channel_label+20` reads `0x01` on ALL captures regardless of range setting — it is NOT this field.** Note: the "SUB 71 write offset = +29" that appears in earlier analysis was an artifact of incorrect BW-style destuffing applied to write frame data — write frame data is RAW, so the literal `0x10` bytes in the channel block header are preserved, and the offset is the same as in the E5 read payload. |
|
||||
| setup_name | ASCII, null-padded, in cfg body |
|
||||
| project / client / operator / sensor_location | ASCII, label-value pairs |
|
||||
|
||||
@@ -746,7 +746,7 @@ offsets in the raw 1A/E5 payload. Only fields with `✅` have confirmed offsets
|
||||
- Geophone Type: Standard Triaxial / 4.5 Hz (bool/enum)
|
||||
- Geophone Channels: Enable all geophones (bool), Trigger Source (bool)
|
||||
- Chan 1-3 Trigger Level (float, in/s) ✅ (`trigger_level_geo`)
|
||||
- Chan 1-3 Maximum Range: Normal 10.000 / 1.25 in/s (enum) ❓ (`uint8` at `channel_label+20`; reads `0x01` on both tested units, both set to Normal 10.000 in/s; hypothesis: `0x01` = Normal (Gain=1, 10 in/s), `0x00` = Sensitive (Gain=8, 1.25 in/s) — UNCONFIRMED, need 1.25 in/s capture to verify). **Note: the float32 at `channel_label+28` (= 6.206053) is NOT this field** — it is the ADC-to-velocity scale factor (1/sensitivity, (in/s)/V); confirmed 2026-04-17 via Interface Handbook §4.5: 1.61133 V × 6.206053 = 10.000 in/s.
|
||||
- Chan 1-3 Maximum Range: Normal 10.000 / 1.25 in/s (enum) ✅ (`geo_range` uint8; **CONFIRMED 2026-04-20** from 4-20-26 geo sensitivity captures: offset = `channel_label+33` in both E5 read and SUB 71 write payloads (same bytes, round-tripped verbatim); `0x00` = Normal 10.000 in/s, `0x01` = Sensitive 1.250 in/s; applied to Tran/Vert/Long channel blocks). **IMPORTANT: `channel_label+20` reads `0x01` on ALL captures and is NOT this field** — it is a constant flag. The float32 at `channel_label+28` = 6.206053 is the ADC-to-velocity scale factor (hardware constant, do NOT write).
|
||||
- Microphone Channels: Enable all microphones (bool), Trigger Source (bool)
|
||||
- Chan 4 Trigger Level (dB or psi depending on units)
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
| 2026-04-11 | §14.6 | **RESOLVED — ACH Session Lifecycle is no longer "Future".** `bridges/ach_server.py` fully implements inbound ACH: POLL handshake, device info, event download. State tracked via `ach_state.json` (key-based, with `max_downloaded_key` for post-erase detection). `--clear-after-download` flag added for the standard delete-after-upload workflow. |
|
||||
| 2026-04-17 | §7.6.2, §14 | **RESOLVED — Float 6.206053 at channel_label+28 is the ADC-to-velocity scale factor.** Confirmed from Series III Interface Handbook §4.5 formula: `Range (×1) = 1.61133 V / Sensitivity (V/unit)`. For the standard Instantel geophone at Normal range (10.000 in/s): Sensitivity = 1.61133 / 10 = 0.161133 V/(in/s). The stored value is the **inverse sensitivity** = 1/0.161133 = **6.206053 (in/s)/V**. Cross-check: 1.61133 V × 6.206053 = 10.000 in/s ✅. The firmware uses it as: `PPV (in/s) = ADC_voltage (V) × 6.206053`. Value is identical on all Instantel standard geophones — it is a hardware/firmware constant, NOT a user-configurable setting. Do NOT write this field. Open question §14 item "Max Geo Range float 6.2061" is now **RESOLVED**. |
|
||||
| 2026-04-20 | §7.6.4 (NEW), §7.9, Appendix B | **CONFIRMED — Recording Mode byte location.** Three targeted captures (4-20-26) confirmed `recording_mode` at **`cfg[5]`** in the SUB 71 write payload (3-chunk compliance write). Method: single Blastware session, one initial E5 config pull, then three sequential "Send to unit" writes changing Recording Mode only. Diff of SUB 71 chunk-1 payloads: only `cfg[5]` and `cfg[1024]` changed; `cfg[1024]` delta exactly equals `cfg[5]` delta (chunk running checksum). In the E5 read response (sub-frame 1, page=0x0010), the field is at **`data[17]`** (= **anchor − 4** from the 10-byte anchor), one position earlier than in the write payload due to an extra `0x10` byte at `data[18]` present only in the read format. Enum: `0x00`=Single Shot, `0x01`=Continuous, `0x03`=Histogram, `0x04`=Histogram+Continuous. `0x02` value not yet observed. See §7.6.4 for full details. |
|
||||
| 2026-04-20 | §7.6.2, §7.9, Appendix B | **CONFIRMED — Geophone maximum range / sensitivity selector byte location.** Two targeted captures (4-20-26, geo sensitivity folder): one at Normal 10.000 in/s, one at Sensitive 1.250 in/s. E5 read payload diff: exactly 3 bytes differ at channel_label+33 for Tran/Vert/Long. Values: `0x00`=Normal 10.000 in/s, `0x01`=Sensitive 1.250 in/s. Same offset applies to the SUB 71 write payload (which is the same 2126-byte E5-format buffer round-tripped verbatim). **`channel_label+20` reads `0x01` in ALL captures regardless of range setting — it is NOT this field.** Previous hypothesis (uint8 at Tran+20, 0x01=Normal) was WRONG. Stored as `geo_range` in `ComplianceConfig`. Encoded to all three geo channel blocks (Tran/Vert/Long) at label+33. |
|
||||
|
||||
---
|
||||
|
||||
@@ -1322,8 +1323,8 @@ Fields visible in the Blastware "Compliance Setup" dialog. ✅ = byte offset co
|
||||
| Geophone — Enable all | bool | ❓ |
|
||||
| Geophone — Trigger Source | bool | ❓ |
|
||||
| Chan 1-3 Trigger Level | float, in/s | ✅ `trigger_level_geo` |
|
||||
| Chan 1-3 Maximum Range (range selector enum) | Normal 10.000 / 1.25 in/s | ❓ `max_range_geo_enum` — uint8 at Tran+20; reads `0x01` on both tested units (Normal range); hypothesis `0x01`=Normal, `0x00`=Sensitive — UNCONFIRMED, need 1.25 in/s capture |
|
||||
| Chan 1-3 ADC Scale Factor | 6.206053 (in/s)/V | ✅ `max_range_geo` at Tran+28 — **CONFIRMED 2026-04-17.** Inverse sensitivity = 1/0.161133. Interface Handbook §4.5: 1.61133 V × 6.206053 = 10.000 in/s. Hardware constant — do NOT write. |
|
||||
| Chan 1-3 Maximum Range (range selector) | Normal 10.000 / 1.25 in/s | ✅ `geo_range` uint8 — **CONFIRMED 2026-04-20.** Offset = Tran+33 (same in E5 read and SUB 71 write — 2126-byte buffer is round-tripped verbatim). `0x00`=Normal 10 in/s, `0x01`=Sensitive 1.25 in/s. Applied to Tran/Vert/Long. **`Tran+20` is NOT this field** (constant 0x01 on all captures). |
|
||||
| Chan 1-3 ADC Scale Factor | 6.206053 (in/s)/V | ✅ `geo_adc_scale` float32 — **CONFIRMED 2026-04-17.** Offset = Tran+28 (same in E5 read and SUB 71 write). Inverse sensitivity = 1/0.161133. Interface Handbook §4.5: 1.61133 V × 6.206053 = 10.000 in/s. Hardware constant — do NOT write. |
|
||||
| Microphone — Enable all | bool | ❓ |
|
||||
| Microphone — Trigger Source | bool | ❓ |
|
||||
| Chan 4 Trigger Level | float, dB or psi | ❓ |
|
||||
@@ -2018,8 +2019,8 @@ The `.bin` files produced by `s3_bridge` are **not raw wire bytes**. The logger
|
||||
| Trigger Level (Mic) | §3.8.6 | Channel block, float | float32 BE | 100–148 dB in 1 dB steps |
|
||||
| Alarm Level (Mic) | §3.9.10 | Channel block, float | float32 BE | higher than mic trigger |
|
||||
| Record Time | §3.8.9 | cfg anchor+10, float32 BE (wire); `.set` +16, uint32 LE (file) | float32 BE (wire) | 1–105 s; confirmed 3→`40400000`, 5→`40A00000`, 8→`41000000`, 13→`41500000`. Use anchor §7.6.1/§7.6.3 — NOT fixed offset. |
|
||||
| ADC Scale Factor (max_range_geo) | §3.8.4 / Interface Handbook §4.5 | Channel block, Tran+28, float32 BE | float32 BE = 6.206053 | ✅ CONFIRMED 2026-04-17 — inverse sensitivity (in/s)/V. `Range = 1.61133 V × 6.206053 = 10.000 in/s`. Firmware: `PPV (in/s) = ADC_voltage × 6.206053`. Hardware constant, identical on all units. Do NOT write. |
|
||||
| Max Geo Range Enum (max_range_geo_enum) | §3.8.4 | Channel block, Tran+20, uint8 | uint8 | ❓ UNCONFIRMED — reads `0x01` on both tested units (Normal range). Hypothesis: `0x01`=Normal (Gain=1, 10 in/s), `0x00`=Sensitive (Gain=8, 1.25 in/s). Need 1.25 in/s capture to verify. |
|
||||
| ADC Scale Factor (geo_adc_scale) | §3.8.4 / Interface Handbook §4.5 | Channel block, Tran+28 (same in E5 read and SUB 71 write), float32 BE | float32 BE = 6.206053 | ✅ CONFIRMED 2026-04-17 — inverse sensitivity (in/s)/V. `Range = 1.61133 V × 6.206053 = 10.000 in/s`. Firmware: `PPV (in/s) = ADC_voltage × 6.206053`. Hardware constant, identical on all units. Do NOT write. |
|
||||
| Max Geo Range (geo_range) | §3.8.4 | Channel block, Tran+33 (same in E5 read and SUB 71 write), uint8; applied to Tran/Vert/Long | uint8 | ✅ CONFIRMED 2026-04-20 — `0x00`=Normal 10.000 in/s, `0x01`=Sensitive 1.250 in/s. **NOTE: `Tran+20` reads `0x01` on ALL captures regardless of range — it is NOT this field.** |
|
||||
| Microphone Units | §3.9.7 | Inline unit string | char[4] | `"psi\0"`, `"pa.\0"`, `"dB\0\0"` |
|
||||
| Sample Rate | §3.8.2 | cfg anchor−2, uint16 BE — anchor=`\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00` in cfg[40:100] | uint16 BE | Normal=1024, Fast=2048, Faster=4096 ✅ CONFIRMED 2026-04-01 (BE11529 S338.17). Anchor required — see §7.6.3 DLE jitter. |
|
||||
| Record Mode | §3.8.1 | Write: `cfg[anchor−3]`, uint8. Read (E5 sf1): `data[anchor−4]`, uint8. Note: extra `0x10` byte at read `data[anchor−3]` shifts offset by 1 vs write. | uint8 | `0x00`=Single Shot, `0x01`=Continuous, `0x02`=unknown, `0x03`=Histogram, `0x04`=Histogram+Continuous. ✅ CONFIRMED 2026-04-20 |
|
||||
|
||||
+43
-16
@@ -854,6 +854,7 @@ class MiniMateClient:
|
||||
# Threshold parameters (geo channels, in/s)
|
||||
trigger_level_geo: Optional[float] = None,
|
||||
alarm_level_geo: Optional[float] = None,
|
||||
geo_range: Optional[int] = None, # 0x00=Normal 10in/s, 0x01=Sensitive 1.25in/s
|
||||
# Project / operator strings
|
||||
project: Optional[str] = None,
|
||||
client_name: Optional[str] = None,
|
||||
@@ -875,9 +876,11 @@ class MiniMateClient:
|
||||
sample_rate : int — samples/sec; valid values: 1024, 2048, 4096
|
||||
record_time : float — record duration in seconds (e.g. 2.0, 3.0)
|
||||
|
||||
Trigger/alarm thresholds (geo channels, in/s):
|
||||
trigger_level_geo : float — trigger threshold (e.g. 0.5)
|
||||
alarm_level_geo : float — alarm threshold (e.g. 1.0)
|
||||
Trigger/alarm thresholds and range (geo channels):
|
||||
trigger_level_geo : float — trigger threshold in/s (e.g. 0.5)
|
||||
alarm_level_geo : float — alarm threshold in/s (e.g. 1.0)
|
||||
geo_range : int — 0x00=Normal 10.000 in/s, 0x01=Sensitive 1.250 in/s
|
||||
(written to Tran/Vert/Long channel blocks)
|
||||
|
||||
Project / operator strings (max 41 ASCII characters each):
|
||||
project : str
|
||||
@@ -923,6 +926,7 @@ class MiniMateClient:
|
||||
histogram_interval_sec=histogram_interval_sec,
|
||||
trigger_level_geo=trigger_level_geo,
|
||||
alarm_level_geo=alarm_level_geo,
|
||||
geo_range=geo_range,
|
||||
project=project,
|
||||
client_name=client_name,
|
||||
operator=operator,
|
||||
@@ -1660,6 +1664,7 @@ def _encode_compliance_config(
|
||||
record_time: Optional[float] = None,
|
||||
trigger_level_geo: Optional[float] = None,
|
||||
alarm_level_geo: Optional[float] = None,
|
||||
geo_range: Optional[int] = None, # 0x00=Normal 10in/s, 0x01=Sensitive 1.25in/s
|
||||
histogram_interval_sec: Optional[int] = None,
|
||||
project: Optional[str] = None,
|
||||
client_name: Optional[str] = None,
|
||||
@@ -1687,11 +1692,16 @@ def _encode_compliance_config(
|
||||
record_time → float32 BE at anchor_pos + 6
|
||||
|
||||
Channel block (anchored on b"Tran" with unit-string guard):
|
||||
geo_range → uint8 at tran_pos + 33 (confirmed 2026-04-20)
|
||||
0x00 = Normal 10.000 in/s, 0x01 = Sensitive 1.250 in/s
|
||||
Written to Tran, Vert, Long channel blocks (all three).
|
||||
adc_scale_factor → float32 BE at tran_pos + 28 (= 6.206053; do NOT write)
|
||||
trigger_level_geo → float32 BE at tran_pos + 34
|
||||
"in.\\x00" → unit string at tran_pos + 38 (layout guard)
|
||||
alarm_level_geo → float32 BE at tran_pos + 42
|
||||
NOTE: tran_pos + 28 (float32 = 6.206053) is the ADC-to-velocity scale factor
|
||||
(= 1/sensitivity, in/s per V) for the standard Instantel geophone. Confirmed
|
||||
from Interface Handbook §4.5: Range = 1.61133 V × 6.206053 = 10.000 in/s.
|
||||
"/s\\x00\\x00" → unit string at tran_pos + 46 (layout guard)
|
||||
NOTE: tran_pos+28 (float32 = 6.206053) is the ADC-to-velocity scale factor
|
||||
(= 1/sensitivity, (in/s)/V — Interface Handbook §4.5: 1.61133 V × 6.206053 = 10.000 in/s).
|
||||
This is a hardware/firmware constant common to all MiniMate Plus S3 units.
|
||||
It must NOT be written — do not add it back as a parameter.
|
||||
|
||||
@@ -1750,10 +1760,11 @@ def _encode_compliance_config(
|
||||
log.debug("_encode_compliance_config: record_time=%.3f -> offset %d", record_time, _anc + 6)
|
||||
|
||||
# ── Numeric: channel block (Tran label + unit-string guard) ───────────────
|
||||
# NOTE: tran_pos+28 (float32 = 6.206053) is the ADC-to-velocity scale factor
|
||||
# (1/sensitivity, (in/s)/V — Interface Handbook §4.5: 1.61133 V × 6.206053 = 10.000 in/s).
|
||||
# Hardware/firmware constant — never written here.
|
||||
_needs_channel = any(v is not None for v in (trigger_level_geo, alarm_level_geo))
|
||||
# NOTE: tran_pos+24 (write format) or tran_pos+28 (E5 read format) is the
|
||||
# ADC-to-velocity scale factor (6.206053, hardware constant — never written).
|
||||
# geo_range is written to ALL THREE geo channel blocks (Tran, Vert, Long),
|
||||
# confirmed from 4-20-26 captures showing the byte at label+29 in each block.
|
||||
_needs_channel = any(v is not None for v in (trigger_level_geo, alarm_level_geo, geo_range))
|
||||
if _needs_channel:
|
||||
_tran = buf.find(b"Tran", 44)
|
||||
_valid = (
|
||||
@@ -1766,7 +1777,7 @@ def _encode_compliance_config(
|
||||
if not _valid:
|
||||
log.warning(
|
||||
"_encode_compliance_config: 'Tran' channel block not found or unit "
|
||||
"guard failed — trigger/alarm will not be written"
|
||||
"guard failed — trigger/alarm/geo_range will not be written"
|
||||
)
|
||||
else:
|
||||
if trigger_level_geo is not None:
|
||||
@@ -1775,6 +1786,19 @@ def _encode_compliance_config(
|
||||
if alarm_level_geo is not None:
|
||||
struct.pack_into(">f", buf, _tran + 42, alarm_level_geo)
|
||||
log.debug("_encode_compliance_config: alarm_level_geo=%.4f -> offset %d", alarm_level_geo, _tran + 42)
|
||||
if geo_range is not None:
|
||||
# Write geo_range to all three geo channel blocks (Tran, Vert, Long).
|
||||
# Field at label+33 in the E5-format compliance bytes (same in read and write
|
||||
# since the 2126-byte payload is round-tripped verbatim).
|
||||
# 0x00 = Normal 10.000 in/s, 0x01 = Sensitive 1.250 in/s.
|
||||
for _ch_label in (b"Tran", b"Vert", b"Long"):
|
||||
_ch = buf.find(_ch_label, 44)
|
||||
if _ch >= 0 and buf[_ch + 4 : _ch + 5] != b"2" and _ch + 34 <= len(buf):
|
||||
buf[_ch + 33] = geo_range & 0xFF
|
||||
log.debug(
|
||||
"_encode_compliance_config: geo_range=0x%02X -> %s+33 offset %d",
|
||||
geo_range, _ch_label.decode(), _ch + 33,
|
||||
)
|
||||
|
||||
# ── ASCII strings (64-byte slot, value at label_pos+22) ───────────────────
|
||||
def _set_string(label: bytes, value: Optional[str]) -> None:
|
||||
@@ -1958,12 +1982,15 @@ def _decode_compliance_config_into(data: bytes, info: DeviceInfo) -> None:
|
||||
# download capture. Cross-checked 2026-04-17 across both BE11529 and BE18189.
|
||||
#
|
||||
# "Tran" label at tran_pos (+0 to +3)
|
||||
# max_range_enum uint8 at tran_pos + 20 (range selector: 0x01=10in/s, 0x00=1.25in/s — unconfirmed)
|
||||
# adc_scale float32_BE at tran_pos + 28 (= 1/sensitivity = 6.206053 (in/s)/V; confirmed: 1.61133 V × 6.206053 = 10.000 in/s Normal range; hardware constant — do NOT write)
|
||||
# adc_scale float32_BE at tran_pos + 28 (= 1/sensitivity = 6.206053 (in/s)/V; hardware constant — do NOT write)
|
||||
# geo_range uint8 at tran_pos + 33 CONFIRMED 2026-04-20
|
||||
# 0x00 = Normal 10.000 in/s, 0x01 = Sensitive 1.250 in/s
|
||||
# Same offset in E5 read and SUB 71 write (bytes are round-tripped verbatim).
|
||||
# NOTE: tran_pos+20 reads 0x01 on ALL captures — constant flag, NOT range field.
|
||||
# trigger float32_BE at tran_pos + 34 (e.g. 0.600000 in/s) ✅
|
||||
# "in.\x00" unit string at tran_pos + 38 ✅ confirmed
|
||||
# "in.\x00" unit string at tran_pos + 38 ✅ confirmed (layout guard)
|
||||
# alarm float32_BE at tran_pos + 42 (e.g. 1.250000 in/s) ✅
|
||||
# "/s\x00\x00" unit string at tran_pos + 46 ✅ confirmed
|
||||
# "/s\x00\x00" unit string at tran_pos + 46 ✅ confirmed (layout guard)
|
||||
#
|
||||
# Unit strings serve as layout anchors — if they match, the float offsets
|
||||
# are reliable. Skip "Tran2" (a later repeated label) via the +4 check.
|
||||
@@ -1976,7 +2003,7 @@ def _decode_compliance_config_into(data: bytes, info: DeviceInfo) -> None:
|
||||
and data[tran_pos + 38 : tran_pos + 42] == b"in.\x00"
|
||||
and data[tran_pos + 46 : tran_pos + 50] == b"/s\x00\x00"
|
||||
):
|
||||
config.geo_range = data[tran_pos + 20] # range selector (0x01=Normal 10in/s, 0x00=Sensitive 1.25in/s — unconfirmed)
|
||||
config.geo_range = data[tran_pos + 33] # CONFIRMED 2026-04-20: 0x00=Normal 10in/s, 0x01=Sensitive 1.25in/s
|
||||
config.geo_adc_scale = struct.unpack_from(">f", data, tran_pos + 28)[0] # hw scale factor (in/s)/V — do NOT write
|
||||
config.trigger_level_geo = struct.unpack_from(">f", data, tran_pos + 34)[0]
|
||||
config.alarm_level_geo = struct.unpack_from(">f", data, tran_pos + 42)[0]
|
||||
|
||||
@@ -361,9 +361,11 @@ class ComplianceConfig:
|
||||
# Firmware uses: PPV (in/s) = ADC_voltage (V) × 6.206053
|
||||
# Identical on BE11529 and BE18189 — same Instantel geophone hardware.
|
||||
# NOT a user-configurable setting. Must NOT be written.
|
||||
geo_range: Optional[int] = None # range selector: uint8 at Tran+20
|
||||
# hypothesis: 0x01 = Normal 10.000 in/s, 0x00 = Sensitive 1.25 in/s
|
||||
# reads 0x01 on all tested units — UNCONFIRMED (need 1.25 in/s capture)
|
||||
geo_range: Optional[int] = None # range/sensitivity selector — CONFIRMED 2026-04-20
|
||||
# 0x00 = Normal 10.000 in/s (standard gain)
|
||||
# 0x01 = Sensitive 1.250 in/s (high gain)
|
||||
# Offset: Tran+33 in both E5 read and SUB 71 write payloads
|
||||
# (same 2126-byte buffer is round-tripped; applied to Tran/Vert/Long)
|
||||
|
||||
# Project/setup strings (sourced from E5 / SUB 71 write payload)
|
||||
# These are the FULL project metadata from compliance config,
|
||||
|
||||
+7
-4
@@ -292,7 +292,7 @@ def _serialise_compliance_config(cc: Optional["ComplianceConfig"]) -> Optional[d
|
||||
"trigger_level_geo": cc.trigger_level_geo,
|
||||
"alarm_level_geo": cc.alarm_level_geo,
|
||||
"geo_adc_scale": cc.geo_adc_scale, # hw scale factor (in/s)/V — informational only, do not write
|
||||
"geo_range": cc.geo_range, # range selector: 0x01=Normal 10in/s, 0x00=Sensitive 1.25in/s (unconfirmed)
|
||||
"geo_range": cc.geo_range, # CONFIRMED 2026-04-20: 0x00=Normal 10in/s, 0x01=Sensitive 1.25in/s
|
||||
"setup_name": cc.setup_name,
|
||||
"project": cc.project,
|
||||
"client": cc.client,
|
||||
@@ -842,10 +842,11 @@ class DeviceConfigBody(BaseModel):
|
||||
sample_rate : Samples per second. Valid values: 1024, 2048, 4096.
|
||||
record_time : Record duration in seconds (e.g. 1.0, 2.0, 3.0).
|
||||
|
||||
Trigger / alarm thresholds (geo channels, in/s)
|
||||
------------------------------------------------
|
||||
Trigger / alarm thresholds and range (geo channels)
|
||||
----------------------------------------------------
|
||||
trigger_level_geo : Trigger threshold in in/s (e.g. 0.5).
|
||||
alarm_level_geo : Alarm threshold in in/s (e.g. 1.0).
|
||||
geo_range : Geophone range/sensitivity. 0=Normal 10.000 in/s, 1=Sensitive 1.250 in/s.
|
||||
Project / operator strings (max 41 ASCII characters each)
|
||||
----------------------------
|
||||
project : Project description.
|
||||
@@ -859,9 +860,10 @@ class DeviceConfigBody(BaseModel):
|
||||
sample_rate: Optional[int] = None
|
||||
record_time: Optional[float] = None
|
||||
histogram_interval_sec: Optional[int] = None # seconds: 2, 5, 15, 60, 300, 900 (mode-gated)
|
||||
# Threshold parameters
|
||||
# Threshold parameters / geo range
|
||||
trigger_level_geo: Optional[float] = None
|
||||
alarm_level_geo: Optional[float] = None
|
||||
geo_range: Optional[int] = None # 0=Normal 10.000 in/s, 1=Sensitive 1.250 in/s
|
||||
# Project / operator strings
|
||||
project: Optional[str] = None
|
||||
client_name: Optional[str] = None
|
||||
@@ -923,6 +925,7 @@ def device_config(
|
||||
histogram_interval_sec=body.histogram_interval_sec,
|
||||
trigger_level_geo=body.trigger_level_geo,
|
||||
alarm_level_geo=body.alarm_level_geo,
|
||||
geo_range=body.geo_range,
|
||||
project=body.project,
|
||||
client_name=body.client_name,
|
||||
operator=body.operator,
|
||||
|
||||
+18
-3
@@ -856,6 +856,16 @@
|
||||
<div class="hint">Alarm flagged when any geo channel exceeds this level</div>
|
||||
</div>
|
||||
|
||||
<div class="cfg-field">
|
||||
<label>Maximum Range — Geo</label>
|
||||
<select id="cfg-geo-range">
|
||||
<option value="">— unchanged —</option>
|
||||
<option value="0">Normal — 10.000 in/s</option>
|
||||
<option value="1">Sensitive — 1.250 in/s</option>
|
||||
</select>
|
||||
<div class="hint">Geophone sensitivity (applies to Tran / Vert / Long channels)</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Project / operator strings -->
|
||||
@@ -1354,6 +1364,7 @@ function populateDeviceTab() {
|
||||
['Record Time', cc.record_time != null ? `${cc.record_time.toFixed(2)} s` : '—'],
|
||||
['Trigger Level (geo)', cc.trigger_level_geo != null ? `${cc.trigger_level_geo.toFixed(4)} in/s` : '—'],
|
||||
['Alarm Level (geo)', cc.alarm_level_geo != null ? `${cc.alarm_level_geo.toFixed(4)} in/s` : '—'],
|
||||
['Max Range (geo)', cc.geo_range != null ? (cc.geo_range === 0 ? 'Normal — 10.000 in/s' : cc.geo_range === 1 ? 'Sensitive — 1.250 in/s' : `0x${cc.geo_range.toString(16).padStart(2,'0')}`) : '—'],
|
||||
['ADC Scale Factor (geo)', cc.geo_adc_scale != null ? `${cc.geo_adc_scale.toFixed(4)} in/s` : '—'],
|
||||
['Setup Name', cc.setup_name || '—'],
|
||||
];
|
||||
@@ -1389,8 +1400,9 @@ function populateConfigFromDeviceInfo() {
|
||||
if (cc.sample_rate) qs('cfg-sample-rate', String(cc.sample_rate));
|
||||
if (cc.histogram_interval_sec != null) qs('cfg-histogram-interval', String(cc.histogram_interval_sec));
|
||||
if (cc.record_time != null) qs('cfg-record-time', cc.record_time.toFixed(1));
|
||||
if (cc.trigger_level_geo != null) qs('cfg-trigger', cc.trigger_level_geo.toFixed(4));
|
||||
if (cc.alarm_level_geo != null) qs('cfg-alarm', cc.alarm_level_geo.toFixed(4));
|
||||
if (cc.trigger_level_geo != null) qs('cfg-trigger', cc.trigger_level_geo.toFixed(4));
|
||||
if (cc.alarm_level_geo != null) qs('cfg-alarm', cc.alarm_level_geo.toFixed(4));
|
||||
if (cc.geo_range != null) qs('cfg-geo-range', String(cc.geo_range));
|
||||
if (cc.project) qs('cfg-project', cc.project);
|
||||
if (cc.client) qs('cfg-client', cc.client);
|
||||
if (cc.operator) qs('cfg-operator', cc.operator);
|
||||
@@ -1400,7 +1412,8 @@ function populateConfigFromDeviceInfo() {
|
||||
|
||||
function clearConfigForm() {
|
||||
['cfg-sample-rate','cfg-record-time','cfg-trigger','cfg-alarm',
|
||||
'cfg-project','cfg-client','cfg-operator','cfg-seis-loc','cfg-notes']
|
||||
'cfg-project','cfg-client','cfg-operator','cfg-seis-loc','cfg-notes',
|
||||
'cfg-recording-mode','cfg-histogram-interval','cfg-geo-range']
|
||||
.forEach(id => { const el = qs(id); el.tagName === 'SELECT' ? el.selectedIndex = 0 : el.value = ''; });
|
||||
setCfgStatus('');
|
||||
}
|
||||
@@ -1441,6 +1454,8 @@ async function writeConfig() {
|
||||
if (trig) body.trigger_level_geo = parseFloat(trig);
|
||||
const alarm = qs('cfg-alarm').value;
|
||||
if (alarm) body.alarm_level_geo = parseFloat(alarm);
|
||||
const gr = qs('cfg-geo-range').value;
|
||||
if (gr !== '') body.geo_range = parseInt(gr, 10);
|
||||
const proj = qs('cfg-project').value.trim();
|
||||
if (proj) body.project = proj;
|
||||
const cli = qs('cfg-client').value.trim();
|
||||
|
||||
Reference in New Issue
Block a user