6 Commits

Author SHA1 Message Date
Joel Wetzell cf2d3a2ae0 mess around with fuzzing 2026-05-23 07:41:52 -05:00
Joel Wetzell acb6708b80 switch to fixed size array 2026-05-23 07:41:42 -05:00
Joel Wetzell e8b3869048 Add LICENSE 2026-05-20 13:27:00 -05:00
Joel Wetzell 64814b9e05 add some basic benchmarks 2026-05-14 21:11:55 -05:00
Joel Wetzell 21beb6316e remove panics 2026-05-14 14:53:15 -05:00
jwetzell 4ec174462b fix readme 2024-12-26 17:41:32 -06:00
7 changed files with 103 additions and 46 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Joel Wetzell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+2 -2
View File
@@ -1,4 +1,4 @@
# free-d-js
# free-d-go
Collection of free-d things written in Go
- [free-d encoding/decoding library](./pkg/free-d/)
- [free-d encoding/decoding library](./free_d.go)
+1 -1
View File
@@ -8,7 +8,7 @@ import (
func main() {
messageBytes := []byte{0xd1, 0x01, 0x5a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x7f, 0xff, 0x40, 0x7f, 0xff, 0x80, 0x7f, 0xff,
messageBytes := [29]byte{0xd1, 0x01, 0x5a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x7f, 0xff, 0x40, 0x7f, 0xff, 0x80, 0x7f, 0xff,
0xc0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 50,
}
+7 -1
View File
@@ -28,7 +28,13 @@ func main() {
if err != nil {
slog.Error("error reading from UDP", "err", err)
} else if length > 0 {
message, err := freeD.Decode(buffer[0:length])
if length != 29 {
slog.Warn("received UDP packet with unexpected length", "length", length)
continue
}
var messageBytes [29]byte
copy(messageBytes[:], buffer[0:length])
message, err := freeD.Decode(messageBytes)
if err != nil {
slog.Error("error decoding", "err", err)
}
+26 -36
View File
@@ -1,7 +1,6 @@
package freeD
import (
"bytes"
"encoding/binary"
"errors"
"math"
@@ -19,40 +18,37 @@ type FreeDPosition struct {
Focus int32
}
func Encode(message FreeDPosition) []byte {
bytes := []byte{}
func Encode(message FreeDPosition) [29]byte {
var bytes [29]byte
bytes = append(bytes, 0xd1)
bytes = append(bytes, message.ID)
bytes[0] = 0xd1
bytes[1] = message.ID
bytes = append(bytes, rotationToFreeDUnits(message.Pan)...)
bytes = append(bytes, rotationToFreeDUnits(message.Tilt)...)
bytes = append(bytes, rotationToFreeDUnits(message.Roll)...)
copy(bytes[2:5], rotationToFreeDUnits(message.Pan))
copy(bytes[5:8], rotationToFreeDUnits(message.Tilt))
copy(bytes[8:11], rotationToFreeDUnits(message.Roll))
bytes = append(bytes, positionToFreeDUnits(message.PosX)...)
bytes = append(bytes, positionToFreeDUnits(message.PosY)...)
bytes = append(bytes, positionToFreeDUnits(message.PosZ)...)
copy(bytes[11:14], positionToFreeDUnits(message.PosX))
copy(bytes[14:17], positionToFreeDUnits(message.PosY))
copy(bytes[17:20], positionToFreeDUnits(message.PosZ))
bytes = append(bytes, uint8(math.Trunc(float64(message.Zoom/65536))))
bytes = append(bytes, uint8(int64(message.Zoom/256)%256))
bytes = append(bytes, uint8(int32(message.Zoom)%256))
bytes[20] = uint8(math.Trunc(float64(message.Zoom / 65536)))
bytes[21] = uint8(int64(message.Zoom/256) % 256)
bytes[22] = uint8(int32(message.Zoom) % 256)
bytes = append(bytes, uint8(math.Trunc(float64(message.Focus/65536))))
bytes = append(bytes, uint8(int64(message.Focus/256)%256))
bytes = append(bytes, uint8(int32(message.Focus)%256))
bytes[23] = uint8(math.Trunc(float64(message.Focus / 65536)))
bytes[24] = uint8(int64(message.Focus/256) % 256)
bytes[25] = uint8(int32(message.Focus) % 256)
// spare area?
bytes = append(bytes, 0, 0)
bytes[26] = 0
bytes[27] = 0
bytes = append(bytes, checksum(bytes[0:28]))
bytes[28] = checksum(bytes[0:28])
return bytes
}
func Decode(bytes []byte) (FreeDPosition, error) {
if len(bytes) != 29 {
return FreeDPosition{}, errors.New("FreeD packet must be 29 bytes long")
}
func Decode(bytes [29]byte) (FreeDPosition, error) {
if bytes[0] != 0xd1 {
return FreeDPosition{}, errors.New("only FreeD position messages are currently supported")
}
@@ -100,23 +96,17 @@ func checksum(bytes []byte) uint8 {
func rotationToFreeDUnits(rotation float32) []byte {
units := int32(rotation * 32768 * 256)
var buf bytes.Buffer
err := binary.Write(&buf, binary.BigEndian, units)
if err != nil {
panic(err)
}
return buf.Bytes()[0:3]
buf := make([]byte, 4)
binary.BigEndian.PutUint32(buf, uint32(units))
return buf[0:3]
}
func positionToFreeDUnits(position float32) []byte {
units := int32(position * 64 * 256)
var buf bytes.Buffer
err := binary.Write(&buf, binary.BigEndian, units)
if err != nil {
panic(err)
}
return buf.Bytes()[0:3]
buf := make([]byte, 4)
binary.BigEndian.PutUint32(buf, uint32(units))
return buf[0:3]
}
func freeDUnitsToRotation(upper uint8, middle uint8, lower uint8) float32 {
+45 -5
View File
@@ -9,12 +9,12 @@ import (
func TestMessageDecoding(t *testing.T) {
testCases := []struct {
description string
bytes []byte
bytes [29]byte
expected FreeDPosition
}{
{
description: "simple position message",
bytes: []byte{0xd1, 0x01, 0x5a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x7f, 0xff, 0x40, 0x7f, 0xff, 0x80, 0x7f, 0xff,
bytes: [29]byte{0xd1, 0x01, 0x5a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x7f, 0xff, 0x40, 0x7f, 0xff, 0x80, 0x7f, 0xff,
0xc0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 50,
},
expected: FreeDPosition{
@@ -48,15 +48,15 @@ func TestMessageDecoding(t *testing.T) {
}
}
func TestMessageEncoding(t *testing.T) {
func TestGoodMessageEncoding(t *testing.T) {
testCases := []struct {
description string
expected []byte
expected [29]byte
message FreeDPosition
}{
{
description: "simple position message",
expected: []byte{0xd1, 0x01, 0x5a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x7f, 0xff, 0x40, 0x7f, 0xff, 0x80, 0x7f, 0xff,
expected: [29]byte{0xd1, 0x01, 0x5a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x7f, 0xff, 0x40, 0x7f, 0xff, 0x80, 0x7f, 0xff,
0xc0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 50,
},
message: FreeDPosition{
@@ -84,3 +84,43 @@ func TestMessageEncoding(t *testing.T) {
}
}
}
func BenchmarkMessageDecoding(b *testing.B) {
data := [29]byte{0xd1, 0x01, 0x5a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x7f, 0xff, 0x40, 0x7f, 0xff, 0x80, 0x7f, 0xff,
0xc0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 50,
}
for b.Loop() {
Decode(data)
}
}
func BenchmarkMessageEncoding(b *testing.B) {
message := FreeDPosition{
ID: 1,
Pan: 180,
Tilt: 90,
Roll: -180,
PosX: 131069,
PosY: 131070,
PosZ: 131071,
Zoom: 66051,
Focus: 263430,
}
for b.Loop() {
Encode(message)
}
}
func FuzzMessageDecoding(f *testing.F) {
f.Add([]byte{0xd1, 0x01, 0x5a, 0x00, 0x00, 0x2d, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x7f, 0xff, 0x40, 0x7f, 0xff, 0x80, 0x7f, 0xff,
0xc0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 50,
})
f.Fuzz(func(t *testing.T, data []byte) {
if len(data) != 29 {
return
}
var dataArray [29]byte
copy(dataArray[:], data)
Decode(dataArray)
})
}
+1 -1
View File
@@ -1,3 +1,3 @@
module github.com/jwetzell/free-d-go
go 1.23.4
go 1.26.2