mirror of
https://github.com/renorris/openfsd
synced 2026-03-22 14:35:36 +08:00
179 lines
5.8 KiB
Go
179 lines
5.8 KiB
Go
package protocol
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
FastPilotPositionTypeFast = iota
|
|
FastPilotPositionTypeSlow
|
|
FastPilotPositionTypeStopped
|
|
)
|
|
|
|
type VelocityVector struct {
|
|
X float64 `validate:"min=-9999.0,max=9999.0"`
|
|
Y float64 `validate:"min=-9999.0,max=9999.0"`
|
|
Z float64 `validate:"min=-9999.0,max=9999.0"`
|
|
}
|
|
|
|
type FastPilotPositionPDU struct {
|
|
Type int `validate:"min=0,max=2"`
|
|
From string `validate:"required,alphanum,max=7"`
|
|
Lat float64 `validate:"min=-90.0,max=90.0"`
|
|
Lng float64 `validate:"min=-180.0,max=180.0"`
|
|
AltitudeTrue float64 `validate:"min=-1500.0,max=99999.0"`
|
|
AltitudeAgl float64 `validate:"min=-1500.0,max=99999.0"`
|
|
Pitch float64 `validate:"min=-360.0,max=360.0"`
|
|
Heading float64 `validate:"min=-360.0,max=360.0"`
|
|
Bank float64 `validate:"min=-360.0,max=360.0"`
|
|
PositionalVelocityVector VelocityVector `validate:""` // meters per second
|
|
RotationalVelocityVector VelocityVector `validate:""` // radians per second
|
|
NoseGearAngle float64 `validate:"min=-360.0,max=360.0"`
|
|
}
|
|
|
|
func (p *FastPilotPositionPDU) Serialize() string {
|
|
switch p.Type {
|
|
case FastPilotPositionTypeFast:
|
|
return fmt.Sprintf("^%s:%.6f:%.6f:%.2f:%.2f:%d:%.4f:%.4f:%.4f:%.4f:%.4f:%.4f:%.2f%s", p.From, p.Lat, p.Lng, p.AltitudeTrue, p.AltitudeAgl, packPitchBankHeading(p.Pitch, p.Bank, p.Heading), p.PositionalVelocityVector.X, p.PositionalVelocityVector.Y, p.PositionalVelocityVector.Z, p.RotationalVelocityVector.X, p.RotationalVelocityVector.Y, p.RotationalVelocityVector.Z, p.NoseGearAngle, PacketDelimeter)
|
|
case FastPilotPositionTypeSlow:
|
|
return fmt.Sprintf("#SL%s:%.6f:%.6f:%.2f:%.2f:%d:%.4f:%.4f:%.4f:%.4f:%.4f:%.4f:%.2f%s", p.From, p.Lat, p.Lng, p.AltitudeTrue, p.AltitudeAgl, packPitchBankHeading(p.Pitch, p.Bank, p.Heading), p.PositionalVelocityVector.X, p.PositionalVelocityVector.Y, p.PositionalVelocityVector.Z, p.RotationalVelocityVector.X, p.RotationalVelocityVector.Y, p.RotationalVelocityVector.Z, p.NoseGearAngle, PacketDelimeter)
|
|
default: // FastPilotPositionTypeStopped
|
|
return fmt.Sprintf("#ST%s:%.6f:%.6f:%.2f:%.2f:%d:%.2f%s", p.From, p.Lat, p.Lng, p.AltitudeTrue, p.AltitudeAgl, packPitchBankHeading(p.Pitch, p.Bank, p.Heading), p.NoseGearAngle, PacketDelimeter)
|
|
}
|
|
}
|
|
|
|
func ParseFastPilotPositionPDU(rawPacket string) (*FastPilotPositionPDU, error) {
|
|
rawPacket = strings.TrimSuffix(rawPacket, PacketDelimeter)
|
|
|
|
var pduType int
|
|
if strings.HasPrefix(rawPacket, "^") {
|
|
pduType = FastPilotPositionTypeFast
|
|
rawPacket = strings.TrimPrefix(rawPacket, "^")
|
|
} else if strings.HasPrefix(rawPacket, "#SL") {
|
|
pduType = FastPilotPositionTypeSlow
|
|
rawPacket = strings.TrimPrefix(rawPacket, "#SL")
|
|
} else if strings.HasPrefix(rawPacket, "#ST") {
|
|
pduType = FastPilotPositionTypeStopped
|
|
rawPacket = strings.TrimPrefix(rawPacket, "#ST")
|
|
} else {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
|
|
fields := strings.Split(rawPacket, Delimeter)
|
|
|
|
if pduType == FastPilotPositionTypeStopped {
|
|
if len(fields) != 7 {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
} else {
|
|
if len(fields) != 13 {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
}
|
|
|
|
lat, err := strconv.ParseFloat(fields[1], 64)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
|
|
lng, err := strconv.ParseFloat(fields[2], 64)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
|
|
altTrue, err := strconv.ParseFloat(fields[3], 64)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
|
|
altAgl, err := strconv.ParseFloat(fields[4], 64)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
|
|
pbh, err := strconv.ParseUint(fields[5], 10, 32)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
pitch, bank, heading := unpackPitchBankHeading(uint32(pbh))
|
|
|
|
var positionalVector VelocityVector
|
|
var rotationalVector VelocityVector
|
|
var noseGearAngle float64
|
|
if pduType == FastPilotPositionTypeStopped {
|
|
positionalVector = VelocityVector{
|
|
X: 0,
|
|
Y: 0,
|
|
Z: 0,
|
|
}
|
|
rotationalVector = VelocityVector{
|
|
X: 0,
|
|
Y: 0,
|
|
Z: 0,
|
|
}
|
|
noseGearAngle, err = strconv.ParseFloat(fields[6], 64)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
} else {
|
|
positionalVector.X, err = strconv.ParseFloat(fields[6], 64)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
|
|
positionalVector.Y, err = strconv.ParseFloat(fields[7], 64)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
|
|
positionalVector.Z, err = strconv.ParseFloat(fields[8], 64)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
|
|
rotationalVector.X, err = strconv.ParseFloat(fields[9], 64)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
|
|
rotationalVector.Y, err = strconv.ParseFloat(fields[10], 64)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
|
|
rotationalVector.Z, err = strconv.ParseFloat(fields[11], 64)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
|
|
noseGearAngle, err = strconv.ParseFloat(fields[12], 64)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
}
|
|
|
|
pdu := FastPilotPositionPDU{
|
|
Type: pduType,
|
|
From: fields[0],
|
|
Lat: lat,
|
|
Lng: lng,
|
|
AltitudeTrue: altTrue,
|
|
AltitudeAgl: altAgl,
|
|
Pitch: pitch,
|
|
Heading: heading,
|
|
Bank: bank,
|
|
PositionalVelocityVector: positionalVector,
|
|
RotationalVelocityVector: rotationalVector,
|
|
NoseGearAngle: noseGearAngle,
|
|
}
|
|
|
|
err = V.Struct(pdu)
|
|
if err != nil {
|
|
return nil, NewGenericFSDError(SyntaxError)
|
|
}
|
|
|
|
return &pdu, nil
|
|
}
|