aux platform

This commit is contained in:
nuknal
2024-05-15 16:32:18 +08:00
parent 6f76b2f131
commit 6a22735d78
16 changed files with 335 additions and 48 deletions

82
extract/aos.go Normal file
View File

@@ -0,0 +1,82 @@
package extract
import (
"bufio"
"fmt"
"os"
"path/filepath"
log "github.com/sirupsen/logrus"
)
const (
AOSFrameLength = 1024
AOSTempDataPrefix = "AOS_"
)
var AOSSyncWord = []byte{0x1A, 0xCF, 0xFC, 0x1D}
type AOSFrame struct {
}
func (p *Extractor) ExtractAosData() error {
// 打开传输帧文件 - 一次读入内存
rawData, err := os.ReadFile(p.params.InputData)
if err != nil {
log.Println("read data from", p.params.InputData, "error:", err.Error())
return err
}
name := filepath.Base(p.params.InputData)
aosDataFile := filepath.Join(p.params.TempPath, AOSTempDataPrefix+name)
foData, err := os.OpenFile(aosDataFile,
os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
if err != nil {
fmt.Println("create data err", err.Error())
return err
}
defer foData.Close()
wData := bufio.NewWriter(foData)
var validFrameCnt int
var errFrameCnt int
for i := 0; i < len(rawData); {
// 寻找帧头 0x1A CF FC 1D
if i+4 > len(rawData) {
log.Info("end of raw data")
break
}
if rawData[i] == 0x1A && rawData[i+1] == 0xCF && rawData[i+2] == 0xFC && rawData[i+3] == 0x1D {
// 找到帧头
log.Debug("detect AOS frame")
} else {
i++
continue
}
if i+1024 > len(rawData) {
log.Info("not enough data for AOS frame")
break
}
// 读取完整帧
if ldpcCheck(rawData[i:i+1024]) != nil {
errFrameCnt++
}
wData.Write(rawData[i+10 : i+894])
i += 1024
validFrameCnt++
}
log.Println("valid AOS frame cnt:", validFrameCnt)
log.Println("err AOS frame cnt:", errFrameCnt)
return nil
}
func ldpcCheck(frame []byte) error {
return nil
}

239
extract/aux.go Normal file
View File

@@ -0,0 +1,239 @@
package extract
import (
"bufio"
"encoding/binary"
"errors"
"os"
"path/filepath"
"strings"
"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus"
)
// 每行传感器数据帧头信息长度为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 // 时间(秒) 量纲1us/bit十六进制无符号整型数
TimeSecFrac uint32 // 秒小数
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])) //binary.BigEndian.Uint32(data[20:23])
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 (p *Extractor) SeprateAuxAndImgData(sDataFile string) error {
// 打开传输帧文件 - 一次读入内存
sData, err := os.ReadFile(sDataFile)
if err != nil {
log.Println("read data from", sDataFile, "error:", err.Error())
return err
}
log.Info("seprate aux and img data from", sDataFile)
name := filepath.Base(sDataFile)
name = strings.TrimRight(name, filepath.Ext(name))
aux0 := filepath.Dir(sDataFile) + "/" + name + "_AUX0.dat"
os.Remove(aux0)
faux0, _ := os.OpenFile(aux0, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
waux0 := bufio.NewWriter(faux0)
defer waux0.Flush()
defer faux0.Close()
aux1 := filepath.Dir(sDataFile) + "/" + name + "_AUX1.dat"
os.Remove(aux1)
faux1, _ := os.OpenFile(aux1, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
waux1 := bufio.NewWriter(faux1)
defer waux1.Flush()
defer faux1.Close()
pan := filepath.Dir(sDataFile) + "/" + name + "_IMG_PAN.RAW"
os.Remove(pan)
fpan, _ := os.OpenFile(pan, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
wpan := bufio.NewWriter(fpan)
defer wpan.Flush()
defer fpan.Close()
panEnviHdr := EnviHdr{}
wpanHdr, _ := NewBSQWriter(filepath.Dir(sDataFile)+"/"+name+"_IMG_PAN.HDR", &panEnviHdr)
defer wpanHdr.Close()
// 先按单波段存储,再按波段组合存储为 BSQ 格式的 MSS
mss := filepath.Dir(sDataFile) + "/" + name + "_IMG_MSS.RAW"
os.Remove(mss)
fmss, _ := os.OpenFile(mss, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
wmss := bufio.NewWriter(fmss)
defer wmss.Flush()
defer fmss.Close()
mssEnviHdr := EnviHdr{}
wmssHdr, _ := NewBSQWriter(filepath.Dir(sDataFile)+"/"+name+"_IMG_MSS.HDR", &mssEnviHdr)
defer wmssHdr.Close()
var afh AuxFrameHead
dataLen := len(sData)
for i := 0; i < dataLen; {
if i+4 > dataLen {
logrus.Println("end of data, dataLen:", dataLen, "i:", i)
break
}
// 包头
if sData[i] == 0xD1 && sData[i+1] == 0x5B && sData[i+2] == 0xD1 && sData[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(sData[i : i+24])
// pp.Println(afh)
if !afh.IsValidFrmHead {
log.Info("invalid frame head of original raw data")
break
}
// 目前只支持线阵模式
if !afh.IsLinerMatrix {
log.Error("not liner matrix mode, only support liner matrix mode")
break
}
// 读取一行数据 sData[i : i+afh.RowLength]
if i+afh.RowLength > dataLen {
log.Info("length of row data is not enough for row length:", afh.RowLength)
break
}
// 存储辅助数据到临时文件
dataIndex := i + 24
waux0.Write(sData[dataIndex : dataIndex+8]) // 8字节焦面电箱辅助数据
dataIndex += 8
waux1.Write(sData[dataIndex : dataIndex+32]) // 32字节中心机辅助数据
dataIndex += 32
// 存储图像数据到临时文件 - 以 ENVI BSQ 格式存储,同时提供 HDR 描述文件
if afh.B0 {
wpan.Write(sData[dataIndex : dataIndex+19040])
dataIndex += 19040
panEnviHdr.Lines += 1
}
if afh.B1 {
wmss.Write(sData[dataIndex : dataIndex+1192])
dataIndex += 1192
mssEnviHdr.Lines += 1
}
if afh.B2 {
wmss.Write(sData[dataIndex : dataIndex+1192])
dataIndex += 1192
}
if afh.B3 {
wmss.Write(sData[dataIndex : dataIndex+1192])
dataIndex += 1192
}
if afh.B4 {
wmss.Write(sData[dataIndex : dataIndex+1192])
dataIndex += 1192
}
i = dataIndex // 完成一行数据解析
}
panEnviHdr.Samples = 9520
panEnviHdr.Bands = 1
wpanHdr.Write([]byte(panEnviHdr.String()))
mssEnviHdr.Lines = mssEnviHdr.Lines / 4 // 多光谱波段分别在 4 行中传输
mssEnviHdr.Bands = 1
mssEnviHdr.Samples = 2384 * 4
wmssHdr.Write([]byte(mssEnviHdr.String()))
return nil
}

8
extract/aux_ebox.go Normal file
View File

@@ -0,0 +1,8 @@
package extract
// 卫星时间起点 北京时间 2000-01-01 20:00:00
// 焦面电箱辅助数据 128 字节 (每 16 行原始图像数据为一组)
type AuxFocalBox struct {
}

190
extract/aux_platform.go Normal file
View File

@@ -0,0 +1,190 @@
package extract
import (
"encoding/binary"
"fmt"
"os"
"github.com/sirupsen/logrus"
)
const AuxPlatformFrmSize = 512 // 512 bytes
// 中心机辅助数据 512 字节 (每 16 行原始图像数据为一组)
type AuxPlatform struct {
// 结构中标注的字节序号从 1 开始计数
UTCTimeSec uint32 // [1-4] 卫星 UTC 时间戳(秒
Waveway uint8 // [5] 波道
Microsecond uint32 // [6-8]卫星秒小数(微秒)
QuatAttstarQ0 float64 // [9-12]定姿四元数(J2000) 的 Q0 值,量纲 1/0x40000000
QuatAttstarQ1 float64 // [13-16]Q1 值
QuatAttstarQ2 float64 // [17-20]Q2 值
QuatAttstarQ3 float64 // [21-24]Q3 值
QuatOrbitQ1 float64 // [25-28]本体相对轨道四元数矢部 的 Q1 值,量纲 1/0x40000000
QuatOrbitQ2 float64 // [29-32]Q2 值
QuatOrbitQ3 float64 // [33-36]Q3 值Q3 值
QuatOrbJQ1 float64 // [37-40]轨道相对惯性系四元数矢部 的 Q1 值,量纲 1/0x40000000
QuatOrbJQ2 float64 // [41-44]Q2 值
QuatOrbJQ3 float64 // [45-48]Q3 值
Eular1 float64 // [49-52]本体相对轨道姿态角量纲1/1000000 单位rad
Eular2 float64 // [53-56]
Eular3 float64 // [57-60]
DotEular1 float64 // [61-62]本体相对轨道角速度量纲1/100000 单位rad
DotEular2 float64 // [63-64]
DotEular3 float64 // [65-66]
ModTime uint32 // [67-70]模式运行时间 秒
DTime int16 // [71-72]姿控调用周期量纲1/1000 单位:秒
AutoState [3]uint8 // [73-75]姿轨控部件使用标志
ProTrack [16]uint8 // [76-91]姿轨控算法执行标记
QeQ1 float64 // [92-95]偏差四元数 double 量纲1/0x40000000 /bit
QeQ2 float64 // [96-99]
QeQ3 float64 // [100-103]
We1 float64 // [104-105]偏差角速度 量纲1/0x100000 单位rad
We2 float64 // [106-107]
We3 float64 // [108-109]
WTFX float64 // [110-111]X飞轮估计摩擦力矩 量纲1/0x400000
WTFY float64 // [112-113]
WTFZ float64 // [114-115]
FbdriftX float64 // [116-117]三轴陀螺X轴角速度漂移估计 1/0x4000000 rad/s
FbdriftY float64 // [118-119]
FbdriftZ float64 // [120-121]
KalbX float64 // [122-123]三轴角速度卡尔曼漂移X 量纲1/0x4000000 单位rad/s
KalbY float64 // [124-125]
KalbZ float64 // [126-127]
HTDX float64 // [128-129]X轴估计环境干扰力矩 量纲1/0x200000
HTDY float64 // [130-131]
HTDZ float64 // [132-133]
CommandWheelX float64 // [134-135]X轴计算飞轮控制力矩 量纲1/0x40000
CommandWheelY float64 // [136-137]
CommandWheelZ float64 // [138-139]
QuatG1 float64 // [140-143]期望四元数矢部1 量纲1/0x40000000 /bit
QuatG2 float64 // [144-147]
QuatG3 float64 // [148-151]
WG1 float64 // [152-153]期望角速度1 量纲1/0x40000
WG2 float64 // [154-155]
WG3 float64 // [156-157]
J2000PosX float64 // [158-161]计算当前J2000位置X 1/0x100 单位m
J2000PosY float64 // [162-165]
J2000PosZ float64 // [166-169]
J2000VelX float64 // [170-173]计算当前J2000速度X 量纲1/0x100 单位m/s
J2000VelY float64 // [174-177]
J2000VelZ float64 // [178-181]
W84PosX float64 // [182-185]计算当前WGS 84位置X 1/0x100 单位m
W84PosY float64 // [186-189]
W84PosZ float64 // [190-193]
W84VelX float64 // [194-197]计算当前WGS 84速度X 量纲1/0x100 单位m/s
W84VelY float64 // [198-201]
W84VelZ float64 // [202-205]
AngleDraft float64 // [206-209]偏流角 量纲1/10000000 单位rad
DataTransLong float64 // [210-211]数传点经度 量纲1/1000
DataTransLat float64 // [212-213]数传点纬度 量纲1/1000
DataTransH float64 // [214-215]数传点地程高 量纲1
StarPrio1 byte // [216] 星敏1定姿方式标志1 0x00星敏1定姿0xA0:星敏1滤波;0x01星敏2定姿0xA1:星敏2滤波;0xff:未使用星敏定姿
StarPrio2 byte // [217]
StarPrio3 byte // [218]
WheelspeedcalX float64 // [219-222]X飞轮期望转速 量纲1/10000 单位rpm
WheelspeedcalY float64 // [223-226]
WheelspeedcalZ float64 // [227-230]
Reserved0 [2]byte // 预留字节
Reserved1 [4]byte // 预留字节
Reserved2 [2]byte // 预留字节
Reserved3 [2]byte // 预留字节
// 241 - 311 暂不解析
WGS84PosX float64 // [312-315]计算当前WGS 84位置X 单位0.01米
WGS84PosY float64 // [316-319]
WGS84PosZ float64 // [320-323]
WGS84VelX float64 // [324-327]计算当前WGS 84速度X 单位0.01米/秒
WGS84VelY float64 // [328-331]
WGS84VelZ float64 // [332-335]
J2000Pos_X float64 // [336-339]计算当前J2000位置X 单位0.01米
J2000Pos_Y float64 // [340-343]
J2000Pos_Z float64 // [344-347]
J2000Vel_X float64 // [348-351]计算当前J2000速度X 单位0.01米/秒
J2000Vel_Y float64 // [352-355]
J2000Vel_Z float64 // [356-359]
// 字节[360-377] 三轴光纤陀螺参数
SS1_UTCTime uint32 // [378-381] 星敏1 UTC时间
SS1_UTCTimeFrac float32 // [382-384] 星敏1 UTC秒小数 单位为40.96 us高字节在前
SS1_Q1 float64 // [385-388] 星敏1四元数q1 当量1/2147483647 星敏坐标系相对于J2000惯性坐标系
SS1_Q2 float64 // [389-392] 星敏1四元数q2
SS1_Q3 float64 // [393-396] 星敏1四元数q3
SS1_Q4 float64 // [397-400] 星敏1四元数q4
SS1_ExposureTime uint8 // [401] 星敏1曝光时间 无符号数单位ms
SS1_ImgFrmNo uint32 // [411-413] 星敏1图像帧号
SS2_UTCTime uint32 // [424-427] 星敏2 UTC时间
SS2_UTCTimeFrac float32 // [428-430] 星敏2 UTC秒小数 单位为40.96 us高字节在前
SS2_Q1 float64 // [431-434] 星敏2四元数q1 当量1/2147483647 星敏坐标系相对于J2000惯性坐标系
SS2_Q2 float64 // [435-438] 星敏2四元数q2
SS2_Q3 float64 // [439-442] 星敏2四元数q3
SS2_Q4 float64 // [443-446] 星敏2四元数q4
SS2_ExposureTime uint8 // [447] 星敏2曝光时间 无符号数单位ms
SS2_ImgFrmNo uint32 // [457-459] 星敏2图像帧号
CheckSum byte // [512] 校验和
}
func (ap *AuxPlatform) Parse(data []byte) error {
if len(data) < 512 {
return ErrAuxPlatformDataLen
}
// 按需解析数据
ap.UTCTimeSec = binary.BigEndian.Uint32(data[0:4])
ap.Microsecond = uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
ap.DataTransLong = float64(int16(binary.BigEndian.Uint16(data[209:211]))) / 1000
ap.DataTransLat = float64(int16(binary.BigEndian.Uint16(data[211:213]))) / 1000
ap.DataTransH = float64(binary.BigEndian.Uint16(data[213:215]))
ap.SS1_UTCTime = binary.BigEndian.Uint32(data[377:381])
ap.SS1_UTCTimeFrac = float32(binary.BigEndian.Uint16(data[381:384])) * 40.96
ap.SS1_ImgFrmNo = uint32(data[410])<<16 | uint32(data[411])<<8 | uint32(data[412])
ap.SS2_UTCTime = binary.BigEndian.Uint32(data[423:427])
ap.SS2_UTCTimeFrac = float32(binary.BigEndian.Uint16(data[427:430])) * 40.96
ap.SS1_ImgFrmNo = uint32(data[456])<<16 | uint32(data[457])<<8 | uint32(data[458])
return nil
}
func (ap AuxPlatform) Print() {
fmt.Println("--- aux frm ----")
fmt.Printf("UTCTimeSec: %d\n", ap.UTCTimeSec)
fmt.Printf("Microsecond: %d\n", ap.Microsecond)
fmt.Printf("DataTransLong: %f\n", ap.DataTransLong)
fmt.Printf("DataTransLat: %f\n", ap.DataTransLat)
fmt.Printf("DataTransH: %f\n", ap.DataTransH)
fmt.Println("--- ss1 ---")
fmt.Printf("SS1_UTCTime: %d\n", ap.SS1_UTCTime)
fmt.Printf("SS1_UTCTimeFrac: %f\n", ap.SS1_UTCTimeFrac)
fmt.Printf("SS1_ImgFrmNo: %d\n", ap.SS1_ImgFrmNo)
fmt.Println("--- ss2 ---")
fmt.Printf("SS2_UTCTime: %d\n", ap.SS2_UTCTime)
fmt.Printf("SS2_UTCTimeFrac: %f\n", ap.SS2_UTCTimeFrac)
fmt.Printf("SS2_ImgFrmNo: %d\n", ap.SS2_ImgFrmNo)
}
// 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())
return nil, err
}
var aps []*AuxPlatform
for i := 0; i < len(data); i += AuxPlatformFrmSize {
ap := AuxPlatform{}
if err := ap.Parse(data[i : i+AuxPlatformFrmSize]); err != nil {
return nil, err
}
ap.Print()
aps = append(aps, &ap)
if i+AuxPlatformFrmSize > len(data) {
logrus.Info("rest of aux data length is not enough", len(data)-i)
break
}
}
return aps, nil
}

69
extract/envi.go Normal file
View File

@@ -0,0 +1,69 @@
package extract
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.}
samples = {{.Samples}}
lines = {{.Lines}}
bands = {{.Bands}}
header offset = 0
file type = ENVI Standard
data type = 12
interleave = bsq
sensor type = Unknown
byte order = 0
wavelength units = Unknown`
// https://www.nv5geospatialsoftware.com/docs/ENVIHeaderFiles.html
type EnviHdr struct {
Samples int // samples (pixels) each line
Lines int
Bands int
}
func (h *EnviHdr) String() string {
var t *template.Template
t = template.New("template")
t, err := t.Parse(HDRTemplate)
if err != nil {
return ""
}
buf := new(bytes.Buffer)
t.Execute(buf, h)
buf = bytes.NewBufferString(html.UnescapeString(buf.String()))
return buf.String()
}
func NewBSQWriter(filepath string, content *EnviHdr) (*BSQWriter, error) {
fio, err := os.Create(filepath)
if err != nil {
return nil, err
}
w := fio
return &BSQWriter{filepath, fio, w, content}, nil
}
func (w *BSQWriter) Write(data []byte) (int, error) {
return w.w.Write(data)
}
func (w *BSQWriter) Close() error {
return w.fio.Close()
}

7
extract/errors.go Normal file
View File

@@ -0,0 +1,7 @@
package extract
import "errors"
var (
ErrAuxPlatformDataLen = errors.New("aux platform data length is not 512 bytes")
)

12
extract/params.go Normal file
View File

@@ -0,0 +1,12 @@
package extract
type Params struct {
InputData string
OutputPath string
Satellite string
DataId string
LogFile string
TempPath string
Report string
Result string
}

15
extract/process.go Normal file
View File

@@ -0,0 +1,15 @@
package extract
import (
"os"
)
type Extractor struct {
params *Params
}
func NewExtractor(params *Params) *Extractor {
os.MkdirAll(params.OutputPath, 0755)
os.MkdirAll(params.TempPath, 0755)
return &Extractor{params: params}
}

18
extract/raw.go Normal file
View File

@@ -0,0 +1,18 @@
package extract
// 图像数据帧头信息、辅助数据以及图像数据组成,
// 帧头信息长度为24字节
// 辅助数据总共640字节分16行传输每行传输40字节
// 其中40字节中包含8字节焦面电箱辅助数据以及32节字的中心机辅助数据
// 图像数据 每个像素16bit
// 线阵模式每 16 行一组,可拼出 640 字节辅助数据
type LineMatrixRawRow struct {
FrameHeader [24]byte // 帧头信息
AuxFocalBox [8]byte // 焦面电箱辅助数据
AuxCenterMachine [32]byte // 中心机辅助数据
B0Data []byte // 全色图像数据 19040 字节9520 像素
B1Data []byte // 多光谱1图像数据 1192 字节,每 4 行一组,共 4768 字节2384 像素
B2Data []byte // 多光谱2图像数据 1192 字节,每 4 行一组,共 4768 字节2384 像素
B3Data []byte // 多光谱3图像数据 1192 字节,每 4 行一组,共 4768 字节2384 像素
B4Data []byte // 多光谱4图像数据 1192 字节,每 4 行一组,共 4768 字节2384 像素
}

12
extract/spectral_img.go Normal file
View File

@@ -0,0 +1,12 @@
package extract
type SpectralImage struct {
Spectrum string // PAN(B0) B1 B2 B3 B4
H uint32 // 9520 像素 2(Margin) + 48(OB) + 38(Margin) + 9344(Effective Pixels) + 38(Margin) + 48(OB) + 2(Margin)
V uint32 // 7056 像素 2(Margin) + 12(OB) + 18(Margin) + 20(Margin) + 7000(Effective Pixels) + 4(Margin)
Pixels [][]*Pixel
}
type Pixel struct {
Bits [2]byte // 16bit
}

120
extract/trans_frame.go Normal file
View File

@@ -0,0 +1,120 @@
package extract
import (
"bufio"
"fmt"
"os"
"path/filepath"
"sort"
log "github.com/sirupsen/logrus"
"github.com/duke-git/lancet/v2/slice"
)
const TransImageFrameLength = 8192
const TransImageFrameHeadLength = 16
const TransImageFrameDataLength = 8176
const ImageFrameHead = 0xE77EE77E
// 智能载荷电箱与数传单机之间图像数据传输帧格式
type TransFrame struct {
Header [4]byte // 帧头 E77EE77E
SNo uint32 // 序列号 大端序
Reserved0 [2]byte // 保留字节
KeyIndex uint16 // 密钥库索引号 大端序
SecretFlag byte // 明密标志 0x55 明传 0xAA 密传
FrameFlag byte // 帧类型 0x00:智能载荷电箱有效帧 0x55: 智能载荷电箱空帧
FileNo uint8 // 文件编号
Reserved1 byte // 保留字节 0x00
Data []byte // 数据区 8176字节
}
func (t *TransFrame) Decode(frame []byte) {
if len(frame) < TransImageFrameLength {
return
}
copy(t.Header[:], frame[:4])
t.SNo = uint32(frame[4])<<24 | uint32(frame[5])<<16 | uint32(frame[6])<<8 | uint32(frame[7])
copy(t.Reserved0[:], frame[8:10])
t.KeyIndex = uint16(frame[10])<<8 | uint16(frame[11])
t.SecretFlag = frame[12]
t.FrameFlag = frame[13]
t.FileNo = frame[14]
t.Reserved1 = frame[15]
t.Data = append(t.Data, frame[16:8192]...)
}
// 提取图像传输帧中的原始图像数据
func (p *Extractor) ExtractOriginalImageData() ([]string, error) {
var sData []string
name := filepath.Base(p.params.InputData)
aosDataFile := filepath.Join(p.params.TempPath, AOSTempDataPrefix+name)
aosData, err := os.ReadFile(aosDataFile)
if err != nil {
log.Println("read data from", aosDataFile, "error:", err.Error())
return nil, err
}
snRange := map[int][]uint32{}
datSet := map[int][]byte{}
var i int
for i < len(aosData) {
if i+4 > len(aosData) {
log.Println("end of trans frame data")
break
}
if aosData[i] == 0xE7 && aosData[i+1] == 0x7E && aosData[i+2] == 0xE7 && aosData[i+3] == 0x7E {
if i+TransImageFrameLength > len(aosData) {
log.Println("trans frame length error")
break
}
var tf TransFrame
tf.Decode(aosData[i : i+TransImageFrameLength])
fileno := int(tf.FileNo)
snRange[fileno] = append(snRange[fileno], tf.SNo)
// 只保留非空帧
if tf.FrameFlag != 0x55 && fileno != 0 {
datSet[fileno] = append(datSet[fileno], tf.Data...)
}
i += TransImageFrameLength
continue
}
i++
}
for k, v := range snRange {
vv := slice.Unique(v)
sort.Slice(vv, func(i, j int) bool { return vv[i] < vv[j] })
fmt.Println("fileno:", k, ", sn range:", vv[0], "-", vv[len(vv)-1], "len", len(vv))
}
for k, v := range datSet {
frameCnt := slice.Unique(snRange[k])
if len(frameCnt) < 4096 {
log.Println("fileno", k, "数据长度不足4096帧")
continue
}
s01 := filepath.Join(p.params.TempPath, fmt.Sprintf("%s_S%d.dat", p.params.DataId, k))
fo, err := os.OpenFile(s01,
os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
if err != nil {
log.Println("create file", s01, "error:", err.Error())
return nil, err
}
w := bufio.NewWriter(fo)
w.Write(v)
w.Flush()
fo.Close()
sData = append(sData, s01)
}
return sData, nil
}