420 lines
12 KiB
Java
420 lines
12 KiB
Java
package sparseviewer.io;
|
|
|
|
import sparseviewer.mda.MdaDetector;
|
|
import sparseviewer.mda.MdaPositioner;
|
|
import sparseviewer.mda.MdaScan;
|
|
import sparseviewer.mda.XdrInput;
|
|
import sparseviewer.model.ScanDataset;
|
|
import sparseviewer.mda.MdaFileData;
|
|
|
|
import ij.IJ;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
public class MdaReader {
|
|
|
|
public static ScanDataset readFiles(List<File> files) throws IOException {
|
|
if (files == null || files.isEmpty()) {
|
|
throw new IOException("No MDA file selected.");
|
|
}
|
|
|
|
if (files.size() > 1) {
|
|
throw new IOException("MDA support currently expects a single file.");
|
|
}
|
|
|
|
File file = files.get(0);
|
|
|
|
MdaFileData mda = readMdaFile(file);
|
|
|
|
if (mda.rank == 1) {
|
|
return toScanDatasetRank1(mda, file);
|
|
}
|
|
|
|
if (mda.rank == 2) {
|
|
return toScanDatasetRank2(mda, file);
|
|
}
|
|
|
|
throw new IOException("Only MDA rank 1 and rank 2 are supported currently. File rank = " + mda.rank);
|
|
}
|
|
private static MdaFileData readMdaFile(File file) throws IOException {
|
|
XdrInput in = new XdrInput(file);
|
|
|
|
try {
|
|
MdaFileData data = new MdaFileData();
|
|
|
|
data.version = in.readFloat();
|
|
|
|
if (Math.abs(data.version - 1.3f) > 0.02f
|
|
&& Math.abs(data.version - 1.4f) > 0.02f) {
|
|
throw new IOException("Unsupported MDA version: " + data.version);
|
|
}
|
|
|
|
data.scanNumber = in.readInt();
|
|
data.rank = in.readInt();
|
|
|
|
if (data.rank < 1 || data.rank > 4) {
|
|
throw new IOException("Invalid MDA rank: " + data.rank);
|
|
}
|
|
|
|
data.dimensions = in.readIntArray(data.rank);
|
|
data.isRegular = in.readInt();
|
|
data.pExtra = in.readInt();
|
|
|
|
long mainScanOffset = in.getFilePointer();
|
|
|
|
data.mainScan = readScan(in, mainScanOffset);
|
|
|
|
if (data.rank == 2) {
|
|
readRank2InnerScans(in, data);
|
|
}
|
|
|
|
IJ.log("MDA loaded: " + file.getName()
|
|
+ ", version=" + data.version
|
|
+ ", rank=" + data.rank
|
|
+ ", scanNumber=" + data.scanNumber);
|
|
|
|
return data;
|
|
|
|
} finally {
|
|
in.close();
|
|
}
|
|
}
|
|
private static void readRank2InnerScans(XdrInput in, MdaFileData data) throws IOException {
|
|
MdaScan outer = data.mainScan;
|
|
|
|
if (outer.lowerScanOffsets == null) {
|
|
throw new IOException("Rank 2 MDA file has no lower scan offsets.");
|
|
}
|
|
|
|
int nOuter = Math.min(outer.currPt, outer.lowerScanOffsets.length);
|
|
|
|
for (int i = 0; i < nOuter; i++) {
|
|
int offset = outer.lowerScanOffsets[i];
|
|
|
|
if (offset <= 0) {
|
|
data.innerScans.add(null);
|
|
continue;
|
|
}
|
|
|
|
MdaScan inner = readScan(in, offset);
|
|
data.innerScans.add(inner);
|
|
}
|
|
}
|
|
private static MdaScan readScan(XdrInput in, long offset) throws IOException {
|
|
in.seek(offset);
|
|
|
|
MdaScan scan = new MdaScan();
|
|
|
|
scan.rank = in.readInt();
|
|
|
|
if (scan.rank < 0 || scan.rank > 20) {
|
|
throw new IOException("Invalid scan rank at offset " + offset + ": " + scan.rank);
|
|
}
|
|
|
|
scan.npts = in.readInt();
|
|
scan.currPt = in.readInt();
|
|
|
|
if (scan.rank > 1) {
|
|
scan.lowerScanOffsets = in.readIntArray(scan.npts);
|
|
}
|
|
|
|
scan.name = in.readStringWithLeadingLength();
|
|
scan.time = in.readStringWithLeadingLength();
|
|
|
|
scan.np = in.readInt();
|
|
scan.nd = in.readInt();
|
|
scan.nt = in.readInt();
|
|
|
|
for (int i = 0; i < scan.np; i++) {
|
|
MdaPositioner p = new MdaPositioner();
|
|
|
|
p.number = in.readInt();
|
|
p.fieldName = posName(p.number);
|
|
|
|
p.name = in.readStringWithLeadingLength();
|
|
p.desc = in.readStringWithLeadingLength();
|
|
p.stepMode = in.readStringWithLeadingLength();
|
|
p.unit = in.readStringWithLeadingLength();
|
|
|
|
p.readbackName = in.readStringWithLeadingLength();
|
|
p.readbackDesc = in.readStringWithLeadingLength();
|
|
p.readbackUnit = in.readStringWithLeadingLength();
|
|
|
|
scan.positioners.add(p);
|
|
}
|
|
|
|
for (int i = 0; i < scan.nd; i++) {
|
|
MdaDetector d = new MdaDetector();
|
|
|
|
d.number = in.readInt();
|
|
d.fieldName = detName(d.number);
|
|
|
|
d.name = in.readStringWithLeadingLength();
|
|
d.desc = in.readStringWithLeadingLength();
|
|
d.unit = in.readStringWithLeadingLength();
|
|
|
|
scan.detectors.add(d);
|
|
}
|
|
|
|
// Triggers are not needed for ScanDataset, but must be consumed.
|
|
for (int i = 0; i < scan.nt; i++) {
|
|
in.readInt(); // trigger number
|
|
in.readStringWithLeadingLength(); // trigger name
|
|
in.readFloat(); // command
|
|
}
|
|
|
|
readScanData(in, scan);
|
|
|
|
return scan;
|
|
}
|
|
private static void readScanData(XdrInput in, MdaScan scan) throws IOException {
|
|
for (int p = 0; p < scan.np; p++) {
|
|
double[] values = new double[scan.npts];
|
|
|
|
for (int i = 0; i < scan.npts; i++) {
|
|
values[i] = in.readDouble();
|
|
}
|
|
|
|
scan.positioners.get(p).data = values;
|
|
}
|
|
|
|
for (int d = 0; d < scan.nd; d++) {
|
|
float[] values = new float[scan.npts];
|
|
|
|
for (int i = 0; i < scan.npts; i++) {
|
|
values[i] = in.readFloat();
|
|
}
|
|
|
|
scan.detectors.get(d).data = values;
|
|
}
|
|
}
|
|
private static ScanDataset toScanDatasetRank1(MdaFileData mda, File file) {
|
|
MdaScan scan = mda.mainScan;
|
|
|
|
int rowCount = scan.currPt;
|
|
int channelCount = scan.np + scan.nd;
|
|
|
|
String[] channelNames = new String[channelCount];
|
|
double[][] columns = new double[channelCount][rowCount];
|
|
int[] fileIndex = new int[rowCount];
|
|
|
|
int c = 0;
|
|
|
|
for (int p = 0; p < scan.np; p++) {
|
|
MdaPositioner pos = scan.positioners.get(p);
|
|
channelNames[c] = makePositionerName("rank1", pos);
|
|
|
|
for (int i = 0; i < rowCount; i++) {
|
|
columns[c][i] = safeDouble(pos.data, i);
|
|
}
|
|
|
|
c++;
|
|
}
|
|
|
|
for (int d = 0; d < scan.nd; d++) {
|
|
MdaDetector det = scan.detectors.get(d);
|
|
channelNames[c] = makeDetectorName("rank1", det);
|
|
|
|
for (int i = 0; i < rowCount; i++) {
|
|
columns[c][i] = safeFloat(det.data, i);
|
|
}
|
|
|
|
c++;
|
|
}
|
|
|
|
for (int i = 0; i < rowCount; i++) {
|
|
fileIndex[i] = 0;
|
|
}
|
|
|
|
List<File> sourceFiles = new ArrayList<File>();
|
|
sourceFiles.add(file);
|
|
|
|
IJ.log("MDA rank 1 converted to ScanDataset: "
|
|
+ rowCount + " rows, " + channelCount + " channels.");
|
|
|
|
return new ScanDataset(channelNames, columns, rowCount, fileIndex, sourceFiles);
|
|
}
|
|
private static ScanDataset toScanDatasetRank2(MdaFileData mda, File file) throws IOException {
|
|
MdaScan outer = mda.mainScan;
|
|
|
|
MdaScan firstInner = findFirstInnerScan(mda);
|
|
|
|
if (firstInner == null) {
|
|
throw new IOException("Rank 2 MDA file contains no readable inner scan.");
|
|
}
|
|
|
|
int rowCount = 0;
|
|
|
|
for (int j = 0; j < mda.innerScans.size(); j++) {
|
|
MdaScan inner = mda.innerScans.get(j);
|
|
|
|
if (inner != null) {
|
|
rowCount += inner.currPt;
|
|
}
|
|
}
|
|
|
|
int channelCount = outer.np + outer.nd + firstInner.np + firstInner.nd;
|
|
|
|
String[] channelNames = new String[channelCount];
|
|
double[][] columns = new double[channelCount][rowCount];
|
|
int[] fileIndex = new int[rowCount];
|
|
|
|
int c = 0;
|
|
|
|
for (int p = 0; p < outer.np; p++) {
|
|
channelNames[c++] = makePositionerName("outer", outer.positioners.get(p));
|
|
}
|
|
|
|
for (int d = 0; d < outer.nd; d++) {
|
|
channelNames[c++] = makeDetectorName("outer", outer.detectors.get(d));
|
|
}
|
|
|
|
for (int p = 0; p < firstInner.np; p++) {
|
|
channelNames[c++] = makePositionerName("inner", firstInner.positioners.get(p));
|
|
}
|
|
|
|
for (int d = 0; d < firstInner.nd; d++) {
|
|
channelNames[c++] = makeDetectorName("inner", firstInner.detectors.get(d));
|
|
}
|
|
|
|
int row = 0;
|
|
|
|
for (int j = 0; j < mda.innerScans.size(); j++) {
|
|
MdaScan inner = mda.innerScans.get(j);
|
|
|
|
if (inner == null) {
|
|
continue;
|
|
}
|
|
|
|
int innerRows = inner.currPt;
|
|
|
|
for (int k = 0; k < innerRows; k++) {
|
|
c = 0;
|
|
|
|
for (int p = 0; p < outer.np; p++) {
|
|
columns[c++][row] = safeDouble(outer.positioners.get(p).data, j);
|
|
}
|
|
|
|
for (int d = 0; d < outer.nd; d++) {
|
|
columns[c++][row] = safeFloat(outer.detectors.get(d).data, j);
|
|
}
|
|
|
|
for (int p = 0; p < firstInner.np; p++) {
|
|
if (p < inner.positioners.size()) {
|
|
columns[c++][row] = safeDouble(inner.positioners.get(p).data, k);
|
|
} else {
|
|
columns[c++][row] = Double.NaN;
|
|
}
|
|
}
|
|
|
|
for (int d = 0; d < firstInner.nd; d++) {
|
|
if (d < inner.detectors.size()) {
|
|
columns[c++][row] = safeFloat(inner.detectors.get(d).data, k);
|
|
} else {
|
|
columns[c++][row] = Double.NaN;
|
|
}
|
|
}
|
|
|
|
fileIndex[row] = 0;
|
|
row++;
|
|
}
|
|
}
|
|
|
|
List<File> sourceFiles = new ArrayList<File>();
|
|
sourceFiles.add(file);
|
|
|
|
IJ.log("MDA rank 2 converted to ScanDataset: "
|
|
+ rowCount + " rows, " + channelCount + " channels.");
|
|
|
|
return new ScanDataset(channelNames, columns, rowCount, fileIndex, sourceFiles);
|
|
}
|
|
private static MdaScan findFirstInnerScan(MdaFileData mda) {
|
|
for (int i = 0; i < mda.innerScans.size(); i++) {
|
|
MdaScan scan = mda.innerScans.get(i);
|
|
|
|
if (scan != null) {
|
|
return scan;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
private static String makePositionerName(String prefix, MdaPositioner p) {
|
|
String pv = clean(p.name);
|
|
String desc = clean(p.desc);
|
|
|
|
if (pv.length() > 0 && desc.length() > 0) {
|
|
return prefix + "/" + p.fieldName + "/" + pv + " {" + desc + "}";
|
|
}
|
|
|
|
if (pv.length() > 0) {
|
|
return prefix + "/" + p.fieldName + "/" + pv;
|
|
}
|
|
|
|
if (desc.length() > 0) {
|
|
return prefix + "/" + p.fieldName + "/{" + desc + "}";
|
|
}
|
|
|
|
return prefix + "/" + p.fieldName;
|
|
}
|
|
private static String makeDetectorName(String prefix, MdaDetector d) {
|
|
String pv = clean(d.name);
|
|
String desc = clean(d.desc);
|
|
|
|
if (pv.length() > 0 && desc.length() > 0) {
|
|
return prefix + "/" + d.fieldName + "/" + pv + " {" + desc + "}";
|
|
}
|
|
|
|
if (pv.length() > 0) {
|
|
return prefix + "/" + d.fieldName + "/" + pv;
|
|
}
|
|
|
|
if (desc.length() > 0) {
|
|
return prefix + "/" + d.fieldName + "/{" + desc + "}";
|
|
}
|
|
|
|
return prefix + "/" + d.fieldName;
|
|
}
|
|
private static String clean(String s) {
|
|
return s == null ? "" : s.trim();
|
|
}
|
|
private static double safeDouble(double[] data, int index) {
|
|
if (data == null || index < 0 || index >= data.length) {
|
|
return Double.NaN;
|
|
}
|
|
|
|
return data[index];
|
|
}
|
|
private static double safeFloat(float[] data, int index) {
|
|
if (data == null || index < 0 || index >= data.length) {
|
|
return Double.NaN;
|
|
}
|
|
|
|
return data[index];
|
|
}
|
|
private static String posName(int i) {
|
|
if (i < 4) {
|
|
return "P" + (i + 1);
|
|
}
|
|
|
|
return "?";
|
|
}
|
|
private static String detName(int i) {
|
|
if (i < 100) {
|
|
int n = i + 1;
|
|
|
|
if (n < 10) {
|
|
return "D0" + n;
|
|
}
|
|
|
|
return "D" + n;
|
|
}
|
|
|
|
return "?";
|
|
}
|
|
}
|