Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ _testmain.go
*.prof

binaries/
.idea/
57 changes: 34 additions & 23 deletions atc2json/atc2json.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,13 @@ func Parse(atcData []byte) (*EcgData, error) {
reader := bytes.NewReader(atcData)

header := AtcFileHeader{}
binary.Read(reader, binary.LittleEndian, &header)
err := binary.Read(reader, binary.LittleEndian, &header)
if err != nil {
return nil, fmt.Errorf("E#2D4V51: Failed to read data. Error: %v\n", err)
}

if header.FileSignature != AtcFileSignature {
return nil, fmt.Errorf("Wrong file signature")
return nil, fmt.Errorf("E#2D4V67: Wrong file signature")
}

blockHeader := BlockHeader{}
Expand All @@ -92,7 +95,7 @@ func Parse(atcData []byte) (*EcgData, error) {
if err == io.EOF {
break
}
return nil, fmt.Errorf("Error reading file: %s", err.Error())
return nil, fmt.Errorf("E#2D4V6H: Error reading file: %s", err.Error())
}

blockType := string(blockHeader.BlockId[:])
Expand All @@ -103,107 +106,112 @@ func Parse(atcData []byte) (*EcgData, error) {
fmtBlock = &FmtBlock{}
err = binary.Read(reader, binary.LittleEndian, fmtBlock)
if err != nil {
return nil, fmt.Errorf("Error reading buffer: %s", err.Error())
return nil, fmt.Errorf("E#2D4V6P: Error reading buffer: %s", err.Error())
}
err = verifyChecksum(atcData, blockStart, blockHeader.Length, reader)
if err != nil {
return nil, err
return nil, fmt.Errorf("E#2D4V75: Checksum verification failed for 'fmt' block. Error: %v\n", err)
}

case "info":
infoBlock = &InfoBlock{}
err = binary.Read(reader, binary.LittleEndian, infoBlock)
if err != nil {
return nil, fmt.Errorf("Error reading buffer: %s", err.Error())
return nil, fmt.Errorf("E#2D4V6P: Error reading buffer: %s", err.Error())
}
err = verifyChecksum(atcData, blockStart, blockHeader.Length, reader)
if err != nil {
return nil, err
return nil, fmt.Errorf("E#2D4VHM: Checksum verification failed for 'info' block. Error: %v\n", err)
}

// Space after word is intended, per spec - cp 2019-2-19
case "ecg ":
leadISamples = make([]int16, blockHeader.Length/2)
err = binary.Read(reader, binary.LittleEndian, &leadISamples)
if err != nil {
return nil, fmt.Errorf("Error reading buffer: %s", err.Error())
return nil, fmt.Errorf("E#2D4V6P: Error reading buffer: %s", err.Error())
}

err = verifyChecksum(atcData, blockStart, blockHeader.Length, reader)
if err != nil {
return nil, err
return nil, fmt.Errorf("E#2D4VHZ: Checksum verification failed for 'ecg' block. Error: %v\n", err)
}

case "ecg2":
leadIISamples = make([]int16, blockHeader.Length/2)
err = binary.Read(reader, binary.LittleEndian, &leadIISamples)
if err != nil {
return nil, fmt.Errorf("Error reading buffer: %s", err.Error())
return nil, fmt.Errorf("E#2D4VFZ: Error reading buffer: %s", err.Error())
}

err = verifyChecksum(atcData, blockStart, blockHeader.Length, reader)
if err != nil {
return nil, err
return nil, fmt.Errorf("E#2D4VIN: Checksum verification failed for 'ecg2' block. Error: %v\n", err)
}

case "ecg3":
leadIIISamples = make([]int16, blockHeader.Length/2)
err = binary.Read(reader, binary.LittleEndian, &leadIIISamples)
if err != nil {
return nil, fmt.Errorf("Error reading buffer: %s", err.Error())
return nil, fmt.Errorf("E#2D4VFP: Error reading buffer: %s", err.Error())
}

err = verifyChecksum(atcData, blockStart, blockHeader.Length, reader)
if err != nil {
return nil, err
return nil, fmt.Errorf("E#2D4VJC: Checksum verification failed for 'ecg3' block. Error: %v\n", err)
}

case "ecg4":
aVRSamples = make([]int16, blockHeader.Length/2)
err = binary.Read(reader, binary.LittleEndian, &aVRSamples)
if err != nil {
return nil, fmt.Errorf("Error reading buffer: %s", err.Error())
return nil, fmt.Errorf("E#2D4VFC: Error reading buffer: %s", err.Error())
}

err = verifyChecksum(atcData, blockStart, blockHeader.Length, reader)
if err != nil {
return nil, err
return nil, fmt.Errorf("E#2D4VJW: Checksum verification failed for 'ecg4' block. Error: %v\n", err)
}

case "ecg5":
aVLSamples = make([]int16, blockHeader.Length/2)
err = binary.Read(reader, binary.LittleEndian, &aVLSamples)
if err != nil {
return nil, fmt.Errorf("Error reading buffer: %s", err.Error())
return nil, fmt.Errorf("E#2D4VEW: Error reading buffer: %s", err.Error())
}

err = verifyChecksum(atcData, blockStart, blockHeader.Length, reader)
if err != nil {
return nil, err
return nil, fmt.Errorf("E#2D4VKT: Checksum verification failed for 'ecg5' block. Error: %v\n", err)
}

case "ecg6":
aVFSamples = make([]int16, blockHeader.Length/2)
err = binary.Read(reader, binary.LittleEndian, &aVFSamples)
if err != nil {
return nil, fmt.Errorf("Error reading buffer: %s", err.Error())
return nil, fmt.Errorf("E#2D4VEI: Error reading buffer: %s", err.Error())
}

err = verifyChecksum(atcData, blockStart, blockHeader.Length, reader)
if err != nil {
return nil, err
return nil, fmt.Errorf("E#2D4VLG: Checksum verification failed for 'ecg6' block. Error: %v\n", err)
}
default:
discardBuf := make([]byte, blockHeader.Length+ChecksumLength)
_, err = reader.Read(discardBuf)
if err != nil {
return nil, fmt.Errorf("Error reading input: %s", err.Error())
return nil, fmt.Errorf("E#2D4VE5: Error reading buffer: %s", err.Error())
}
}
}

