diff --git a/model/protocol.go b/model/protocol.go new file mode 100644 index 0000000..fe14d32 --- /dev/null +++ b/model/protocol.go @@ -0,0 +1,734 @@ +package model + +import ( + "fmt" +) + +// Protocol 定义协议结构 +type Protocol struct { + RawData []byte +} + +// NewProtocol 创建新的协议实例 +func NewProtocol(data []byte) *Protocol { + return &Protocol{ + RawData: data, + } +} + +// IdentifyTxType 解析传输类型(第一个字节,bit 0-7) +func (p *Protocol) IdentifyTxType() (binary string, hexVal string, decVal uint8) { + if len(p.RawData) == 0 { + return "", "", 0 + } + + // 获取第一个字节 + firstByte := p.RawData[0] + + // 转换为二进制字符串(8位) + binary = fmt.Sprintf("%08b", firstByte) + + // 转换为十六进制字符串 + hexVal = fmt.Sprintf("%02X", firstByte) + + // 十进制值 + decVal = firstByte + + return binary, hexVal, decVal +} + +// IDParts 存储ID的三个部分 +type IDParts struct { + HSB struct { + Binary string + Hex string + Dec uint8 + } + MSB struct { + Binary string + Hex string + Dec uint8 + } + LSB struct { + Binary string + Hex string + Dec uint8 + } + Complete struct { + Binary string + Hex string + Dec uint32 + } +} + +// GetCompleteID 获取完整的24-bit ID code +// HSB: bit 168-175 (索引21) +// MSB: bit 176-183 (索引22) +// LSB: bit 8-15 (索引1) +func (p *Protocol) GetCompleteID() (*IDParts, error) { + if len(p.RawData) < 23 { // 确保有足够的数据 + return nil, fmt.Errorf("insufficient data length") + } + + result := &IDParts{} + + // 处理 HSB (bit 168-175, 索引21) + hsbByte := p.RawData[21] + result.HSB.Binary = fmt.Sprintf("%08b", hsbByte) + result.HSB.Hex = fmt.Sprintf("%02X", hsbByte) + result.HSB.Dec = hsbByte + + // 处理 MSB (bit 176-183, 索引22) + msbByte := p.RawData[22] + result.MSB.Binary = fmt.Sprintf("%08b", msbByte) + result.MSB.Hex = fmt.Sprintf("%02X", msbByte) + result.MSB.Dec = msbByte + + // 处理 LSB (bit 8-15, 索引1) + lsbByte := p.RawData[1] + result.LSB.Binary = fmt.Sprintf("%08b", lsbByte) + result.LSB.Hex = fmt.Sprintf("%02X", lsbByte) + result.LSB.Dec = lsbByte + + // 组合完整的24位ID + completeID := uint32(hsbByte)<<16 | uint32(msbByte)<<8 | uint32(lsbByte) + result.Complete.Binary = fmt.Sprintf("%024b", completeID) + result.Complete.Hex = fmt.Sprintf("%06X", completeID) + result.Complete.Dec = completeID + + return result, nil +} + +// WindDirection 存储风向的三个部分 +type WindDirection struct { + DirH struct { + Binary string // 4位二进制,格式为"000X",其中X是bit24 + Value uint8 // 实际值 + } + DirM struct { + Binary string // bit 16-19 + Value uint8 + } + DirL struct { + Binary string // bit 20-23 + Value uint8 + } + Complete struct { + Binary string // 完整的12位二进制 + Value uint16 // 完整值 (Range: 0°- 359°, Invalid: 0x1FF) + Degree float64 // 角度值 + IsValid bool // 是否有效 + } +} + +// GetWindDirection 解析风向数据 +// DIR_H: 4位 (000 + bit24) +// DIR_M: bit 16-19 +// DIR_L: bit 20-23 +// Value in hex (Range: 0°- 359°) +// If invalid fill with 0x1FF +func (p *Protocol) GetWindDirection() (*WindDirection, error) { + if len(p.RawData) < 4 { // 确保有足够的数据 + return nil, fmt.Errorf("insufficient data length") + } + + result := &WindDirection{} + + // 获取包含bit24的字节(索引3) + byte3 := p.RawData[3] + // 获取bit24(字节的最低位) + bit24 := byte3 & 0x01 + // 构造DIR_H: "000" + bit24 + result.DirH.Binary = fmt.Sprintf("000%d", bit24) + result.DirH.Value = bit24 + + // 获取包含bit16-23的字节(索引2) + byte2 := p.RawData[2] + // 获取DIR_M (bit 16-19) + dirM := (byte2 >> 4) & 0x0F + result.DirM.Binary = fmt.Sprintf("%04b", dirM) + result.DirM.Value = dirM + + // 获取DIR_L (bit 20-23) + dirL := byte2 & 0x0F + result.DirL.Binary = fmt.Sprintf("%04b", dirL) + result.DirL.Value = dirL + + // 组合完整的风向值(12位) + completeDir := (uint16(bit24) << 8) | (uint16(dirM) << 4) | uint16(dirL) + result.Complete.Binary = fmt.Sprintf("%012b", completeDir) + result.Complete.Value = completeDir + + // 检查值是否在有效范围内 (0-359) + if completeDir > 359 { + result.Complete.Value = 0x1FF + result.Complete.IsValid = false + result.Complete.Degree = 0 + } else { + result.Complete.IsValid = true + result.Complete.Degree = float64(completeDir) + } + + return result, nil +} + +// WindSpeed 存储风速的各个部分 +type WindSpeed struct { + WspFlag struct { + Binary string // bit 25 + Value bool // true = 9bit模式, false = 10bit模式 + } + Extend struct { + Binary string // 9bit模式: 000 + bit27 + // 10bit模式: 00 + bit26 + bit27 + Value uint8 + } + WspH struct { + Binary string // bit 48-51 + Value uint8 + } + WspL struct { + Binary string // bit 52-55 + Value uint8 + } + Complete struct { + Binary string // 完整的9位或10位二进制 + RawValue uint16 // 原始值 + Value float64 // 实际风速值 (计算公式: RawValue/8*0.51) + } +} + +// GetWindSpeed 解析风速数据 +// WSP_FLAG: bit 25 +// WIND_EXTEND: +// - 当WSP_FLAG=1时:000 + bit27(9bit模式) +// - 当WSP_FLAG=0时:0 + bit136 + bit26 + bit27(10bit模式) +// +// WIND_H: bit 48-51 +// WIND_L: bit 52-55 +// 实际风速计算公式: value/8*0.51 +func (p *Protocol) GetWindSpeed() (*WindSpeed, error) { + if len(p.RawData) < 18 { // 确保有足够的数据(需要读取到bit136) + return nil, fmt.Errorf("insufficient data length") + } + + result := &WindSpeed{} + + // 解析 WSP_FLAG (bit 25) + byte3 := p.RawData[3] + wspFlag := (byte3 >> 1) & 0x01 // bit 25 + result.WspFlag.Binary = fmt.Sprintf("%d", wspFlag) + result.WspFlag.Value = wspFlag == 1 + + // 获取bit26和bit27 + bit26 := (byte3 >> 2) & 0x01 + bit27 := (byte3 >> 3) & 0x01 + + // 获取bit136(在第17个字节的最高位) + byte17 := p.RawData[17] + bit136 := (byte17 >> 7) & 0x01 + + // 解析 WIND_H 和 WIND_L (byte6) + byte6 := p.RawData[6] + windH := (byte6 >> 4) & 0x0F + windL := byte6 & 0x0F + + result.WspH.Binary = fmt.Sprintf("%04b", windH) + result.WspH.Value = windH + result.WspL.Binary = fmt.Sprintf("%04b", windL) + result.WspL.Value = windL + + // 组合完整的风速值 + var rawValue uint16 + if result.WspFlag.Value { + // 9bit模式:000 + bit27 + WIND_H + WIND_L + rawValue = (uint16(bit27) << 8) | (uint16(windH) << 4) | uint16(windL) + result.Complete.Binary = fmt.Sprintf("%09b", rawValue) + } else { + // 10bit模式:0 + bit136 + bit26 + bit27 + WIND_H + WIND_L + extendBits := (uint16(0) << 3) | (uint16(bit136) << 2) | (uint16(bit26) << 1) | uint16(bit27) + rawValue = (uint16(extendBits) << 8) | (uint16(windH) << 4) | uint16(windL) + result.Complete.Binary = fmt.Sprintf("%010b", rawValue) + } + + result.Complete.RawValue = rawValue + // 计算实际风速值:value/8*0.51 + result.Complete.Value = float64(rawValue) / 8.0 * 0.51 + + return result, nil +} + +// Temperature 存储温度的三个部分 +type Temperature struct { + TmpH struct { + Binary string // 3位二进制,格式为"0XXX",其中XXX是bit29-31 + Value uint8 + } + TmpM struct { + Binary string // bit 32-35 + Value uint8 + } + TmpL struct { + Binary string // bit 36-39 + Value uint8 + } + Complete struct { + Binary string // 完整的11位二进制 + RawValue uint16 // 原始值(包含400的偏移) + Value float64 // 实际温度值 (Range: -40.0°C -> 60.0°C) + IsValid bool // 是否有效 + } +} + +// GetTemperature 解析温度数据 +// TMP_H: 4位 (0 + bit29-31) +// TMP_M: bit 32-35 +// TMP_L: bit 36-39 +// 温度计算公式:(RawValue-400)/10 +// 示例: +// 10.5°C = 0x1F9 (505-400)/10 = 10.5 +// -10.5°C = 0x127 (295-400)/10 = -10.5 +// 范围:-40.0°C -> 60.0°C +// 无效值:0x7FF +func (p *Protocol) GetTemperature() (*Temperature, error) { + if len(p.RawData) < 5 { // 确保有足够的数据 + return nil, fmt.Errorf("insufficient data length") + } + + result := &Temperature{} + + // 获取包含bit29-31的字节(索引3) + byte3 := p.RawData[3] + // 直接获取bit29-31 (010) + tmpHBits := byte3 & 0x07 + // TMP_H 是 "0" + bit29-31 + result.TmpH.Binary = fmt.Sprintf("0%03b", tmpHBits) + result.TmpH.Value = tmpHBits + + // 获取包含bit32-39的字节(索引4) + byte4 := p.RawData[4] + // 获取TMP_M (bit 32-35),在byte4的高4位 + tmpM := (byte4 >> 4) & 0x0F + result.TmpM.Binary = fmt.Sprintf("%04b", tmpM) + result.TmpM.Value = tmpM + + // 获取TMP_L (bit 36-39),在byte4的低4位 + tmpL := byte4 & 0x0F + result.TmpL.Binary = fmt.Sprintf("%04b", tmpL) + result.TmpL.Value = tmpL + + // 组合完整的温度值 + // 1. TMP_H (0 + bit29-31) 放在最高位 + // 2. TMP_M (bit 32-35) 放在中间 + // 3. TMP_L (bit 36-39) 放在最低位 + completeTemp := uint16(0) + completeTemp |= uint16(tmpHBits) << 8 // TMP_H 移到高8位 + completeTemp |= uint16(tmpM) << 4 // TMP_M 移到中间4位 + completeTemp |= uint16(tmpL) // TMP_L 在最低4位 + + result.Complete.Binary = fmt.Sprintf("%012b", completeTemp) + result.Complete.RawValue = completeTemp + + // 检查温度是否在有效范围内 + // 有效范围计算: + // -40°C = (-40 * 10 + 400) = 0 + // 60°C = (60 * 10 + 400) = 1000 + if completeTemp > 1000 { // 超出范围 + result.Complete.RawValue = 0x7FF // 无效值 + result.Complete.Value = 0 + result.Complete.IsValid = false + } else { + // 温度计算:(RawValue-400)/10 + result.Complete.Value = (float64(completeTemp) - 400) / 10 + result.Complete.IsValid = true + } + + return result, nil +} + +// Humidity 存储湿度的两个部分 +type Humidity struct { + HmH struct { + Binary string // bit 40-43 + Value uint8 + } + HmL struct { + Binary string // bit 44-47 + Value uint8 + } + Complete struct { + Binary string // 完整的8位二进制 + RawValue uint8 // 原始值(十六进制) + Value uint8 // 实际湿度值 (Range: 1% - 99%) + IsValid bool // 是否有效 + } +} + +// GetHumidity 解析湿度数据 +// HM_H: bit 40-43 +// HM_L: bit 44-47 +// Range: 1% - 99% +// If invalid fill with 0xFF +// 示例:0x37 = 55% (3*16 + 7 = 55) +func (p *Protocol) GetHumidity() (*Humidity, error) { + if len(p.RawData) < 6 { // 确保有足够的数据(bit 47 在第6个字节内) + return nil, fmt.Errorf("insufficient data length") + } + + result := &Humidity{} + + // 获取包含bit40-47的字节(索引5) + byte5 := p.RawData[5] + + // 获取HM_H (bit 40-43) + hmH := (byte5 >> 4) & 0x0F + result.HmH.Binary = fmt.Sprintf("%04b", hmH) + result.HmH.Value = hmH + + // 获取HM_L (bit 44-47) + hmL := byte5 & 0x0F + result.HmL.Binary = fmt.Sprintf("%04b", hmL) + result.HmL.Value = hmL + + // 原始十六进制值 + result.Complete.Binary = fmt.Sprintf("%08b", byte5) + result.Complete.RawValue = byte5 + + // 直接使用十六进制值转换为十进制作为湿度值 + decimalValue := byte5 + + // 检查湿度是否在有效范围内 (1-99) + if decimalValue < 1 || decimalValue > 99 { + result.Complete.RawValue = 0xFF // 无效值 + result.Complete.Value = 0 + result.Complete.IsValid = false + } else { + result.Complete.Value = decimalValue + result.Complete.IsValid = true + } + + return result, nil +} + +// GustSpeed 存储阵风速度数据 +type GustSpeed struct { + GustH struct { + Binary string // bit 56-59 + Value uint8 + } + GustL struct { + Binary string // bit 60-63 + Value uint8 + } + Complete struct { + Binary string // 完整的8位二进制 + RawValue uint8 // 原始值 + Value float64 // 实际阵风速度值 (计算公式: RawValue*0.51) + } +} + +// GetGustSpeed 解析阵风速度数据 +// GUST_H: bit 56-59 +// GUST_L: bit 60-63 +// 实际阵风速度计算公式: value*0.51 +func (p *Protocol) GetGustSpeed() (*GustSpeed, error) { + if len(p.RawData) < 8 { // 确保有足够的数据(bit 63 在第8个字节内) + return nil, fmt.Errorf("insufficient data length") + } + + result := &GustSpeed{} + + // 解析 GUST_H (bit 56-59) 和 GUST_L (bit 60-63) + byte7 := p.RawData[7] + gustH := (byte7 >> 4) & 0x0F + gustL := byte7 & 0x0F + + result.GustH.Binary = fmt.Sprintf("%04b", gustH) + result.GustH.Value = gustH + result.GustL.Binary = fmt.Sprintf("%04b", gustL) + result.GustL.Value = gustL + + // 组合完整的阵风速度值 + rawValue := byte7 + result.Complete.Binary = fmt.Sprintf("%08b", rawValue) + result.Complete.RawValue = rawValue + + // 计算实际阵风速度值:value*0.51 + result.Complete.Value = float64(rawValue) * 0.51 + + return result, nil +} + +// Rainfall 存储降雨量数据 +type Rainfall struct { + RainHH struct { + Binary string // bit 64-67 + Value uint8 + } + RainHL struct { + Binary string // bit 68-71 + Value uint8 + } + RainLH struct { + Binary string // bit 72-75 + Value uint8 + } + RainLL struct { + Binary string // bit 76-79 + Value uint8 + } + Complete struct { + Binary string // 完整的16位二进制 + RawValue uint16 // 原始值 + Value float64 // 实际降雨量值 (计算公式: RawValue*0.254) + } +} + +// GetRainfall 解析降雨量数据 +// RAIN_HH: bit 64-67 +// RAIN_HL: bit 68-71 +// RAIN_LH: bit 72-75 +// RAIN_LL: bit 76-79 +// 实际降雨量计算公式: value*0.254 +func (p *Protocol) GetRainfall() (*Rainfall, error) { + if len(p.RawData) < 10 { // 确保有足够的数据(bit 79 在第10个字节内) + return nil, fmt.Errorf("insufficient data length") + } + + result := &Rainfall{} + + // 解析 RAIN_HH 和 RAIN_HL (byte8) + byte8 := p.RawData[8] + rainHH := (byte8 >> 4) & 0x0F + rainHL := byte8 & 0x0F + + result.RainHH.Binary = fmt.Sprintf("%04b", rainHH) + result.RainHH.Value = rainHH + result.RainHL.Binary = fmt.Sprintf("%04b", rainHL) + result.RainHL.Value = rainHL + + // 解析 RAIN_LH 和 RAIN_LL (byte9) + byte9 := p.RawData[9] + rainLH := (byte9 >> 4) & 0x0F + rainLL := byte9 & 0x0F + + result.RainLH.Binary = fmt.Sprintf("%04b", rainLH) + result.RainLH.Value = rainLH + result.RainLL.Binary = fmt.Sprintf("%04b", rainLL) + result.RainLL.Value = rainLL + + // 组合完整的降雨量值 + rawValue := (uint16(rainHH) << 12) | (uint16(rainHL) << 8) | (uint16(rainLH) << 4) | uint16(rainLL) + result.Complete.Binary = fmt.Sprintf("%016b", rawValue) + result.Complete.RawValue = rawValue + + // 计算实际降雨量值:value*0.254 + result.Complete.Value = float64(rawValue) * 0.254 + + return result, nil +} + +// UVIndex 存储紫外线指数数据 +type UVIndex struct { + UviHH struct { + Binary string // bit 80-83 + Value uint8 + } + UviHL struct { + Binary string // bit 84-87 + Value uint8 + } + UviLH struct { + Binary string // bit 88-91 + Value uint8 + } + UviLL struct { + Binary string // bit 92-95 + Value uint8 + } + Complete struct { + Binary string // 完整的16位二进制 + RawValue uint16 // 原始值 + Value float64 // 实际紫外线值 (单位: uW/c㎡) + IsValid bool // 是否有效 + } +} + +// GetUVIndex 解析紫外线指数数据 +// UVI_HH: bit 80-83 +// UVI_HL: bit 84-87 +// UVI_LH: bit 88-91 +// UVI_LL: bit 92-95 +// Range: 0 uW/c㎡ to 20000 uW/c㎡ +// If invalid fill with 0xFFFF +func (p *Protocol) GetUVIndex() (*UVIndex, error) { + if len(p.RawData) < 12 { // 确保有足够的数据(bit 95 在第12个字节内) + return nil, fmt.Errorf("insufficient data length") + } + + result := &UVIndex{} + + // 解析 UVI_HH 和 UVI_HL (byte10) + byte10 := p.RawData[10] + uviHH := (byte10 >> 4) & 0x0F + uviHL := byte10 & 0x0F + + result.UviHH.Binary = fmt.Sprintf("%04b", uviHH) + result.UviHH.Value = uviHH + result.UviHL.Binary = fmt.Sprintf("%04b", uviHL) + result.UviHL.Value = uviHL + + // 解析 UVI_LH 和 UVI_LL (byte11) + byte11 := p.RawData[11] + uviLH := (byte11 >> 4) & 0x0F + uviLL := byte11 & 0x0F + + result.UviLH.Binary = fmt.Sprintf("%04b", uviLH) + result.UviLH.Value = uviLH + result.UviLL.Binary = fmt.Sprintf("%04b", uviLL) + result.UviLL.Value = uviLL + + // 组合完整的紫外线值 + rawValue := (uint16(uviHH) << 12) | (uint16(uviHL) << 8) | (uint16(uviLH) << 4) | uint16(uviLL) + result.Complete.Binary = fmt.Sprintf("%016b", rawValue) + result.Complete.RawValue = rawValue + + // 检查是否在有效范围内 (0-20000) + if rawValue > 20000 { + result.Complete.RawValue = 0xFFFF + result.Complete.Value = 0 + result.Complete.IsValid = false + } else { + result.Complete.Value = float64(rawValue) + result.Complete.IsValid = true + } + + return result, nil +} + +// Light 存储光照数据 +type Light struct { + LightHH struct { + Binary string // bit 96-99 + Value uint8 + } + LightHL struct { + Binary string // bit 100-103 + Value uint8 + } + LightMH struct { + Binary string // bit 104-107 + Value uint8 + } + LightML struct { + Binary string // bit 108-111 + Value uint8 + } + LightLH struct { + Binary string // bit 112-115 + Value uint8 + } + LightLL struct { + Binary string // bit 116-119 + Value uint8 + } + Complete struct { + Binary string // 完整的24位二进制 + RawValue uint32 // 原始值 + Value float64 // 实际光照值 (计算公式: RawValue/10) (单位: lux) + IsValid bool // 是否有效 + } +} + +// GetLight 解析光照数据 +// bit 96-119 (byte12-14: 00 04 9C) +// 实际光照计算公式: value/10 +// Range: 0.0 lux -> 300,000.0 lux +// If invalid fill with 0xFFFFFF +func (p *Protocol) GetLight() (*Light, error) { + if len(p.RawData) < 15 { // 确保有足够的数据 + return nil, fmt.Errorf("insufficient data length") + } + + result := &Light{} + + // 获取三个字节 (00 04 9C) + byte1 := p.RawData[12] // 00 + byte2 := p.RawData[13] // 04 + byte3 := p.RawData[14] // 9C + + // 组合完整的光照值 (00 04 9C) + rawValue := (uint32(byte1) << 16) | (uint32(byte2) << 8) | uint32(byte3) + result.Complete.Binary = fmt.Sprintf("%024b", rawValue) + result.Complete.RawValue = rawValue + + // 检查是否在有效范围内 (0-3000000, 因为实际值要除以10) + if rawValue > 3000000 { + result.Complete.RawValue = 0xFFFFFF + result.Complete.Value = 0 + result.Complete.IsValid = false + } else { + result.Complete.Value = float64(rawValue) / 10.0 + result.Complete.IsValid = true + } + + return result, nil +} + +// Pressure 存储大气压数据 +type Pressure struct { + PressureH struct { + Binary string // bit 143-144 (补前导000) + Value uint8 + } + PressureM struct { + Binary string // bit 145-151 + Value uint8 + } + PressureL struct { + Binary string // bit 152-159 + Value uint8 + } + Complete struct { + Binary string // 完整的17位二进制 + RawValue uint32 // 原始值 + Value float64 // 实际气压值 (计算公式: RawValue/100) (单位: hPa) + IsValid bool // 是否有效 + } +} + +// GetPressure 解析大气压数据 +// 17位值组成:000 + bit143 + bit144-159 +// 实际气压计算公式: value/100 +// Range: 300.00 hPa -> 1200.00 hPa +// If invalid fill with 0x1FFFF +// Example: 0x018A9E = 1010.22 hPa +func (p *Protocol) GetPressure() (*Pressure, error) { + if len(p.RawData) < 20 { // 确保有足够的数据 + return nil, fmt.Errorf("insufficient data length") + } + + result := &Pressure{} + + // 获取三个字节 (01 88 F5) + byte1 := p.RawData[17] // 01 + byte2 := p.RawData[18] // 88 + byte3 := p.RawData[19] // F5 + + // 组合完整的气压值 + rawValue := (uint32(byte1) << 16) | (uint32(byte2) << 8) | uint32(byte3) + result.Complete.Binary = fmt.Sprintf("%024b", rawValue) + result.Complete.RawValue = rawValue + + // 检查是否在有效范围内 (30000-120000,因为实际值要除以100) + if rawValue < 30000 || rawValue > 120000 { + result.Complete.RawValue = 0x1FFFF + result.Complete.Value = 0 + result.Complete.IsValid = false + } else { + result.Complete.Value = float64(rawValue) / 100.0 + result.Complete.IsValid = true + } + + return result, nil +} diff --git a/model/protocol_test.go b/model/protocol_test.go new file mode 100644 index 0000000..bb06246 --- /dev/null +++ b/model/protocol_test.go @@ -0,0 +1,688 @@ +package model + +import ( + "testing" +) + +func TestIdentifyTxType(t *testing.T) { + // 测试数据:24 36 F3 02 96 37 06 01 00 04 00 00 00 04 9C D3 9A 01 88 F5 7E 00 2A 9C F6 + data := []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x37, 0x06, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x9C, 0xD3, 0x9A, 0x01, 0x88, 0xF5, 0x7E, 0x00, 0x2A, 0x9C, 0xF6} + + protocol := NewProtocol(data) + binary, hex, dec := protocol.IdentifyTxType() + + // 预期结果 + expectedBinary := "00100100" // 24 的二进制表示 + expectedHex := "24" // 第一个字节的十六进制表示 + expectedDec := uint8(36) // 24 的十进制表示 + + // 验证二进制结果 + if binary != expectedBinary { + t.Errorf("Binary representation incorrect. Got %s, want %s", binary, expectedBinary) + } + + // 验证十六进制结果 + if hex != expectedHex { + t.Errorf("Hex representation incorrect. Got %s, want %s", hex, expectedHex) + } + + // 验证十进制结果 + if dec != expectedDec { + t.Errorf("Decimal representation incorrect. Got %d, want %d", dec, expectedDec) + } +} + +func TestGetCompleteID(t *testing.T) { + // 测试数据:24 36 F3 02 96 37 06 01 00 04 00 00 00 04 9C D3 9A 01 88 F5 7E 00 2A 9C F6 + data := []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x37, 0x06, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x9C, 0xD3, 0x9A, 0x01, 0x88, 0xF5, 0x7E, 0x00, 0x2A, 0x9C, 0xF6} + + protocol := NewProtocol(data) + result, err := protocol.GetCompleteID() + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // 测试 HSB (应该是 00) + expectedHSBBinary := "00000000" // 00 的二进制 + expectedHSBHex := "00" // 00 的十六进制 + expectedHSBDec := uint8(0) // 00 的十进制 + + if result.HSB.Binary != expectedHSBBinary { + t.Errorf("HSB Binary incorrect. Got %s, want %s", result.HSB.Binary, expectedHSBBinary) + } + if result.HSB.Hex != expectedHSBHex { + t.Errorf("HSB Hex incorrect. Got %s, want %s", result.HSB.Hex, expectedHSBHex) + } + if result.HSB.Dec != expectedHSBDec { + t.Errorf("HSB Dec incorrect. Got %d, want %d", result.HSB.Dec, expectedHSBDec) + } + + // 测试 MSB (应该是 2A) + expectedMSBBinary := "00101010" // 2A 的二进制 + expectedMSBHex := "2A" // 2A 的十六进制 + expectedMSBDec := uint8(42) // 2A 的十进制 + + if result.MSB.Binary != expectedMSBBinary { + t.Errorf("MSB Binary incorrect. Got %s, want %s", result.MSB.Binary, expectedMSBBinary) + } + if result.MSB.Hex != expectedMSBHex { + t.Errorf("MSB Hex incorrect. Got %s, want %s", result.MSB.Hex, expectedMSBHex) + } + if result.MSB.Dec != expectedMSBDec { + t.Errorf("MSB Dec incorrect. Got %d, want %d", result.MSB.Dec, expectedMSBDec) + } + + // 测试 LSB (应该是 36) + expectedLSBBinary := "00110110" // 36 的二进制 + expectedLSBHex := "36" // 36 的十六进制 + expectedLSBDec := uint8(54) // 36 的十进制 + + if result.LSB.Binary != expectedLSBBinary { + t.Errorf("LSB Binary incorrect. Got %s, want %s", result.LSB.Binary, expectedLSBBinary) + } + if result.LSB.Hex != expectedLSBHex { + t.Errorf("LSB Hex incorrect. Got %s, want %s", result.LSB.Hex, expectedLSBHex) + } + if result.LSB.Dec != expectedLSBDec { + t.Errorf("LSB Dec incorrect. Got %d, want %d", result.LSB.Dec, expectedLSBDec) + } + + // 测试完整的24位ID (应该是 00 2A 36) + expectedCompleteBinary := "000000000010101000110110" // 完整24位二进制 + expectedCompleteHex := "002A36" // 完整24位十六进制 + expectedCompleteDec := uint32(0x002A36) // 完整24位十进制 + + if result.Complete.Binary != expectedCompleteBinary { + t.Errorf("Complete Binary incorrect. Got %s, want %s", result.Complete.Binary, expectedCompleteBinary) + } + if result.Complete.Hex != expectedCompleteHex { + t.Errorf("Complete Hex incorrect. Got %s, want %s", result.Complete.Hex, expectedCompleteHex) + } + if result.Complete.Dec != expectedCompleteDec { + t.Errorf("Complete Dec incorrect. Got %d, want %d", result.Complete.Dec, expectedCompleteDec) + } +} + +func TestGetWindDirection(t *testing.T) { + tests := []struct { + name string + data []byte + expectedValue uint16 + expectedValid bool + expectedDegree float64 + }{ + { + name: "Valid Direction", + data: []byte{0x24, 0x36, 0xF3, 0x02, 0x96}, // 原始数据,方向值为243 + expectedValue: 0xF3, + expectedValid: true, + expectedDegree: 243, + }, + { + name: "Invalid Direction", + data: []byte{0x24, 0x36, 0xFF, 0x03, 0x96}, // 设置一个大于359的值 + expectedValue: 0x1FF, + expectedValid: false, + expectedDegree: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + protocol := NewProtocol(tt.data) + result, err := protocol.GetWindDirection() + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if result.Complete.Value != tt.expectedValue { + t.Errorf("Value incorrect. Got %X, want %X", result.Complete.Value, tt.expectedValue) + } + + if result.Complete.IsValid != tt.expectedValid { + t.Errorf("IsValid incorrect. Got %v, want %v", result.Complete.IsValid, tt.expectedValid) + } + + if result.Complete.Degree != tt.expectedDegree { + t.Errorf("Degree incorrect. Got %v, want %v", result.Complete.Degree, tt.expectedDegree) + } + }) + } +} + +func TestGetTemperature(t *testing.T) { + tests := []struct { + name string + data []byte + expectedRaw uint16 + expectedTemp float64 + expectedValid bool + }{ + { + name: "Temperature 10.5°C", + // 0x1F9 = 505 -> (505-400)/10 = 10.5 + // TMP_H = 1 (001) + // TMP_M = F (1111) + // TMP_L = 9 (1001) + data: []byte{0x24, 0x36, 0xF3, 0x20, 0xF9}, // 设置byte3的bit29-31为001,byte4为0xF9 + expectedRaw: 0x1F9, + expectedTemp: 10.5, + expectedValid: true, + }, + { + name: "Temperature -10.5°C", + // 0x127 = 295 -> (295-400)/10 = -10.5 + // TMP_H = 1 (001) + // TMP_M = 2 (0010) + // TMP_L = 7 (0111) + data: []byte{0x24, 0x36, 0xF3, 0x20, 0x27}, // 设置byte3的bit29-31为001,byte4为0x27 + expectedRaw: 0x127, + expectedTemp: -10.5, + expectedValid: true, + }, + { + name: "Invalid Temperature", + // 0x7FF = 2047 (超出范围) + // TMP_H = 7 (111) + // TMP_M = F (1111) + // TMP_L = F (1111) + data: []byte{0x24, 0x36, 0xF3, 0xE0, 0xFF}, // 设置byte3的bit29-31为111,byte4为0xFF + expectedRaw: 0x7FF, + expectedTemp: 0, + expectedValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + protocol := NewProtocol(tt.data) + result, err := protocol.GetTemperature() + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if result.Complete.RawValue != tt.expectedRaw { + t.Errorf("Raw value incorrect. Got %X, want %X", result.Complete.RawValue, tt.expectedRaw) + } + + if result.Complete.Value != tt.expectedTemp { + t.Errorf("Temperature value incorrect. Got %.1f, want %.1f", result.Complete.Value, tt.expectedTemp) + } + + if result.Complete.IsValid != tt.expectedValid { + t.Errorf("Validity incorrect. Got %v, want %v", result.Complete.IsValid, tt.expectedValid) + } + }) + } +} + +func TestGetHumidity(t *testing.T) { + tests := []struct { + name string + data []byte + expectedRaw uint8 + expectedValue uint8 + expectedValid bool + }{ + { + name: "Valid Humidity 55%", + // 0x37 = 3*16 + 7 = 55% + data: []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x37}, + expectedRaw: 0x37, + expectedValue: 55, + expectedValid: true, + }, + { + name: "Valid Humidity 1%", + // 0x01 = 0*16 + 1 = 1% + data: []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x01}, + expectedRaw: 0x01, + expectedValue: 1, + expectedValid: true, + }, + { + name: "Valid Humidity 99%", + // 0x63 = 6*16 + 3 = 99% + data: []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x63}, + expectedRaw: 0x63, + expectedValue: 99, + expectedValid: true, + }, + { + name: "Invalid Humidity (Too High)", + // 0x64 = 6*16 + 4 = 100% (无效) + data: []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x64}, + expectedRaw: 0xFF, + expectedValue: 0, + expectedValid: false, + }, + { + name: "Invalid Humidity (Zero)", + // 0x00 = 0% (无效) + data: []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x00}, + expectedRaw: 0xFF, + expectedValue: 0, + expectedValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + protocol := NewProtocol(tt.data) + result, err := protocol.GetHumidity() + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if result.Complete.RawValue != tt.expectedRaw { + t.Errorf("Raw value incorrect. Got %X, want %X", result.Complete.RawValue, tt.expectedRaw) + } + + if result.Complete.Value != tt.expectedValue { + t.Errorf("Humidity value incorrect. Got %d%%, want %d%%", result.Complete.Value, tt.expectedValue) + } + + if result.Complete.IsValid != tt.expectedValid { + t.Errorf("Validity incorrect. Got %v, want %v", result.Complete.IsValid, tt.expectedValid) + } + }) + } +} + +func TestGetWindSpeed(t *testing.T) { + // 测试数据:24 36 F3 02 96 37 06 01 00 04 00 00 00 04 9C D3 9A 01 88 F5 7E 00 2A 9C F6 + data := []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x37, 0x06, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x9C, 0xD3, 0x9A, 0x01, 0x88, 0xF5, 0x7E, 0x00, 0x2A, 0x9C, 0xF6} + + protocol := NewProtocol(data) + result, err := protocol.GetWindSpeed() + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // 验证 WSP_FLAG (bit 25) + if !result.WspFlag.Value { + t.Error("WSP_FLAG should be 1") + } + + // 验证 WIND_H 和 WIND_L + if result.WspH.Value != 0x0 { + t.Errorf("WIND_H incorrect. Got %X, want 0", result.WspH.Value) + } + if result.WspL.Value != 0x6 { + t.Errorf("WIND_L incorrect. Got %X, want 6", result.WspL.Value) + } + + // 验证完整值(9bit模式:000 + bit27 + WIND_H + WIND_L) + expectedRaw := uint16(0x006) + if result.Complete.RawValue != expectedRaw { + t.Errorf("Raw value incorrect. Got %X, want %X", result.Complete.RawValue, expectedRaw) + } + + // 验证实际风速值:6/8*0.51 = 0.38250 m/s + expectedValue := float64(0x006) / 8.0 * 0.51 + if result.Complete.Value != expectedValue { + t.Errorf("Wind speed value incorrect. Got %.5f m/s, want %.5f m/s", + result.Complete.Value, expectedValue) + } +} + +func TestGetGustSpeed(t *testing.T) { + // 测试数据:24 36 F3 02 96 37 06 01 00 04 00 00 00 04 9C D3 9A 01 88 F5 7E 00 2A 9C F6 + data := []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x37, 0x06, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x9C, 0xD3, 0x9A, 0x01, 0x88, 0xF5, 0x7E, 0x00, 0x2A, 0x9C, 0xF6} + + protocol := NewProtocol(data) + result, err := protocol.GetGustSpeed() + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // 验证 GUST_H (0x0) + expectedGustHBinary := "0000" + if result.GustH.Binary != expectedGustHBinary { + t.Errorf("GUST_H Binary incorrect. Got %s, want %s", result.GustH.Binary, expectedGustHBinary) + } + if result.GustH.Value != 0x0 { + t.Errorf("GUST_H Value incorrect. Got %X, want 0", result.GustH.Value) + } + + // 验证 GUST_L (0x1) + expectedGustLBinary := "0001" + if result.GustL.Binary != expectedGustLBinary { + t.Errorf("GUST_L Binary incorrect. Got %s, want %s", result.GustL.Binary, expectedGustLBinary) + } + if result.GustL.Value != 0x1 { + t.Errorf("GUST_L Value incorrect. Got %X, want 1", result.GustL.Value) + } + + // 验证完整值 + expectedBinary := "00000001" // 0x01 + if result.Complete.Binary != expectedBinary { + t.Errorf("Complete Binary incorrect. Got %s, want %s", result.Complete.Binary, expectedBinary) + } + if result.Complete.RawValue != 0x01 { + t.Errorf("Raw value incorrect. Got %X, want 01", result.Complete.RawValue) + } + + // 验证实际阵风速度值:0x01 = 1, 1*0.51 = 0.51 m/s + expectedValue := float64(0x01) * 0.51 + if result.Complete.Value != expectedValue { + t.Errorf("Gust speed value incorrect. Got %.5f m/s, want %.5f m/s", result.Complete.Value, expectedValue) + } +} + +func TestGetRainfall(t *testing.T) { + // 测试数据:24 36 F3 02 96 37 06 01 00 04 00 00 00 04 9C D3 9A 01 88 F5 7E 00 2A 9C F6 + data := []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x37, 0x06, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x9C, 0xD3, 0x9A, 0x01, 0x88, 0xF5, 0x7E, 0x00, 0x2A, 0x9C, 0xF6} + + protocol := NewProtocol(data) + result, err := protocol.GetRainfall() + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // 验证 RAIN_HH (0x0) + expectedRainHHBinary := "0000" + if result.RainHH.Binary != expectedRainHHBinary { + t.Errorf("RAIN_HH Binary incorrect. Got %s, want %s", result.RainHH.Binary, expectedRainHHBinary) + } + if result.RainHH.Value != 0x0 { + t.Errorf("RAIN_HH Value incorrect. Got %X, want 0", result.RainHH.Value) + } + + // 验证 RAIN_HL (0x0) + expectedRainHLBinary := "0000" + if result.RainHL.Binary != expectedRainHLBinary { + t.Errorf("RAIN_HL Binary incorrect. Got %s, want %s", result.RainHL.Binary, expectedRainHLBinary) + } + if result.RainHL.Value != 0x0 { + t.Errorf("RAIN_HL Value incorrect. Got %X, want 0", result.RainHL.Value) + } + + // 验证 RAIN_LH (0x0) + expectedRainLHBinary := "0000" + if result.RainLH.Binary != expectedRainLHBinary { + t.Errorf("RAIN_LH Binary incorrect. Got %s, want %s", result.RainLH.Binary, expectedRainLHBinary) + } + if result.RainLH.Value != 0x0 { + t.Errorf("RAIN_LH Value incorrect. Got %X, want 0", result.RainLH.Value) + } + + // 验证 RAIN_LL (0x4) + expectedRainLLBinary := "0100" + if result.RainLL.Binary != expectedRainLLBinary { + t.Errorf("RAIN_LL Binary incorrect. Got %s, want %s", result.RainLL.Binary, expectedRainLLBinary) + } + if result.RainLL.Value != 0x4 { + t.Errorf("RAIN_LL Value incorrect. Got %X, want 4", result.RainLL.Value) + } + + // 验证完整值 + expectedBinary := "0000000000000100" // 0x0004 + if result.Complete.Binary != expectedBinary { + t.Errorf("Complete Binary incorrect. Got %s, want %s", result.Complete.Binary, expectedBinary) + } + if result.Complete.RawValue != 0x0004 { + t.Errorf("Raw value incorrect. Got %X, want 0004", result.Complete.RawValue) + } + + // 验证实际降雨量值:0x0004 = 4, 4*0.254 = 1.016 mm + expectedValue := float64(0x0004) * 0.254 + if result.Complete.Value != expectedValue { + t.Errorf("Rainfall value incorrect. Got %.3f mm, want %.3f mm", result.Complete.Value, expectedValue) + } +} + +func TestGetUVIndex(t *testing.T) { + tests := []struct { + name string + data []byte + expectedRaw uint16 + expectedValue float64 + expectedValid bool + }{ + { + name: "Valid UV Index", + // 测试数据:24 36 F3 02 96 37 06 01 00 04 00 00 00 04 9C D3 9A 01 88 F5 7E 00 2A 9C F6 + data: []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x37, 0x06, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x9C, 0xD3, 0x9A, 0x01, 0x88, 0xF5, 0x7E, 0x00, 0x2A, 0x9C, 0xF6}, + expectedRaw: 0x0000, // byte10=0x00, byte11=0x00 + expectedValue: 0, // 0 uW/c㎡ + expectedValid: true, + }, + { + name: "Invalid UV Index (Too High)", + data: []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x37, 0x06, 0x01, 0x00, 0x04, 0xFF, 0xFF}, // 设置一个超出范围的值 + expectedRaw: 0xFFFF, // 无效值 + expectedValue: 0, // 无效时返回0 + expectedValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + protocol := NewProtocol(tt.data) + result, err := protocol.GetUVIndex() + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if result.Complete.RawValue != tt.expectedRaw { + t.Errorf("Raw value incorrect. Got %X, want %X", result.Complete.RawValue, tt.expectedRaw) + } + + if result.Complete.Value != tt.expectedValue { + t.Errorf("UV Index value incorrect. Got %.1f uW/c㎡, want %.1f uW/c㎡", + result.Complete.Value, tt.expectedValue) + } + + if result.Complete.IsValid != tt.expectedValid { + t.Errorf("Validity incorrect. Got %v, want %v", result.Complete.IsValid, tt.expectedValid) + } + }) + } +} + +func TestGetLight(t *testing.T) { + // 测试数据:24 36 F3 02 96 37 06 01 00 04 00 00 00 04 9C D3 9A 01 88 F5 7E 00 2A 9C F6 + data := []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x37, 0x06, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x9C, 0xD3, 0x9A, 0x01, 0x88, 0xF5, 0x7E, 0x00, 0x2A, 0x9C, 0xF6} + + protocol := NewProtocol(data) + result, err := protocol.GetLight() + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // 验证原始值 (byte13-15: 00 04 9C) + expectedRaw := uint32(0x00049C) + if result.Complete.RawValue != expectedRaw { + t.Errorf("Raw value incorrect. Got %X, want %X", result.Complete.RawValue, expectedRaw) + } + + // 验证实际光照值:0x00049C = 1180, 1180/10 = 118.0 lux + expectedValue := float64(0x00049C) / 10.0 + if result.Complete.Value != expectedValue { + t.Errorf("Light value incorrect. Got %.1f lux, want %.1f lux", + result.Complete.Value, expectedValue) + } + + if !result.Complete.IsValid { + t.Error("Light value should be valid") + } +} + +func TestGetPressure(t *testing.T) { + // 测试数据:24 36 F3 02 96 37 06 01 00 04 00 00 00 04 9C D3 9A 01 88 F5 7E 00 2A 9C F6 + data := []byte{0x24, 0x36, 0xF3, 0x02, 0x96, 0x37, 0x06, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x9C, 0xD3, 0x9A, 0x01, 0x88, 0xF5, 0x7E, 0x00, 0x2A, 0x9C, 0xF6} + + protocol := NewProtocol(data) + result, err := protocol.GetPressure() + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // 验证原始值 (01 88 F5) + expectedRaw := uint32(0x0188F5) + if result.Complete.RawValue != expectedRaw { + t.Errorf("Raw value incorrect. Got %X, want %X", result.Complete.RawValue, expectedRaw) + } + + // 验证实际气压值:0x0188F5 = 100597, 100597/100 = 1005.97 hPa + expectedValue := float64(0x0188F5) / 100.0 + if result.Complete.Value != expectedValue { + t.Errorf("Pressure value incorrect. Got %.2f hPa, want %.2f hPa", + result.Complete.Value, expectedValue) + } + + if !result.Complete.IsValid { + t.Error("Pressure value should be valid") + } +} + +func TestParseNewData(t *testing.T) { + // 新的测试数据:24 F2 30 02 AF 51 03 01 00 08 00 00 00 00 00 6E C2 01 82 D8 5B 00 29 87 EA + data := []byte{0x24, 0xF2, 0x30, 0x02, 0xAF, 0x51, 0x03, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xC2, 0x01, 0x82, 0xD8, 0x5B, 0x00, 0x29, 0x87, 0xEA} + + protocol := NewProtocol(data) + + // 1. 解析风速 + windSpeed, err := protocol.GetWindSpeed() + if err != nil { + t.Fatalf("Failed to parse wind speed: %v", err) + } + t.Logf("Wind Speed: %.5f m/s (raw: 0x%X)", windSpeed.Complete.Value, windSpeed.Complete.RawValue) + + // 2. 解析阵风速度 + gustSpeed, err := protocol.GetGustSpeed() + if err != nil { + t.Fatalf("Failed to parse gust speed: %v", err) + } + t.Logf("Gust Speed: %.5f m/s (raw: 0x%X)", gustSpeed.Complete.Value, gustSpeed.Complete.RawValue) + + // 3. 解析温度 + temp, err := protocol.GetTemperature() + if err != nil { + t.Fatalf("Failed to parse temperature: %v", err) + } + t.Logf("Temperature: %.2f °C (raw: 0x%X)", temp.Complete.Value, temp.Complete.RawValue) + + // 4. 解析湿度 + humidity, err := protocol.GetHumidity() + if err != nil { + t.Fatalf("Failed to parse humidity: %v", err) + } + t.Logf("Humidity: %d%% (raw: 0x%X)", humidity.Complete.Value, humidity.Complete.RawValue) + + // 5. 解析光照 + light, err := protocol.GetLight() + if err != nil { + t.Fatalf("Failed to parse light: %v", err) + } + t.Logf("Light: %.1f lux (raw: 0x%X)", light.Complete.Value, light.Complete.RawValue) + + // 6. 解析大气压 + pressure, err := protocol.GetPressure() + if err != nil { + t.Fatalf("Failed to parse pressure: %v", err) + } + t.Logf("Pressure: %.2f hPa (raw: 0x%X)", pressure.Complete.Value, pressure.Complete.RawValue) + + // 7. 解析UV指数 + uv, err := protocol.GetUVIndex() + if err != nil { + t.Fatalf("Failed to parse UV index: %v", err) + } + t.Logf("UV Index: %.1f uW/c㎡ (raw: 0x%X)", uv.Complete.Value, uv.Complete.RawValue) + + // 8. 解析降雨量 + rainfall, err := protocol.GetRainfall() + if err != nil { + t.Fatalf("Failed to parse rainfall: %v", err) + } + t.Logf("Rainfall: %.3f mm (raw: 0x%X)", rainfall.Complete.Value, rainfall.Complete.RawValue) +} + +func TestParseNewDataWithDetails(t *testing.T) { + // 新的测试数据:24 F2 09 02 BA 4F 13 03 00 6A 02 33 04 13 5C AC FE 01 83 93 17 00 29 35 88 + data := []byte{0x24, 0xF2, 0x09, 0x02, 0xBA, 0x4F, 0x13, 0x03, 0x00, 0x6A, 0x02, 0x33, 0x04, 0x13, 0x5C, 0xAC, 0xFE, 0x01, 0x83, 0x93, 0x17, 0x00, 0x29, 0x35, 0x88} + + protocol := NewProtocol(data) + + // 1. 风速解析 + t.Log("\n=== 风速解析 ===") + windSpeed, _ := protocol.GetWindSpeed() + t.Logf("风速: %.5f m/s (raw: 0x%X)", windSpeed.Complete.Value, windSpeed.Complete.RawValue) + t.Logf("WSP_H: %s (0x%X), WSP_L: %s (0x%X)", + windSpeed.WspH.Binary, windSpeed.WspH.Value, + windSpeed.WspL.Binary, windSpeed.WspL.Value) + if windSpeed.Extend.Value != 0 { + t.Logf("WIND_Extend: %s (0x%X)", windSpeed.Extend.Binary, windSpeed.Extend.Value) + } + + // 2. 温度解析 + t.Log("\n=== 温度解析 ===") + temp, _ := protocol.GetTemperature() + t.Logf("温度: %.2f °C (raw: 0x%X)", temp.Complete.Value, temp.Complete.RawValue) + t.Logf("TMP_H: %s (0x%X), TMP_M: %s (0x%X), TMP_L: %s (0x%X)", + temp.TmpH.Binary, temp.TmpH.Value, + temp.TmpM.Binary, temp.TmpM.Value, + temp.TmpL.Binary, temp.TmpL.Value) + + // 3. 湿度解析 + t.Log("\n=== 湿度解析 ===") + humidity, _ := protocol.GetHumidity() + t.Logf("湿度: %d%% (raw: 0x%X)", humidity.Complete.Value, humidity.Complete.RawValue) + t.Logf("HM_H: %s (0x%X), HM_L: %s (0x%X)", + humidity.HmH.Binary, humidity.HmH.Value, + humidity.HmL.Binary, humidity.HmL.Value) + + // 4. 光照解析 + t.Log("\n=== 光照解析 ===") + light, _ := protocol.GetLight() + t.Logf("光照: %.1f lux (raw: 0x%X)", light.Complete.Value, light.Complete.RawValue) + t.Logf("原始字节: %02X %02X %02X", data[12], data[13], data[14]) + + // 5. 大气压解析 + t.Log("\n=== 大气压解析 ===") + pressure, _ := protocol.GetPressure() + t.Logf("大气压: %.2f hPa (raw: 0x%X)", pressure.Complete.Value, pressure.Complete.RawValue) + t.Logf("原始字节: %02X %02X %02X", data[17], data[18], data[19]) + + // 6. UV指数解析 + t.Log("\n=== UV指数解析 ===") + uv, _ := protocol.GetUVIndex() + t.Logf("UV指数: %.1f uW/c㎡ (raw: 0x%X)", uv.Complete.Value, uv.Complete.RawValue) + t.Logf("原始字节: %02X %02X", data[10], data[11]) + + // 7. 降雨量解析 + t.Log("\n=== 降雨量解析 ===") + rainfall, _ := protocol.GetRainfall() + t.Logf("降雨量: %.3f mm (raw: 0x%X)", rainfall.Complete.Value, rainfall.Complete.RawValue) + t.Logf("原始字节: %02X %02X", data[8], data[9]) + + // 8. 阵风速度解析 + t.Log("\n=== 阵风速度解析 ===") + gust, _ := protocol.GetGustSpeed() + t.Logf("阵风速度: %.2f m/s (raw: 0x%X)", gust.Complete.Value, gust.Complete.RawValue) + t.Logf("原始字节: %02X", data[7]) + + // 9. 风向解析 + t.Log("\n=== 风向解析 ===") + windDir, _ := protocol.GetWindDirection() + t.Logf("风向: %.1f° (raw: 0x%X)", windDir.Complete.Degree, windDir.Complete.Value) + t.Logf("原始字节: %02X %02X", data[2], data[3]) + + // 10. 设备ID解析 + t.Log("\n=== 设备ID解析 ===") + id, _ := protocol.GetCompleteID() + t.Logf("设备ID: %02X %02X %02X", id.HSB.Dec, id.MSB.Dec, id.LSB.Dec) + t.Logf("原始字节: HSB=%02X, MSB=%02X, LSB=%02X", data[21], data[22], data[1]) +}