Analyzers for Tristan
This commit is contained in:
parent
cf3268244e
commit
ae97c0be68
@ -12,7 +12,6 @@ version = "0.2.0";
|
||||
|
||||
dependencies {
|
||||
compile project(':numass-control')
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
|
||||
}
|
||||
|
||||
task testDevice(dependsOn: classes, type: JavaExec) {
|
||||
@ -22,28 +21,7 @@ task testDevice(dependsOn: classes, type: JavaExec) {
|
||||
description "Start application in debug mode with default virtual port"
|
||||
group "test"
|
||||
}
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.1.60'
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//task testRun(dependsOn: classes, type: JavaExec) {
|
||||
// main mainClass
|
||||
|
@ -33,13 +33,9 @@ internal fun createChannel(meta: Meta): PKT8Channel {
|
||||
when (transformationType) {
|
||||
"default", "hyperbolic" -> {
|
||||
val coefs = meta.getValue("coefs").listValue()
|
||||
val r0 = meta.getDouble("r0", 1000.0)!!
|
||||
val r0 = meta.getDouble("r0", 1000.0)
|
||||
return PKT8Channel(meta) { r ->
|
||||
if (coefs == null) {
|
||||
-1.0
|
||||
} else {
|
||||
coefs.indices.sumByDouble { coefs[it].doubleValue() * Math.pow(r0 / r, it.toDouble()) }
|
||||
}
|
||||
coefs?.indices?.sumByDouble { coefs[it].doubleValue() * Math.pow(r0 / r, it.toDouble()) } ?: -1.0
|
||||
}
|
||||
}
|
||||
else -> throw RuntimeException("Unknown transformation type")
|
||||
@ -50,13 +46,6 @@ internal fun createChannel(meta: Meta): PKT8Channel {
|
||||
}
|
||||
}
|
||||
|
||||
data class PKT8Result(val channel: String, val rawValue: Double, val temperature: Double) {
|
||||
|
||||
val rawString: String = String.format("%.2f", rawValue)
|
||||
|
||||
val temperatureString: String = String.format("%.2f", temperature)
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by darksnake on 28-Sep-16.
|
||||
*/
|
||||
|
@ -39,6 +39,7 @@ import hep.dataforge.utils.DateTimeUtils
|
||||
import inr.numass.control.DeviceView
|
||||
import inr.numass.control.StorageHelper
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
|
||||
|
||||
@ -52,7 +53,7 @@ import java.util.*
|
||||
RoleDef(name = Roles.VIEW_ROLE)
|
||||
)
|
||||
@ValueDef(name = "port", def = "virtual", info = "The name of the port for this PKT8")
|
||||
@StateDef(ValueDef(name = "storing"))
|
||||
@StateDef(value = ValueDef(name = "storing", info = "Define if this device is currently writes to storage"), writable = true)
|
||||
@DeviceView(PKT8Display::class)
|
||||
class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) {
|
||||
/**
|
||||
@ -103,9 +104,9 @@ class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) {
|
||||
|
||||
//read channel configuration
|
||||
if (meta.hasMeta("channel")) {
|
||||
for (node in getMeta().getMetaList("channel")) {
|
||||
for (node in meta.getMetaList("channel")) {
|
||||
val designation = node.getString("designation", "default")
|
||||
this.channels.put(designation, createChannel(node))
|
||||
this.channels[designation] = createChannel(node)
|
||||
}
|
||||
} else {
|
||||
//set default channel configuration
|
||||
@ -229,13 +230,18 @@ class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) {
|
||||
}
|
||||
|
||||
private fun notifyChannelResult(designation: String, rawValue: Double) {
|
||||
updateLogicalState("raw.$designation", rawValue)
|
||||
|
||||
val channel = channels[designation]
|
||||
|
||||
if (channel != null) {
|
||||
collector.put(channel.name, channel.getTemperature(rawValue))
|
||||
} else {
|
||||
result(PKT8Result(designation, rawValue, -1.0))
|
||||
val temperature = channel?.let {
|
||||
val temp = it.getTemperature(rawValue)
|
||||
updateLogicalState("temp.$designation", temp)
|
||||
collector.put(it.name, temp)
|
||||
temp
|
||||
}
|
||||
forEachConnection(PKT8ValueListener::class.java) {
|
||||
it.report(PKT8Reading(channel?.name ?: designation, rawValue, temperature))
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,5 +319,15 @@ class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) {
|
||||
const val ABUF = "abuf"
|
||||
private val CHANNEL_DESIGNATIONS = arrayOf("a", "b", "c", "d", "e", "f", "g", "h")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class PKT8Reading(val channel: String, val rawValue: Double, val temperature: Double?) {
|
||||
|
||||
val rawString: String = String.format("%.2f", rawValue)
|
||||
|
||||
val temperatureString: String = String.format("%.2f", temperature)
|
||||
}
|
||||
|
||||
interface PKT8ValueListener {
|
||||
fun report(reading: PKT8Reading, time: Instant = Instant.now())
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package inr.numass.control.cryotemp
|
||||
|
||||
import hep.dataforge.control.devices.Sensor
|
||||
import hep.dataforge.control.measurements.Measurement
|
||||
import hep.dataforge.control.measurements.MeasurementListener
|
||||
import hep.dataforge.description.Descriptors
|
||||
import hep.dataforge.fx.bindWindow
|
||||
import hep.dataforge.fx.dfIconView
|
||||
@ -15,7 +13,6 @@ import hep.dataforge.plots.PlotUtils
|
||||
import hep.dataforge.plots.data.TimePlot
|
||||
import hep.dataforge.plots.jfreechart.JFreeChartFrame
|
||||
import inr.numass.control.DeviceDisplay
|
||||
import javafx.application.Platform
|
||||
import javafx.beans.binding.ListBinding
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.collections.FXCollections
|
||||
@ -33,11 +30,11 @@ import java.time.Instant
|
||||
/**
|
||||
* Created by darksnake on 30-May-17.
|
||||
*/
|
||||
class PKT8Display : DeviceDisplay<PKT8Device>(), MeasurementListener {
|
||||
class PKT8Display : DeviceDisplay<PKT8Device>(), PKT8ValueListener {
|
||||
|
||||
override fun buildView(device: PKT8Device) = CryoView()
|
||||
|
||||
internal val table = FXCollections.observableHashMap<String, PKT8Result>()
|
||||
internal val table = FXCollections.observableHashMap<String, PKT8Reading>()
|
||||
val lastUpdateProperty = SimpleObjectProperty<String>("NEVER")
|
||||
|
||||
|
||||
@ -47,16 +44,24 @@ class PKT8Display : DeviceDisplay<PKT8Device>(), MeasurementListener {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMeasurementFailed(measurement: Measurement<*>, exception: Throwable) {
|
||||
// override fun onMeasurementFailed(measurement: Measurement<*>, exception: Throwable) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// override fun onMeasurementResult(measurement: Measurement<*>, result: Any, time: Instant) {
|
||||
// if (result is PKT8Result) {
|
||||
// Platform.runLater {
|
||||
// lastUpdateProperty.set(time.toString())
|
||||
// table[result.channel] = result;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
override fun onMeasurementResult(measurement: Measurement<*>, result: Any, time: Instant) {
|
||||
if (result is PKT8Result) {
|
||||
Platform.runLater {
|
||||
lastUpdateProperty.set(time.toString())
|
||||
table.put(result.channel, result);
|
||||
}
|
||||
override fun report(reading: PKT8Reading, time: Instant) {
|
||||
runLater {
|
||||
lastUpdateProperty.set(time.toString())
|
||||
table[reading.channel] = reading;
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,24 +109,24 @@ class PKT8Display : DeviceDisplay<PKT8Device>(), MeasurementListener {
|
||||
}
|
||||
}
|
||||
center {
|
||||
tableview<PKT8Result> {
|
||||
items = object : ListBinding<PKT8Result>() {
|
||||
tableview<PKT8Reading> {
|
||||
items = object : ListBinding<PKT8Reading>() {
|
||||
init {
|
||||
bind(table)
|
||||
}
|
||||
|
||||
override fun computeValue(): ObservableList<PKT8Result> {
|
||||
override fun computeValue(): ObservableList<PKT8Reading> {
|
||||
return FXCollections.observableArrayList(table.values).apply {
|
||||
sortBy { it.channel }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
column("Sensor", PKT8Result::channel);
|
||||
column("Resistance", PKT8Result::rawValue).cellFormat {
|
||||
column("Sensor", PKT8Reading::channel);
|
||||
column("Resistance", PKT8Reading::rawValue).cellFormat {
|
||||
text = String.format("%.2f", it)
|
||||
}
|
||||
column("Temperature", PKT8Result::temperature).cellFormat {
|
||||
column("Temperature", PKT8Reading::temperature).cellFormat {
|
||||
text = String.format("%.2f", it)
|
||||
}
|
||||
}
|
||||
|
@ -18,26 +18,29 @@ import java.util.function.Supplier
|
||||
/**
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
class PKT8VirtualPort(portName: String, meta: Meta) : VirtualPort(meta), Metoid {
|
||||
class PKT8VirtualPort(private val portName: String, meta: Meta) : VirtualPort(meta), Metoid {
|
||||
|
||||
private val generator = Random()
|
||||
|
||||
init {
|
||||
super.configureValue("id", portName)
|
||||
}
|
||||
// init {
|
||||
// super.configureValue("id", portName)
|
||||
// }
|
||||
|
||||
override fun getName(): String = portName
|
||||
|
||||
|
||||
@Synchronized override fun evaluateRequest(request: String) {
|
||||
when (request) {
|
||||
"s" -> {
|
||||
val letters = arrayOf("a", "b", "c", "d", "e", "f", "g", "h")
|
||||
for (letter in letters) {
|
||||
val channelMeta = MetaUtils.findNodeByValue(getMeta(), "channel", "letter", Value.of(letter)).orElse(Meta.empty())
|
||||
val channelMeta = MetaUtils.findNodeByValue(meta, "channel", "letter", Value.of(letter)).orElse(Meta.empty())
|
||||
|
||||
val average: Double
|
||||
val sigma: Double
|
||||
if (channelMeta != null) {
|
||||
average = channelMeta.getDouble("av", 1200.0)!!
|
||||
sigma = channelMeta.getDouble("sigma", 50.0)!!
|
||||
average = channelMeta.getDouble("av", 1200.0)
|
||||
sigma = channelMeta.getDouble("sigma", 50.0)
|
||||
} else {
|
||||
average = 1200.0
|
||||
sigma = 50.0
|
||||
@ -56,7 +59,7 @@ class PKT8VirtualPort(portName: String, meta: Meta) : VirtualPort(meta), Metoid
|
||||
}
|
||||
"p" -> {
|
||||
cancelByTag("measurement")
|
||||
this.receivePhrase("Stopped")
|
||||
planResponse("Stopped", Duration.ofMillis(50))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ import java.util.function.Consumer
|
||||
StateDef(ValueDef(name = "filamentStatus", info = "Filament status"))
|
||||
)
|
||||
@DeviceView(MspDisplay::class)
|
||||
class MspDevice(context: Context, meta: Meta) : PortSensor<Values>(context, meta) {
|
||||
class MspDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
|
||||
|
||||
private var measurementDelegate: Consumer<MspResponse>? = null
|
||||
|
||||
|
@ -5,13 +5,13 @@ import hep.dataforge.control.connections.Roles
|
||||
import hep.dataforge.control.devices.Device
|
||||
import hep.dataforge.control.devices.DeviceFactory
|
||||
import hep.dataforge.exceptions.ControlException
|
||||
import hep.dataforge.kodex.optional
|
||||
import hep.dataforge.meta.Meta
|
||||
import javafx.scene.Scene
|
||||
import javafx.stage.Stage
|
||||
import org.slf4j.LoggerFactory
|
||||
import tornadofx.*
|
||||
import java.util.*
|
||||
import java.util.function.Predicate
|
||||
|
||||
/**
|
||||
* Created by darksnake on 14-May-17.
|
||||
@ -25,6 +25,7 @@ abstract class NumassControlApplication<in D : Device> : App() {
|
||||
rootLogger.level = Level.INFO
|
||||
|
||||
device = setupDevice()
|
||||
|
||||
val controller = device.getDisplay()
|
||||
device.connect(controller, Roles.VIEW_ROLE, Roles.DEVICE_LISTENER_ROLE)
|
||||
val scene = Scene(controller.view?.root ?: controller.getBoardView())
|
||||
@ -47,12 +48,11 @@ abstract class NumassControlApplication<in D : Device> : App() {
|
||||
protected abstract fun acceptDevice(meta: Meta): Boolean
|
||||
|
||||
private fun setupDevice(): D {
|
||||
val config = getConfig(this)
|
||||
.orElseGet { readResourceMeta("/config/devices.xml") }
|
||||
val config = getConfig(this).optional.orElseGet { readResourceMeta("/config/devices.xml") }
|
||||
|
||||
val ctx = setupContext(config)
|
||||
val deviceConfig = findDeviceMeta(config, Predicate<Meta> { this.acceptDevice(it) })
|
||||
.orElseThrow { RuntimeException("Device configuration not found") }
|
||||
val deviceConfig = findDeviceMeta(config) { this.acceptDevice(it) }
|
||||
?: throw RuntimeException("Device configuration not found")
|
||||
|
||||
|
||||
try {
|
||||
|
@ -8,6 +8,7 @@ import hep.dataforge.exceptions.StorageException
|
||||
import hep.dataforge.fx.dfIcon
|
||||
import hep.dataforge.io.MetaFileReader
|
||||
import hep.dataforge.io.XMLMetaReader
|
||||
import hep.dataforge.kodex.nullable
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.storage.commons.StorageConnection
|
||||
import hep.dataforge.storage.commons.StorageManager
|
||||
@ -19,8 +20,6 @@ import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.text.ParseException
|
||||
import java.util.*
|
||||
import java.util.function.Predicate
|
||||
|
||||
/**
|
||||
* Created by darksnake on 08-May-17.
|
||||
@ -70,10 +69,10 @@ fun readResourceMeta(path: String): Meta {
|
||||
|
||||
}
|
||||
|
||||
fun getConfig(app: Application): Optional<Meta> {
|
||||
fun getConfig(app: Application): Meta? {
|
||||
val debugConfig = app.parameters.named["config.resource"]
|
||||
if (debugConfig != null) {
|
||||
return Optional.ofNullable(readResourceMeta(debugConfig))
|
||||
return readResourceMeta(debugConfig)
|
||||
}
|
||||
|
||||
var configFileName: String? = app.parameters.named["config"]
|
||||
@ -84,25 +83,17 @@ fun getConfig(app: Application): Optional<Meta> {
|
||||
}
|
||||
val configFile = Paths.get(configFileName)
|
||||
|
||||
if (Files.exists(configFile)) {
|
||||
try {
|
||||
val config = MetaFileReader.read(configFile)
|
||||
return Optional.of(config)
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException(e)
|
||||
} catch (e: ParseException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
|
||||
return if (Files.exists(configFile)) {
|
||||
MetaFileReader.read(configFile)
|
||||
} else {
|
||||
logger.warn("Configuration file not found")
|
||||
return Optional.empty<Meta>()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun findDeviceMeta(config: Meta, criterion: Predicate<Meta>): Optional<Meta> {
|
||||
return config.getMetaList("device").stream().filter(criterion).findFirst().map { it -> it }
|
||||
fun findDeviceMeta(config: Meta, criterion: (Meta) -> Boolean): Meta? {
|
||||
return config.getMetaList("device").stream().filter(criterion).findFirst().nullable
|
||||
}
|
||||
|
||||
fun setupContext(meta: Meta): Context {
|
||||
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.data;
|
||||
|
||||
import hep.dataforge.meta.Meta;
|
||||
import hep.dataforge.meta.MetaBuilder;
|
||||
import hep.dataforge.tables.BasicAdapter;
|
||||
import hep.dataforge.tables.ValueMap;
|
||||
import hep.dataforge.tables.ValuesAdapter;
|
||||
import hep.dataforge.values.Value;
|
||||
import hep.dataforge.values.Values;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static hep.dataforge.tables.Adapters.*;
|
||||
|
||||
/**
|
||||
* @author Darksnake
|
||||
*/
|
||||
public class SpectrumAdapter extends BasicAdapter {
|
||||
|
||||
private static final String POINT_LENGTH_NAME = "time";
|
||||
|
||||
public SpectrumAdapter(Meta meta) {
|
||||
super(meta);
|
||||
}
|
||||
|
||||
public SpectrumAdapter(String xName, String yName, String yErrName, String measurementTime) {
|
||||
super(new MetaBuilder(ValuesAdapter.ADAPTER_KEY)
|
||||
.setValue(X_VALUE_KEY, xName)
|
||||
.setValue(Y_VALUE_KEY, yName)
|
||||
.setValue(Y_ERROR_KEY, yErrName)
|
||||
.setValue(POINT_LENGTH_NAME, measurementTime)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public SpectrumAdapter(String xName, String yName, String measurementTime) {
|
||||
super(new MetaBuilder(ValuesAdapter.ADAPTER_KEY)
|
||||
.setValue(X_VALUE_KEY, xName)
|
||||
.setValue(Y_VALUE_KEY, yName)
|
||||
.setValue(POINT_LENGTH_NAME, measurementTime)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public double getTime(Values point) {
|
||||
return this.optComponent(point, POINT_LENGTH_NAME).map(Value::doubleValue).orElse(1d);
|
||||
}
|
||||
|
||||
public Values buildSpectrumDataPoint(double x, long count, double t) {
|
||||
return ValueMap.of(new String[]{getComponentName(X_VALUE_KEY), getComponentName(Y_VALUE_KEY),
|
||||
getComponentName(POINT_LENGTH_NAME)},
|
||||
x, count, t);
|
||||
}
|
||||
|
||||
public Values buildSpectrumDataPoint(double x, long count, double countErr, double t) {
|
||||
return ValueMap.of(new String[]{getComponentName(X_VALUE_KEY), getComponentName(Y_VALUE_KEY),
|
||||
getComponentName(Y_ERROR_KEY), getComponentName(POINT_LENGTH_NAME)},
|
||||
x, count, countErr, t);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Optional<Value> optComponent(Values values, String component) {
|
||||
switch (component) {
|
||||
case "count":
|
||||
return super.optComponent(values, Y_VALUE_KEY);
|
||||
case Y_VALUE_KEY:
|
||||
return super.optComponent(values, Y_VALUE_KEY)
|
||||
.map(it -> it.doubleValue() / getTime(values))
|
||||
.map(Value::of);
|
||||
case Y_ERROR_KEY:
|
||||
Optional<Value> err = super.optComponent(values, Y_ERROR_KEY);
|
||||
if (err.isPresent()) {
|
||||
return Optional.of(Value.of(err.get().doubleValue() / getTime(values)));
|
||||
} else {
|
||||
double y = getComponent(values, Y_VALUE_KEY).doubleValue();
|
||||
if (y < 0) {
|
||||
return Optional.empty();
|
||||
} else if (y == 0) {
|
||||
//avoid infinite weights
|
||||
return Optional.of(Value.of(1d / getTime(values)));
|
||||
} else {
|
||||
return Optional.of(Value.of(Math.sqrt(y) / getTime(values)));
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return super.optComponent(values, component);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> listComponents() {
|
||||
return Stream.concat(super.listComponents(), Stream.of(X_VALUE_KEY, Y_VALUE_KEY, POINT_LENGTH_NAME)).distinct();
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package inr.numass.data.api;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A block constructed from a set of other blocks. Internal blocks are not necessary subsequent. Blocks are automatically sorted.
|
||||
* Created by darksnake on 16.07.2017.
|
||||
*/
|
||||
public class MetaBlock implements NumassBlock {
|
||||
private SortedSet<NumassBlock> blocks = new TreeSet<>(Comparator.comparing(NumassBlock::getStartTime));
|
||||
|
||||
public MetaBlock(NumassBlock... blocks) {
|
||||
this.blocks.addAll(Arrays.asList(blocks));
|
||||
}
|
||||
|
||||
public MetaBlock(Collection<NumassBlock> blocks) {
|
||||
this.blocks.addAll(blocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getStartTime() {
|
||||
return blocks.first().getStartTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getLength() {
|
||||
return Duration.ofNanos(blocks.stream().mapToLong(block -> block.getLength().toNanos()).sum());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<NumassEvent> getEvents() {
|
||||
return blocks.stream()
|
||||
.sorted(Comparator.comparing(NumassBlock::getStartTime))
|
||||
.flatMap(NumassBlock::getEvents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<NumassFrame> getFrames() {
|
||||
return blocks.stream()
|
||||
.sorted(Comparator.comparing(NumassBlock::getStartTime))
|
||||
.flatMap(NumassBlock::getFrames);
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package inr.numass.data.api;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A single continuous measurement block. The block can contain both isolated events and signal frames
|
||||
* <p>
|
||||
* Created by darksnake on 06-Jul-17.
|
||||
*/
|
||||
public interface NumassBlock {
|
||||
/**
|
||||
* The absolute start time of the block
|
||||
* @return
|
||||
*/
|
||||
Instant getStartTime();
|
||||
|
||||
/**
|
||||
* The length of the block
|
||||
* @return
|
||||
*/
|
||||
Duration getLength();
|
||||
|
||||
/**
|
||||
* Stream of isolated events. Could be empty
|
||||
* @return
|
||||
*/
|
||||
Stream<NumassEvent> getEvents();
|
||||
|
||||
/**
|
||||
* Stream of frames. Could be empty
|
||||
* @return
|
||||
*/
|
||||
Stream<NumassFrame> getFrames();
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.data.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* A single numass event with given amplitude and time.
|
||||
*
|
||||
* @author Darksnake
|
||||
*/
|
||||
public class NumassEvent implements Serializable {
|
||||
// channel
|
||||
private final short chanel;
|
||||
//The time of the block start
|
||||
private final Instant blockTime;
|
||||
//time in nanoseconds relative to block start
|
||||
private final long timeOffset;
|
||||
|
||||
public NumassEvent(short chanel, Instant blockTime, long offset) {
|
||||
this.chanel = chanel;
|
||||
this.blockTime = blockTime;
|
||||
this.timeOffset = offset;
|
||||
}
|
||||
|
||||
public NumassEvent(short chanel, long offset) {
|
||||
this(chanel, Instant.EPOCH, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the chanel
|
||||
*/
|
||||
public short getChanel() {
|
||||
return chanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* time in nanoseconds relative to block start
|
||||
* @return the time
|
||||
*/
|
||||
public long getTimeOffset() {
|
||||
return timeOffset;
|
||||
}
|
||||
|
||||
public Instant getBlockTime() {
|
||||
return blockTime;
|
||||
}
|
||||
|
||||
public Instant getTime() {
|
||||
return blockTime.plusNanos(timeOffset);
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package inr.numass.data.api;
|
||||
|
||||
import java.nio.ShortBuffer;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* The continuous frame of digital detector data
|
||||
* Created by darksnake on 06-Jul-17.
|
||||
*/
|
||||
public class NumassFrame {
|
||||
|
||||
/**
|
||||
* The absolute start time of the frame
|
||||
*/
|
||||
private final Instant startTime;
|
||||
|
||||
/**
|
||||
* The buffered signal shape in ticks
|
||||
*/
|
||||
private final ShortBuffer signal;
|
||||
|
||||
/**
|
||||
* The time interval per tick
|
||||
*/
|
||||
private final Duration tickSize;
|
||||
|
||||
|
||||
public NumassFrame(Instant startTime, Duration tickSize, ShortBuffer signal) {
|
||||
this.startTime = startTime;
|
||||
this.signal = signal;
|
||||
this.tickSize = tickSize;
|
||||
}
|
||||
|
||||
public Instant getTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public ShortBuffer getSignal() {
|
||||
return signal;
|
||||
}
|
||||
|
||||
public Duration getTickSize() {
|
||||
return tickSize;
|
||||
}
|
||||
|
||||
public Duration getLength() {
|
||||
return tickSize.multipliedBy(signal.capacity());
|
||||
}
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
package inr.numass.data.api;
|
||||
|
||||
import hep.dataforge.meta.Metoid;
|
||||
import hep.dataforge.values.Value;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Created by darksnake on 06-Jul-17.
|
||||
*/
|
||||
public interface NumassPoint extends Metoid, NumassBlock {
|
||||
|
||||
String START_TIME_KEY = "start";
|
||||
String LENGTH_KEY = "length";
|
||||
String HV_KEY = "voltage";
|
||||
String INDEX_KEY = "index";
|
||||
|
||||
|
||||
Stream<NumassBlock> getBlocks();
|
||||
|
||||
/**
|
||||
* Get the voltage setting for the point
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
default double getVoltage() {
|
||||
return getMeta().getDouble(HV_KEY, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index for this point in the set
|
||||
* @return
|
||||
*/
|
||||
default int getIndex() {
|
||||
return getMeta().getInt(INDEX_KEY, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first block if it exists. Throw runtime exception otherwise.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
default NumassBlock getFirstBlock() {
|
||||
return getBlocks().findFirst().orElseThrow(() -> new RuntimeException("The point is empty"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the starting time from meta or from first block
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
default Instant getStartTime() {
|
||||
return getMeta().optValue(START_TIME_KEY).map(Value::timeValue).orElseGet(() -> getFirstBlock().getStartTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length key of meta or calculate length as a sum of block lengths. The latter could be a bit slow
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
default Duration getLength() {
|
||||
return Duration.ofNanos(
|
||||
getMeta().optValue(LENGTH_KEY).map(Value::longValue)
|
||||
.orElseGet(() -> getBlocks().mapToLong(it -> it.getLength().toNanos()).sum())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all events it all blocks as a single sequence
|
||||
* <p>
|
||||
* Some performance analysis of different stream concatenation approaches is given here: https://www.techempower.com/blog/2016/10/19/efficient-multiple-stream-concatenation-in-java/
|
||||
* </p>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
default Stream<NumassEvent> getEvents() {
|
||||
return getBlocks().flatMap(NumassBlock::getEvents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all frames in all blocks as a single sequence
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
default Stream<NumassFrame> getFrames() {
|
||||
return getBlocks().flatMap(NumassBlock::getFrames);
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package inr.numass.data.api;
|
||||
|
||||
import hep.dataforge.meta.Metoid;
|
||||
import hep.dataforge.names.Named;
|
||||
import hep.dataforge.providers.Provider;
|
||||
import hep.dataforge.providers.Provides;
|
||||
import hep.dataforge.providers.ProvidesNames;
|
||||
import hep.dataforge.tables.Table;
|
||||
import hep.dataforge.values.Value;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A single set of numass points previously called file.
|
||||
*
|
||||
* @author <a href="mailto:altavir@gmail.com">Alexander Nozik</a>
|
||||
*/
|
||||
public interface NumassSet extends Named, Metoid, Iterable<NumassPoint>, Provider {
|
||||
String DESCRIPTION_KEY = "info";
|
||||
String NUMASS_POINT_PROVIDER_KEY = "point";
|
||||
|
||||
Stream<NumassPoint> getPoints();
|
||||
|
||||
// default String getDescription() {
|
||||
// return getMeta().getString(DESCRIPTION_KEY, "");
|
||||
// }
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
default Iterator<NumassPoint> iterator() {
|
||||
return getPoints().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first point if it exists. Throw runtime exception otherwise.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
default NumassPoint getFirstPoint() {
|
||||
return getPoints().findFirst().orElseThrow(() -> new RuntimeException("The set is empty"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the starting time from meta or from first point
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
default Instant getStartTime() {
|
||||
return getMeta().optValue(NumassPoint.START_TIME_KEY).map(Value::timeValue).orElseGet(() -> getFirstPoint().getStartTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find first point with given voltage
|
||||
*
|
||||
* @param voltage
|
||||
* @return
|
||||
*/
|
||||
default Optional<NumassPoint> optPoint(double voltage) {
|
||||
return getPoints().filter(it -> it.getVoltage() == voltage).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* List all points with given voltage
|
||||
*
|
||||
* @param voltage
|
||||
* @return
|
||||
*/
|
||||
default List<NumassPoint> getPoints(double voltage) {
|
||||
return getPoints().filter(it -> it.getVoltage() == voltage).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Provides(NUMASS_POINT_PROVIDER_KEY)
|
||||
default Optional<NumassPoint> optPoint(String voltage) {
|
||||
return optPoint(Double.parseDouble(voltage));
|
||||
}
|
||||
|
||||
@Override
|
||||
default String defaultTarget() {
|
||||
return NUMASS_POINT_PROVIDER_KEY;
|
||||
}
|
||||
|
||||
@ProvidesNames(NUMASS_POINT_PROVIDER_KEY)
|
||||
default Stream<String> listPoints() {
|
||||
return getPoints().map(it -> Double.toString(it.getVoltage()));
|
||||
}
|
||||
|
||||
default Optional<Table> getHvData() {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package inr.numass.data.api;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* An ancestor to numass frame analyzers
|
||||
* Created by darksnake on 07.07.2017.
|
||||
*/
|
||||
public interface SignalProcessor {
|
||||
Stream<NumassEvent> analyze(NumassFrame frame);
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package inr.numass.data.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A simple in-memory implementation of block of events. No frames are allowed
|
||||
* Created by darksnake on 08.07.2017.
|
||||
*/
|
||||
public class SimpleBlock implements NumassBlock, Serializable {
|
||||
private final Instant startTime;
|
||||
private final Duration length;
|
||||
private final List<NumassEvent> events;
|
||||
|
||||
public SimpleBlock(Instant startTime, Duration length, List<NumassEvent> events) {
|
||||
this.startTime = startTime;
|
||||
this.length = length;
|
||||
this.events = events;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<NumassEvent> getEvents() {
|
||||
return events.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<NumassFrame> getFrames() {
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package inr.numass.data.api;
|
||||
|
||||
import hep.dataforge.meta.Meta;
|
||||
import hep.dataforge.meta.MetaBuilder;
|
||||
import hep.dataforge.meta.MetaHolder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A simple static implementation of NumassPoint
|
||||
* Created by darksnake on 08.07.2017.
|
||||
*/
|
||||
public class SimpleNumassPoint extends MetaHolder implements NumassPoint {
|
||||
private final List<NumassBlock> blocks;
|
||||
|
||||
/**
|
||||
* Input blocks must be sorted
|
||||
* @param voltage
|
||||
* @param blocks
|
||||
*/
|
||||
public SimpleNumassPoint(double voltage, Collection<? extends NumassBlock> blocks) {
|
||||
super(new MetaBuilder("point").setValue(HV_KEY, voltage));
|
||||
this.blocks = new ArrayList<>(blocks);
|
||||
this.blocks.sort(Comparator.comparing(NumassBlock::getStartTime));
|
||||
}
|
||||
|
||||
public SimpleNumassPoint(Meta meta, Collection<? extends NumassBlock> blocks) {
|
||||
super(meta);
|
||||
this.blocks = new ArrayList<>(blocks);
|
||||
this.blocks.sort(Comparator.comparing(NumassBlock::getStartTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<NumassBlock> getBlocks() {
|
||||
return blocks.stream();
|
||||
}
|
||||
}
|
@ -1,265 +0,0 @@
|
||||
package inr.numass.data.legacy;
|
||||
|
||||
import hep.dataforge.meta.Meta;
|
||||
import hep.dataforge.meta.MetaBuilder;
|
||||
import inr.numass.data.api.*;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static inr.numass.data.api.NumassPoint.HV_KEY;
|
||||
import static java.nio.file.StandardOpenOption.READ;
|
||||
|
||||
/**
|
||||
* Created by darksnake on 08.07.2017.
|
||||
*/
|
||||
public class NumassDatFile implements NumassSet {
|
||||
private final String name;
|
||||
private final Path path;
|
||||
private final Meta meta;
|
||||
|
||||
public NumassDatFile(Path path, Meta meta) throws IOException {
|
||||
this(FilenameUtils.getBaseName(path.getFileName().toString()),path,meta);
|
||||
}
|
||||
|
||||
public NumassDatFile(String name, Path path, Meta meta) throws IOException {
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
String head = readHead(path);//2048
|
||||
this.meta = new MetaBuilder(meta)
|
||||
.setValue("info", head)
|
||||
.setValue(NumassPoint.START_TIME_KEY, readDate(head))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Meta getMeta() {
|
||||
return meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
private double getHVdev() {
|
||||
return getMeta().getDouble("dat.hvDev", 2.468555393226049);
|
||||
}
|
||||
|
||||
private boolean hasUset() {
|
||||
return getMeta().getBoolean("dat.uSet", true);
|
||||
}
|
||||
|
||||
private static String readHead(Path path) throws IOException {
|
||||
try (SeekableByteChannel channel = Files.newByteChannel(path, READ)) {
|
||||
channel.position(0);
|
||||
ByteBuffer buffer = ByteBuffer.allocate(2048);
|
||||
channel.read(buffer);
|
||||
return new String(buffer.array()).replaceAll("\u0000", "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the block at current position
|
||||
*
|
||||
* @param channel
|
||||
* @param length
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private ByteBuffer readBlock(SeekableByteChannel channel, int length) throws IOException {
|
||||
ByteBuffer res = ByteBuffer.allocate(length);
|
||||
channel.read(res);
|
||||
res.order(ByteOrder.LITTLE_ENDIAN);
|
||||
res.flip();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the point at current position
|
||||
*
|
||||
* @param channel
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized NumassPoint readPoint(SeekableByteChannel channel) throws
|
||||
IOException {
|
||||
|
||||
ByteBuffer rx = readBlock(channel, 32);
|
||||
|
||||
int voltage = rx.getInt();
|
||||
|
||||
short length = rx.getShort();//(short) (rx[6] + 256 * rx[7]);
|
||||
boolean phoneFlag = rx.get(19) != 0;//(rx[19] != 0);
|
||||
|
||||
|
||||
double timeDiv;
|
||||
switch (length) {
|
||||
case 5:
|
||||
case 10:
|
||||
timeDiv = 2e7;
|
||||
break;
|
||||
case 15:
|
||||
case 20:
|
||||
timeDiv = 1e7;
|
||||
break;
|
||||
case 50:
|
||||
timeDiv = 5e6;
|
||||
break;
|
||||
case 100:
|
||||
timeDiv = 2.5e6;
|
||||
break;
|
||||
case 200:
|
||||
timeDiv = 1.25e6;
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unknown time divider in input data");
|
||||
}
|
||||
|
||||
if (phoneFlag) {
|
||||
timeDiv /= 20.0;
|
||||
length *= 20;
|
||||
}
|
||||
|
||||
List<NumassEvent> events = new ArrayList<>();
|
||||
int lab = readBlock(channel, 1).get();
|
||||
|
||||
while (lab == 0xBF) {
|
||||
ByteBuffer buffer = readBlock(channel, 5);
|
||||
lab = buffer.get(4);
|
||||
}
|
||||
|
||||
do {
|
||||
events.add(readEvent(channel, lab, timeDiv));
|
||||
lab = readBlock(channel, 1).get();
|
||||
} while (lab != 0xAF);
|
||||
|
||||
//point end
|
||||
ByteBuffer ending = readBlock(channel, 64);
|
||||
|
||||
int hours = ending.get(37);
|
||||
int minutes = ending.get(38);
|
||||
|
||||
LocalDateTime start = LocalDateTime.from(getStartTime());
|
||||
LocalDateTime absoluteTime = start.withHour(hours).withMinute(minutes);
|
||||
|
||||
//проверяем, не проскочили ли мы полночь
|
||||
if (absoluteTime.isBefore(start)) {
|
||||
absoluteTime = absoluteTime.plusDays(1);
|
||||
}
|
||||
|
||||
|
||||
int uRead = ending.getInt(39);
|
||||
|
||||
double uSet;
|
||||
if (!this.hasUset()) {
|
||||
uSet = uRead / 10d / getHVdev();
|
||||
} else {
|
||||
uSet = voltage / 10d;
|
||||
}
|
||||
|
||||
NumassBlock block = new SimpleBlock(absoluteTime.toInstant(ZoneOffset.UTC), Duration.ofSeconds(length), events);
|
||||
|
||||
Meta pointMeta = new MetaBuilder("point")
|
||||
.setValue(HV_KEY, uSet)
|
||||
.setValue("uRead", uRead / 10 / getHVdev())
|
||||
.setValue("source", "legacy");
|
||||
|
||||
|
||||
return new SimpleNumassPoint(pointMeta, Collections.singletonList(block));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<NumassPoint> getPoints() {
|
||||
try (SeekableByteChannel channel = Files.newByteChannel(path, READ)) {
|
||||
|
||||
//int lab = readBlock(channel,1).get();
|
||||
int lab;
|
||||
List<NumassPoint> points = new ArrayList<>();
|
||||
do {
|
||||
//TODO check point start
|
||||
points.add(readPoint(channel));
|
||||
lab = readBlock(channel, 1).get();
|
||||
} while (lab != 0xff);
|
||||
return points.stream();
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private LocalDateTime readDate(String head) throws IOException {
|
||||
// Должны считать 14 символов
|
||||
Scanner sc = new Scanner(head);
|
||||
sc.nextLine();
|
||||
String dateStr = sc.nextLine().trim();
|
||||
//DD.MM.YY HH:MM
|
||||
//12:35:16 19-11-2013
|
||||
DateTimeFormatter format = DateTimeFormatter.ofPattern("HH:mm:ss dd-MM-yyyy");
|
||||
|
||||
return LocalDateTime.parse(dateStr, format);
|
||||
}
|
||||
|
||||
private NumassEvent readEvent(SeekableByteChannel channel, int b, double timeDiv) throws IOException {
|
||||
short chanel;
|
||||
long time;
|
||||
|
||||
int hb = (b & 0x0f);
|
||||
int lab = (b & 0xf0);
|
||||
|
||||
switch (lab) {
|
||||
case 0x10:
|
||||
chanel = readChanel(channel, hb);
|
||||
time = readTime(channel);
|
||||
break;
|
||||
case 0x20:
|
||||
chanel = 0;
|
||||
time = readTime(channel);
|
||||
break;
|
||||
case 0x40:
|
||||
time = 0;
|
||||
chanel = readChanel(channel, hb);
|
||||
break;
|
||||
case 0x80:
|
||||
time = 0;
|
||||
chanel = 0;
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Event head expected");
|
||||
}
|
||||
|
||||
return new NumassEvent(chanel, (long) (time / timeDiv));
|
||||
}
|
||||
|
||||
private short readChanel(SeekableByteChannel channel, int hb) throws IOException {
|
||||
assert hb < 127;
|
||||
ByteBuffer buffer = readBlock(channel, 1);
|
||||
return (short) (buffer.get() + 256 * hb);
|
||||
}
|
||||
|
||||
private long readTime(SeekableByteChannel channel) throws IOException {
|
||||
ByteBuffer rx = readBlock(channel, 4);
|
||||
return rx.getLong();//rx[0] + 256 * rx[1] + 65536 * rx[2] + 256 * 65536 * rx[3];
|
||||
}
|
||||
|
||||
// private void skip(int length) throws IOException {
|
||||
// long n = stream.skip(length);
|
||||
// if (n != length) {
|
||||
// stream.skip(length - n);
|
||||
// }
|
||||
// }
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package inr.numass.data.legacy;
|
||||
|
||||
import hep.dataforge.io.envelopes.EnvelopeTag;
|
||||
import hep.dataforge.storage.filestorage.FileEnvelope;
|
||||
import inr.numass.NumassEnvelopeType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static java.nio.file.StandardOpenOption.READ;
|
||||
|
||||
public class NumassFileEnvelope extends FileEnvelope {
|
||||
|
||||
public static byte[] LEGACY_START_SEQUENCE = {'#','!'};
|
||||
public static byte[] LEGACY_END_SEQUENCE = {'!','#','\r','\n'};
|
||||
|
||||
public static FileEnvelope open(Path path, boolean readOnly) {
|
||||
// if (!Files.exists(path)) {
|
||||
// throw new RuntimeException("File envelope does not exist");
|
||||
// }
|
||||
|
||||
try (FileChannel channel = FileChannel.open(path,READ)) {
|
||||
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, 2);
|
||||
if (buffer.compareTo(ByteBuffer.wrap(LEGACY_START_SEQUENCE)) == 0) {
|
||||
return new NumassFileEnvelope(path, readOnly);
|
||||
} else {
|
||||
return FileEnvelope.Companion.open(path, readOnly);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to open file envelope", e);
|
||||
}
|
||||
}
|
||||
|
||||
private NumassFileEnvelope(Path path, boolean readOnly) {
|
||||
super(path, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnvelopeTag buildTag() {
|
||||
return new NumassEnvelopeType.LegacyTag();
|
||||
}
|
||||
}
|
102
numass-core/src/main/kotlin/inr/numass/data/SpectrumAdapter.kt
Normal file
102
numass-core/src/main/kotlin/inr/numass/data/SpectrumAdapter.kt
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.data
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaBuilder
|
||||
import hep.dataforge.tables.Adapters.*
|
||||
import hep.dataforge.tables.BasicAdapter
|
||||
import hep.dataforge.tables.ValueMap
|
||||
import hep.dataforge.tables.ValuesAdapter
|
||||
import hep.dataforge.values.Value
|
||||
import hep.dataforge.values.Values
|
||||
import java.util.*
|
||||
import java.util.stream.Stream
|
||||
|
||||
/**
|
||||
* @author Darksnake
|
||||
*/
|
||||
class SpectrumAdapter : BasicAdapter {
|
||||
|
||||
constructor(meta: Meta) : super(meta) {}
|
||||
|
||||
constructor(xName: String, yName: String, yErrName: String, measurementTime: String) : super(MetaBuilder(ValuesAdapter.ADAPTER_KEY)
|
||||
.setValue(X_VALUE_KEY, xName)
|
||||
.setValue(Y_VALUE_KEY, yName)
|
||||
.setValue(Y_ERROR_KEY, yErrName)
|
||||
.setValue(POINT_LENGTH_NAME, measurementTime)
|
||||
.build()
|
||||
) {
|
||||
}
|
||||
|
||||
constructor(xName: String, yName: String, measurementTime: String) : super(MetaBuilder(ValuesAdapter.ADAPTER_KEY)
|
||||
.setValue(X_VALUE_KEY, xName)
|
||||
.setValue(Y_VALUE_KEY, yName)
|
||||
.setValue(POINT_LENGTH_NAME, measurementTime)
|
||||
.build()
|
||||
) {
|
||||
}
|
||||
|
||||
fun getTime(point: Values): Double {
|
||||
return this.optComponent(point, POINT_LENGTH_NAME).map<Double> { it.doubleValue() }.orElse(1.0)
|
||||
}
|
||||
|
||||
fun buildSpectrumDataPoint(x: Double, count: Long, t: Double): Values {
|
||||
return ValueMap.of(arrayOf(getComponentName(X_VALUE_KEY), getComponentName(Y_VALUE_KEY), getComponentName(POINT_LENGTH_NAME)),
|
||||
x, count, t)
|
||||
}
|
||||
|
||||
fun buildSpectrumDataPoint(x: Double, count: Long, countErr: Double, t: Double): Values {
|
||||
return ValueMap.of(arrayOf(getComponentName(X_VALUE_KEY), getComponentName(Y_VALUE_KEY), getComponentName(Y_ERROR_KEY), getComponentName(POINT_LENGTH_NAME)),
|
||||
x, count, countErr, t)
|
||||
}
|
||||
|
||||
|
||||
override fun optComponent(values: Values, component: String): Optional<Value> {
|
||||
when (component) {
|
||||
"count" -> return super.optComponent(values, Y_VALUE_KEY)
|
||||
Y_VALUE_KEY -> return super.optComponent(values, Y_VALUE_KEY)
|
||||
.map { it -> it.doubleValue() / getTime(values) }
|
||||
.map { Value.of(it) }
|
||||
Y_ERROR_KEY -> {
|
||||
val err = super.optComponent(values, Y_ERROR_KEY)
|
||||
return if (err.isPresent) {
|
||||
Optional.of(Value.of(err.get().doubleValue() / getTime(values)))
|
||||
} else {
|
||||
val y = getComponent(values, Y_VALUE_KEY).doubleValue()
|
||||
if (y < 0) {
|
||||
Optional.empty()
|
||||
} else if (y == 0.0) {
|
||||
//avoid infinite weights
|
||||
Optional.of(Value.of(1.0 / getTime(values)))
|
||||
} else {
|
||||
Optional.of(Value.of(Math.sqrt(y) / getTime(values)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> return super.optComponent(values, component)
|
||||
}
|
||||
}
|
||||
|
||||
override fun listComponents(): Stream<String> {
|
||||
return Stream.concat(super.listComponents(), Stream.of(X_VALUE_KEY, Y_VALUE_KEY, POINT_LENGTH_NAME)).distinct()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val POINT_LENGTH_NAME = "time"
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ import hep.dataforge.tables.TableFormat
|
||||
import hep.dataforge.tables.TableFormatBuilder
|
||||
import inr.numass.data.api.NumassBlock
|
||||
import inr.numass.data.api.NumassEvent
|
||||
import inr.numass.data.api.NumassPoint.HV_KEY
|
||||
import inr.numass.data.api.NumassPoint.Companion.HV_KEY
|
||||
import inr.numass.data.api.NumassSet
|
||||
import inr.numass.data.api.SignalProcessor
|
||||
import java.lang.IllegalArgumentException
|
||||
@ -48,7 +48,7 @@ abstract class AbstractAnalyzer @JvmOverloads constructor(private val processor:
|
||||
val loChannel = meta.getInt("window.lo", 0)
|
||||
val upChannel = meta.getInt("window.up", Integer.MAX_VALUE)
|
||||
var res = getAllEvents(block).filter { event ->
|
||||
event.chanel.toInt() in loChannel..(upChannel - 1)
|
||||
event.amp.toInt() in loChannel..(upChannel - 1)
|
||||
}
|
||||
if (meta.getBoolean("sort", false)) {
|
||||
res = res.sorted(Comparator.comparing<NumassEvent, Long> { it.timeOffset })
|
||||
|
@ -24,7 +24,7 @@ import hep.dataforge.values.Values
|
||||
import inr.numass.data.api.NumassBlock
|
||||
import inr.numass.data.api.NumassEvent
|
||||
import inr.numass.data.api.NumassPoint
|
||||
import inr.numass.data.api.NumassPoint.HV_KEY
|
||||
import inr.numass.data.api.NumassPoint.Companion.HV_KEY
|
||||
import inr.numass.data.api.NumassSet
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
@ -56,7 +56,7 @@ interface NumassAnalyzer {
|
||||
*/
|
||||
fun analyzePoint(point: NumassPoint, config: Meta = Meta.empty()): Values {
|
||||
val map = HashMap(analyze(point, config).asMap())
|
||||
map.put(HV_KEY, Value.of(point.voltage))
|
||||
map[HV_KEY] = Value.of(point.voltage)
|
||||
return ValueMap(map)
|
||||
}
|
||||
|
||||
@ -161,7 +161,7 @@ fun getAmplitudeSpectrum(events: Sequence<NumassEvent>, length: Double, config:
|
||||
//optimized for fastest computation
|
||||
val spectrum: MutableMap<Int, AtomicLong> = HashMap()
|
||||
events.forEach { event ->
|
||||
val channel = event.chanel.toInt()
|
||||
val channel = event.amp.toInt()
|
||||
spectrum.getOrPut(channel) {
|
||||
AtomicLong(0)
|
||||
}.incrementAndGet()
|
||||
|
@ -29,7 +29,7 @@ import hep.dataforge.values.Values
|
||||
import inr.numass.data.api.NumassBlock
|
||||
import inr.numass.data.api.NumassEvent
|
||||
import inr.numass.data.api.NumassPoint
|
||||
import inr.numass.data.api.NumassPoint.HV_KEY
|
||||
import inr.numass.data.api.NumassPoint.Companion.HV_KEY
|
||||
import inr.numass.data.api.SignalProcessor
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
38
numass-core/src/main/kotlin/inr/numass/data/api/MetaBlock.kt
Normal file
38
numass-core/src/main/kotlin/inr/numass/data/api/MetaBlock.kt
Normal file
@ -0,0 +1,38 @@
|
||||
package inr.numass.data.api
|
||||
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.stream.Stream
|
||||
|
||||
/**
|
||||
* A block constructed from a set of other blocks. Internal blocks are not necessary subsequent. Blocks are automatically sorted.
|
||||
* Created by darksnake on 16.07.2017.
|
||||
*/
|
||||
class MetaBlock : NumassBlock {
|
||||
private val blocks = TreeSet(Comparator.comparing<NumassBlock, Instant>{ it.startTime })
|
||||
|
||||
override val startTime: Instant
|
||||
get() = blocks.first().startTime
|
||||
|
||||
override val length: Duration
|
||||
get() = Duration.ofNanos(blocks.stream().mapToLong { block -> block.length.toNanos() }.sum())
|
||||
|
||||
override val events: Stream<NumassEvent>
|
||||
get() = blocks.stream()
|
||||
.sorted(Comparator.comparing<NumassBlock, Instant>{ it.startTime })
|
||||
.flatMap{ it.events }
|
||||
|
||||
override val frames: Stream<NumassFrame>
|
||||
get() = blocks.stream()
|
||||
.sorted(Comparator.comparing<NumassBlock, Instant>{ it.startTime })
|
||||
.flatMap{ it.frames }
|
||||
|
||||
constructor(vararg blocks: NumassBlock) {
|
||||
this.blocks.addAll(Arrays.asList(*blocks))
|
||||
}
|
||||
|
||||
constructor(blocks: Collection<NumassBlock>) {
|
||||
this.blocks.addAll(blocks)
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package inr.numass.data.api
|
||||
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.stream.Stream
|
||||
|
||||
|
||||
typealias NumassChannel = Int
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A single continuous measurement block. The block can contain both isolated events and signal frames
|
||||
*
|
||||
*
|
||||
* Created by darksnake on 06-Jul-17.
|
||||
*/
|
||||
interface NumassBlock {
|
||||
|
||||
/**
|
||||
* A channel
|
||||
*/
|
||||
val channel: NumassChannel
|
||||
get() = DEFAULT_CHANNEL
|
||||
|
||||
/**
|
||||
* The absolute start time of the block
|
||||
*/
|
||||
val startTime: Instant
|
||||
|
||||
/**
|
||||
* The length of the block
|
||||
*/
|
||||
val length: Duration
|
||||
|
||||
/**
|
||||
* Stream of isolated events. Could be empty
|
||||
*/
|
||||
val events: Stream<NumassEvent>
|
||||
|
||||
/**
|
||||
* Stream of frames. Could be empty
|
||||
*/
|
||||
val frames: Stream<NumassFrame>
|
||||
|
||||
companion object {
|
||||
val DEFAULT_CHANNEL: NumassChannel = -1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.data.api
|
||||
|
||||
import java.io.Serializable
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* A single numass event with given amplitude and time.
|
||||
*
|
||||
* @author Darksnake
|
||||
* @property amp the amplitude of the event
|
||||
* @property blockTime
|
||||
* @property timeOffset time in nanoseconds relative to block start
|
||||
*
|
||||
*/
|
||||
class NumassEvent(val amp: Short, val timeOffset: Long, val block: NumassBlock? = null) : Serializable {
|
||||
|
||||
val channel: NumassChannel?
|
||||
get() = block?.channel
|
||||
|
||||
val time: Instant
|
||||
get() = (block?.startTime ?: Instant.EPOCH).plusNanos(timeOffset)
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package inr.numass.data.api
|
||||
|
||||
import java.nio.ShortBuffer
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* The continuous frame of digital detector data
|
||||
* Created by darksnake on 06-Jul-17.
|
||||
*/
|
||||
class NumassFrame(
|
||||
/**
|
||||
* The absolute start time of the frame
|
||||
*/
|
||||
val time: Instant,
|
||||
/**
|
||||
* The time interval per tick
|
||||
*/
|
||||
val tickSize: Duration,
|
||||
/**
|
||||
* The buffered signal shape in ticks
|
||||
*/
|
||||
val signal: ShortBuffer) {
|
||||
|
||||
val length: Duration
|
||||
get() = tickSize.multipliedBy(signal.capacity().toLong())
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package inr.numass.data.api
|
||||
|
||||
import hep.dataforge.meta.Metoid
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.stream.Stream
|
||||
|
||||
/**
|
||||
* Created by darksnake on 06-Jul-17.
|
||||
*/
|
||||
interface NumassPoint : Metoid, NumassBlock {
|
||||
|
||||
|
||||
val blocks: Stream<NumassBlock>
|
||||
|
||||
/**
|
||||
* Get the voltage setting for the point
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
val voltage: Double
|
||||
get() = meta.getDouble(HV_KEY, 0.0)
|
||||
|
||||
/**
|
||||
* Get the index for this point in the set
|
||||
* @return
|
||||
*/
|
||||
val index: Int
|
||||
get() = meta.getInt(INDEX_KEY, -1)
|
||||
|
||||
/**
|
||||
* Get the first block if it exists. Throw runtime exception otherwise.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
val firstBlock: NumassBlock
|
||||
get() = blocks.findFirst().orElseThrow { RuntimeException("The point is empty") }
|
||||
|
||||
/**
|
||||
* Get the starting time from meta or from first block
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
override val startTime: Instant
|
||||
get() = meta.optValue(START_TIME_KEY).map<Instant>{ it.timeValue() }.orElseGet { firstBlock.startTime }
|
||||
|
||||
/**
|
||||
* Get the length key of meta or calculate length as a sum of block lengths. The latter could be a bit slow
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
override val length: Duration
|
||||
get() = Duration.ofNanos(
|
||||
meta.optValue(LENGTH_KEY).map<Long>{ it.longValue() }
|
||||
.orElseGet { blocks.mapToLong { it -> it.length.toNanos() }.sum() }
|
||||
)
|
||||
|
||||
/**
|
||||
* Get all events it all blocks as a single sequence
|
||||
*
|
||||
*
|
||||
* Some performance analysis of different stream concatenation approaches is given here: https://www.techempower.com/blog/2016/10/19/efficient-multiple-stream-concatenation-in-java/
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
override val events: Stream<NumassEvent>
|
||||
get() = blocks.flatMap{ it.events }
|
||||
|
||||
/**
|
||||
* Get all frames in all blocks as a single sequence
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
override val frames: Stream<NumassFrame>
|
||||
get() = blocks.flatMap{ it.frames }
|
||||
|
||||
companion object {
|
||||
|
||||
const val START_TIME_KEY = "start"
|
||||
const val LENGTH_KEY = "length"
|
||||
const val HV_KEY = "voltage"
|
||||
const val INDEX_KEY = "index"
|
||||
}
|
||||
}
|
93
numass-core/src/main/kotlin/inr/numass/data/api/NumassSet.kt
Normal file
93
numass-core/src/main/kotlin/inr/numass/data/api/NumassSet.kt
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package inr.numass.data.api
|
||||
|
||||
import hep.dataforge.kodex.toList
|
||||
import hep.dataforge.meta.Metoid
|
||||
import hep.dataforge.names.Named
|
||||
import hep.dataforge.providers.Provider
|
||||
import hep.dataforge.providers.Provides
|
||||
import hep.dataforge.providers.ProvidesNames
|
||||
import hep.dataforge.tables.Table
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.stream.Stream
|
||||
|
||||
/**
|
||||
* A single set of numass points previously called file.
|
||||
*
|
||||
* @author [Alexander Nozik](mailto:altavir@gmail.com)
|
||||
*/
|
||||
interface NumassSet : Named, Metoid, Iterable<NumassPoint>, Provider {
|
||||
|
||||
val points: Stream<NumassPoint>
|
||||
|
||||
/**
|
||||
* Get the first point if it exists. Throw runtime exception otherwise.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
val firstPoint: NumassPoint
|
||||
get() = points.findFirst().orElseThrow { RuntimeException("The set is empty") }
|
||||
|
||||
/**
|
||||
* Get the starting time from meta or from first point
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
val startTime: Instant
|
||||
get() = meta.optValue(NumassPoint.START_TIME_KEY).map<Instant>{ it.timeValue() }.orElseGet { firstPoint.startTime }
|
||||
|
||||
val hvData: Optional<Table>
|
||||
get() = Optional.empty()
|
||||
|
||||
// default String getDescription() {
|
||||
// return getMeta().getString(DESCRIPTION_KEY, "");
|
||||
// }
|
||||
|
||||
override fun iterator(): Iterator<NumassPoint> {
|
||||
return points.iterator()
|
||||
}
|
||||
|
||||
/**
|
||||
* Find first point with given voltage
|
||||
*
|
||||
* @param voltage
|
||||
* @return
|
||||
*/
|
||||
fun optPoint(voltage: Double): Optional<NumassPoint> {
|
||||
return points.filter { it -> it.voltage == voltage }.findFirst()
|
||||
}
|
||||
|
||||
/**
|
||||
* List all points with given voltage
|
||||
*
|
||||
* @param voltage
|
||||
* @return
|
||||
*/
|
||||
fun getPoints(voltage: Double): List<NumassPoint> {
|
||||
return points.filter { it -> it.voltage == voltage }.toList()
|
||||
}
|
||||
|
||||
@Provides(NUMASS_POINT_PROVIDER_KEY)
|
||||
fun optPoint(voltage: String): Optional<NumassPoint> {
|
||||
return optPoint(java.lang.Double.parseDouble(voltage))
|
||||
}
|
||||
|
||||
override fun defaultTarget(): String {
|
||||
return NUMASS_POINT_PROVIDER_KEY
|
||||
}
|
||||
|
||||
@ProvidesNames(NUMASS_POINT_PROVIDER_KEY)
|
||||
fun listPoints(): Stream<String> {
|
||||
return points.map { it -> java.lang.Double.toString(it.voltage) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DESCRIPTION_KEY = "info"
|
||||
const val NUMASS_POINT_PROVIDER_KEY = "point"
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package inr.numass.data.api
|
||||
|
||||
import java.util.stream.Stream
|
||||
|
||||
/**
|
||||
* An ancestor to numass frame analyzers
|
||||
* Created by darksnake on 07.07.2017.
|
||||
*/
|
||||
interface SignalProcessor {
|
||||
fun analyze(frame: NumassFrame): Stream<NumassEvent>
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package inr.numass.data.api
|
||||
|
||||
import java.io.Serializable
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.stream.Stream
|
||||
|
||||
/**
|
||||
* A simple in-memory implementation of block of events. No frames are allowed
|
||||
* Created by darksnake on 08.07.2017.
|
||||
*/
|
||||
class SimpleBlock(override val startTime: Instant, override val length: Duration, private val eventList: List<NumassEvent>) : NumassBlock, Serializable {
|
||||
|
||||
override val frames: Stream<NumassFrame> = Stream.empty()
|
||||
|
||||
override val events: Stream<NumassEvent>
|
||||
get() = eventList.stream()
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package inr.numass.data.api
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaBuilder
|
||||
import hep.dataforge.meta.MetaHolder
|
||||
|
||||
import java.util.stream.Stream
|
||||
|
||||
/**
|
||||
* A simple static implementation of NumassPoint
|
||||
* Created by darksnake on 08.07.2017.
|
||||
*/
|
||||
class SimpleNumassPoint : MetaHolder, NumassPoint {
|
||||
private val blockList: List<NumassBlock>
|
||||
|
||||
/**
|
||||
* Input blocks must be sorted
|
||||
* @param voltage
|
||||
* @param blocks
|
||||
*/
|
||||
constructor(voltage: Double, blocks: Collection<NumassBlock>) : super(MetaBuilder("point").setValue(NumassPoint.HV_KEY, voltage)) {
|
||||
this.blockList = blocks.sortedBy { it.startTime }
|
||||
}
|
||||
|
||||
constructor(meta: Meta, blocks: Collection<NumassBlock>) : super(meta) {
|
||||
this.blockList = blocks.sortedBy { it.startTime }
|
||||
}
|
||||
|
||||
override val blocks: Stream<NumassBlock>
|
||||
get() = blockList.stream()
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
package inr.numass.data.legacy
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaBuilder
|
||||
import inr.numass.data.api.*
|
||||
import inr.numass.data.api.NumassPoint.Companion.HV_KEY
|
||||
import org.apache.commons.io.FilenameUtils
|
||||
import java.io.IOException
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.nio.channels.SeekableByteChannel
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardOpenOption.READ
|
||||
import java.time.Duration
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
import java.util.stream.Stream
|
||||
|
||||
/**
|
||||
* Created by darksnake on 08.07.2017.
|
||||
*/
|
||||
class NumassDatFile @Throws(IOException::class)
|
||||
constructor(private val name: String, private val path: Path, meta: Meta) : NumassSet {
|
||||
private val meta: Meta
|
||||
|
||||
private val hVdev: Double
|
||||
get() = getMeta().getDouble("dat.hvDev", 2.468555393226049)
|
||||
|
||||
override//int lab = readBlock(channel,1).get();
|
||||
//TODO check point start
|
||||
val points: Stream<NumassPoint>
|
||||
get() = try {
|
||||
Files.newByteChannel(path, READ).use { channel ->
|
||||
var lab: Int
|
||||
val points = ArrayList<NumassPoint>()
|
||||
do {
|
||||
points.add(readPoint(channel))
|
||||
lab = readBlock(channel, 1).get().toInt()
|
||||
} while (lab != 0xff)
|
||||
return points.stream()
|
||||
}
|
||||
} catch (ex: IOException) {
|
||||
throw RuntimeException(ex)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
constructor(path: Path, meta: Meta) : this(FilenameUtils.getBaseName(path.fileName.toString()), path, meta) {
|
||||
}
|
||||
|
||||
init {
|
||||
val head = readHead(path)//2048
|
||||
this.meta = MetaBuilder(meta)
|
||||
.setValue("info", head)
|
||||
.setValue(NumassPoint.START_TIME_KEY, readDate(head))
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun getMeta(): Meta {
|
||||
return meta
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return name
|
||||
}
|
||||
|
||||
private fun hasUset(): Boolean {
|
||||
return getMeta().getBoolean("dat.uSet", true)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun readHead(path: Path): String {
|
||||
Files.newByteChannel(path, READ).use { channel ->
|
||||
channel.position(0)
|
||||
val buffer = ByteBuffer.allocate(2048)
|
||||
channel.read(buffer)
|
||||
return String(buffer.array()).replace("\u0000".toRegex(), "")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the block at current position
|
||||
*
|
||||
* @param channel
|
||||
* @param length
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
private fun readBlock(channel: SeekableByteChannel, length: Int): ByteBuffer {
|
||||
val res = ByteBuffer.allocate(length)
|
||||
channel.read(res)
|
||||
res.order(ByteOrder.LITTLE_ENDIAN)
|
||||
res.flip()
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the point at current position
|
||||
*
|
||||
* @param channel
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
@Synchronized
|
||||
@Throws(IOException::class)
|
||||
private fun readPoint(channel: SeekableByteChannel): NumassPoint {
|
||||
|
||||
val rx = readBlock(channel, 32)
|
||||
|
||||
val voltage = rx.int
|
||||
|
||||
var length = rx.short//(short) (rx[6] + 256 * rx[7]);
|
||||
val phoneFlag = rx.get(19).toInt() != 0//(rx[19] != 0);
|
||||
|
||||
|
||||
var timeDiv: Double = when (length.toInt()) {
|
||||
5, 10 -> 2e7
|
||||
15, 20 -> 1e7
|
||||
50 -> 5e6
|
||||
100 -> 2.5e6
|
||||
200 -> 1.25e6
|
||||
else -> throw IOException("Unknown time divider in input data")
|
||||
}
|
||||
|
||||
if (phoneFlag) {
|
||||
timeDiv /= 20.0
|
||||
length = (length*20).toShort()
|
||||
}
|
||||
|
||||
val events = ArrayList<NumassEvent>()
|
||||
var lab = readBlock(channel, 1).get().toInt()
|
||||
|
||||
while (lab == 0xBF) {
|
||||
val buffer = readBlock(channel, 5)
|
||||
lab = buffer.get(4).toInt()
|
||||
}
|
||||
|
||||
do {
|
||||
events.add(readEvent(channel, lab, timeDiv))
|
||||
lab = readBlock(channel, 1).get().toInt()
|
||||
} while (lab != 0xAF)
|
||||
|
||||
//point end
|
||||
val ending = readBlock(channel, 64)
|
||||
|
||||
val hours = ending.get(37).toInt()
|
||||
val minutes = ending.get(38).toInt()
|
||||
|
||||
val start = LocalDateTime.from(startTime)
|
||||
var absoluteTime = start.withHour(hours).withMinute(minutes)
|
||||
|
||||
//проверяем, не проскочили ли мы полночь
|
||||
if (absoluteTime.isBefore(start)) {
|
||||
absoluteTime = absoluteTime.plusDays(1)
|
||||
}
|
||||
|
||||
|
||||
val uRead = ending.getInt(39)
|
||||
|
||||
val uSet: Double
|
||||
if (!this.hasUset()) {
|
||||
uSet = uRead.toDouble() / 10.0 / hVdev
|
||||
} else {
|
||||
uSet = voltage / 10.0
|
||||
}
|
||||
|
||||
val block = SimpleBlock(absoluteTime.toInstant(ZoneOffset.UTC), Duration.ofSeconds(length.toLong()), events)
|
||||
|
||||
val pointMeta = MetaBuilder("point")
|
||||
.setValue(HV_KEY, uSet)
|
||||
.setValue("uRead", uRead.toDouble() / 10.0 / hVdev)
|
||||
.setValue("source", "legacy")
|
||||
|
||||
|
||||
return SimpleNumassPoint(pointMeta, listOf<NumassBlock>(block))
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun readDate(head: String): LocalDateTime {
|
||||
// Должны считать 14 символов
|
||||
val sc = Scanner(head)
|
||||
sc.nextLine()
|
||||
val dateStr = sc.nextLine().trim { it <= ' ' }
|
||||
//DD.MM.YY HH:MM
|
||||
//12:35:16 19-11-2013
|
||||
val format = DateTimeFormatter.ofPattern("HH:mm:ss dd-MM-yyyy")
|
||||
|
||||
return LocalDateTime.parse(dateStr, format)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun readEvent(channel: SeekableByteChannel, b: Int, timeDiv: Double): NumassEvent {
|
||||
val chanel: Short
|
||||
val time: Long
|
||||
|
||||
val hb = b and 0x0f
|
||||
val lab = b and 0xf0
|
||||
|
||||
when (lab) {
|
||||
0x10 -> {
|
||||
chanel = readChanel(channel, hb)
|
||||
time = readTime(channel)
|
||||
}
|
||||
0x20 -> {
|
||||
chanel = 0
|
||||
time = readTime(channel)
|
||||
}
|
||||
0x40 -> {
|
||||
time = 0
|
||||
chanel = readChanel(channel, hb)
|
||||
}
|
||||
0x80 -> {
|
||||
time = 0
|
||||
chanel = 0
|
||||
}
|
||||
else -> throw IOException("Event head expected")
|
||||
}
|
||||
|
||||
return NumassEvent(chanel, (time / timeDiv).toLong())
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun readChanel(channel: SeekableByteChannel, hb: Int): Short {
|
||||
assert(hb < 127)
|
||||
val buffer = readBlock(channel, 1)
|
||||
return (buffer.get() + 256 * hb).toShort()
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun readTime(channel: SeekableByteChannel): Long {
|
||||
val rx = readBlock(channel, 4)
|
||||
return rx.long//rx[0] + 256 * rx[1] + 65536 * rx[2] + 256 * 65536 * rx[3];
|
||||
}
|
||||
|
||||
// private void skip(int length) throws IOException {
|
||||
// long n = stream.skip(length);
|
||||
// if (n != length) {
|
||||
// stream.skip(length - n);
|
||||
// }
|
||||
// }
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package inr.numass.data.legacy
|
||||
|
||||
import hep.dataforge.io.envelopes.EnvelopeTag
|
||||
import hep.dataforge.storage.filestorage.FileEnvelope
|
||||
import inr.numass.NumassEnvelopeType
|
||||
import java.io.IOException
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.channels.FileChannel
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardOpenOption.READ
|
||||
|
||||
class NumassFileEnvelope private constructor(path: Path, readOnly: Boolean) : FileEnvelope(path, readOnly) {
|
||||
|
||||
override fun buildTag(): EnvelopeTag {
|
||||
return NumassEnvelopeType.LegacyTag()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val LEGACY_START_SEQUENCE = byteArrayOf('#'.toByte(), '!'.toByte())
|
||||
val LEGACY_END_SEQUENCE = byteArrayOf('!'.toByte(), '#'.toByte(), '\r'.toByte(), '\n'.toByte())
|
||||
|
||||
fun open(path: Path, readOnly: Boolean): FileEnvelope {
|
||||
// if (!Files.exists(path)) {
|
||||
// throw new RuntimeException("File envelope does not exist");
|
||||
// }
|
||||
|
||||
try {
|
||||
FileChannel.open(path, READ).use { channel ->
|
||||
val buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, 2)
|
||||
return if (buffer.compareTo(ByteBuffer.wrap(LEGACY_START_SEQUENCE)) == 0) {
|
||||
NumassFileEnvelope(path, readOnly)
|
||||
} else {
|
||||
FileEnvelope.open(path, readOnly)
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException("Failed to open file envelope", e)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -112,7 +112,7 @@ class NumassDataLoader(
|
||||
}
|
||||
|
||||
override fun getStartTime(): Instant {
|
||||
return meta.optValue("start_time").map<Instant> { it.timeValue() }.orElseGet { super.getStartTime() }
|
||||
return meta.optValue("start_time").map<Instant> { it.timeValue() }.orElseGet { super.startTime }
|
||||
}
|
||||
|
||||
override val isOpen: Boolean
|
||||
|
@ -1,5 +1,7 @@
|
||||
package inr.numass.data.storage
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.context.Global
|
||||
import hep.dataforge.io.envelopes.Envelope
|
||||
import hep.dataforge.meta.Meta
|
||||
import inr.numass.data.NumassProto
|
||||
@ -30,14 +32,14 @@ class ProtoNumassPoint(private val envelope: Envelope) : NumassPoint {
|
||||
throw RuntimeException("Failed to read point via protobuf")
|
||||
}
|
||||
|
||||
override fun getBlocks(): Stream<NumassBlock> {
|
||||
return point.channelsList.stream()
|
||||
override val blocks: Stream<NumassBlock>
|
||||
get() = point.channelsList.stream()
|
||||
.flatMap { channel ->
|
||||
channel.blocksList.stream()
|
||||
.map { block -> ProtoBlock(channel.num.toInt(), block, meta) }
|
||||
.sorted(Comparator.comparing<ProtoBlock, Instant> { it.startTime })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun getMeta(): Meta {
|
||||
return envelope.meta
|
||||
@ -48,6 +50,10 @@ class ProtoNumassPoint(private val envelope: Envelope) : NumassPoint {
|
||||
return ProtoNumassPoint(NumassFileEnvelope.open(path, true))
|
||||
}
|
||||
|
||||
fun readFile(path: String, context: Context = Global): ProtoNumassPoint {
|
||||
return readFile(context.io.getFile(path).absolutePath)
|
||||
}
|
||||
|
||||
fun ofEpochNanos(nanos: Long): Instant {
|
||||
val seconds = Math.floorDiv(nanos, 1e9.toInt().toLong())
|
||||
val reminder = (nanos % 1e9).toInt()
|
||||
@ -56,32 +62,31 @@ class ProtoNumassPoint(private val envelope: Envelope) : NumassPoint {
|
||||
}
|
||||
}
|
||||
|
||||
class ProtoBlock(val channel: Int, private val block: NumassProto.Point.Channel.Block, private val meta: Meta) : NumassBlock {
|
||||
class ProtoBlock(override val channel: Int, private val block: NumassProto.Point.Channel.Block, private val meta: Meta) : NumassBlock {
|
||||
|
||||
override fun getStartTime(): Instant {
|
||||
return ProtoNumassPoint.ofEpochNanos(block.time)
|
||||
}
|
||||
override val startTime: Instant
|
||||
get() = ProtoNumassPoint.ofEpochNanos(block.time)
|
||||
|
||||
override fun getLength(): Duration {
|
||||
return Duration.ofNanos((meta.getDouble("params.b_size") / meta.getDouble("params.sample_freq") * 1e9).toLong())
|
||||
}
|
||||
override val length: Duration
|
||||
get() = Duration.ofNanos((meta.getDouble("params.b_size") / meta.getDouble("params.sample_freq") * 1e9).toLong())
|
||||
|
||||
override fun getEvents(): Stream<NumassEvent> {
|
||||
val blockTime = startTime
|
||||
if (block.hasEvents()) {
|
||||
|
||||
override val events: Stream<NumassEvent>
|
||||
get() = if (block.hasEvents()) {
|
||||
val events = block.events
|
||||
return IntStream.range(0, events.timesCount).mapToObj { i -> NumassEvent(events.getAmplitudes(i).toShort(), blockTime, events.getTimes(i)) }
|
||||
IntStream.range(0, events.timesCount).mapToObj { i -> NumassEvent(events.getAmplitudes(i).toShort(), events.getTimes(i), this) }
|
||||
} else {
|
||||
return Stream.empty()
|
||||
Stream.empty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFrames(): Stream<NumassFrame> {
|
||||
val tickSize = Duration.ofNanos((1e9 / meta.getInt("params.sample_freq")).toLong())
|
||||
return block.framesList.stream().map { frame ->
|
||||
val time = startTime.plusNanos(frame.time)
|
||||
val data = frame.data.asReadOnlyByteBuffer()
|
||||
NumassFrame(time, tickSize, data.asShortBuffer())
|
||||
|
||||
override val frames: Stream<NumassFrame>
|
||||
get() {
|
||||
val tickSize = Duration.ofNanos((1e9 / meta.getInt("params.sample_freq")).toLong())
|
||||
return block.framesList.stream().map { frame ->
|
||||
val time = startTime.plusNanos(frame.time)
|
||||
val data = frame.data.asReadOnlyByteBuffer()
|
||||
NumassFrame(time, tickSize, data.asShortBuffer())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ new GrindShell(ctx).eval {
|
||||
|
||||
def table = new SimpleHistogram([0d, 0d] as Double[], [2d, 100d] as Double[])
|
||||
.fill(new TimeAnalyzer().getEventsWithDelay(point, Meta.empty()).map {
|
||||
[it.value / 1000, it.key.chanel] as Double[]
|
||||
[it.value / 1000, it.key.amp] as Double[]
|
||||
}).asTable()
|
||||
|
||||
ColumnedDataWriter.writeTable(System.out, table, "hist")
|
||||
|
@ -200,7 +200,7 @@ public class MonitorCorrectAction extends OneToOneAction<Table, Table> {
|
||||
}
|
||||
|
||||
private boolean isMonitorPoint(double monitor, Values point) {
|
||||
return point.getValue(NumassPoint.HV_KEY).doubleValue() == monitor;
|
||||
return point.getValue(NumassPoint.Companion.getHV_KEY()).doubleValue() == monitor;
|
||||
}
|
||||
|
||||
private Instant getTime(Values point) {
|
||||
|
@ -67,8 +67,8 @@ public class NMEventGenerator {
|
||||
// public void loadSpectrum(RawNMPoint point, int minChanel, int maxChanel) {
|
||||
// List<Short> shorts = new ArrayList<>();
|
||||
// point.getEvents().stream()
|
||||
// .filter((event) -> ((event.getChanel() > minChanel) && (event.getChanel() < maxChanel)))
|
||||
// .forEach((event) -> shorts.add(event.getChanel()));
|
||||
// .filter((event) -> ((event.getAmp() > minChanel) && (event.getAmp() < maxChanel)))
|
||||
// .forEach((event) -> shorts.add(event.getAmp()));
|
||||
// double[] doubles = new double[shorts.size()];
|
||||
//
|
||||
// for (int i = 0; i < shorts.size(); i++) {
|
||||
|
@ -120,7 +120,7 @@ public class PileUpSimulator {
|
||||
//not counting double pileups
|
||||
if (generated.size() > 1) {
|
||||
double delay = (next.getTimeOffset() - lastRegisteredTime) / us; //time between events in microseconds
|
||||
if (nextEventRegistered(next.getChanel(), delay)) {
|
||||
if (nextEventRegistered(next.getAmp(), delay)) {
|
||||
//just register new event
|
||||
registered.add(next);
|
||||
lastRegisteredTime = next.getTimeOffset();
|
||||
@ -131,7 +131,7 @@ public class PileUpSimulator {
|
||||
doublePileup.incrementAndGet();
|
||||
} else {
|
||||
//pileup event
|
||||
short newChannel = pileupChannel(delay, next.getChanel(), next.getChanel());
|
||||
short newChannel = pileupChannel(delay, next.getAmp(), next.getAmp());
|
||||
NumassEvent newEvent = new NumassEvent(newChannel, next.getBlockTime(), next.getTimeOffset());
|
||||
//replace already registered event by event with new channel
|
||||
registered.remove(registered.size() - 1);
|
||||
|
@ -32,7 +32,7 @@ private fun correlation(sequence: Stream<NumassEvent>): Double {
|
||||
val amplitudes: MutableList<Double> = ArrayList()
|
||||
val times: MutableList<Double> = ArrayList()
|
||||
sequence.forEach {
|
||||
amplitudes.add(it.chanel.toDouble())
|
||||
amplitudes.add(it.amp.toDouble())
|
||||
times.add(it.timeOffset.toDouble())
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,34 @@
|
||||
package inr.numass.scripts.tristan
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.tables.Table
|
||||
import hep.dataforge.values.Values
|
||||
import inr.numass.data.analyzers.NumassAnalyzer
|
||||
import inr.numass.data.api.NumassBlock
|
||||
import inr.numass.data.api.NumassEvent
|
||||
import inr.numass.data.api.NumassSet
|
||||
import inr.numass.data.storage.ProtoNumassPoint
|
||||
import java.util.stream.Stream
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val analyzer = object : NumassAnalyzer{
|
||||
override fun analyze(block: NumassBlock, config: Meta): Values {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getEvents(block: NumassBlock, meta: Meta): Stream<NumassEvent> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun analyzeSet(set: NumassSet, config: Meta): Table {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
val file = ProtoNumassPoint.readFile("D:\\Work\\Numass\\data\\TRISTAN_11_2017\\df\\gun_16_19.df ")
|
||||
val events = Sequence { file.events.iterator() }.sortedBy { it.time }
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user