Files
openfsd/protocol/fast_pilot_position.go
Reese Norris 05ed593a4b initial commit
2024-04-04 19:40:43 -07:00

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
}