Skip to content

Commit

Permalink
Better CCD Json (#80)
Browse files Browse the repository at this point in the history
* Convert exposure time to nanoseconds and iso-time
* Flatten structure so BC (Bad pixels) are right next to the rest of the header info.
* Expands complex fields such as WDW to its understandable parts
  • Loading branch information
local-minimum authored May 14, 2020
1 parent 8ae2815 commit 64f2c7e
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 9 deletions.
76 changes: 75 additions & 1 deletion internal/aez/ccd_image.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package aez

import "fmt"
import (
"encoding/json"
"fmt"
"time"
)

// CCDImage is a container for the invariant CCDImagePackData header and the variable BadColumns that follow
type CCDImage struct {
Expand All @@ -23,3 +27,73 @@ func (ccd CCDImage) CSVRow() []string {
row := ccd.PackData.CSVRow()
return append(row, fmt.Sprintf("%v", ccd.BadColumns))
}

// MarshalJSON jsonifies content
func (ccd *CCDImage) MarshalJSON() ([]byte, error) {
wdwhigh, wdwlow, _ := ccd.PackData.WDW.InputDataWindow()
return json.Marshal(&struct {
Specification string `json:"specification"`
CCDSEL uint8
EXPNanoseconds int64
EXPDate string
WDWMode string
WDWInputDataWindow string
WDWOV uint16
JPEGQ uint8
FRAME uint16
NROW uint16
NRBIN uint16
NRSKIP uint16
NCOL uint16
NCBINFPGAColumns int
NCBINCCDColumns int
NCSKIP uint16
NFLUSH uint16
TEXPMS uint32
GAINMode string
GAINTiming string
TEMP uint16
FBINOV uint16
LBLNK uint16
TBLNK uint16
ZERO uint16
TIMING1 uint16
TIMING2 uint16
VERSION uint16
TIMING3 uint16
NBC uint16
BC []uint16
}{
Specification,
ccd.PackData.CCDSEL,
ccd.PackData.Nanoseconds(),
ccd.PackData.Time(gpsTime).Format(time.RFC3339Nano),
ccd.PackData.WDW.Mode().String(),
fmt.Sprintf("%v..%v", wdwhigh, wdwlow),
ccd.PackData.WDWOV,
ccd.PackData.JPEGQ,
ccd.PackData.FRAME,
ccd.PackData.NROW,
ccd.PackData.NRBIN,
ccd.PackData.NRSKIP,
ccd.PackData.NCOL,
ccd.PackData.NCBIN.FPGAColumns(),
ccd.PackData.NCBIN.CCDColumns(),
ccd.PackData.NCSKIP,
ccd.PackData.NFLUSH,
ccd.PackData.TEXPMS,
ccd.PackData.GAIN.Mode().String(),
ccd.PackData.GAIN.Timing().String(),
ccd.PackData.TEMP,
ccd.PackData.FBINOV,
ccd.PackData.LBLNK,
ccd.PackData.TBLNK,
ccd.PackData.ZERO,
ccd.PackData.TIMING1,
ccd.PackData.TIMING2,
ccd.PackData.VERSION,
ccd.PackData.TIMING3,
ccd.PackData.NBC,
ccd.BadColumns,
})
}
14 changes: 14 additions & 0 deletions internal/aez/ccd_image_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package aez

import (
"encoding/json"
"reflect"
"testing"
)
Expand Down Expand Up @@ -54,3 +55,16 @@ func TestCCDImage_CSVRow_AddsBC(t *testing.T) {
}
}
}

func TestCCDImage_MarshalJSON(t *testing.T) {
ccd := &CCDImage{}
got, err := ccd.MarshalJSON()
if err != nil {
t.Errorf("CCDImage.MarshalJSON() error = %v", err)
return
}
var js map[string]interface{}
if json.Unmarshal(got, &js) != nil {
t.Errorf("DataRecord.MarshalJSON() = %v, not a valid json", string(got))
}
}
65 changes: 57 additions & 8 deletions internal/common/datarecord.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,72 @@
package common

import (
"encoding/json"
"strings"

"github.com/innosat-mats/rac-extract-payload/internal/aez"
"github.com/innosat-mats/rac-extract-payload/internal/innosat"
"github.com/innosat-mats/rac-extract-payload/internal/ramses"
)

