Fix NL43 DRD field mapping to match official specification

Corrected the parsing of NL43 DRD (Dynamic Range Data) and DOD (Data On Demand)
responses according to the NL43 Communications Guide. The previous implementation
incorrectly mapped d0 (counter field) as a measurement.

Changes:
- Updated DRD/DOD parsing to skip d0 (counter: 1-600)
- Correctly map d1-d5 to lp/leq/lmax/lmin/lpeak measurements
- Added inline documentation referencing DRD format specification
- Included database migration script to revert incorrect field names

DRD format per NL43 spec:
- d0 = counter (1-600) - NOT a measurement
- d1 = Lp (instantaneous sound pressure level)
- d2 = Leq (equivalent continuous sound level)
- d3 = Lmax (maximum level)
- d4 = Lmin (minimum level)
- d5 = Lpeak (peak level)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
serversdwn
2026-01-07 03:42:26 +00:00
parent 50c9370b8e
commit 1fb786c262
4 changed files with 151 additions and 32 deletions

View File

@@ -29,11 +29,11 @@ class NL43Status(Base):
unit_id = Column(String, primary_key=True, index=True)
last_seen = Column(DateTime, default=func.now())
measurement_state = Column(String, default="unknown") # Measure/Stop
lp = Column(String, nullable=True)
leq = Column(String, nullable=True)
lmax = Column(String, nullable=True)
lmin = Column(String, nullable=True)
lpeak = Column(String, nullable=True)
lp = Column(String, nullable=True) # Instantaneous sound pressure level
leq = Column(String, nullable=True) # Equivalent continuous sound level
lmax = Column(String, nullable=True) # Maximum level
lmin = Column(String, nullable=True) # Minimum level
lpeak = Column(String, nullable=True) # Peak level
battery_level = Column(String, nullable=True)
power_source = Column(String, nullable=True)
sd_remaining_mb = Column(String, nullable=True)

View File

@@ -652,11 +652,11 @@ async def stream_live(websocket: WebSocket, unit_id: str):
"unit_id": unit_id,
"timestamp": datetime.utcnow().isoformat(),
"measurement_state": snap.measurement_state,
"lp": snap.lp,
"leq": snap.leq,
"lmax": snap.lmax,
"lmin": snap.lmin,
"lpeak": snap.lpeak,
"lp": snap.lp, # Instantaneous sound pressure level
"leq": snap.leq, # Equivalent continuous sound level
"lmax": snap.lmax, # Maximum level
"lmin": snap.lmin, # Minimum level
"lpeak": snap.lpeak, # Peak level
"raw_payload": snap.raw_payload,
})
except Exception as e:

View File

@@ -25,11 +25,11 @@ logger = logging.getLogger(__name__)
class NL43Snapshot:
unit_id: str
measurement_state: str = "unknown"
lp: Optional[str] = None
leq: Optional[str] = None
lmax: Optional[str] = None
lmin: Optional[str] = None
lpeak: Optional[str] = None
lp: Optional[str] = None # Instantaneous sound pressure level
leq: Optional[str] = None # Equivalent continuous sound level
lmax: Optional[str] = None # Maximum level
lmin: Optional[str] = None # Minimum level
lpeak: Optional[str] = None # Peak level
battery_level: Optional[str] = None
power_source: Optional[str] = None
sd_remaining_mb: Optional[str] = None
@@ -188,19 +188,20 @@ class NL43Client:
snap = NL43Snapshot(unit_id="", raw_payload=resp, measurement_state="Measure")
# Parse known positions (based on NL43 communication guide)
# DOD format: Main Lp, Main Leq, Main LE, Main Lmax, Main Lmin, LN1-5, Lpeak, LIeq, Leq,mov, Ltm5, flags...
# Parse known positions (based on NL43 communication guide - DRD format)
# DRD format: d0=counter, d1=Lp, d2=Leq, d3=Lmax, d4=Lmin, d5=Lpeak, d6=LIeq, ...
try:
if len(parts) >= 1:
snap.lp = parts[0]
# Skip d0 (counter) - start from d1
if len(parts) >= 2:
snap.leq = parts[1]
snap.lp = parts[1] # d1: Instantaneous sound pressure level
if len(parts) >= 3:
snap.leq = parts[2] # d2: Equivalent continuous sound level
if len(parts) >= 4:
snap.lmax = parts[3]
snap.lmax = parts[3] # d3: Maximum level
if len(parts) >= 5:
snap.lmin = parts[4]
if len(parts) >= 11:
snap.lpeak = parts[10]
snap.lmin = parts[4] # d4: Minimum level
if len(parts) >= 6:
snap.lpeak = parts[5] # d5: Peak level
except (IndexError, ValueError) as e:
logger.warning(f"Error parsing DOD data points: {e}")
@@ -439,18 +440,20 @@ class NL43Client:
snap = NL43Snapshot(unit_id="", raw_payload=line, measurement_state="Measure")
# Parse known positions
# Parse known positions (DRD format - same as DOD)
# DRD format: d0=counter, d1=Lp, d2=Leq, d3=Lmax, d4=Lmin, d5=Lpeak, d6=LIeq, ...
try:
if len(parts) >= 1:
snap.lp = parts[0]
# Skip d0 (counter) - start from d1
if len(parts) >= 2:
snap.leq = parts[1]
snap.lp = parts[1] # d1: Instantaneous sound pressure level
if len(parts) >= 3:
snap.leq = parts[2] # d2: Equivalent continuous sound level
if len(parts) >= 4:
snap.lmax = parts[3]
snap.lmax = parts[3] # d3: Maximum level
if len(parts) >= 5:
snap.lmin = parts[4]
if len(parts) >= 11:
snap.lpeak = parts[10]
snap.lmin = parts[4] # d4: Minimum level
if len(parts) >= 6:
snap.lpeak = parts[5] # d5: Peak level
except (IndexError, ValueError) as e:
logger.warning(f"Error parsing DRD data points: {e}")