Skip to content

Commit

Permalink
feat: add support for floating point signals.
Browse files Browse the repository at this point in the history
We add support for the SIG_VALTYPE_ DBC declaration for 32-bit floating
point signals, in addition to the already existing support for integer
typed signals.
In this commit only SIG_VALTYPE : 1 is supported for floating points,
due to inabillity to test 64-bit floating point signals for the time.
  • Loading branch information
Ezhik452 authored and Jassob committed May 24, 2024
1 parent 0a81a70 commit 1166f24
Show file tree
Hide file tree
Showing 11 changed files with 570 additions and 49 deletions.
20 changes: 20 additions & 0 deletions internal/generate/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,26 @@ func (c *compiler) collectDescriptors() {
func (c *compiler) addMetadata() {
for _, def := range c.defs {
switch def := def.(type) {
case *dbc.SignalValueTypeDef:
signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName))
if !ok {
c.addWarning(&compileError{def: def, reason: "no declared signal"})
continue
}
switch def.SignalValueType {
case dbc.SignalValueTypeInt:
signal.IsFloat = false
case dbc.SignalValueTypeFloat32:
if signal.Length == 32 {
signal.IsFloat = true
} else {
reason := fmt.Sprintf("incorrect float signal length: %d", signal.Length)
c.addWarning(&compileError{def: def, reason: reason})
}
default:
reason := fmt.Sprintf("unsupported signal value type: %v", def.SignalValueType)
c.addWarning(&compileError{def: def, reason: reason})
}
case *dbc.CommentDef:
switch def.ObjectType {
case dbc.ObjectTypeMessage:
Expand Down
70 changes: 70 additions & 0 deletions internal/generate/compile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,34 @@ func TestCompile_ExampleDBC(t *testing.T) {
},
},
},
{
ID: 600,
Name: "IOFloat32",
Length: 8,
SenderNode: "IO",
SendType: descriptor.SendTypeNone,
Signals: []*descriptor.Signal{
{
Name: "Float32ValueNoRange",
Length: 32,
IsSigned: true,
IsFloat: true,
Scale: 1,
ReceiverNodes: []string{"DBG"},
},
{
Name: "Float32WithRange",
Start: 32,
Length: 32,
IsSigned: true,
IsFloat: true,
Scale: 1,
Min: -100,
Max: 100,
ReceiverNodes: []string{"DBG"},
},
},
},
},
}
input, err := os.ReadFile(exampleDBCFile)
Expand All @@ -306,3 +334,45 @@ func TestCompile_ExampleDBC(t *testing.T) {
}
assert.DeepEqual(t, exampleDatabase, result.Database)
}

func TestCompile_Float64SignalWarningExpected(t *testing.T) {
finish := runTestInDir(t, "../..")
defer finish()
const exampleDBCFile = "testdata/dbc-invalid/example/example_float64_signal.dbc"
input, err := os.ReadFile(exampleDBCFile)
assert.NilError(t, err)
result, err := Compile(exampleDBCFile, input)
if err != nil {
t.Fatal(err)
}
// We expect one warning from unsupported float64 signal
assert.Equal(t, len(result.Warnings), 1)
}

func TestCompile_Float32InvalidSignalNameWarningExpected(t *testing.T) {
finish := runTestInDir(t, "../..")
defer finish()
const exampleDBCFile = "testdata/dbc-invalid/example/example_float32_invalid_signal_name.dbc"
input, err := os.ReadFile(exampleDBCFile)
assert.NilError(t, err)
result, err := Compile(exampleDBCFile, input)
if err != nil {
t.Fatal(err)
}
// We expect one warning for incorrect signal name in SIGVAL_TYPE_ declaration
assert.Equal(t, len(result.Warnings), 1)
}

func TestCompile_Float32InvalidSignalLengthWarningExpected(t *testing.T) {
finish := runTestInDir(t, "../..")
defer finish()
const exampleDBCFile = "testdata/dbc-invalid/example/example_float32_invalid_signal_length.dbc"
input, err := os.ReadFile(exampleDBCFile)
assert.NilError(t, err)
result, err := Compile(exampleDBCFile, input)
if err != nil {
t.Fatal(err)
}
// We expect one warning for incorrect signal length in declaration of float32 signal
assert.Equal(t, len(result.Warnings), 1)
}
26 changes: 26 additions & 0 deletions internal/generate/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,32 @@ func TestExampleDatabase_MarshalUnmarshal(t *testing.T) {
Data: can.Data{0x00, 0xe0, 0x2e},
},
},

