|
| 1 | +# Space Packet Protocol (SPP) Guide |
| 2 | + |
| 3 | +## Introduction |
| 4 | + |
| 5 | +This guide covers how to use our Space Packet Protocol (SPP) package for spacecraft communications. The SPP package implements the CCSDS 133.0-B-2 standard, providing tools for creating and managing space packets in your satellite communication systems. |
| 6 | + |
| 7 | +## Quick Start |
| 8 | + |
| 9 | +### Creating a Basic Telemetry Packet |
| 10 | + |
| 11 | +```go |
| 12 | +import "github.com/ravisuhag/astro/pkg/spp" |
| 13 | + |
| 14 | +// Create a telemetry packet with APID 123 |
| 15 | +data := []byte("temperature=22.5,pressure=1013.2") |
| 16 | +packet, err := spp.NewTMPacket(123, data) |
| 17 | +if err != nil { |
| 18 | + log.Fatal(err) |
| 19 | +} |
| 20 | + |
| 21 | +// Encode the packet for transmission |
| 22 | +encoded, err := packet.Encode() |
| 23 | +if err != nil { |
| 24 | + log.Fatal(err) |
| 25 | +} |
| 26 | +// encoded is now ready for transmission |
| 27 | +``` |
| 28 | + |
| 29 | +### Creating a Telecommand Packet |
| 30 | + |
| 31 | +```go |
| 32 | +// Create a telecommand packet with APID 456 |
| 33 | +command := []byte("SET_MODE=SAFE") |
| 34 | +packet, err := spp.NewTCPacket(456, command) |
| 35 | +if err != nil { |
| 36 | + log.Fatal(err) |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +## Common Use Cases |
| 41 | + |
| 42 | +### 1. Sensor Data Transmission |
| 43 | + |
| 44 | +When sending sensor data from your satellite: |
| 45 | + |
| 46 | +```go |
| 47 | +type SensorData struct { |
| 48 | + Temperature float64 |
| 49 | + Pressure float64 |
| 50 | + Timestamp int64 |
| 51 | +} |
| 52 | + |
| 53 | +func SendSensorData(data SensorData) error { |
| 54 | + // Convert sensor data to bytes |
| 55 | + payload := fmt.Sprintf("temp=%.2f,press=%.2f,time=%d", |
| 56 | + data.Temperature, |
| 57 | + data.Pressure, |
| 58 | + data.Timestamp, |
| 59 | + ) |
| 60 | + |
| 61 | + // Create telemetry packet with APID 100 (sensor data) |
| 62 | + packet, err := spp.NewTMPacket(100, []byte(payload)) |
| 63 | + if err != nil { |
| 64 | + return fmt.Errorf("failed to create packet: %w", err) |
| 65 | + } |
| 66 | + |
| 67 | + // Add timestamp in secondary header |
| 68 | + secondaryHeader := spp.SecondaryHeader{ |
| 69 | + Timestamp: uint64(time.Now().UnixNano()), |
| 70 | + } |
| 71 | + packet, err = spp.NewTMPacket(100, []byte(payload), |
| 72 | + spp.WithSecondaryHeader(secondaryHeader)) |
| 73 | + |
| 74 | + // Encode and send... |
| 75 | + return nil |
| 76 | +} |
| 77 | +``` |
| 78 | + |
| 79 | +### 2. Command Reception |
| 80 | + |
| 81 | +When receiving commands: |
| 82 | + |
| 83 | +```go |
| 84 | +func ProcessPacket(rawData []byte) error { |
| 85 | + // Decode the received packet |
| 86 | + packet, err := spp.Decode(rawData) |
| 87 | + if err != nil { |
| 88 | + return fmt.Errorf("failed to decode packet: %w", err) |
| 89 | + } |
| 90 | + |
| 91 | + // Check if it's a command packet |
| 92 | + if packet.PrimaryHeader.Type != 1 { // Type 1 = TC |
| 93 | + return fmt.Errorf("expected command packet, got type %d", |
| 94 | + packet.PrimaryHeader.Type) |
| 95 | + } |
| 96 | + |
| 97 | + // Process based on APID |
| 98 | + switch packet.PrimaryHeader.APID { |
| 99 | + case 456: // Command APID |
| 100 | + return processCommand(packet.UserData) |
| 101 | + default: |
| 102 | + return fmt.Errorf("unknown APID: %d", packet.PrimaryHeader.APID) |
| 103 | + } |
| 104 | +} |
| 105 | +``` |
| 106 | + |
| 107 | +### 3. Large Data Transmission |
| 108 | + |
| 109 | +When sending large amounts of data that need to be split across multiple packets: |
| 110 | + |
| 111 | +```go |
| 112 | +func SendLargeData(data []byte, apid uint16) error { |
| 113 | + // Maximum data size per packet (minus headers) |
| 114 | + const maxDataSize = 65535 - 6 // 6 bytes for primary header |
| 115 | + |
| 116 | + // Split data into chunks |
| 117 | + for i := 0; i < len(data); i += maxDataSize { |
| 118 | + end := i + maxDataSize |
| 119 | + if end > len(data) { |
| 120 | + end = len(data) |
| 121 | + } |
| 122 | + |
| 123 | + chunk := data[i:end] |
| 124 | + var seqFlags uint8 |
| 125 | + |
| 126 | + switch { |
| 127 | + case i == 0 && end == len(data): |
| 128 | + seqFlags = 3 // Standalone packet |
| 129 | + case i == 0: |
| 130 | + seqFlags = 1 // First packet |
| 131 | + case end == len(data): |
| 132 | + seqFlags = 2 // Last packet |
| 133 | + default: |
| 134 | + seqFlags = 0 // Continuation packet |
| 135 | + } |
| 136 | + |
| 137 | + // Create packet with sequence flags |
| 138 | + packet, err := spp.NewTMPacket(apid, chunk) |
| 139 | + if err != nil { |
| 140 | + return err |
| 141 | + } |
| 142 | + packet.PrimaryHeader.SequenceFlags = seqFlags |
| 143 | + |
| 144 | + // Encode and send... |
| 145 | + } |
| 146 | + return nil |
| 147 | +} |
| 148 | +``` |
| 149 | + |
| 150 | +## Best Practices |
| 151 | + |
| 152 | +### APID Management |
| 153 | + |
| 154 | +1. **Reserved APIDs**: Keep a registry of APIDs and their purposes |
| 155 | + - 0-63: Reserved for system use |
| 156 | + - 64-127: Telemetry data |
| 157 | + - 128-191: Science data |
| 158 | + - 192-255: Command and control |
| 159 | + |
| 160 | +2. **APID Documentation Example**: |
| 161 | +```go |
| 162 | +const ( |
| 163 | + APID_HOUSEKEEPING = 64 // Basic spacecraft telemetry |
| 164 | + APID_THERMAL = 65 // Thermal subsystem data |
| 165 | + APID_POWER = 66 // Power subsystem data |
| 166 | + APID_ATTITUDE = 67 // Attitude determination data |
| 167 | + APID_PAYLOAD = 128 // Science payload data |
| 168 | + APID_COMMAND = 192 // Command packets |
| 169 | +) |
| 170 | +``` |
| 171 | + |
| 172 | +### Error Handling |
| 173 | + |
| 174 | +Always handle common error cases: |
| 175 | + |
| 176 | +```go |
| 177 | +packet, err := spp.NewTMPacket(apid, data) |
| 178 | +switch { |
| 179 | +case errors.Is(err, spp.ErrInvalidAPID): |
| 180 | + // Handle invalid APID |
| 181 | +case errors.Is(err, spp.ErrDataTooLong): |
| 182 | + // Handle data too long |
| 183 | +case err != nil: |
| 184 | + // Handle other errors |
| 185 | +} |
| 186 | +``` |
| 187 | + |
| 188 | +### Packet Validation |
| 189 | + |
| 190 | +Always validate received packets: |
| 191 | + |
| 192 | +```go |
| 193 | +func validatePacket(packet *spp.SpacePacket) error { |
| 194 | + // Check packet length |
| 195 | + if len(packet.UserData) == 0 { |
| 196 | + return fmt.Errorf("empty packet data") |
| 197 | + } |
| 198 | + |
| 199 | + // Verify APID is in valid range |
| 200 | + if packet.PrimaryHeader.APID > 2047 { |
| 201 | + return fmt.Errorf("invalid APID") |
| 202 | + } |
| 203 | + |
| 204 | + // Additional validation... |
| 205 | + return nil |
| 206 | +} |
| 207 | +``` |
| 208 | + |
| 209 | +## Performance Tips |
| 210 | + |
| 211 | +1. **Buffer Reuse**: For high-frequency packet processing, reuse buffers: |
| 212 | + |
| 213 | +```go |
| 214 | +// Create a buffer pool |
| 215 | +var bufferPool = sync.Pool{ |
| 216 | + New: func() interface{} { |
| 217 | + return make([]byte, 65542) // Max packet size |
| 218 | + }, |
| 219 | +} |
| 220 | + |
| 221 | +func processPacketsEfficiently(data []byte) { |
| 222 | + buffer := bufferPool.Get().([]byte) |
| 223 | + defer bufferPool.Put(buffer) |
| 224 | + |
| 225 | + // Use buffer for packet processing... |
| 226 | +} |
| 227 | +``` |
| 228 | + |
| 229 | +2. **Batch Processing**: When sending multiple packets, batch them: |
| 230 | + |
| 231 | +```go |
| 232 | +func sendPacketBatch(packets []*spp.SpacePacket) error { |
| 233 | + encodedData := make([][]byte, 0, len(packets)) |
| 234 | + |
| 235 | + // Encode all packets first |
| 236 | + for _, packet := range packets { |
| 237 | + encoded, err := packet.Encode() |
| 238 | + if err != nil { |
| 239 | + return err |
| 240 | + } |
| 241 | + encodedData = append(encodedData, encoded) |
| 242 | + } |
| 243 | + |
| 244 | + // Then send in batch |
| 245 | + return sendBatch(encodedData) |
| 246 | +} |
| 247 | +``` |
| 248 | + |
| 249 | +## Debugging Tips |
| 250 | + |
| 251 | +1. Use the `Humanize()` method for debugging: |
| 252 | + |
| 253 | +```go |
| 254 | +packet, _ := spp.NewTMPacket(123, data) |
| 255 | +log.Printf("Packet details:\n%s", packet.Humanize()) |
| 256 | +``` |
| 257 | + |
| 258 | +2. Enable packet logging in development: |
| 259 | + |
| 260 | +```go |
| 261 | +func logPacket(packet *spp.SpacePacket) { |
| 262 | + if os.Getenv("DEBUG") == "1" { |
| 263 | + log.Printf("APID: %d, Type: %d, Seq: %d, Length: %d", |
| 264 | + packet.PrimaryHeader.APID, |
| 265 | + packet.PrimaryHeader.Type, |
| 266 | + packet.PrimaryHeader.SequenceCount, |
| 267 | + packet.PrimaryHeader.PacketLength) |
| 268 | + } |
| 269 | +} |
| 270 | +``` |
| 271 | + |
| 272 | +## Common Pitfalls |
| 273 | + |
| 274 | +1. **Packet Size Limits**: Don't exceed maximum packet size (65542 bytes) |
| 275 | +2. **APID Range**: Ensure APIDs are within valid range (0-2047) |
| 276 | +3. **Sequence Counting**: Handle sequence number wraparound correctly |
| 277 | +4. **Secondary Headers**: Don't forget to set the secondary header flag when using secondary headers |
| 278 | + |
| 279 | +## Further Reading |
| 280 | + |
| 281 | +- [CCSDS 133.0-B-2](https://public.ccsds.org/Pubs/133x0b2c1.pdf) - Space Packet Protocol |
| 282 | +- [ECSS-E-70-41A](https://ecss.nl) - Ground systems and operations |
| 283 | +- [Package Documentation](https://godoc.org/github.com/your-org/astro/pkg/spp) |
0 commit comments