// Package pst sends commands to PstRotator over its UDP listener. // // PstRotator (Codrut Buda YO3DMU) exposes a simple text/XML protocol on // a configurable UDP port (default 12000 on localhost). Each command is a // single fire-and-forget datagram — no handshake, no response. This keeps // us connectionless and means a misconfigured port silently no-ops rather // than hanging the UI. Run the matching "Test" action to confirm the link. package pst import ( "fmt" "net" "time" ) // Client is a stateless UDP sender. Safe to construct cheaply per call — // the underlying socket only lives for the length of one Write. type Client struct { Host string // hostname or IP of the PstRotator host (usually "127.0.0.1") Port int // UDP port (PstRotator default = 12000) } // New returns a Client with sane defaults applied for empty fields. func New(host string, port int) *Client { if host == "" { host = "127.0.0.1" } if port <= 0 || port > 65535 { port = 12000 } return &Client{Host: host, Port: port} } // GoTo points the antenna at azimuth (0-359°). If hasElevation is true // and el >= 0 the elevation field is included too (VHF/satellite setups); // otherwise PstRotator just turns in azimuth. func (c *Client) GoTo(az int, hasElevation bool, el int) error { az = ((az % 360) + 360) % 360 // normalise to [0,360) if hasElevation && el >= 0 && el <= 180 { return c.send(fmt.Sprintf("%d%d", az, el)) } return c.send(fmt.Sprintf("%d", az)) } // Stop interrupts any in-progress rotation. func (c *Client) Stop() error { return c.send("1") } // Park sends the rotator to its parked position (configured inside // PstRotator itself — we just trigger it). func (c *Client) Park() error { return c.send("1") } func (c *Client) send(payload string) error { addr := fmt.Sprintf("%s:%d", c.Host, c.Port) conn, err := net.DialTimeout("udp", addr, 2*time.Second) if err != nil { return fmt.Errorf("dial PstRotator at %s: %w", addr, err) } defer conn.Close() _ = conn.SetWriteDeadline(time.Now().Add(2 * time.Second)) if _, err := conn.Write([]byte(payload)); err != nil { return fmt.Errorf("send to PstRotator: %w", err) } return nil }