845 lines
21 KiB
Go
845 lines
21 KiB
Go
package model
|
||
|
||
import (
|
||
"fmt"
|
||
"time"
|
||
)
|
||
|
||
// 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
|
||
}
|
||
|
||
// RS485Protocol 定义RS485协议结构
|
||
type RS485Protocol struct {
|
||
RawData []byte
|
||
}
|
||
|
||
// NewRS485Protocol 创建新的RS485协议实例
|
||
func NewRS485Protocol(data []byte) *RS485Protocol {
|
||
return &RS485Protocol{
|
||
RawData: data,
|
||
}
|
||
}
|
||
|
||
// ValidateRS485Data 验证RS485数据是否有效
|
||
func ValidateRS485Data(data []byte) bool {
|
||
// 检查数据长度是否为25字节
|
||
if len(data) != 25 {
|
||
return false
|
||
}
|
||
// 检查起始字节是否为0x24
|
||
if data[0] != 0x24 {
|
||
return false
|
||
}
|
||
return true
|
||
}
|
||
|
||
// RS485WeatherData 存储RS485气象数据
|
||
type RS485WeatherData struct {
|
||
Temperature float64 // 温度
|
||
Humidity float64 // 湿度
|
||
WindSpeed float64 // 风速
|
||
WindDirection float64 // 风向
|
||
Rainfall float64 // 雨量
|
||
Light float64 // 光照
|
||
UV float64 // 紫外线
|
||
Pressure float64 // 气压
|
||
DeviceID string // 设备ID
|
||
ReceivedAt time.Time // 接收时间
|
||
RawDataHex string // 原始数据十六进制
|
||
}
|
||
|
||
// ParseRS485Data 解析RS485数据
|
||
func (p *RS485Protocol) ParseRS485Data() (*RS485WeatherData, error) {
|
||
if !ValidateRS485Data(p.RawData) {
|
||
return nil, fmt.Errorf("无效的RS485数据格式")
|
||
}
|
||
|
||
data := &RS485WeatherData{}
|
||
|
||
// 解析温度 (索引5-6)
|
||
tempRaw := int16(p.RawData[5])<<8 | int16(p.RawData[6])
|
||
data.Temperature = float64(tempRaw) / 10.0
|
||
|
||
// 解析湿度 (索引7-8)
|
||
humRaw := int16(p.RawData[7])<<8 | int16(p.RawData[8])
|
||
data.Humidity = float64(humRaw) / 10.0
|
||
|
||
// 解析风速 (索引9-10)
|
||
windSpeedRaw := int16(p.RawData[9])<<8 | int16(p.RawData[10])
|
||
data.WindSpeed = float64(windSpeedRaw) / 10.0
|
||
|
||
// 解析风向 (索引11-12)
|
||
windDirRaw := int16(p.RawData[11])<<8 | int16(p.RawData[12])
|
||
data.WindDirection = float64(windDirRaw)
|
||
|
||
// 解析雨量 (索引13-14)
|
||
rainRaw := int16(p.RawData[13])<<8 | int16(p.RawData[14])
|
||
data.Rainfall = float64(rainRaw) / 10.0
|
||
|
||
// 解析光照 (索引15-16)
|
||
lightRaw := int16(p.RawData[15])<<8 | int16(p.RawData[16])
|
||
data.Light = float64(lightRaw)
|
||
|
||
// 解析紫外线 (索引17-18)
|
||
uvRaw := int16(p.RawData[17])<<8 | int16(p.RawData[18])
|
||
data.UV = float64(uvRaw) / 10.0
|
||
|
||
// 解析气压 (索引19-20)
|
||
pressureRaw := int16(p.RawData[19])<<8 | int16(p.RawData[20])
|
||
data.Pressure = float64(pressureRaw) / 10.0
|
||
|
||
return data, nil
|
||
}
|
||
|
||
// String 返回RS485WeatherData的字符串表示
|
||
func (w *RS485WeatherData) String() string {
|
||
return fmt.Sprintf(`
|
||
设备ID: RS485-%s
|
||
温度: %.1f°C
|
||
湿度: %.1f%%
|
||
风向: %.1f°
|
||
风速: %.2f m/s
|
||
降雨量: %.2f mm
|
||
光照: %.2f lux
|
||
紫外线: %.2f
|
||
气压: %.2f hPa
|
||
时间: %s`,
|
||
w.DeviceID,
|
||
w.Temperature,
|
||
w.Humidity,
|
||
w.WindDirection,
|
||
w.WindSpeed,
|
||
w.Rainfall,
|
||
w.Light,
|
||
w.UV,
|
||
w.Pressure,
|
||
w.ReceivedAt.Format("2006-01-02 15:04:05"),
|
||
)
|
||
}
|