在某个夏日炎炎的午后,正在摸鱼的小邓接到行任务需要从某个adcp设备的记录PD0文件中解析对应的gps数据,如下:
使用vscode打开对应的pd0文件(使用插件HEx editor打开此二进制文件)如下找到gps对应的数据块:
相关的解析代码如下:
@Getter
public class WHM {
//java
private byte[] array;
private List<Integer> lists = new ArrayList<>();
private int _fileLength = -1;
private StringBuffer errorMessage = new StringBuffer();
public List<WHMData> getWHMData(String fileName) {
List<WHMData> list = new ArrayList<>();
if (!readData(fileName)) {
return null;
}
int num = _fileLength = lists.size();
int num2 = 0;
if (num == 0) {
errorMessage.append(", 没有数据");
return null;
}
while (num2 < num) {
//pd0文件的记录开始为7F(lsb) 和7F (msb) 对应的10进制为127
if (this.lists.get(num2) == 127 && this.lists.get(num2 + 1) == 127) {
//num2 + 2 后面的2个字节描述的是pd0记录的总大小
int num3 = BitConverterUtil.ToUInt16(this.array, num2 + 2);
//由于可变leader和固定leader的长度加起来超过120 没有超过的就是不合法的
if (num3 > 120) {
//检查数据是否在合法范围中
if (verifyEnsembleData(num2, num3)) {
int num4 = num2;
//类别的种类
int num5 = this.array[num2 + 5];
int[] array = new int[num5];
for (int i = 0; i < num5; i++) {
//每个类别的数据长度
array[i] = BitConverterUtil.ToUInt16(this.array, num2 + 6 + i * 2);
}
//类别的起始位置
num2 = num4 + array[0];
//固定leader的开头位置 00 00
if (this.lists.get(num2) == 0 && this.lists.get(num2 + 1) == 0) {
WHMData wHMData = new WHMData();
for (int j = 1; j < num5; j++) {
num2 = num4 + array[j];
//gps数据你开始位置0x2220 对应的10进制为 34 和 32
if (this.lists.get(num2) == 34 && this.lists.get(num2 + 1) == 32) {
//最后的类别的长度用总长-起点位置
int charLength = j + 1 >= num5 ? num3 - num2 : num4 + array[j + 1] - num2;
String asciiStringFromList = getAsciiStringFromList(lists, num2, charLength);
System.out.println(asciiStringFromList);
}
}
list.add(wHMData);
} else {
errorMessage.append(", 数据格式错误");
}
num2 = num4 + num3 + 2;
} else {
num2++;
}
} else {
num2++;
}
} else {
num2++;
}
}
return list;
}
public static String getAsciiStringFromList(List<Integer> list, int startIndex, int x) {
// 确保起始索引和数量在列表范围内
if (startIndex < 0 || startIndex >= list.size() || startIndex + x > list.size()) {
throw new IllegalArgumentException("起始索引或数量超出列表范围");
}
StringBuilder result = new StringBuilder();
// 从指定位置开始,取出x个元素
for (int i = startIndex; i < startIndex + x; i++) {
// 将整数转换为ASCII字符并追加到结果字符串
result.append((char) list.get(i).intValue());
}
return result.toString();
}
public static void main(String[] args) {
WHM whm = new WHM();
List<WHMData> whmData = whm.getWHMData("E:\\temp\\002GPSDown.PD0");
}
private boolean readData(String fileName) {
File file = new File(fileName);
try (FileInputStream fis = new FileInputStream(file)) {
// 获取文件大小
long fileSize = file.length();
// 根据文件大小创建byte数组
array = new byte[(int) fileSize];
// 读取文件内容到byte数组
int bytesRead = fis.read(array);
// 检查是否成功读取了所有字节
if (bytesRead != fileSize) {
// 如果读取的字节数不等于文件大小,可能发生了错误(尽管这里可能是EOF,但为简单起见,我们处理为错误)
errorMessage.append(", 无法完全读取文件");
return false;
}
// 转换byte数组超过范围的值
for (int i = 0; i < array.length; i++) {
//java中的byte是-128 到127之间 将其转为正数存在list中
lists.add(i, array[i] & 0xFF);
}
// 如果一切正常,返回true
return true;
} catch (Exception e) {
errorMessage.append(", 无法打开文件 " + e);
return false;
}
}
private boolean verifyEnsembleData(int startPos, int ensembleLength) {
//2723 startPos 716 ensembleLength 734
long num = 0L;
for (int i = 0; i < ensembleLength; i++) {
num += this.lists.get(startPos + i);
}
num %= 65536;
long num2 = BitConverterUtil.ToUInt16(array, startPos + ensembleLength);
return num == num2;
}
}
结果如下: