A dynamic Go implementation of the Inter-Module Communication (IMC) protocol. This library allows for communication with LSTS systems (Neptus, DUNE, etc.) by reading the protocol specification directly from an IMC.xml file.
- Dynamic Protocol Loading: Load Any
IMC.xmlversion at runtime. - Generic Message Handling: Work with messages using a simple
map[string]anyinterface. - UDP Transporter: Support for unicast and multicast communication.
- Multicast Coexistence: Implements
SO_REUSEADDRandSO_REUSEPORTto allow multiple processes to share the same IMC ports. - Typed Message Helpers: Auto-generated Go structs for type-safe field access and IDE autocomplete.
- Nested Message Support: Full recursion for
messageandmessage-listtypes. - Strict Validation: CRC16 checks and synchronization number verification.
To use imc-go in your own project:
-
Initialize your module:
go mod init my-project
-
Add the dependency: If the module is local, use the
replacedirective:go mod edit -replace imc-go=../path/to/imc-go go get imc-go
-
Import and use:
import "github.com/oceanscan/imc-go"
- Go 1.22 or later
- Access to an
IMC.xmlspecification file.
go mod tidyimport "github.com/oceanscan/imc-go"
// Parse the XML specification
xmlProto, _ := imc.ParseXML("IMC.xml")
// Create a protocol engine
proto := imc.NewProtocol(xmlProto)// Create a new UDP transporter
trans, _ := imc.NewUDPTransporter(proto, "0.0.0.0:0")
defer trans.Close()
// Create an Announce message
msg, _ := proto.CreateMessage("Announce")
msg.Fields["sys_name"] = "My-Go-Node"
msg.Fields["sys_type"] = uint8(0) // CCU
// Send to a specific address
trans.Send(msg, "127.0.0.1:6002")// Listen on the standard IMC multicast port
trans, _ := imc.NewUDPTransporter(proto, "0.0.0.0:30101")
defer trans.Close()
// Join the IMC multicast group
trans.JoinMulticast("224.0.75.69:30101")
for {
msg, addr, err := trans.Receive()
if err == nil {
abbrev := proto.Messages[msg.Header.MGID].Abbrev
fmt.Printf("Received %s from %v\n", abbrev, addr)
}
}For a more convenient development experience, you can use generated structs to avoid raw map access:
import "github.com/oceanscan/imc-go"
// Receiving and converting to typed
msg, addr, _ := trans.Receive()
abbrev := proto.Messages[msg.Header.MGID].Abbrev
// Use CreateTyped for easy conversion
typedMsg, err := proto.CreateTyped(abbrev, msg)
if err == nil {
switch m := typedMsg.(type) {
case *imc.Announce:
fmt.Printf("Received Announce from %s: Name=%s, Type=%d\n",
addr, m.SysName, m.SysType)
case *imc.EstimatedState:
fmt.Printf("Vehicle Position: Lat=%f, Lon=%f\n", m.Lat, m.Lon)
}
}
// Creating a typed message for sending
ann := imc.Announce{
SysName: "MyNode",
SysType: 2, // UUV
}
trans.Send(ann.ToMessage(), "127.0.0.1:6001")If the IMC.xml specification changes or you want to update the generated helpers, use the included generator tool:
- Place the new
IMC.xmlin the root of the project. - Run the generator:
go run cmd/gen_messages/main.goThis will update messages.go with structs for all messages defined in the XML.
The project includes several command-line tools in the cmd/ directory to demonstrate the library's capabilities.
Joins the standard LSTS multicast group and prints received Announce messages from other systems on the network.
go run cmd/multicast_listener/main.goParses a binary .lsf log file and prints message statistics.
# Make sure the test data is available
go run cmd/parse_lsf/main.goUpdates the typed message structs in messages.go based on the current IMC.xml.
go run cmd/gen_messages/main.goTo compile the tools for a Raspberry Pi or other ARM devices from your development machine:
GOOS=linux GOARCH=arm64 go build -o listener-arm64 cmd/multicast_listener/main.goGOOS=linux GOARCH=arm go build -o listener-arm cmd/multicast_listener/main.goThe library is cross-platform and supports Windows. To compile for Windows from Linux or macOS:
GOOS=windows GOARCH=amd64 go build -o listener.exe cmd/multicast_listener/main.go