This commit is contained in:
nuknal
2024-05-21 16:16:54 +08:00
parent dd23760dbb
commit 84c8b5d23c
15 changed files with 989 additions and 513 deletions

70
calculator/quaternion.go Normal file
View File

@@ -0,0 +1,70 @@
package calculator
import (
"fmt"
"math"
)
// Quaternion represents a quaternion with scalar (w) and vector (x, y, z) parts
type Quaternion struct {
w, x, y, z float64
}
// Quaternion multiplication
func (q1 Quaternion) Mul(q2 Quaternion) Quaternion {
return Quaternion{
w: q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z,
x: q1.w*q2.x + q1.x*q2.w + q1.y*q2.z - q1.z*q2.y,
y: q1.w*q2.y - q1.x*q2.z + q1.y*q2.w + q1.z*q2.x,
z: q1.w*q2.z + q1.x*q2.y - q1.y*q2.x + q1.z*q2.w,
}
}
// Quaternion conjugate
func (q Quaternion) Conjugate() Quaternion {
return Quaternion{w: q.w, x: -q.x, y: -q.y, z: -q.z}
}
// Rotate vector by quaternion
func (q Quaternion) Rotate(v [3]float64) [3]float64 {
qv := Quaternion{w: 0, x: v[0], y: v[1], z: v[2]}
qConj := q.Conjugate()
qvRotated := q.Mul(qv).Mul(qConj)
return [3]float64{qvRotated.x, qvRotated.y, qvRotated.z}
}
func main() {
// 示例数据
qBI := Quaternion{w: 1, x: 0, y: 0, z: 0} // 本体相对惯性系四元数
posJ2000 := [3]float64{7000, 0, 0} // J2000位置
// velJ2000 := [3]float64{0, 7.5, 0} // J2000速度
// 相机参数
const numPixels = 9520
const fov = 10.0 * math.Pi / 180 // 假设视场角为10度
// 逐像素计算地面交点
for i := 0; i < numPixels; i++ {
// 计算像素点相对光轴的偏角
pixelOffset := (float64(i) - float64(numPixels)/2) / float64(numPixels)
angle := pixelOffset * fov
// 假设光轴在本体坐标系中指向-z方向计算视线方向
dBody := [3]float64{-math.Sin(angle), 0, -math.Cos(angle)}
// 转换到惯性系
dInertial := qBI.Rotate(dBody)
// 计算地面交点假设dInertial已经标准化
k := -posJ2000[2] / dInertial[2] // 简化的交点计算
groundPoint := [3]float64{
posJ2000[0] + k*dInertial[0],
posJ2000[1] + k*dInertial[1],
posJ2000[2] + k*dInertial[2],
}
// 转换到地理坐标
lat, lon, _ := ECEFToGeodetic(groundPoint[0], groundPoint[1], groundPoint[2])
fmt.Printf("Pixel %d: Latitude: %f, Longitude: %f\n", i, lat, lon)
}
}

View File

