Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: decoder on component expansion #66

Merged
merged 1 commit into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/fitconv/fitcsv/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ func (c *FitToCsvConv) writeMesg(mesg proto.Message) {
if c.options.unknownNumber && field.Name == factory.NameUnknown {
name = formatUnknown(int(field.Num))
}
if subField, ok := field.SubFieldSubtitution(&mesg); ok {
if subField := field.SubFieldSubtitution(&mesg); subField != nil {
name, units = subField.Name, subField.Units
}

Expand Down
12 changes: 6 additions & 6 deletions decoder/accumulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func NewAccumulator() *Accumulator {
return &Accumulator{} // No need to make AccumulatedValues as it will be created on append anyway.
}

func (a *Accumulator) Collect(mesgNum typedef.MesgNum, destFieldNum byte, value int64) {
func (a *Accumulator) Collect(mesgNum typedef.MesgNum, destFieldNum byte, value uint32) {
for i := range a.AccumulatedValues {
field := &a.AccumulatedValues[i]
if field.MesgNum == mesgNum && field.DestFieldNum == destFieldNum {
Expand All @@ -33,7 +33,7 @@ func (a *Accumulator) Collect(mesgNum typedef.MesgNum, destFieldNum byte, value
})
}

func (a *Accumulator) Accumulate(mesgNum typedef.MesgNum, destFieldNum byte, value int64, bits byte) int64 {
func (a *Accumulator) Accumulate(mesgNum typedef.MesgNum, destFieldNum byte, value uint32, bits byte) uint32 {
for i := range a.AccumulatedValues {
av := &a.AccumulatedValues[i]
if av.MesgNum == mesgNum && av.DestFieldNum == destFieldNum {
Expand All @@ -48,12 +48,12 @@ func (a *Accumulator) Reset() { a.AccumulatedValues = a.AccumulatedValues[:0] }
type AccumulatedValue struct {
MesgNum typedef.MesgNum
DestFieldNum byte
Last int64
Value int64
Last uint32
Value uint32
}

func (a *AccumulatedValue) Accumulate(value int64, bits byte) int64 {
var mask int64 = (1 << bits) - 1
func (a *AccumulatedValue) Accumulate(value uint32, bits byte) uint32 {
var mask uint32 = (1 << bits) - 1
a.Value += (value - a.Last) & mask
a.Last = value
return a.Value
Expand Down
4 changes: 2 additions & 2 deletions decoder/accumulator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ func TestCollect(t *testing.T) {
type value struct {
mesgNum typedef.MesgNum
destFieldNum byte
value int64
expected int64
value uint32
expected uint32
}

tt := []struct {
Expand Down
78 changes: 78 additions & 0 deletions decoder/bits.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package decoder

import (
"github.com/muktihari/fit/profile/basetype"
)

const (
bit = 8
maxBit = 32
size = maxBit / bit
)

// bitsFromValue convert value into 32-bits unsigned integer.
//
// Profile.xlsx (on Bits header's comment) says: Current implementation only supports Bits value of max 32.
func bitsFromValue(value any) (bits uint32, ok bool) {
switch val := value.(type) {
case int8:
return uint32(val), true
case uint8:
return uint32(val), true
case int16:
return uint32(val), true
case uint16:
return uint32(val), true
case int32:
return uint32(val), true
case uint32:
return uint32(val), true
case int64:
return uint32(val), true
case uint64:
return uint32(val), true
case float32:
return uint32(val), true
case float64:
return uint32(val), true
case []byte:
if len(val) > size {
return 0, false
}
for i := range val {
if val[i] == basetype.ByteInvalid { // all values must be valid
return 0, false
}
bits |= uint32(val[i]) << (i * bit) // little-endian
}
return bits, true
}
return 0, false
}

// valueFromBits cast back bits into it's original value.
func valueFromBits(bits uint32, baseType basetype.BaseType) any {
switch baseType {
case basetype.Sint8:
return int8(bits)
case basetype.Uint8, basetype.Uint8z:
return uint8(bits)
case basetype.Sint16:
return int16(bits)
case basetype.Uint16, basetype.Uint16z:
return uint16(bits)
case basetype.Sint32:
return int32(bits)
case basetype.Uint32, basetype.Uint32z:
return uint32(bits)
case basetype.Float32:
return float32(bits)
case basetype.Float64:
return float64(bits)
case basetype.Sint64:
return int64(bits)
case basetype.Uint64, basetype.Uint64z:
return uint64(bits)
}
return baseType.Invalid()
}
73 changes: 73 additions & 0 deletions decoder/bits_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package decoder

import (
"fmt"
"testing"

"github.com/muktihari/fit/profile/basetype"
)

func TestBitsFromValue(t *testing.T) {
tt := []struct {
value any
expected uint32
ok bool
}{
{value: int8(10), expected: 10, ok: true},
{value: uint8(10), expected: 10, ok: true},
{value: int16(10), expected: 10, ok: true},
{value: uint16(10), expected: 10, ok: true},
{value: int32(10), expected: 10, ok: true},
{value: uint32(10), expected: 10, ok: true},
{value: int64(10), expected: 10, ok: true},
{value: uint64(10), expected: 10, ok: true},
{value: float32(10), expected: 10, ok: true},
{value: float64(10), expected: 10, ok: true},
{value: []byte{1, 1, 1}, expected: 1<<0 | 1<<8 | 1<<16, ok: true},
{value: []byte{1, 255, 1}, expected: 0, ok: false},
{value: make([]byte, 33), expected: 0, ok: false},
{value: "string value", expected: 0, ok: false},
}

for _, tc := range tt {
t.Run(fmt.Sprintf("%v (%T)", tc.value, tc.value), func(t *testing.T) {
res, ok := bitsFromValue(tc.value)
if ok != tc.ok {
t.Fatalf("expected ok: %t, got: %t", tc.ok, ok)
}
if res != tc.expected {
t.Fatalf("expected: %d, got: %d", tc.expected, res)
}
})
}
}

func TestValueFromBits(t *testing.T) {
tt := []struct {
sbits uint32
basetype basetype.BaseType
value any
ok bool
}{
{sbits: 10, basetype: basetype.Sint8, value: int8(10), ok: true},
{sbits: 10, basetype: basetype.Uint8, value: uint8(10), ok: true},
{sbits: 10, basetype: basetype.Sint16, value: int16(10), ok: true},
{sbits: 10, basetype: basetype.Uint16, value: uint16(10), ok: true},
{sbits: 10, basetype: basetype.Sint32, value: int32(10), ok: true},
{sbits: 10, basetype: basetype.Uint32, value: uint32(10), ok: true},
{sbits: 10, basetype: basetype.Sint64, value: int64(10), ok: true},
{sbits: 10, basetype: basetype.Uint64, value: uint64(10), ok: true},
{sbits: 10, basetype: basetype.Float32, value: float32(10), ok: true},
{sbits: 10, basetype: basetype.Float64, value: float64(10), ok: true},
{sbits: 10, basetype: basetype.String, value: basetype.StringInvalid, ok: false},
}

for _, tc := range tt {
t.Run(fmt.Sprintf("%s %v (%T)", tc.basetype, tc.value, tc.value), func(t *testing.T) {
res := valueFromBits(tc.sbits, tc.basetype)
if res != tc.value {
t.Fatalf("expected: %v, got: %v", tc.value, res)
}
})
}
}
Loading