diff --git a/calculator/quaternion.go b/calculator/quaternion.go new file mode 100644 index 0000000..e19b58b --- /dev/null +++ b/calculator/quaternion.go @@ -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) + } +} diff --git a/cmd/extract.go b/cmd/extract.go index 0837a0c..c0e37fd 100644 --- a/cmd/extract.go +++ b/cmd/extract.go @@ -10,6 +10,7 @@ import ( var ( dataId string batch bool + output string ) var extractCmd = &cobra.Command{ @@ -31,7 +32,7 @@ var extractCmd = &cobra.Command{ } else { p := &extract.Params{ 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), DataId: dataId, Satellite: "SJY01", @@ -51,16 +52,18 @@ func init() { extractCmd.Flags().StringVarP(&dataId, "data-id", "d", "051622", "051622") extractCmd.Flags().BoolVarP(&batch, "batch", "b", false, "true | false") + extractCmd.Flags().StringVarP(&output, "out", "o", "demo/output", "demo/output") } func params() []*extract.Params { var params []*extract.Params - datas := []string{"051513", "051622", - "051712", "051721", "051821", "051823", "051921", "051922", "Q051723"} + datas := []string{"051622", "051712", "051721", + "051813", "051821", "051823", + "051921", "051922", "052022"} for _, d := range datas { params = append(params, &extract.Params{ 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), DataId: d, Satellite: "SJY01", diff --git a/cmd/parse.go b/cmd/parse.go index 642e452..1c08a7d 100644 --- a/cmd/parse.go +++ b/cmd/parse.go @@ -24,12 +24,14 @@ var parseCmd = &cobra.Command{ e := extract.NewExtractor(¶ms) // p.ParseAuxPlatformWithHead("demo/ref/辅助数据.dat") 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 { logrus.Error(err) } // 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") }, } diff --git a/extract/attitude.go b/extract/attitude.go deleted file mode 100644 index 0cc19c1..0000000 --- a/extract/attitude.go +++ /dev/null @@ -1,10 +0,0 @@ -package extract - -/* - 将欧拉角 (yaw, pitch, roll) 转换为旋转矩阵 - - 参数: - - yaw: 偏航角(绕Z轴旋转) - - pitch: 俯仰角(绕Y轴旋转) - - roll: 滚转角(绕X轴旋转) -*/ diff --git a/extract/aux.go b/extract/aux.go index 37d8b85..e643f32 100644 --- a/extract/aux.go +++ b/extract/aux.go @@ -1,380 +1,45 @@ package extract import ( - "encoding/binary" - "errors" - "fmt" - "math" "os" - "strings" - "time" - "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus" + "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 -} - -// 从传输帧文件中分离辅助数据,分别存储到辅助数据文件 _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()) +func (e Extractor) ExtractAux(auxfile, xlsxfile string) error { + if err := createAuxXlxs(xlsxfile); err != nil { 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 // 完成一行数据解析 + wb, err := xlsx.OpenFile(xlsxfile) + if err != nil { + panic(err) } - // 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 - // } - // } + sh := wb.Sheets[0] - 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") + data, err := os.ReadFile(auxfile) + if err != nil { + log.Println("read aux data from", auxfile, "error:", err.Error()) + return err } - 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()) - } - } + for i := 0; i < len(data); i += 24 + 128 + 512 { + row := sh.AddRow() - panEnviHdr.Samples = 9520 - panEnviHdr.Bands = 1 - lw.ws[PAN_HDR].w.Write([]byte(panEnviHdr.String())) + var head AuxFrameHead + head.Decode(data[i : i+24]) + head.SaveXlsx(row) - mssEnviHdr.Lines = mssEnviHdr.Lines / 4 // 多光谱波段分别在 4 行中传输 - mssEnviHdr.Samples = 2384 * 4 - mssEnviHdr.Bands = 1 - lw.ws[MSS_HDR].w.Write([]byte(mssEnviHdr.String())) + var box AuxFocalBox + box.Decode(data[i+24 : i+24+128]) + box.SaveXlsx(row) - // 帧头+辅助数据 - 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]) + var plat AuxPlatform + plat.Decode(data[i+24+128 : i+24+128+512]) + box.SaveXlsx(row) } 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 -} diff --git a/extract/aux_ebox.go b/extract/aux_ebox.go index 7cc22e0..13dc718 100644 --- a/extract/aux_ebox.go +++ b/extract/aux_ebox.go @@ -6,6 +6,7 @@ import ( "github.com/k0kubun/pp/v3" "github.com/sirupsen/logrus" + "github.com/tealeg/xlsx" ) // 卫星时间起点 北京时间 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) { data, err := os.ReadFile(auxfile) if err != nil { diff --git a/extract/aux_elements.go b/extract/aux_elements.go new file mode 100644 index 0000000..16bc4b3 --- /dev/null +++ b/extract/aux_elements.go @@ -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时间秒", + "输入姿轨控数据时间秒小数", + "保留", + "校验和", +} diff --git a/extract/aux_head.go b/extract/aux_head.go new file mode 100644 index 0000000..d2d8816 --- /dev/null +++ b/extract/aux_head.go @@ -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 "不输出" +} diff --git a/extract/aux_platform.go b/extract/aux_platform.go index ffdf32f..24c14d9 100644 --- a/extract/aux_platform.go +++ b/extract/aux_platform.go @@ -7,7 +7,8 @@ import ( "time" "github.com/k0kubun/pp/v3" - "github.com/sirupsen/logrus" + "github.com/tealeg/xlsx" + log "github.com/sirupsen/logrus" "starwiz.cn/sjy01/preprocessing/calculator" ) @@ -129,7 +130,7 @@ type AuxPlatform struct { SS1_ExtenalImage bool // [407.(1)] 星敏1外部图像标志位 0x01:外部图像;0x00:关闭 SS1_AttitudeActive bool // [407.(0)] 星敏1姿态有效标志位 0x01:激活;0x00:关闭 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_ZAV float64 // [421-422] SS2_UTCTime uint32 // [424-427] 星敏2 UTC时间 @@ -170,7 +171,7 @@ type AuxPlatform struct { CheckSum byte // [512] 校验和 } -func (ap *AuxPlatform) Parse(data []byte) error { +func (ap *AuxPlatform) Decode(data []byte) error { if len(data) < 512 { return ErrAuxPlatformDataLen } @@ -337,19 +338,23 @@ func (ap AuxPlatform) Print() { pp.Println(ap) } +func (ap AuxPlatform) SaveXlsx(row *xlsx.Row) error { + return nil +} + // Extractor 辅助数据提取器 // auxfile 辅助数据文件路径 func (e *Extractor) ParseAuxPlatform(auxfile string) ([]*AuxPlatform, error) { data, err := os.ReadFile(auxfile) 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 } var aps []*AuxPlatform for i := 0; i < len(data); i += 24 + 128 + 512 { 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 } @@ -387,7 +392,7 @@ func (e *Extractor) ParseAuxPlatform(auxfile string) ([]*AuxPlatform, error) { func (e *Extractor) ParseAuxPlatformWithHead(auxfile string) ([]*AuxPlatform, error) { data, err := os.ReadFile(auxfile) 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 } diff --git a/extract/calculator.go b/extract/calculator.go deleted file mode 100644 index 0f5bb0f..0000000 --- a/extract/calculator.go +++ /dev/null @@ -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) -} diff --git a/extract/envi.go b/extract/envi.go index 5811506..3a12f3d 100644 --- a/extract/envi.go +++ b/extract/envi.go @@ -4,18 +4,9 @@ import ( "bytes" "html" - "io" - "os" "text/template" ) -type BSQWriter struct { - filepath string - fio *os.File - w io.Writer - content *EnviHdr -} - var HDRTemplate = `ENVI description = { File Imported into ENVI.} diff --git a/extract/orbit.go b/extract/orbit.go deleted file mode 100644 index 7e3d3b9..0000000 --- a/extract/orbit.go +++ /dev/null @@ -1 +0,0 @@ -package extract diff --git a/extract/seperate.go b/extract/seperate.go new file mode 100644 index 0000000..20b9bec --- /dev/null +++ b/extract/seperate.go @@ -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 +} diff --git a/go.mod b/go.mod index 62e673d..805157d 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,12 @@ go 1.21 toolchain go1.21.0 -require github.com/twpayne/go-proj/v10 v10.2.0 - require ( github.com/jonboulle/clockwork v0.4.0 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/onsi/ginkgo v1.16.5 // 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/net v0.25.0 // indirect golang.org/x/term v0.20.0 // indirect diff --git a/go.sum b/go.sum index d1e3425..f6b5e36 100644 --- a/go.sum +++ b/go.sum @@ -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/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= @@ -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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 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/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs= 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/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= 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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 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/twpayne/go-proj/v10 v10.2.0/go.mod h1:BU5zbXY6XmKenoyf+DbIg7joKlKTJM2rEssPMPJZYjY= +github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE= +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/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= 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.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 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=