result := &EcgData{}

// If there was no fmtblock that was found, that's an obvious error
if fmtBlock == nil {
return nil, fmt.Errorf("E#2D4VQS: fmt not found in data")
}

result.Gain = 1e6 / float32(fmtBlock.Resolution)

result.Frequency = float32(fmtBlock.Frequency)
Expand Down Expand Up @@ -265,14 +273,17 @@ func calcChecksum(data []byte) uint32 {
return uint32(sum)
}

func verifyChecksum(data []byte, blockStart int64, blockLen uint32, reader io.Reader) (err error) {
func verifyChecksum(data []byte, blockStart int64, blockLen uint32, reader io.Reader) error {
var checksum uint32
binary.Read(reader, binary.LittleEndian, &checksum)
err := binary.Read(reader, binary.LittleEndian, &checksum)
if err != nil {
return fmt.Errorf("E#2D4VCY: Could not read binary data. Error: %v\n", err)
}

sum := calcChecksum(data[blockStart : blockStart+8+int64(blockLen)])

if checksum != sum {
return fmt.Errorf("Checksum does not match. Expected: [%v] Calculated:[%v]", checksum, sum)
return fmt.Errorf("checksum does not match. Expected: [%v] Calculated:[%v]", checksum, sum)
}
return nil
}
Expand Down
21 changes: 17 additions & 4 deletions atc2json/atc2json_test.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
package atc2json

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestCalcChecksum(t *testing.T) {
data := []byte{'A', 2, 3, 'z'}
res := calcChecksum(data)
assert.Equal(t, uint32(192), res, "192 and %d should be equal", res)
assert.NotEqual(t, uint(0), res, "0 and %d should be not equal", res)
if uint32(192) != res {
t.Errorf("192 and %d should be equal", res)
}
//assert.Equal(t, uint32(192), res, "192 and %d should be equal", res)
if uint32(0) != res {
t.Errorf("0 and %d should be equal", res)
}
//assert.NotEqual(t, uint(0), res, "0 and %d should be not equal", res)
}

func TestCalcMillivolts(t *testing.T) {
data := []int16{2000, 1000, 0, -1000, -2000}
scale := float32(2000)
res := calcMillivolts(data, scale)
assert.Equal(t, []float32{1, 0.5, 0, -0.5, -1}, res, "Arrays should be equal")
if len(res) != 5 {
t.Errorf("len(res) should be 5")
}
expected := []float32{1, 0.5, 0, -0.5, -1}
for i := 0; i < 5; i++ {
if expected[i] != res[i] {
t.Errorf("Arrays should be equal. res[%d] is %f, expected %f", i, res[i], expected[i])
}
}
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/alivecor/atc2json

go 1.24
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package main

import (
"fmt"
"io/ioutil"
"io"
"log"
"os"

"github.com/alivecor/atc2json/atc2json"
)

func main() {
atcData, err := ioutil.ReadAll(os.Stdin)
atcData, err := io.ReadAll(os.Stdin)
if err != nil {
log.Fatal(err)
return
Expand All @@ -19,7 +19,7 @@ func main() {
jsonOut, err := atc2json.Convert(atcData)

if err != nil {
log.Fatalln(err)
log.Fatalln("P#2D4UOQ: ", err)
}

fmt.Printf(jsonOut)
Expand Down