@@ -10,6 +10,7 @@ import (
var ( var (
dataId string dataId string
batch bool batch bool
output string
) )
var extractCmd = &cobra.Command{ var extractCmd = &cobra.Command{
@@ -31,7 +32,7 @@ var extractCmd = &cobra.Command{
} else { } else {
p := &extract.Params{ p := &extract.Params{
InputData: fmt.Sprintf("demo/data/%s.dat", dataId), InputData: fmt.Sprintf("demo/data/%s.dat", dataId),
OutputPath: fmt.Sprintf("demo/output/%s", dataId), OutputPath: fmt.Sprintf("%s/%s", output, dataId),
TempPath: fmt.Sprintf("demo/temp/%s", dataId), TempPath: fmt.Sprintf("demo/temp/%s", dataId),
DataId: dataId, DataId: dataId,
Satellite: "SJY01", Satellite: "SJY01",
@@ -51,16 +52,18 @@ func init() {
extractCmd.Flags().StringVarP(&dataId, "data-id", "d", "051622", "051622") extractCmd.Flags().StringVarP(&dataId, "data-id", "d", "051622", "051622")
extractCmd.Flags().BoolVarP(&batch, "batch", "b", false, "true | false") extractCmd.Flags().BoolVarP(&batch, "batch", "b", false, "true | false")
extractCmd.Flags().StringVarP(&output, "out", "o", "demo/output", "demo/output")
} }
func params() []*extract.Params { func params() []*extract.Params {
var params []*extract.Params var params []*extract.Params
datas := []string{"051513", "051622", datas := []string{"051622", "051712", "051721",
"051712", "051721", "051821", "051823", "051921", "051922", "Q051723"} "051813", "051821", "051823",
"051921", "051922", "052022"}
for _, d := range datas { for _, d := range datas {
params = append(params, &extract.Params{ params = append(params, &extract.Params{
InputData: fmt.Sprintf("demo/data/%s.dat", d), InputData: fmt.Sprintf("demo/data/%s.dat", d),
OutputPath: fmt.Sprintf("demo/output/%s", d), OutputPath: fmt.Sprintf("%s/%s", output, d),
TempPath: fmt.Sprintf("demo/temp/%s", d), TempPath: fmt.Sprintf("demo/temp/%s", d),
DataId: d, DataId: d,
Satellite: "SJY01", Satellite: "SJY01",

View File

@@ -24,12 +24,14 @@ var parseCmd = &cobra.Command{
e := extract.NewExtractor(&params) e := extract.NewExtractor(&params)
// p.ParseAuxPlatformWithHead("demo/ref/辅助数据.dat") // p.ParseAuxPlatformWithHead("demo/ref/辅助数据.dat")
fmt.Println("Reference Time: 2000-01-01 12:00:00 UTC, seconds:", extract.Time2000UTCSec()) fmt.Println("Reference Time: 2000-01-01 12:00:00 UTC, seconds:", extract.Time2000UTCSec())
_, err := e.ParseAuxPlatform("demo/output/051622/SJY01_PMS_20240516_101236_051622_096.AUX") err := e.ExtractAux("demo/output/051622/SJY01_PMS_20240516_101236_051622_096.AUX",
"demo/temp/1.xlsx")
if err != nil { if err != nil {
logrus.Error(err) logrus.Error(err)
} }
// p.ParseAuxEBox("demo/output/SJY01_PMS_20240516_101236_051622_096_EB.AUX") // p.ParseAuxEBox("demo/output/SJY01_PMS_20240516_101236_051622_096_EB.AUX")
e.ParseAuxPlatform("demo/output/Q052100/SJY01_PMS_20240519_121433_Q052100_102.AUX") // e.ParseAuxPlatform("demo/output/Q052100/SJY01_PMS_20240519_121433_Q052100_102.AUX")
}, },
} }

View File

@@ -1,10 +0,0 @@
package extract
/*
将欧拉角 (yaw, pitch, roll) 转换为旋转矩阵
参数:
- yaw: 偏航角绕Z轴旋转
- pitch: 俯仰角绕Y轴旋转
- roll: 滚转角绕X轴旋转
*/

View File

@@ -1,380 +1,45 @@
package extract package extract
import ( import (
"encoding/binary"
"errors"
"fmt"
"math"
"os" "os"
"strings"
"time"
"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/tealeg/xlsx"
) )
// 每行传感器数据帧头信息长度为24字节 func (e Extractor) ExtractAux(auxfile, xlsxfile string) error {
type AuxFrameHead struct { if err := createAuxXlxs(xlsxfile); err != nil {
PkgHead [4]byte // 包头头标识符固定为0xD15BD15B
FillByte0 byte // 填充字节 0x00
FrmHead [6]byte // 帧头标识符固定为0xEB90EB90EB90
// 图像模式
// B7 模式标识 000为线阵模式
// B6
// B5
// B4 B4谱状态 0不输出1输出
// B3 B3谱状态 0不输出1输出
// B2 B2谱状态 0不输出1输出
// B1 B1谱状态 0不输出1输出
// B0 全色状态 0不输出1输出
ImgMode byte
SerialNo uint32 // 当前流水号
TimeSec uint32 // 时间(秒)
TimeSecFrac uint32 // 秒小数 量纲1us/bit十六进制无符号整型数
FileNo uint8 // 文件号
IsValidFrmHead bool
B0 bool
B1 bool
B2 bool
B3 bool
B4 bool
IsLinerMatrix bool
RowLength int
}
func (afh *AuxFrameHead) Decode(data []byte) error {
if len(data) < 24 {
return errors.New("length of AuxFrameHead is not 24")
}
afh.PkgHead = [4]byte{data[0], data[1], data[2], data[3]}
afh.FillByte0 = data[4]
afh.FrmHead = [6]byte{data[5], data[6], data[7], data[8], data[9], data[10]}
afh.ImgMode = data[11]
afh.B0 = (afh.ImgMode&(1<<0) != 0x0)
afh.B1 = (afh.ImgMode&(1<<1) != 0x0)
afh.B2 = (afh.ImgMode&(1<<2) != 0x0)
afh.B3 = (afh.ImgMode&(1<<3) != 0x0)
afh.B4 = (afh.ImgMode&(1<<4) != 0x0)
afh.IsLinerMatrix = (afh.ImgMode&(1<<5) == 0x0 && afh.ImgMode&(1<<6) == 0x0 && afh.ImgMode&(1<<7) == 0x0)
afh.SerialNo = binary.BigEndian.Uint32(data[12:16])
afh.TimeSec = binary.BigEndian.Uint32(data[16:20])
afh.TimeSecFrac = uint32(uint32(data[20])<<16 | uint32(data[21])<<8 | uint32(data[22]))
afh.FileNo = data[23]
afh.RowLength = afh.LengthOfRow()
afh.IsValidFrmHead = afh.CheckFrmHead()
return nil
}
// 计算一行数据长度:帧头+辅助数据+图像数据
func (afh AuxFrameHead) LengthOfRow() int {
if !afh.IsLinerMatrix {
return 14344
}
length := 64 // 帧头+辅助数据长度
if afh.B0 {
length += 19040
}
if afh.B1 {
length += 1192
}
if afh.B2 {
length += 1192
}
if afh.B3 {
length += 1192
}
if afh.B4 {
length += 1192
}
return length
}
func (afh AuxFrameHead) CheckFrmHead() bool {
if afh.FrmHead[0] == 0xEB && afh.FrmHead[1] == 0x90 &&
afh.FrmHead[2] == 0xEB && afh.FrmHead[3] == 0x90 &&
afh.FrmHead[4] == 0xEB && afh.FrmHead[5] == 0x90 {
return true
}
return false
}
// 从传输帧文件中分离辅助数据,分别存储到辅助数据文件 _AUX.dat 和图像数据文件 _IMG_{波谱}.dat 中
func (e *Extractor) SeprateAuxAndImgData(dataFile string, segmentIndex int) error {
// 打开传输帧文件 - 一次读入内存
data, err := os.ReadFile(dataFile)
if err != nil {
log.Println("read data from", dataFile, "error:", err.Error())
return err return err
} }
log.Info("seprate aux and img data from", dataFile) wb, err := xlsx.OpenFile(xlsxfile)
if err != nil {
dataLen := len(data) panic(err)
log.Info("length of original data: ", dataLen)
var firstFrmHead *AuxFrameHead
data, firstFrmHead = e.trimImgRawData(data)
dataLen = len(data)
log.Info("after trim,length of data: ", dataLen)
e.quanlityAnalysis(data)
outputDir := e.params.OutputPath
name := strings.Join([]string{
e.params.Satellite,
"PMS",
time.Unix(int64(firstFrmHead.TimeSec)+int64(ReferenceTime2000),
int64(firstFrmHead.TimeSecFrac)*1000).
Format("20060102_150405"),
e.params.DataId,
fmt.Sprintf("%03d", firstFrmHead.FileNo),
}, "_")
lw := newL0Writer(outputDir, name)
defer lw.Close()
var panEnviHdr, mssEnviHdr EnviHdr
var afh AuxFrameHead
msdata := make([][]byte, 4)
var header []byte
var ebAux []byte
var platAux []byte
for i := 0; i < dataLen; {
if i+4 > dataLen {
logrus.Println("end of data, dataLen:", dataLen, "i:", i)
break
}
// 包头
if data[i] == 0xD1 && data[i+1] == 0x5B && data[i+2] == 0xD1 && data[i+3] == 0x5B {
log.Debug("find package head: 0xD15BD15B")
} else {
i++
continue
}
// 解析帧
if i+24 > dataLen {
log.Info("length of frame head is not engough for frame head")
break
}
afh.Decode(data[i : i+24])
if !afh.IsValidFrmHead {
log.Info("invalid frame head of original raw data")
i += 1
continue
}
// 目前只支持线阵模式
if !afh.IsLinerMatrix {
log.Error("not liner matrix mode, only support liner matrix mode")
break
}
// 读取一行数据 data[i : i+afh.RowLength]
if i+afh.RowLength > dataLen {
log.Infof("length of image row data %v is not enough: %v", dataLen-i, afh.RowLength)
break
}
auxTime := binary.BigEndian.Uint32(data[i+32 : i+36])
if math.Abs(float64(auxTime)-float64(afh.TimeSec)) < 30 {
header = append(header, data[i:i+24]...)
}
// 存储辅助数据到临时文件
dataIndex := i + 24
// lw.ws[EB_AUX].w.Write(data[dataIndex : dataIndex+8]) // 8字节焦面电箱辅助数据
ebAux = append(ebAux, data[dataIndex:dataIndex+8]...)
dataIndex += 8
// lw.ws[PLAT_AUX].w.Write(data[dataIndex : dataIndex+32]) // 32字节中心机辅助数据
platAux = append(platAux, data[dataIndex:dataIndex+32]...)
dataIndex += 32
// 存储图像数据到临时文件 - 以 ENVI BSQ 格式存储,同时提供 HDR 描述文件
if afh.B0 {
// wpan.Write(data[dataIndex : dataIndex+19040])
write16bPixelLittleEndian(lw.ws[PAN_RAW].w, data[dataIndex:dataIndex+19040])
dataIndex += 19040
panEnviHdr.Lines += 1
}
if afh.B1 {
// write16bPixelLittleEndian(wmss, data[dataIndex:dataIndex+1192])
msdata[0] = append(msdata[0], data[dataIndex:dataIndex+1192]...)
dataIndex += 1192
mssEnviHdr.Lines += 1
}
if afh.B2 {
// write16bPixelLittleEndian(wmss, data[dataIndex:dataIndex+1192])
msdata[1] = append(msdata[1], data[dataIndex:dataIndex+1192]...)
dataIndex += 1192
}
if afh.B3 {
// write16bPixelLittleEndian(wmss, data[dataIndex:dataIndex+1192])
msdata[2] = append(msdata[2], data[dataIndex:dataIndex+1192]...)
dataIndex += 1192
}
if afh.B4 {
// write16bPixelLittleEndian(wmss, data[dataIndex:dataIndex+1192])
msdata[3] = append(msdata[3], data[dataIndex:dataIndex+1192]...)
dataIndex += 1192
}
i = dataIndex // 完成一行数据解析
} }
// var bands = 0 sh := wb.Sheets[0]
// for i := 0; i < 4; i++ {
// if len(msdata[i]) > 0 {
// log.Println("write mss data of band B", i+1)
// _, err := write16bPixelLittleEndian(lw.ws[MSS_RAW].w, msdata[i])
// if err != nil {
// log.Error("write mss data error:", err.Error())
// }
// bands += 1
// }
// }
if len(msdata[0]) != len(msdata[1]) || len(msdata[2]) != len(msdata[3]) { data, err := os.ReadFile(auxfile)
log.Error("mss data of bands B1-B4 are not equal") if err != nil {
return errors.New("mss data of bands B1-B4 are not equal") log.Println("read aux data from", auxfile, "error:", err.Error())
return err
} }
mssRowLen := 1192 * 4 for i := 0; i < len(data); i += 24 + 128 + 512 {
for i := 0; i < len(msdata[0]); i += mssRowLen { row := sh.AddRow()
var err error
_, err = write16bPixelLittleEndian(lw.ws[MSS_RAW].w, msdata[0][i:i+mssRowLen])
if err != nil {
log.Error("write mss 1 data error:", err.Error())
}
_, err = write16bPixelLittleEndian(lw.ws[MSS_RAW].w, msdata[1][i:i+mssRowLen])
if err != nil {
log.Error("write mss 2 data error:", err.Error())
}
_, err = write16bPixelLittleEndian(lw.ws[MSS_RAW].w, msdata[2][i:i+mssRowLen])
if err != nil {
log.Error("write mss 3 data error:", err.Error())
}
_, err = write16bPixelLittleEndian(lw.ws[MSS_RAW].w, msdata[3][i:i+mssRowLen])
if err != nil {
log.Error("write mss 4 data error:", err.Error())
}
}
panEnviHdr.Samples = 9520 var head AuxFrameHead
panEnviHdr.Bands = 1 head.Decode(data[i : i+24])
lw.ws[PAN_HDR].w.Write([]byte(panEnviHdr.String())) head.SaveXlsx(row)
mssEnviHdr.Lines = mssEnviHdr.Lines / 4 // 多光谱波段分别在 4 行中传输 var box AuxFocalBox
mssEnviHdr.Samples = 2384 * 4 box.Decode(data[i+24 : i+24+128])
mssEnviHdr.Bands = 1 box.SaveXlsx(row)
lw.ws[MSS_HDR].w.Write([]byte(mssEnviHdr.String()))
// 帧头+辅助数据 var plat AuxPlatform
if len(header)/24 < len(ebAux)/128 || len(ebAux)/128 != len(platAux)/512 { plat.Decode(data[i+24+128 : i+24+128+512])
fmt.Println("aux data length:", len(header)/24, len(ebAux)/128, len(platAux)/512) box.SaveXlsx(row)
return errors.New("aux data length is not equal")
}
auxRows := len(header) / 24
for i := 0; i < auxRows; i++ {
lw.ws[AUX].w.Write(header[i*24 : (i+1)*24])
lw.ws[AUX].w.Write(ebAux[i*128 : (i+1)*128])
lw.ws[AUX].w.Write(platAux[i*512 : (i+1)*512])
} }
return nil return nil
} }
func (e *Extractor) quanlityAnalysis(data []byte) {
// 数据质量分析
fimg, _ := os.Create("demo/temp/" + e.params.DataId + "/aux.txt")
defer fimg.Close()
fimg.WriteString("字节数 帧头流水号 文件号 帧头时间 中心辅助数据前4字节(行33-36)\n")
var startAuxLine bool
var preSN uint32
for i := 0; i < len(data); {
afh := &AuxFrameHead{}
afh.Decode(data[i : i+24])
if !afh.IsValidFrmHead {
log.Errorf("invalid frame head of original raw data i: %v, len: %v", i, len(data))
return
}
t := time.Unix(int64(afh.TimeSec+uint32(ReferenceTime2000)), int64(afh.TimeSecFrac)*1000)
utcTime := binary.BigEndian.Uint32(data[i+32 : i+36])
tAux := time.Unix(int64(utcTime+uint32(ReferenceTime2000)), 0)
startAuxLine = math.Abs(float64(utcTime)-float64(afh.TimeSec)) < 30
if startAuxLine {
fimg.WriteString(fmt.Sprintf("%d %d %d %s %s\n",
i,
afh.SerialNo,
afh.FileNo,
t.String(),
tAux.String(),
))
if afh.SerialNo-preSN != 16 && preSN != 0 {
log.Println("serial number not continuous", afh.SerialNo, preSN)
}
preSN = afh.SerialNo
}
i += afh.RowLength
}
}
// 裁剪图像数据,只保留有效数据
func (e *Extractor) trimImgRawData(data []byte) ([]byte, *AuxFrameHead) {
var start, end int
afh := &AuxFrameHead{}
// 将中心辅助数据时间合理的帧作为第一帧
var startAuxLine bool
for i := 0; i < len(data); {
// 解析帧
if i+24 > len(data) {
log.Info("length of original image frame head is not engough: ", len(data)-i)
break
}
if data[i] == 0xD1 && data[i+1] == 0x5B && data[i+2] == 0xD1 && data[i+3] == 0x5B {
log.Debug("find package head: 0xD15BD15B")
} else {
i++
continue
}
afh.Decode(data[i : i+24])
if !afh.IsValidFrmHead {
log.Debugf("invalid frame head of original raw data %v", i)
i += 1
continue
}
utcTime := binary.BigEndian.Uint32(data[i+32 : i+36]) // 相对于2000年的秒数
startAuxLine = math.Abs(float64(utcTime)-float64(afh.TimeSec)) < 30 // 时间差小于30秒认为是有效数据
if !startAuxLine {
i += afh.RowLength
} else {
start = i
nLen := (len(data) - i - 1)
nLen = nLen - nLen%(afh.RowLength*16)
end = i + nLen
break
}
}
return data[start:end], afh
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/k0kubun/pp/v3" "github.com/k0kubun/pp/v3"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/tealeg/xlsx"
) )
// 卫星时间起点 北京时间 2000-01-01 20:00:00 // 卫星时间起点 北京时间 2000-01-01 20:00:00
@@ -140,6 +141,10 @@ func (ab AuxFocalBox) PGAGainValue() string {
} }
} }
func (ab AuxFocalBox) SaveXlsx(row *xlsx.Row) error {
return nil
}
func (e *Extractor) ParseAuxEBox(auxfile string) ([]*AuxFocalBox, error) { func (e *Extractor) ParseAuxEBox(auxfile string) ([]*AuxFocalBox, error) {
data, err := os.ReadFile(auxfile) data, err := os.ReadFile(auxfile)
if err != nil { if err != nil {

436
extract/aux_elements.go Normal file
View File

@@ -0,0 +1,436 @@
package extract
import "github.com/tealeg/xlsx"
func createAuxXlxs(fname string) error {
nwb := xlsx.NewFile()
sheet, err := nwb.AddSheet("辅助数据")
if err != nil {
panic(err)
}
row := sheet.AddRow()
for _, header := range AuxHeader {
cell := row.AddCell()
cell.SetString(header)
}
return nwb.Save(fname)
}
var AuxHeader = []string{
"包头信息",
"填充位",
"帧头信息",
"当前图像模式",
"B4谱状态",
"B3谱状态",
"B2谱状态",
"B1谱状态",
"全色状态",
"当前行流水号",
"时间秒",
"秒小数",
"文件号",
"执行行转移时间",
"Trainingdone",
"工作模式",
"积分方向",
"PGA增益",
"P谱段积分级数",
"B1积分级数",
"B2积分级数",
"B3积分级数",
"B4积分级数",
"秒脉冲状态",
"保留",
"暗场偏置",
"全色开窗地址",
"多光谱1开窗地址",
"多光谱2开窗地址",
"多光谱3开窗地址",
"多光谱4开窗地址",
"面阵模式Linetime时钟周期数",
"面阵模式开窗地址",
"面阵模式开窗大小",
"面阵曝光时间粗调EXP_C",
"面阵曝光时间精调EXP_F",
"面阵模式最小读出行",
"硬盘1温度",
"硬盘2温度",
"保留",
"传感器温度",
"FPGA逻辑版本号",
"工作模式",
"原始盘可用存储容量",
"压缩盘可用存储容量",
"原始盘状态",
"原始盘Host初始化状态",
"原始盘SATA控制器状态",
"原始盘SATA错误计数",
"压缩盘状态",
"压缩盘Host初始化状态",
"压缩盘SATA控制器状态",
"压缩盘SATA错误计数",
"保留",
"DDR初始化状态",
"原始图像硬盘状态",
"压缩数据硬盘状态",
"硬盘1读写状态",
"硬盘2读写状态",
"硬盘1初始化状态",
"硬盘2初始化状态",
"保留",
"保留",
"硬盘1禁用标志",
"硬盘2禁用标志",
"保留",
"B2数据移位",
"B1数据移位",
"B4数据移位",
"B3数据移位",
"指令计数",
"最后一条指令编码",
"指令接收状态",
"错误指令计数",
"错误指令帧编号",
"保留",
"传感器数字电路温度",
"卫星UTC时间秒",
"卫星复波道标志位",
"卫星秒小数",
"定姿四元数(J2000)q0",
"定姿四元数(J2000)q1",
"定姿四元数(J2000)q2",
"定姿四元数(J2000)q3",
"本体相对轨道四元数矢部q1",
"本体相对轨道四元数矢部q2",
"本体相对轨道四元数矢部q3",
"轨道相对惯性系四元数矢部q1",
"轨道相对惯性系四元数矢部q2",
"轨道相对惯性系四元数矢部q3",
"本体相对轨道姿态角1",
"本体相对轨道姿态角2",
"本体相对轨道姿态角3",
"本体相对轨道角速度1",
"本体相对轨道角速度2",
"本体相对轨道角速度3",
"模式运行时间",
"姿控调用周期",
"姿轨控部件使用标志",
"姿轨控算法执行标记",
"偏差四元数q1",
"偏差四元数q2",
"偏差四元数q3",
"偏差角速度1",
"偏差角速度2",
"偏差角速度3",
"X飞轮估计摩擦力矩",
"Y飞轮估计摩擦力矩",
"Z飞轮估计摩擦力矩",
"三轴陀螺X轴角速度漂移估计",
"三轴陀螺Y轴角速度漂移估计",
"三轴陀螺Z轴角速度漂移估计",
"三轴角速度卡尔曼漂移X",
"三轴角速度卡尔曼漂移Y",
"三轴角速度卡尔曼漂移Z",
"X轴估计环境干扰力矩",
"Y轴估计环境干扰力矩",
"Z轴估计环境干扰力矩",
"X轴计算飞轮控制力矩",
"Y轴计算飞轮控制力矩",
"Z轴计算飞轮控制力矩",
"期望四元数矢部1",
"期望四元数矢部2",
"期望四元数矢部3",
"期望角速度1",
"期望角速度2",
"期望角速度3",
"计算当前J2000位置X",
"计算当前J2000位置Y",
"计算当前J2000位置Z",
"计算当前J2000速度X",
"计算当前J2000速度Y",
"计算当前J2000速度Z",
"计算当前84位置X",
"计算当前84位置Y",
"计算当前84位置Z",
"计算当前84速度X",
"计算当前84速度Y",
"计算当前84速度Z",
"偏流角",
"数传点经度",
"数传点纬度",
"数传点地程高",
"定姿方式标志1",
"定姿方式标志2",
"定姿方式标志3",
"X飞轮期望转速",
"Y飞轮期望转速",
"Z飞轮期望转速",
"保留",
"保留",
"保留",
"保留",
"锂电池温度",
"相机主镜温度",
"相机次镜温度",
"推进模块温度",
"负X侧相机桁架杆温度",
"正X正Y侧相机桁架杆温度",
"正X负Y侧相机桁架杆温度",
"正X侧相机支撑背板温度",
"负X侧相机支撑背板温度",
"星敏支架温度",
"成像电箱温度",
"正Y帆板温度",
"电源下位机温度",
"配电热控驱动温度",
"电源控制器温度",
"数字太阳敏矢量数据有效位",
"数字太阳敏矢量数据X",
"数字太阳敏矢量数据Y",
"数字太阳敏位置X1",
"数字太阳敏位置X2",
"数字太阳敏位置Y1",
"数字太阳敏位置Y2",
"太阳敏温度",
"数字太阳敏当前正在使用的阈值(源码)",
"数字太阳敏当前正在使用的增益",
"保留",
"保留",
"保留",
"错误码计数",
"单粒子错误计数",
"配电错误码1",
"配电错误码2",
"配电错误码3",
"配电错误码4",
"配电错误码5",
"GPS天内秒",
"GPSUTC时间累计秒",
"太阳阵电流",
"母线电流",
"负载电流",
"蓄电池电压",
"电源母线电压",
"CPU5.2V电压遥测值",
"5.2V配电电压遥测值",
"保留",
"蓄电池组当前电量",
"模式运行时间秒",
"卫星现运行模式",
"组合业务标识",
"当前业务状态",
"中心机指令接收总计数",
"中心机错误指令计数",
"执行指令所在分系统",
"执行指令的指令代码",
"执行延时指令总计数",
"当前延时指令计数",
"执行延时指令所在分系统",
"执行延时指令的指令代码",
"当前延时业务计数",
"成功执行业务计数",
"异常中止业务计数",
"指令执行状态",
"业务异常中止状态",
"测控数传一体机通信状态",
"保留",
"北斗短报文通信状态",
"GPS接收机通信状态",
"数字太阳敏通信状态",
"星敏1通信状态",
"星敏2通信状态",
"光纤陀螺通信状态",
"MEMS陀螺通信状态",
"飞轮1通信状态",
"飞轮2通信状态",
"飞轮3通信状态",
"飞轮4通信状态",
"智能载荷通信状态",
"保留",
"保留",
"电磁阀开关状态",
"业务保存状态",
"卫星类型标识",
"卫星序号标识",
"保留",
"保留",
"保留",
"保留",
"锂电池加热器通断状态",
"相机主镜加热器通断状态",
"相机次镜加热器通断状态",
"推进模块加热器通断状态",
"相机负X侧桁架杆加热器通断状态",
"成像电箱加热器通断状态",
"相机正X正Y侧桁架杆加热器通断状态",
"相机正X负Y侧桁架杆加热器通断状态",
"相机正X侧支撑背板加热器通断状态",
"相机负X侧支撑背板加热器通断状态",
"星敏支架加热器通断状态",
"保留",
"温度修正系数校验状态",
"电源下位机广播帧监视功能",
"当前控温码表",
"默认控温码表",
"飞轮1电源供电状态",
"飞轮2电源供电状态",
"飞轮3电源供电状态",
"SADA1电源供电状态",
"SADA2电源供电状态",
"测控数传电源供电状态",
"保留",
"保留",
"北斗短报文供电状态",
"推进电源供电状态",
"焦面电源供电状态",
"飞轮4电源供电状态",
"星敏1电源供电状态",
"星敏2电源供电状态",
"数字太阳敏电源供电状态",
"导航电源供电状态",
"三轴光纤陀螺电源供电状态",
"MEMS陀螺电源供电状态",
"热控正线状态",
"热控1状态",
"热控2状态",
"保留",
"保留",
"保留",
"锂电池加热器控温模式",
"相机主镜加热器控温模式",
"相机次镜加热器控温模式",
"推进模块加热器控温模式",
"相机负X侧桁架杆加热器控温模式",
"相机正X正Y侧桁架杆加热器控温模式",
"相机正X负Y侧桁架杆加热器控温模式",
"相机正X侧支撑背板加热器控温模式",
"相机负X侧支撑背板加热器控温模式",
"星敏支架加热器控温模式",
"成像电箱加热器控温模式",
"保留",
"保留",
"保留",
"保留",
"保留",
"保留",
"保留",
"接收机时间来源",
"定位模式",
"轨道数据有效标示",
"主备机标志",
"PPS状态",
"GPS最高信噪比",
"BD最高信噪比",
"参与定位的GPS导航星数",
"参与定位的BD导航星数",
"GPS几何精度因子",
"GPS连续工作时间",
"保留",
"保留",
"WGS-84系X位置",
"WGS-84系Y位置",
"WGS-84系Z位置",
"WGS-84系X速度",
"WGS-84系Y速度",
"WGS-84系Z速度",
"J2000系X位置",
"J2000系Y位置",
"J2000系Z位置",
"J2000系X速度",
"J2000系Y速度",
"J2000系Z速度",
"三轴光纤陀螺X轴角速度",
"三轴光纤陀螺Y轴角速度",
"三轴光纤陀螺Z轴角速度",
"三轴光纤陀螺X轴角速度1",
"三轴光纤陀螺Y轴角速度1",
"三轴光纤陀螺Z轴角速度1",
"星敏AUTC时间",
"星敏AUTC秒小数",
"星敏A四元数q1",
"星敏A四元数q2",
"星敏A四元数q3",
"星敏A四元数q4",
"星敏A曝光时间",
"星敏A阈值",
"星敏A背景值",
"星敏A上电进入boot标志",
"星敏AEDAC打开标志",
"星敏A程序版本",
"星敏A四元数滤波标志",
"星敏A系统内部工作进程代号",
"星敏A系统工作模式",
"星敏A提取星数",
"星敏A四元数有效时导航星数",
"星敏A图像增益",
"星敏A识别星数",
"星敏A打开或者关断外部图像",
"星敏A姿态数据有效标志",
"星敏A内部软件版本号低3位",
"星敏A产品设备编号低5位",
"星敏A成像传感器温度",
"星敏A在轨EDAC错误计数",
"星敏A图像帧号",
"星敏A四星寻找阈值",
"星敏A跟踪阈值",
"星敏ASAA阈值",
"星敏ASAA工作模式",
"星敏A动态模式标志位",
"星敏AX方向角速度",
"星敏AY方向角速度",
"星敏AZ方向角速度",
"星敏A星点阈值自适应功能",
"保留",
"星敏A速率质量",
"星敏BUTC时间",
"星敏BUTC秒小数",
"星敏B四元数q1",
"星敏B四元数q2",
"星敏B四元数q3",
"星敏B四元数q4",
"星敏B曝光时间",
"星敏B阈值",
"星敏B背景值",
"星敏B上电进入boot标志",
"星敏BEDAC打开标志",
"星敏B程序版本",
"星敏B四元数滤波标志",
"星敏B系统内部工作进程代号",
"星敏B系统工作模式",
"星敏B提取星数",
"星敏B四元数有效时导航星数",
"星敏B图像增益",
"星敏B识别星数",
"星敏B打开或者关断外部图像",
"星敏B姿态数据有效标志",
"星敏B内部软件版本号低3位",
"星敏B产品设备编号低5位",
"星敏B成像传感器温度",
"星敏B在轨EDAC错误计数",
"星敏B图像帧号",
"星敏B四星寻找阈值",
"星敏B跟踪阈值",
"星敏BSAA阈值",
"星敏BSAA工作模式",
"星敏B动态模式标志位",
"保留",
"飞轮1转速",
"飞轮1当前电流",
"飞轮2转速",
"飞轮2当前电流",
"飞轮3转速",
"飞轮3当前电流",
"飞轮4转速",
"飞轮4当前电流",
"MEMS陀螺X方向角速度",
"MEMS陀螺Y方向角速度",
"MEMS陀螺Z方向角速度",
"X方向磁场强度",
"Y方向磁场强度",
"Z方向磁场强度",
"输入姿轨控数据UTC时间秒",
"输入姿轨控数据时间秒小数",
"保留",
"校验和",
}

138
extract/aux_head.go Normal file
View File

@@ -0,0 +1,138 @@
package extract
import (
"encoding/binary"
"errors"
"fmt"
"github.com/tealeg/xlsx"
)
// 每行传感器数据帧头信息长度为24字节
type AuxFrameHead struct {
PkgHead [4]byte // 包头头标识符固定为0xD15BD15B
FillByte0 byte // 填充字节 0x00
FrmHead [6]byte // 帧头标识符固定为0xEB90EB90EB90
// 图像模式
// B7 模式标识 000为线阵模式
// B6
// B5
// B4 B4谱状态 0不输出1输出
// B3 B3谱状态 0不输出1输出
// B2 B2谱状态 0不输出1输出
// B1 B1谱状态 0不输出1输出
// B0 全色状态 0不输出1输出
ImgMode byte
SerialNo uint32 // 当前流水号
TimeSec uint32 // 时间(秒)
TimeSecFrac uint32 // 秒小数 量纲1us/bit十六进制无符号整型数
FileNo uint8 // 文件号
IsValidFrmHead bool
B0 bool
B1 bool
B2 bool
B3 bool
B4 bool
IsLinerMatrix bool
RowLength int
}
func (afh *AuxFrameHead) Decode(data []byte) error {
if len(data) < 24 {
return errors.New("length of AuxFrameHead is not 24")
}
afh.PkgHead = [4]byte{data[0], data[1], data[2], data[3]}
afh.FillByte0 = data[4]
afh.FrmHead = [6]byte{data[5], data[6], data[7], data[8], data[9], data[10]}
afh.ImgMode = data[11]
afh.B0 = (afh.ImgMode&(1<<0) != 0x0)
afh.B1 = (afh.ImgMode&(1<<1) != 0x0)
afh.B2 = (afh.ImgMode&(1<<2) != 0x0)
afh.B3 = (afh.ImgMode&(1<<3) != 0x0)
afh.B4 = (afh.ImgMode&(1<<4) != 0x0)
afh.IsLinerMatrix = (afh.ImgMode&(1<<5) == 0x0 && afh.ImgMode&(1<<6) == 0x0 && afh.ImgMode&(1<<7) == 0x0)
afh.SerialNo = binary.BigEndian.Uint32(data[12:16])
afh.TimeSec = binary.BigEndian.Uint32(data[16:20])
afh.TimeSecFrac = uint32(uint32(data[20])<<16 | uint32(data[21])<<8 | uint32(data[22]))
afh.FileNo = data[23]
afh.RowLength = afh.LengthOfRow()
afh.IsValidFrmHead = afh.CheckFrmHead()
return nil
}
// 计算一行数据长度:帧头+辅助数据+图像数据
func (afh AuxFrameHead) LengthOfRow() int {
if !afh.IsLinerMatrix {
return 14344
}
length := 64 // 帧头+辅助数据长度
if afh.B0 {
length += 19040
}
if afh.B1 {
length += 1192
}
if afh.B2 {
length += 1192
}
if afh.B3 {
length += 1192
}
if afh.B4 {
length += 1192
}
return length
}
func (afh AuxFrameHead) CheckFrmHead() bool {
if afh.FrmHead[0] == 0xEB && afh.FrmHead[1] == 0x90 &&
afh.FrmHead[2] == 0xEB && afh.FrmHead[3] == 0x90 &&
afh.FrmHead[4] == 0xEB && afh.FrmHead[5] == 0x90 {
return true
}
return false
}
func (afh AuxFrameHead) SaveXlsx(row *xlsx.Row) error {
values := []string{
fmt.Sprintf("0x%x", afh.PkgHead),
"",
fmt.Sprintf("0x%x", afh.FrmHead),
afh.ImageMode(),
afh.BandStatus(afh.B4),
afh.BandStatus(afh.B3),
afh.BandStatus(afh.B2),
afh.BandStatus(afh.B1),
afh.BandStatus(afh.B0),
fmt.Sprintf("%d", afh.SerialNo),
fmt.Sprintf("%d", afh.TimeSec),
fmt.Sprintf("%d", afh.TimeSecFrac),
fmt.Sprintf("%d", afh.FileNo),
}
for _, v := range values {
row.AddCell().SetString(v)
}
return nil
}
func (afh AuxFrameHead) ImageMode() string {
if afh.IsLinerMatrix {
return "线阵模式"
}
return "面阵模式"
}
func (afh AuxFrameHead) BandStatus(b bool) string {
if b {
return "输出"
}
return "不输出"
}

View File

@@ -7,7 +7,8 @@ import (
"time" "time"
"github.com/k0kubun/pp/v3" "github.com/k0kubun/pp/v3"
"github.com/sirupsen/logrus" "github.com/tealeg/xlsx"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"starwiz.cn/sjy01/preprocessing/calculator" "starwiz.cn/sjy01/preprocessing/calculator"
) )
@@ -129,7 +130,7 @@ type AuxPlatform struct {
SS1_ExtenalImage bool // [407.(1)] 星敏1外部图像标志位 0x01外部图像0x00关闭 SS1_ExtenalImage bool // [407.(1)] 星敏1外部图像标志位 0x01外部图像0x00关闭
SS1_AttitudeActive bool // [407.(0)] 星敏1姿态有效标志位 0x01激活0x00关闭 SS1_AttitudeActive bool // [407.(0)] 星敏1姿态有效标志位 0x01激活0x00关闭
SS1_ImgFrmNo uint32 // [411-413] 星敏1图像帧号 SS1_ImgFrmNo uint32 // [411-413] 星敏1图像帧号
SS1_XAV float64 // [417-418] 星敏1X方向角速度 单位2-11 °/s SS1_XAV float64 // [417-418] 星敏1X方向角速度 单位2e-11 °/s
SS1_YAV float64 // [419-420] SS1_YAV float64 // [419-420]
SS1_ZAV float64 // [421-422] SS1_ZAV float64 // [421-422]
SS2_UTCTime uint32 // [424-427] 星敏2 UTC时间 SS2_UTCTime uint32 // [424-427] 星敏2 UTC时间
@@ -170,7 +171,7 @@ type AuxPlatform struct {
CheckSum byte // [512] 校验和 CheckSum byte // [512] 校验和
} }
func (ap *AuxPlatform) Parse(data []byte) error { func (ap *AuxPlatform) Decode(data []byte) error {
if len(data) < 512 { if len(data) < 512 {
return ErrAuxPlatformDataLen return ErrAuxPlatformDataLen
} }
@@ -337,19 +338,23 @@ func (ap AuxPlatform) Print() {
pp.Println(ap) pp.Println(ap)
} }
func (ap AuxPlatform) SaveXlsx(row *xlsx.Row) error {
return nil
}
// Extractor 辅助数据提取器 // Extractor 辅助数据提取器
// auxfile 辅助数据文件路径 // auxfile 辅助数据文件路径
func (e *Extractor) ParseAuxPlatform(auxfile string) ([]*AuxPlatform, error) { func (e *Extractor) ParseAuxPlatform(auxfile string) ([]*AuxPlatform, error) {
data, err := os.ReadFile(auxfile) data, err := os.ReadFile(auxfile)
if err != nil { if err != nil {
logrus.Println("read aux data from", auxfile, "error:", err.Error()) log.Println("read aux data from", auxfile, "error:", err.Error())
return nil, err return nil, err
} }
var aps []*AuxPlatform var aps []*AuxPlatform
for i := 0; i < len(data); i += 24 + 128 + 512 { for i := 0; i < len(data); i += 24 + 128 + 512 {
ap := AuxPlatform{} ap := AuxPlatform{}
if err := ap.Parse(data[i+24+128 : i+24+128+512]); err != nil { if err := ap.Decode(data[i+24+128 : i+24+128+512]); err != nil {
return nil, err return nil, err
} }
@@ -387,7 +392,7 @@ func (e *Extractor) ParseAuxPlatform(auxfile string) ([]*AuxPlatform, error) {
func (e *Extractor) ParseAuxPlatformWithHead(auxfile string) ([]*AuxPlatform, error) { func (e *Extractor) ParseAuxPlatformWithHead(auxfile string) ([]*AuxPlatform, error) {
data, err := os.ReadFile(auxfile) data, err := os.ReadFile(auxfile)
if err != nil { if err != nil {
logrus.Println("read aux data from", auxfile, "error:", err.Error()) log.Println("read aux data from", auxfile, "error:", err.Error())
return nil, err return nil, err
} }

View File

@@ -1,114 +0,0 @@
package extract
import (
"fmt"
"math"
)
// 定义常数
const (
GM = 3.986004418e14 // 地球引力常数m^3/s^2
Re = 6378137.0 // 地球半径m
)
// 四元数
type Quaternion struct {
w, x, y, z float64
}
// 矢量
type Vector3 struct {
x, y, z float64
}
// 计算轨道参数
func calculateOrbitalParameters(position, velocity Vector3) (float64, float64, float64, float64, float64) {
r := math.Sqrt(position.x*position.x + position.y*position.y + position.z*position.z)
v := math.Sqrt(velocity.x*velocity.x + velocity.y*velocity.y + velocity.z*velocity.z)
// 计算轨道参数
energy := 0.5*v*v - GM/r // 比能
a := -GM / (2 * energy) // 轨道半长轴
e := math.Sqrt(1 - (math.Pow(math.Sqrt(math.Pow(position.x*velocity.y-position.y*velocity.x, 2)+
math.Pow(position.y*velocity.z-position.z*velocity.y, 2)+
math.Pow(position.z*velocity.x-position.x*velocity.z, 2)), 2) / (GM * a)))
i := math.Acos(position.z / r) // 轨道倾角
Omega := math.Atan2(position.x, -position.y) // 升交点赤经
omega := math.Atan2(position.z*velocity.x-position.x*velocity.z, position.x*velocity.y-position.y*velocity.x) // 升交点赤纬
return a, e, i * 180 / math.Pi, Omega * 180 / math.Pi, omega * 180 / math.Pi
}
// 四元数到旋转矩阵
func quaternionToRotationMatrix(q Quaternion) [3][3]float64 {
w, x, y, z := q.w, q.x, q.y, q.z
return [3][3]float64{
{1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w},
{2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w},
{2*x*z - 2*y*w, 2*y*z + 2*x*w, 1 - 2*x*x - 2*y*y},
}
}
// 向量加法
func add(v1, v2 Vector3) Vector3 {
return Vector3{v1.x + v2.x, v1.y + v2.y, v1.z + v2.z}
}
// 向量数乘
func scale(v Vector3, c float64) Vector3 {
return Vector3{v.x * c, v.y * c, v.z * c}
}
// 矩阵乘法
func matrixMult(m [3][3]float64, v Vector3) Vector3 {
return Vector3{
m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z,
m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z,
m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z,
}
}
// 计算图像位置
func calculateImagePosition(roll, pitch, yaw float64, quat Quaternion, satellitePos, satelliteVel Vector3) (float64, float64) {
// 将角度转换为弧度
roll = roll * math.Pi / 180
pitch = pitch * math.Pi / 180
yaw = yaw * math.Pi / 180
// 构造旋转矩阵
R := quaternionToRotationMatrix(quat)
// 传感器坐标系中的Z轴向量
sensorZ := Vector3{0, 0, 1}
// 将Z轴向量通过旋转矩阵转换到卫星坐标系中
lineOfSight := matrixMult(R, sensorZ)
// 计算交点经纬度
lat := math.Asin(lineOfSight.z) * 180 / math.Pi
lon := math.Atan2(lineOfSight.y, lineOfSight.x) * 180 / math.Pi
return lat, lon
}
func Calculate(satellitePos, satelliteVel Vector3, quat Quaternion) {
// 假设一些已知数据
// satellitePos := Vector3{x: 7000e3, y: 0, z: 0} // 假设一个简单的地心坐标位置
// satelliteVel := Vector3{x: 0, y: 7.5e3, z: 0} // 假设一个简单的速度
// quat := Quaternion{w: 0.7071, x: 0.7071, y: 0, z: 0} // 假设一个简单的四元数
roll := 0.0
pitch := 0.0
yaw := 0.0
// 计算轨道参数
a, e, i, Omega, omega := calculateOrbitalParameters(satellitePos, satelliteVel)
// 打印轨道参数
fmt.Println("轨道参数:")
fmt.Printf("轨道半长轴 (a): %.2f m\n", a)
fmt.Printf("偏心率 (e): %.6f\n", e)
fmt.Printf("轨道倾角 (i): %.2f 度\n", i)
fmt.Printf("升交点赤经 (Ω): %.2f 度\n", Omega)
fmt.Printf("近地点幅角 (ω): %.2f 度\n", omega)
// 计算图像位置
lat, lon := calculateImagePosition(roll, pitch, yaw, quat, satellitePos, satelliteVel)
fmt.Printf("\n图像位置\n纬度: %.6f\n经度: %.6f\n", lat, lon)
}

View File

@@ -4,18 +4,9 @@ import (
"bytes" "bytes"
"html" "html"
"io"
"os"
"text/template" "text/template"
) )
type BSQWriter struct {
filepath string
fio *os.File
w io.Writer
content *EnviHdr
}
var HDRTemplate = `ENVI var HDRTemplate = `ENVI
description = { description = {
File Imported into ENVI.} File Imported into ENVI.}

View File

@@ -1 +0,0 @@
package extract

289
extract/seperate.go Normal file
View File

@@ -0,0 +1,289 @@
package extract
import (
"encoding/binary"
"errors"
"fmt"
"math"
"os"
"strings"
"time"
"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus"
)
// 从传输帧文件中分离辅助数据,分别存储到辅助数据文件 _AUX.dat 和图像数据文件 _IMG_{波谱}.dat 中
func (e *Extractor) SeprateAuxAndImgData(dataFile string, segmentIndex int) error {
// 打开传输帧文件 - 一次读入内存
data, err := os.ReadFile(dataFile)
if err != nil {
log.Println("read data from", dataFile, "error:", err.Error())
return err
}
log.Info("seprate aux and img data from", dataFile)
dataLen := len(data)
log.Info("length of original data: ", dataLen)
var firstFrmHead *AuxFrameHead
data, firstFrmHead = e.trimImgRawData(data)
dataLen = len(data)
log.Info("after trim,length of data: ", dataLen)
e.quanlityAnalysis(data)
outputDir := e.params.OutputPath
name := strings.Join([]string{
e.params.Satellite,
"PMS",
time.Unix(int64(firstFrmHead.TimeSec)+int64(ReferenceTime2000),
int64(firstFrmHead.TimeSecFrac)*1000).
Format("20060102_150405"),
e.params.DataId,
fmt.Sprintf("%03d", firstFrmHead.FileNo),
}, "_")
lw := newL0Writer(outputDir, name)
defer lw.Close()
var panEnviHdr, mssEnviHdr EnviHdr
var afh AuxFrameHead
msdata := make([][]byte, 4)
var header []byte
var ebAux []byte
var platAux []byte
for i := 0; i < dataLen; {
if i+4 > dataLen {
logrus.Println("end of data, dataLen:", dataLen, "i:", i)
break
}
// 包头
if data[i] == 0xD1 && data[i+1] == 0x5B && data[i+2] == 0xD1 && data[i+3] == 0x5B {
log.Debug("find package head: 0xD15BD15B")
} else {
i++
continue
}
// 解析帧
if i+24 > dataLen {
log.Info("length of frame head is not engough for frame head")
break
}
afh.Decode(data[i : i+24])
if !afh.IsValidFrmHead {
log.Info("invalid frame head of original raw data")
i += 1
continue
}
// 目前只支持线阵模式
if !afh.IsLinerMatrix {
log.Error("not liner matrix mode, only support liner matrix mode")
break
}
// 读取一行数据 data[i : i+afh.RowLength]
if i+afh.RowLength > dataLen {
log.Infof("length of image row data %v is not enough: %v", dataLen-i, afh.RowLength)
break
}
auxTime := binary.BigEndian.Uint32(data[i+32 : i+36])
if math.Abs(float64(auxTime)-float64(afh.TimeSec)) < 30 {
header = append(header, data[i:i+24]...)
}
// 存储辅助数据到临时文件
dataIndex := i + 24
// lw.ws[EB_AUX].w.Write(data[dataIndex : dataIndex+8]) // 8字节焦面电箱辅助数据
ebAux = append(ebAux, data[dataIndex:dataIndex+8]...)
dataIndex += 8
// lw.ws[PLAT_AUX].w.Write(data[dataIndex : dataIndex+32]) // 32字节中心机辅助数据
platAux = append(platAux, data[dataIndex:dataIndex+32]...)
dataIndex += 32
// 存储图像数据到临时文件 - 以 ENVI BSQ 格式存储,同时提供 HDR 描述文件
if afh.B0 {
// wpan.Write(data[dataIndex : dataIndex+19040])
write16bPixelLittleEndian(lw.ws[PAN_RAW].w, data[dataIndex:dataIndex+19040])
dataIndex += 19040
panEnviHdr.Lines += 1
}
if afh.B1 {
// write16bPixelLittleEndian(wmss, data[dataIndex:dataIndex+1192])
msdata[0] = append(msdata[0], data[dataIndex:dataIndex+1192]...)
dataIndex += 1192
mssEnviHdr.Lines += 1
}
if afh.B2 {
// write16bPixelLittleEndian(wmss, data[dataIndex:dataIndex+1192])
msdata[1] = append(msdata[1], data[dataIndex:dataIndex+1192]...)
dataIndex += 1192
}
if afh.B3 {
// write16bPixelLittleEndian(wmss, data[dataIndex:dataIndex+1192])
msdata[2] = append(msdata[2], data[dataIndex:dataIndex+1192]...)
dataIndex += 1192
}
if afh.B4 {
// write16bPixelLittleEndian(wmss, data[dataIndex:dataIndex+1192])
msdata[3] = append(msdata[3], data[dataIndex:dataIndex+1192]...)
dataIndex += 1192
}
i = dataIndex // 完成一行数据解析
}
// var bands = 0
// for i := 0; i < 4; i++ {
// if len(msdata[i]) > 0 {
// log.Println("write mss data of band B", i+1)
// _, err := write16bPixelLittleEndian(lw.ws[MSS_RAW].w, msdata[i])
// if err != nil {
// log.Error("write mss data error:", err.Error())
// }
// bands += 1
// }
// }
if len(msdata[0]) != len(msdata[1]) || len(msdata[2]) != len(msdata[3]) {
log.Error("mss data of bands B1-B4 are not equal")
return errors.New("mss data of bands B1-B4 are not equal")
}
mssRowLen := 1192 * 4
for i := 0; i < len(msdata[0]); i += mssRowLen {
var err error
_, err = write16bPixelLittleEndian(lw.ws[MSS_RAW].w, msdata[0][i:i+mssRowLen])
if err != nil {
log.Error("write mss 1 data error:", err.Error())
}
_, err = write16bPixelLittleEndian(lw.ws[MSS_RAW].w, msdata[1][i:i+mssRowLen])
if err != nil {
log.Error("write mss 2 data error:", err.Error())
}
_, err = write16bPixelLittleEndian(lw.ws[MSS_RAW].w, msdata[2][i:i+mssRowLen])
if err != nil {
log.Error("write mss 3 data error:", err.Error())
}
_, err = write16bPixelLittleEndian(lw.ws[MSS_RAW].w, msdata[3][i:i+mssRowLen])
if err != nil {
log.Error("write mss 4 data error:", err.Error())
}
}
panEnviHdr.Samples = 9520
panEnviHdr.Bands = 1
lw.ws[PAN_HDR].w.Write([]byte(panEnviHdr.String()))
mssEnviHdr.Lines = mssEnviHdr.Lines / 4 // 多光谱波段分别在 4 行中传输
mssEnviHdr.Samples = 2384 * 4
mssEnviHdr.Bands = 1
lw.ws[MSS_HDR].w.Write([]byte(mssEnviHdr.String()))
// 帧头+辅助数据
if len(header)/24 < len(ebAux)/128 || len(ebAux)/128 != len(platAux)/512 {
fmt.Println("aux data length:", len(header)/24, len(ebAux)/128, len(platAux)/512)
return errors.New("aux data length is not equal")
}
auxRows := len(header) / 24
for i := 0; i < auxRows; i++ {
lw.ws[AUX].w.Write(header[i*24 : (i+1)*24])
lw.ws[AUX].w.Write(ebAux[i*128 : (i+1)*128])
lw.ws[AUX].w.Write(platAux[i*512 : (i+1)*512])
}
return nil
}
func (e *Extractor) quanlityAnalysis(data []byte) {
// 数据质量分析
fimg, _ := os.Create("demo/temp/" + e.params.DataId + "/aux.txt")
defer fimg.Close()
fimg.WriteString("字节数 帧头流水号 文件号 帧头时间 中心辅助数据前4字节(行33-36)\n")
var startAuxLine bool
var preSN uint32
for i := 0; i < len(data); {
afh := &AuxFrameHead{}
afh.Decode(data[i : i+24])
if !afh.IsValidFrmHead {
log.Errorf("invalid frame head of original raw data i: %v, len: %v", i, len(data))
return
}
t := time.Unix(int64(afh.TimeSec+uint32(ReferenceTime2000)), int64(afh.TimeSecFrac)*1000)
utcTime := binary.BigEndian.Uint32(data[i+32 : i+36])
tAux := time.Unix(int64(utcTime+uint32(ReferenceTime2000)), 0)
startAuxLine = math.Abs(float64(utcTime)-float64(afh.TimeSec)) < 30
if startAuxLine {
fimg.WriteString(fmt.Sprintf("%d %d %d %s %s\n",
i,
afh.SerialNo,
afh.FileNo,
t.String(),
tAux.String(),
))
if afh.SerialNo-preSN != 16 && preSN != 0 {
log.Println("serial number not continuous", afh.SerialNo, preSN)
}
preSN = afh.SerialNo
}
i += afh.RowLength
}
}
// 裁剪图像数据,只保留有效数据
func (e *Extractor) trimImgRawData(data []byte) ([]byte, *AuxFrameHead) {
var start, end int
afh := &AuxFrameHead{}
// 将中心辅助数据时间合理的帧作为第一帧
var startAuxLine bool
for i := 0; i < len(data); {
// 解析帧
if i+24 > len(data) {
log.Info("length of original image frame head is not engough: ", len(data)-i)
break
}
if data[i] == 0xD1 && data[i+1] == 0x5B && data[i+2] == 0xD1 && data[i+3] == 0x5B {
log.Debug("find package head: 0xD15BD15B")
} else {
i++
continue
}
afh.Decode(data[i : i+24])
if !afh.IsValidFrmHead {
log.Debugf("invalid frame head of original raw data %v", i)
i += 1
continue
}
utcTime := binary.BigEndian.Uint32(data[i+32 : i+36]) // 相对于2000年的秒数
startAuxLine = math.Abs(float64(utcTime)-float64(afh.TimeSec)) < 30 // 时间差小于30秒认为是有效数据
if !startAuxLine {
i += afh.RowLength
} else {
start = i
nLen := (len(data) - i - 1)
nLen = nLen - nLen%(afh.RowLength*16)
end = i + nLen
break
}
}
return data[start:end], afh
}

3
go.mod
View File

@@ -4,13 +4,12 @@ go 1.21
toolchain go1.21.0 toolchain go1.21.0
require github.com/twpayne/go-proj/v10 v10.2.0
require ( require (
github.com/jonboulle/clockwork v0.4.0 // indirect github.com/jonboulle/clockwork v0.4.0 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.33.1 // indirect github.com/onsi/gomega v1.33.1 // indirect
github.com/tealeg/xlsx v1.0.5 // indirect
golang.org/x/crypto v0.23.0 // indirect golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0 // indirect golang.org/x/net v0.25.0 // indirect
golang.org/x/term v0.20.0 // indirect golang.org/x/term v0.20.0 // indirect

14
go.sum
View File

@@ -1,7 +1,3 @@
github.com/alecthomas/assert/v2 v2.8.0 h1:8b0foWfS2dH6MltxQMPvWdSGN1wE4K1vyab9PGW4qgE=
github.com/alecthomas/assert/v2 v2.8.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -24,8 +20,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
@@ -33,6 +27,9 @@ github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs= github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs=
github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA= github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
@@ -75,8 +72,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/twpayne/go-proj/v10 v10.2.0 h1:EurvRTcUMqsiARMhcF0KDvBjKZeCzJqAPQ0WAbrDFdQ= github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
github.com/twpayne/go-proj/v10 v10.2.0/go.mod h1:BU5zbXY6XmKenoyf+DbIg7joKlKTJM2rEssPMPJZYjY= github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -132,6 +129,7 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=