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
68 changes: 49 additions & 19 deletions archive2/archive2.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"sort"
"time"

Expand All @@ -32,7 +31,7 @@ type Archive2 struct {
}

// Extract data from a given archive 2 data file.
func Extract(f io.ReadSeeker) *Archive2 {
func Extract(f io.ReadSeeker) (*Archive2, error) {
ar2ExtractTimeStart := time.Now()
defer func() {
logrus.Debugf("ar2: done %s", time.Since(ar2ExtractTimeStart))
Expand All @@ -47,16 +46,19 @@ func Extract(f io.ReadSeeker) *Archive2 {
// older archive2 files are gzipped, check for those and decompress if found
if yes, ctype := isCompressed(f); yes {
if ctype != "gz" {
logrus.Fatalf("unsupported compression %s", ctype)
logrus.Errorf("unsupported compression %s", ctype)
return nil, fmt.Errorf("unsupported compression %s", ctype)
}
var gzd *gzip.Reader
var err error
if gzd, err = gzip.NewReader(f); err != nil {
logrus.Fatalf("failed to open gzip file: %s", err)
logrus.Errorf("failed to open gzip file: %s", err)
return nil, fmt.Errorf("failed to open gzip file: %s", err)
}
gzb, err := ioutil.ReadAll(gzd)
gzb, err := io.ReadAll(gzd)
if err != nil {
logrus.Fatalf("failed to read gzip file: %s", err)
logrus.Errorf("failed to read gzip file: %s", err)
return nil, fmt.Errorf("failed to read gzip file: %s", err)
}
f = bytes.NewReader(gzb)
}
Expand All @@ -71,7 +73,9 @@ func Extract(f io.ReadSeeker) *Archive2 {
// Archive II filename.

// read in the 24 byte volume header record
binary.Read(f, binary.BigEndian, &ar2.VolumeHeader)
if err := binary.Read(f, binary.BigEndian, &ar2.VolumeHeader); err != nil {
return nil, err
}

logrus.Debug(ar2.VolumeHeader)

Expand All @@ -92,9 +96,10 @@ func Extract(f io.ReadSeeker) *Archive2 {
// read in control word (size) of LDM record
if err := binary.Read(f, binary.BigEndian, &ldm.Size); err != nil {
if err != io.EOF {
logrus.Panic(err.Error())
logrus.Errorf(err.Error())
return nil, err
}
return &ar2
return &ar2, nil
}

// As the control word contains a negative size under some circumstances,
Expand All @@ -104,7 +109,11 @@ func Extract(f io.ReadSeeker) *Archive2 {
ldm.Size = -ldm.Size
} else if ldm.Size == 0 {
// older files don't have LDM records? Backup 4 bytes (int32 for size)
f.Seek(-4, io.SeekCurrent)
_, err := f.Seek(-4, io.SeekCurrent)
if err != nil {
logrus.Errorf("failed to seek back 4 bytes: %s", err)
return nil, err
}
}

logrus.WithFields(logrus.Fields{
Expand Down Expand Up @@ -132,13 +141,18 @@ func Extract(f io.ReadSeeker) *Archive2 {
// begin until byte 13 (halfword 7 or full word 4). This 12 byte
// offset is due to legacy compliance (previously known as the "CTM
//header"). See the RDA/RPG ICD for more details (Message Header Data)
msgBuf.Seek(LegacyCTMHeaderLen, io.SeekCurrent)
_, err := msgBuf.Seek(LegacyCTMHeaderLen, io.SeekCurrent)
if err != nil {
logrus.Errorf("failed to seek forward CTM header: %s", err)
return nil, err
}

msgHeader := MessageHeader{}
if err := binary.Read(msgBuf, binary.BigEndian, &msgHeader); err != nil {
if err != io.EOF {
logrus.Debugf("processed %d messages", numMessages)
logrus.Panic(err.Error())
logrus.Errorf("failed to read message header: %s", err)
return nil, err
}
break
}
Expand All @@ -155,18 +169,30 @@ func Extract(f io.ReadSeeker) *Archive2 {
switch msgHeader.MessageType {
case 2:
m2 := Message2{}
binary.Read(msgBuf, binary.BigEndian, &m2)
if err := binary.Read(msgBuf, binary.BigEndian, &m2); err != nil {
logrus.Errorf("failed to read message 2: %s", err)
return nil, err
}
ar2.RadarStatus = &m2

// move to the end of the message
msgBuf.Seek(MessageBodySize-Message2Length, io.SeekCurrent)
if _, err := msgBuf.Seek(MessageBodySize-Message2Length, io.SeekCurrent); err != nil {
logrus.Errorf("failed to seek forward header message size: %s", err)
return nil, err
}
case 3:
m3 := Message3{}
binary.Read(msgBuf, binary.BigEndian, &m3)
if err := binary.Read(msgBuf, binary.BigEndian, &m3); err != nil {
logrus.Errorf("failed to read message 3: %s", err)
return nil, err
}
ar2.RadarPerformance = &m3

// move to the end of the message
msgBuf.Seek(MessageBodySize-Message3Length, io.SeekCurrent)
if _, err := msgBuf.Seek(MessageBodySize-Message3Length, io.SeekCurrent); err != nil {
logrus.Errorf("failed to seek forward header message size: %s", err)
return nil, err
}
// case 5:
// m5 := Message5{}
// binary.Read(msgBuf, binary.BigEndian, &m5.Message5Header)
Expand All @@ -184,7 +210,11 @@ func Extract(f io.ReadSeeker) *Archive2 {
// // move to the end of the message
// msgBuf.Seek(MessageBodySize-int64(msgHeader.MessageSize), io.SeekCurrent)
case 31:
m31 := msg31(msgBuf)
m31, err := msg31(msgBuf)
if err != nil {
logrus.Errorf("failed to read message 31: %s", err)
return nil, err
}
// logrus.Trace(m31.Header.String())
ar2.ElevationScans[int(m31.Header.ElevationNumber)] = append(ar2.ElevationScans[int(m31.Header.ElevationNumber)], m31)
default:
Expand All @@ -193,15 +223,15 @@ func Extract(f io.ReadSeeker) *Archive2 {
}
_, err := msgBuf.Seek(MessageBodySize, io.SeekCurrent)
if err != nil {
logrus.Panic("failed to seek forward header message size")
logrus.Errorf("failed to seek forward header message size")
return nil, err
}
}

messageCounts[msgHeader.MessageType]++
}
logrus.Tracef("ar2: ldm: done: %s messages:%v", time.Since(ldmExtractTimeStart), messageCounts)
}
return &ar2
}

func (ar2 *Archive2) String() string {
Expand Down
78 changes: 49 additions & 29 deletions archive2/message31.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,16 @@ func (h *Message31Header) AzimuthResolutionSpacing() float64 {
return 1
}

func msg31(r io.ReadSeeker) *Message31 {
func msg31(r io.ReadSeeker) (*Message31, error) {
m31h := Message31Header{}

// save the position of the first byte so we can easily process data blocks later.
startPos, _ := r.Seek(0, io.SeekCurrent)

binary.Read(r, binary.BigEndian, &m31h)
if err := binary.Read(r, binary.BigEndian, &m31h); err != nil {
logrus.Errorf(err.Error())
return nil, err
}

m31 := Message31{
Header: m31h,
Expand All @@ -119,7 +122,8 @@ func msg31(r io.ReadSeeker) *Message31 {

blockPointers := make([]uint32, m31h.DataBlockCount)
if err := binary.Read(r, binary.BigEndian, blockPointers); err != nil {
logrus.Panic(err.Error())
logrus.Errorf(err.Error())
return nil, err
}

// check for more DataBlockPointers
Expand All @@ -130,12 +134,16 @@ func msg31(r io.ReadSeeker) *Message31 {
maxLoops := 20
for i := 0; true; i++ {
if err := binary.Read(r, binary.BigEndian, &lookahead); err != nil {
logrus.Panic(err.Error())
logrus.Errorf(err.Error())
return nil, err
}

if bytes.Equal(lookahead, hexRVOL) {
// backup 4 bytes to keep the block intact
r.Seek(-4, io.SeekCurrent)
if _, err := r.Seek(-4, io.SeekCurrent); err != nil {
logrus.Errorf(err.Error())
return nil, err
}
break
}

Expand All @@ -146,7 +154,8 @@ func msg31(r io.ReadSeeker) *Message31 {

// prevent infinite loop
if i == maxLoops {
logrus.Fatal("M31 Header: failed to find the end of the datablock pointers.")
logrus.Error("M31 Header: failed to find the end of the datablock pointers.")
return nil, fmt.Errorf("m31 header: failed to find the end of the datablock pointers")
}
i++
}
Expand All @@ -155,40 +164,47 @@ func msg31(r io.ReadSeeker) *Message31 {
for _, bptr := range blockPointers {
// logrus.Tracef("ar2: m31: processing datablock %d", i)

r.Seek(startPos+int64(bptr), io.SeekStart)
if _, err := r.Seek(startPos+int64(bptr), io.SeekStart); err != nil {
logrus.Errorf(err.Error())
return nil, err
}

d := DataBlock{}
if err := binary.Read(r, binary.BigEndian, &d); err != nil {
logrus.Panic(err.Error())
logrus.Errorf(err.Error())
return nil, err
}

// rewind from reading the datalblock
r.Seek(-4, io.SeekCurrent)
if _, err := r.Seek(-4, io.SeekCurrent); err != nil {
logrus.Errorf(err.Error())
return nil, err
}

blockName := string(d.DataName[:])

switch blockName {
case "VOL":
binary.Read(r, binary.BigEndian, &m31.VolumeData)
if err := binary.Read(r, binary.BigEndian, &m31.VolumeData); err != nil {
logrus.Errorf(err.Error())
return nil, err
}
case "ELV":
binary.Read(r, binary.BigEndian, &m31.ElevationData)
if err := binary.Read(r, binary.BigEndian, &m31.ElevationData); err != nil {
logrus.Errorf(err.Error())
return nil, err
}
case "RAD":
binary.Read(r, binary.BigEndian, &m31.RadialData)
case "REF":
fallthrough
case "VEL":
fallthrough
case "CFP":
fallthrough
case "SW ":
fallthrough
case "ZDR":
fallthrough
case "PHI":
fallthrough
case "RHO":
if err := binary.Read(r, binary.BigEndian, &m31.RadialData); err != nil {
logrus.Errorf(err.Error())
return nil, err
}
case "REF", "VEL", "CFP", "SW ", "ZDR", "PHI", "RHO":
m := GenericDataMoment{}
binary.Read(r, binary.BigEndian, &m)
if err := binary.Read(r, binary.BigEndian, &m); err != nil {
logrus.Errorf(err.Error())
return nil, err
}

// LDM is the amount of space in bytes required for a data moment
// array and equals ((NG * DWS) / 8) where NG is the number of gates
Expand All @@ -197,7 +213,10 @@ func msg31(r io.ReadSeeker) *Message31 {
ldm := m.NumberDataMomentGates * uint16(m.DataWordSize) / 8

data := make([]byte, ldm)
binary.Read(r, binary.BigEndian, data)
if err := binary.Read(r, binary.BigEndian, data); err != nil {
logrus.Errorf(err.Error())
return nil, err
}

d := &DataMoment{
GenericDataMoment: m,
Expand All @@ -222,8 +241,9 @@ func msg31(r io.ReadSeeker) *Message31 {
}
default:
// preview(r, 256)
logrus.Panicf("Data Block - unknown type '%s'", blockName)
logrus.Errorf("Data Block - unknown type '%s'", blockName)
return nil, fmt.Errorf("data block - unknown type '%s'", blockName)
}
}
return &m31
return &m31, nil
}
5 changes: 4 additions & 1 deletion cmd/ar2v-dump/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ func main() {
return
}

ar2 := archive2.Extract(f)
ar2, err := archive2.Extract(f)
if err != nil {
logrus.Fatal(err)
}

fmt.Printf("Station: %s\n", ar2.VolumeHeader.ICAO)
fmt.Printf("Date: %s\n", ar2.VolumeHeader.Date())
Expand Down
20 changes: 15 additions & 5 deletions cmd/nexrad-render/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"image"
"image/color"
"image/draw"
"io/ioutil"
"math"
"os"
"runtime"
Expand Down Expand Up @@ -130,7 +129,7 @@ func run(cmd *cobra.Command, args []string) {
}

func animate(dir, outdir, prod string) {
files, err := ioutil.ReadDir(dir)
files, err := os.ReadDir(dir)
if err != nil {
logrus.Fatal(err)
}
Expand All @@ -155,7 +154,10 @@ func animate(dir, outdir, prod string) {
logrus.Error(err)
return
}
ar2 := archive2.Extract(f)
ar2, err := archive2.Extract(f)
if err != nil {
logrus.Fatal(err)
}
f.Close()
render(outf, ar2.ElevationScans[elevation], fmt.Sprintf("%s - %s", ar2.VolumeHeader.ICAO, ar2.VolumeHeader.Date()))
bar.Increment()
Expand Down Expand Up @@ -185,11 +187,19 @@ func single(in, out, product string) {
}
defer f.Close()

ar2 := archive2.Extract(f)
ar2, err := archive2.Extract(f)
if err != nil {
logrus.Fatal(err)
}
logrus.Debug(ar2)

label := fmt.Sprintf("%s %f %s VCP:%d %s %s", ar2.VolumeHeader.ICAO, ar2.ElevationScans[2][0].Header.ElevationAngle, strings.ToUpper(product), ar2.RadarStatus.VolumeCoveragePatternNum, ar2.VolumeHeader.FileName(), ar2.VolumeHeader.Date().Format(time.RFC3339))
logrus.Info("Generating %s from %s -> %s\n", strings.ToUpper(product), in, out)
logrus.Info(fmt.Sprintf("Generating %s from %s -> %s\n", strings.ToUpper(product), in, out))
logrus.WithFields(logrus.Fields{
"product": strings.ToUpper(product),
"file": in,
"output": out,
}).Info("Generating product")
render(out, ar2.ElevationScans[elevation], label)
}

Expand Down
5 changes: 4 additions & 1 deletion cmd/nexrad-tui/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,10 @@ func main() {
return
}

ar2 = archive2.Extract(f)
ar2, err = archive2.Extract(f)
if err != nil {
logrus.Fatal(err)
}

if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
log.Panicln(err)
Expand Down
Loading