/*-
* Copyright 2016 Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package uk.ac.diamond.scisoft.analysis.io;
import org.eclipse.dawnsci.analysis.api.io.ScanFileHolderException;
import org.eclipse.january.IMonitor;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class MCALoader extends AbstractFileLoader {
protected Map<String,String> mcaParameters;
transient protected static final Logger logger = LoggerFactory.getLogger(MCALoader.class);
public MCALoader() {
}
public MCALoader(final String fileName) {
setFile(fileName);
}
@Override
public void setFile(String fileName) {
mcaParameters = new HashMap<String,String>();
super.setFile(fileName);
}
@Override
protected void clearMetadata() {
metadata = null;
mcaParameters.clear();
}
@Override
public DataHolder loadFile() throws ScanFileHolderException {
return loadFile((IMonitor)null);
}
/**
* Function that loads in the standard MCA datafile
*
* @return The package which contains the data that has been loaded
* @throws ScanFileHolderException
*/
@Override
public DataHolder loadFile(final IMonitor mon) throws ScanFileHolderException {
// first instantiate the return object.
final DataHolder result = new DataHolder();
// then try to read the file given
Scanner scanner = null;
try {
File file = new File(fileName);
scanner = new Scanner(file);
String line;
int channel_begin = Integer.MIN_VALUE;
int channel_end = Integer.MIN_VALUE;
int nchannels = Integer.MIN_VALUE;
double zero = Double.NaN;
double gain = Double.NaN;
//skip empty lines if present
do {
line = scanner.nextLine().trim();
} while (line.trim().equals(""));
if (!line.substring(0, 2).equals("#F")) {
//when this happens, we are not dealing with a valid MCA file
throw new Exception("first line must start with #F");
}
//next continue reading until a single empty line is encountered
do {
line = scanner.nextLine().trim();
if (line.equals(""))
break;
else if (line.charAt(0) != '#')
throw new Exception("within global metadata block all lines shall start with #");
String id = line.charAt(1) + "_GLOBAL";
String value = line.substring(3);
mcaParameters.put(id, value);
} while (true);
//for now assume that MCA files contain just one dataset...
//if an example file shows up with multiple datasets, then the following code should be placed in a while loop
line = scanner.next();
if (!line.equals("#S")) {
throw new Exception("a scan block shall start with #S");
}
String scan_number = scanner.next();
//ignore the rest of the line
scanner.nextLine();
//now several more lines starting with '#' should follow
do {
line = scanner.next().trim();
String id = null;
if (line.equals("@A")) {
break;
} else if (line.charAt(0) != '#' || line.length() == 1) {
throw new Exception("within scan metadata block all lines shall start with a '#', followed by at least one character that is not whitespace");
} else if (line.substring(0, 2).equals("#@")) {
id = line.substring(2);
} else {
id = line.substring(1);
}
switch (id) {
case "CHANN":
//there should be four ints following here
nchannels = scanner.nextInt();
channel_begin = scanner.nextInt();
channel_end = scanner.nextInt();
scanner.nextLine();
mcaParameters.put("channel_begin_" + scan_number, Integer.toString(channel_begin));
mcaParameters.put("channel_end_" + scan_number, Integer.toString(channel_end));
break;
case "CALIB":
//there should be three doubles here
//I cannot be sure based on the example file but the last value may be used for second degree polynomial calibration
zero = scanner.nextDouble();
gain = scanner.nextDouble();
scanner.nextLine();
mcaParameters.put("zero_" + scan_number, Double.toString(zero));
mcaParameters.put("gain_" + scan_number, Double.toString(gain));
break;
default:
mcaParameters.put(id + "_" + scan_number, scanner.nextLine().trim());
}
} while(true);
// see if we got CHANN
if (nchannels == Integer.MIN_VALUE) {
throw new Exception("no information regarding number of channels found in scan metadata block");
}
//allocate memory for our data
double[] array = new double[nchannels];
int channel_index = 0;
while (channel_index < nchannels) {
String text = scanner.next();
if (text.equals("\\"))
continue;
array[channel_index++] = Double.parseDouble(text);
}
scanner.close();
if (zero != Double.NaN) {
final Dataset energies = DatasetFactory.createRange(nchannels, Dataset.FLOAT64);
energies.imultiply(gain).iadd(zero);
energies.setName("Energy_" + scan_number);
result.addDataset("Energy_" + scan_number, energies);
}
final Dataset counts = DatasetFactory.createFromObject(array);
counts.setName("Counts_" + scan_number);
result.addDataset("Counts_" + scan_number, counts);
if (loadMetadata) {
metadata = new ExtendedMetadata(new File(fileName));
metadata.setMetadata(mcaParameters);
result.setMetadata(metadata);
}
} catch (Exception e) {
throw new ScanFileHolderException("MCALoader.loadFile exception loading " + fileName, e);
} finally {
if (scanner != null)
scanner.close();
}
return result;
}
}