// DataRecord holds the full decode from one or many Ramses packages
type DataRecord struct {
Origin OriginDescription `json:"origin"` // Describes the origin of the data like filename or data batch name
RamsesHeader ramses.Ramses `json:"ramsesHeader"` // Ramses header information
RamsesTMHeader ramses.TMHeader `json:"ramsesTMHeader"` // The CCSDS compliant OHBSE TM Packet header
SourceHeader innosat.SourcePacketHeader `json:"sourceHeader"` // Source header from the innosat platform
TMHeader innosat.TMHeader `json:"tmHeader"` // Data header information
Origin OriginDescription // Describes the origin of the data like filename or data batch name
RamsesHeader ramses.Ramses // Ramses header information
RamsesTMHeader ramses.TMHeader // The CCSDS compliant OHBSE TM Packet header
SourceHeader innosat.SourcePacketHeader // Source header from the innosat platform
TMHeader innosat.TMHeader // Data header information
SID aez.SID // SID of the Data if any
RID aez.RID // RID of Data if any
Data Exporter `json:"data"` // The data payload itself, HK report, jpeg image etc.
Error error `json:"error,omitempty"` // First propagated error from the decoding process
Buffer []byte `json:"-"` // Currently unprocessed data (payload)
Data Exporter // The data payload itself, HK report, jpeg image etc.
Error error // First propagated error from the decoding process
Buffer []byte // Currently unprocessed data (payload)
}

// MarshalJSON makes a custom json of what is of interest in the struct
func (record *DataRecord) MarshalJSON() ([]byte, error) {
var dataJSON []byte
var dataJSONErr error
switch record.Data.(type) {
case aez.CCDImage:
ccd, ok := record.Data.(aez.CCDImage)
if ok {
dataJSON, dataJSONErr = ccd.MarshalJSON()
} else {
dataJSON, dataJSONErr = json.Marshal("Could not marshal ccd data into json")
}
default:
dataJSON, dataJSONErr = json.Marshal(record.Data)
}
buf, err := json.Marshal(&struct {
Origin OriginDescription `json:"origin"`
RamsesHeader ramses.Ramses `json:"ramsesHeader"`
RamsesTMHeader ramses.TMHeader `json:"ramsesTMHeader"`
SourceHeader innosat.SourcePacketHeader `json:"sourceHeader"`
TMHeader innosat.TMHeader `json:"tmHeader"`
SID aez.SID
RID aez.RID
Data string `json:"data"`
Error error `json:"error,omitempty"`
JSONError error `json:"errorJSON,omitempty"`
}{
Origin: record.Origin,
RamsesHeader: record.RamsesHeader,
RamsesTMHeader: record.RamsesTMHeader,
SourceHeader: record.SourceHeader,
TMHeader: record.TMHeader,
SID: record.SID,
RID: record.RID,
Data: "$1",
Error: record.Error,
JSONError: dataJSONErr,
})

//Inject the specially marshalled Data in the right place
jsonAsStr := string(buf)
pattern := "\"data\":\"$1\""
idx := strings.Index(jsonAsStr, pattern)
return append(append(buf[0:idx+7], dataJSON...), buf[idx+len(pattern):]...), err
}

// CSVSpecifications returns specifications used to generate content in CSV compatible format
Expand Down
35 changes: 35 additions & 0 deletions internal/common/datarecord_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package common

import (
"encoding/json"
"errors"
"io"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -290,3 +292,36 @@ func TestDataRecord_CSVRow(t *testing.T) {
})
}
}

func TestDataRecord_MarshalJSON(t *testing.T) {
type fields struct {
Data Exporter
Error error
}
tests := []struct {
name string
fields fields
}{
{"No Data, No Error", fields{}},
{"Error", fields{Error: io.EOF}},
{"Image Data", fields{Data: aez.CCDImage{}}},
{"Non-image Data", fields{Data: aez.STAT{}}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
record := &DataRecord{
Data: tt.fields.Data,
Error: tt.fields.Error,
}
got, err := record.MarshalJSON()
if err != nil {
t.Errorf("DataRecord.MarshalJSON() error = %v", err)
return
}
var js map[string]interface{}
if json.Unmarshal(got, &js) != nil {
t.Errorf("DataRecord.MarshalJSON() = %v, not a valid json", string(got))
}
})
}
}

0 comments on commit 64f2c7e

Please sign in to comment.