{
name: "IOFloat32_1",
m: examplecan.NewIOFloat32().
SetFloat32ValueNoRange(3.14).
SetFloat32WithRange(42),
f: can.Frame{
ID: 600,
Length: 8,
Data: can.Data{0xc3, 0xf5, 0x48, 0x40, 0x00, 0x00, 0x28, 0x42},
},
},

{
name: "IOFloat32_2",
m: examplecan.NewIOFloat32().
SetFloat32ValueNoRange(42.42).
// This value is out of range, so we expect SaturatedCast for
// rightmost range border: 100
SetFloat32WithRange(142),
f: can.Frame{
ID: 600,
Length: 8,
Data: can.Data{0x14, 0xae, 0x29, 0x42, 0x00, 0x00, 0xc8, 0x42},
},
},
} {
tt := tt
t.Run(tt.name, func(t *testing.T) {
Expand Down
13 changes: 11 additions & 2 deletions internal/generate/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -810,9 +810,12 @@ func hasPhysicalRepresentation(s *descriptor.Signal) bool {
hasOffset := s.Offset != 0
hasRange := s.Min != 0 || s.Max != 0
var hasConstrainedRange bool
if s.IsSigned {
switch {
case s.IsFloat:
hasConstrainedRange = s.Min > s.MinFloat() || s.Max < s.MaxFloat()
case s.IsSigned:
hasConstrainedRange = s.Min > float64(s.MinSigned()) || s.Max < float64(s.MaxSigned())
} else {
default:
hasConstrainedRange = s.Min > 0 || s.Max < float64(s.MaxUnsigned())
}
return hasScale || hasOffset || hasRange && hasConstrainedRange
Expand Down Expand Up @@ -841,6 +844,8 @@ func signalType(m *descriptor.Message, s *descriptor.Signal) string {
func signalPrimitiveType(s *descriptor.Signal) types.Type {
var t types.BasicKind
switch {
case s.Length == 32 && s.IsFloat:
t = types.Float32
case s.Length == 1:
t = types.Bool
case s.Length <= 8 && s.IsSigned:
Expand All @@ -866,6 +871,8 @@ func signalPrimitiveType(s *descriptor.Signal) types.Type {
func signalPrimitiveSuperType(s *descriptor.Signal) types.Type {
var t types.BasicKind
switch {
case s.IsFloat:
t = types.Float64
case s.Length == 1:
t = types.Bool
case s.IsSigned:
Expand All @@ -878,6 +885,8 @@ func signalPrimitiveSuperType(s *descriptor.Signal) types.Type {

func signalSuperType(s *descriptor.Signal) string {
switch {
case s.Length <= 32 && s.IsFloat:
return "Float"
case s.Length == 1:
return "Bool"
case s.IsSigned:
Expand Down
45 changes: 45 additions & 0 deletions pkg/descriptor/signal.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package descriptor

import (
"math"
"unsafe"

"go.einride.tech/can"
)
Expand All @@ -18,6 +19,8 @@ type Signal struct {
IsBigEndian bool
// IsSigned is true if the signal uses raw signed values.
IsSigned bool
// IsFloat is true if the signal uses 32-bit floating point values
IsFloat bool
// IsMultiplexer is true if the signal is the multiplexor of a multiplexed message.
IsMultiplexer bool
// IsMultiplexed is true if the signal is multiplexed.
Expand Down Expand Up @@ -144,6 +147,17 @@ func (s *Signal) UnmarshalBool(d can.Data) bool {
return d.Bit(s.Start)
}

// UnmarshalFloat returns the float64 value of the signam in the provided CAN frame.
func (s *Signal) UnmarshalFloat(d can.Data) float64 {
var i uint64
if s.IsBigEndian {
i = d.UnsignedBitsBigEndian(s.Start, s.Length)
} else {
i = d.UnsignedBitsLittleEndian(s.Start, s.Length)
}
return float64(*((*float32)(unsafe.Pointer(&i))))
}

// MarshalUnsigned sets the unsigned value of the signal in the provided CAN frame.
func (s *Signal) MarshalUnsigned(d *can.Data, value uint64) {
if s.IsBigEndian {
Expand All @@ -167,6 +181,13 @@ func (s *Signal) MarshalBool(d *can.Data, value bool) {
d.SetBit(s.Start, value)
}

// Marshalfloat sets the float64 value of the signal in the provided CAN frame.
func (s *Signal) MarshalFloat(d *can.Data, value float64) {
f := float32(value)
i := uint64(*((*uint32)(unsafe.Pointer(&f))))
s.MarshalUnsigned(d, i)
}

// MaxUnsigned returns the maximum unsigned value representable by the signal.
func (s *Signal) MaxUnsigned() uint64 {
return (2 << (s.Length - 1)) - 1
Expand All @@ -182,6 +203,16 @@ func (s *Signal) MaxSigned() int64 {
return (2 << (s.Length - 1) / 2) - 1
}

// MinSigned returns the minimum signed value representable by the signal.
func (s *Signal) MinFloat() float64 {
return -math.MaxFloat32
}

// MaxSigned returns the maximum signed value representable by the signal.
func (s *Signal) MaxFloat() float64 {
return math.MaxFloat32
}

// SaturatedCastSigned performs a saturated cast of an int64 to the value domain of the signal.
func (s *Signal) SaturatedCastSigned(value int64) int64 {
min := s.MinSigned()
Expand All @@ -204,3 +235,17 @@ func (s *Signal) SaturatedCastUnsigned(value uint64) uint64 {
}
return value
}

// SaturatedCastUnsigned performs a saturated cast of a uint64 to the value domain of the signal.
func (s *Signal) SaturatedCastFloat(value float64) float64 {
min := s.MinFloat()
max := s.MaxFloat()
switch {
case value < min:
return min
case value > max:
return max
default:
return value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
VERSION ""

NS_ :

BS_:

BU_: DBG IO

BO_ 42 IOFloat32: 8 IO
SG_ Float32Signal : 0|16@1- (1,0) [0|0] "" DBG

SIG_VALTYPE_ 42 Float32Signal: 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
VERSION ""

NS_ :

BS_:

BU_: DBG IO

BO_ 42 IOFloat32: 8 IO
SG_ Float32Signal : 0|32@1- (1,0) [0|0] "" DBG

SIG_VALTYPE_ 42 SomeOtherSignal: 1;
12 changes: 12 additions & 0 deletions testdata/dbc-invalid/example/example_float64_signal.dbc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
VERSION ""

NS_ :

BS_:

BU_: DBG IO

BO_ 42 IOFloat64: 8 IO
SG_ Float64Signal : 0|64@1- (1,0) [0|0] "" DBG

SIG_VALTYPE_ 42 Float64Signal: 2;
7 changes: 7 additions & 0 deletions testdata/dbc/example/example.dbc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ BO_ 500 IODebug: 6 IO
SG_ TestBoolEnum : 32|1@1+ (1,0) [0|0] "" DBG
SG_ TestScaledEnum : 40|2@1+ (2,0) [0|6] "" DBG

BO_ 600 IOFloat32: 8 IO
SG_ Float32ValueNoRange : 0|32@1- (1,0) [0|0] "" DBG
SG_ Float32WithRange : 32|32@1- (1,0) [-100|100] "" DBG

EV_ BrakeEngaged: 0 [0|1] "" 0 10 DUMMY_NODE_VECTOR0 Vector__XXX;
EV_ Torque: 1 [0|30000] "mNm" 500 16 DUMMY_NODE_VECTOR0 Vector__XXX;

Expand Down Expand Up @@ -77,3 +81,6 @@ VAL_ 100 Command 3 "Headlights On" 2 "Reboot" 1 "Sync" 0 "None" ;
VAL_ 500 TestEnum 2 "Two" 1 "One" ;
VAL_ 500 TestScaledEnum 3 "Six" 2 "Four" 1 "Two" 0 "Zero" ;
VAL_ 500 TestBoolEnum 1 "One" 0 "Zero" ;

SIG_VALTYPE_ 600 Float32ValueNoRange: 1;
SIG_VALTYPE_ 600 Float32WithRange: 1;
Loading

0 comments on commit 1166f24

Please sign in to comment.