基本功能完成

This commit is contained in:
2026-06-20 21:43:09 +08:00
parent 84590d5477
commit a89d23b43c
45 changed files with 3508 additions and 1261 deletions
+38 -17
View File
@@ -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 };
}