基本功能完成
This commit is contained in:
+38
-17
@@ -8,6 +8,7 @@ export class TemFrameParser {
|
||||
private buf: Uint8Array;
|
||||
private len = 0;
|
||||
private readonly capacity: number;
|
||||
onOverflow?: () => void;
|
||||
|
||||
constructor(capacity = 2 * 1024 * 1024) {
|
||||
this.capacity = capacity;
|
||||
@@ -25,8 +26,9 @@ export class TemFrameParser {
|
||||
}
|
||||
|
||||
if (this.len + incoming.length > this.capacity) {
|
||||
// Buffer overflow — reset and resync
|
||||
if (__DEV__) console.warn(`[Parser] buffer overflow: ${this.len} + ${incoming.length} > ${this.capacity}, resetting`);
|
||||
this.len = 0;
|
||||
this.onOverflow?.();
|
||||
}
|
||||
this.buf.set(incoming, this.len);
|
||||
this.len += incoming.length;
|
||||
@@ -50,19 +52,31 @@ export class TemFrameParser {
|
||||
|
||||
const flag = this.buf[offset + 4];
|
||||
const func = this.buf[offset + 5];
|
||||
const payloadLen = this.readUint32LE(offset + 6);
|
||||
const is2Byte = !!(flag & 0x01);
|
||||
const payloadLen = is2Byte
|
||||
? this.readUint16LE(offset + 6)
|
||||
: this.readUint32LE(offset + 6);
|
||||
|
||||
if (__DEV__) console.log(
|
||||
`[Parser] header @ ${offset}: flag=0x${flag.toString(16)} func=0x${func.toString(16)} lenMode=${is2Byte ? '2B' : '4B'} payloadLen=${payloadLen} bufLen=${this.len}`,
|
||||
'hdr:', Array.from(this.buf.slice(offset, offset + 10)).map(b => '0x' + b.toString(16).padStart(2, '0')).join(' '),
|
||||
);
|
||||
|
||||
if (payloadLen > MAX_PAYLOAD_SIZE) {
|
||||
// Bogus length — skip this magic and search again
|
||||
if (__DEV__) console.warn(`[Parser] bogus payloadLen ${payloadLen} > MAX ${MAX_PAYLOAD_SIZE}, skipping magic`);
|
||||
offset += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
const totalLen = 10 + payloadLen;
|
||||
if (this.len - offset < totalLen) break; // wait for more data
|
||||
if (this.len - offset < totalLen) {
|
||||
if (__DEV__) console.log(`[Parser] incomplete frame: have ${this.len - offset}, need ${totalLen}`);
|
||||
break;
|
||||
}
|
||||
|
||||
const payload = new Uint8Array(totalLen - 10);
|
||||
payload.set(this.buf.subarray(offset + 10, offset + totalLen));
|
||||
if (__DEV__) console.log(`[Parser] ✓ frame complete: func=0x${func.toString(16)} payload=${payloadLen}B`);
|
||||
results.push({ flag, func, payload });
|
||||
offset += totalLen;
|
||||
}
|
||||
@@ -91,6 +105,10 @@ export class TemFrameParser {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private readUint16LE(offset: number): number {
|
||||
return this.buf[offset] | (this.buf[offset + 1] << 8);
|
||||
}
|
||||
|
||||
private readUint32LE(offset: number): number {
|
||||
const b = this.buf;
|
||||
return b[offset] | (b[offset + 1] << 8) | (b[offset + 2] << 16) | (b[offset + 3] * 0x1000000);
|
||||
@@ -128,22 +146,24 @@ export function parseMetadata(payload: Uint8Array): MeasurementMeta | null {
|
||||
}
|
||||
|
||||
// Parse ADC sample data from a 0x85/0x95 payload into per-channel arrays
|
||||
const ADC_FULL_SCALE_V = 5.0;
|
||||
const INT32_MAX = 0x7fffffff;
|
||||
// raw → μV: (raw / 0x7FFFFFFF) * 5V / gain * 1e6
|
||||
const ADC_UV_SCALE = (ADC_FULL_SCALE_V * 1e6) / INT32_MAX;
|
||||
|
||||
export function parseAdcData(
|
||||
payload: Uint8Array,
|
||||
channelNum: number,
|
||||
accNum: number,
|
||||
_accNum: number,
|
||||
): { adcRaw: Int32Array[]; adcUV: Float64Array[] } {
|
||||
const META_SIZE = 54;
|
||||
const sampleDataLen = payload.length - META_SIZE;
|
||||
const totalSamples = sampleDataLen / 4; // int32 per sample
|
||||
const totalSamples = sampleDataLen / 4;
|
||||
const samplesPerChannel = Math.floor(totalSamples / channelNum);
|
||||
|
||||
const view = new DataView(payload.buffer, payload.byteOffset + META_SIZE, sampleDataLen);
|
||||
const ampRatio = payload[9]; // ampRatio in metadata at offset 9 from payload start...
|
||||
// actually re-derive from parseMetadata
|
||||
const gain = AMP_GAIN[payload[9 + 1]] ?? 1; // byte 10 of payload (after devId+utc+lon+lat+alt+height+sdGps = 1+4+8+8+4+4+1 = 30)
|
||||
// Correct: ampRatio is at offset 31 in payload (1+4+8+8+4+4+1 = 30, then ampRatio at 30)
|
||||
const correctGain = AMP_GAIN[payload[30]] ?? 1;
|
||||
const gain = AMP_GAIN[payload[30]] ?? 1;
|
||||
const scale = ADC_UV_SCALE / gain;
|
||||
|
||||
const adcRaw: Int32Array[] = [];
|
||||
const adcUV: Float64Array[] = [];
|
||||
@@ -152,10 +172,10 @@ export function parseAdcData(
|
||||
const raw = new Int32Array(samplesPerChannel);
|
||||
const uv = new Float64Array(samplesPerChannel);
|
||||
for (let i = 0; i < samplesPerChannel; i++) {
|
||||
const idx = (ch * samplesPerChannel + i) * 4;
|
||||
const idx = (i * channelNum + ch) * 4;
|
||||
const val = view.getInt32(idx, true);
|
||||
raw[i] = val;
|
||||
uv[i] = val / (accNum || 1) / correctGain;
|
||||
uv[i] = val * scale;
|
||||
}
|
||||
adcRaw.push(raw);
|
||||
adcUV.push(uv);
|
||||
@@ -168,11 +188,12 @@ export function parseAdcData(
|
||||
export function parseAck(payload: Uint8Array): AckPacket {
|
||||
const result = payload[0] ?? 0x02;
|
||||
let reason = '';
|
||||
// reason string starts at offset 22 in the payload, max 32 chars
|
||||
const reasonOffset = 22;
|
||||
for (let i = reasonOffset; i < Math.min(reasonOffset + 32, payload.length); i++) {
|
||||
if (payload[i] === 0) break;
|
||||
reason += String.fromCharCode(payload[i]);
|
||||
if (payload.length > reasonOffset) {
|
||||
for (let i = reasonOffset; i < Math.min(reasonOffset + 32, payload.length); i++) {
|
||||
if (payload[i] === 0) break;
|
||||
reason += String.fromCharCode(payload[i]);
|
||||
}
|
||||
}
|
||||
return { result, reason };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user