From b7eba8e74892ec2aecfd6460e88292ae38755b20 Mon Sep 17 00:00:00 2001 From: Michael Wiebusch Date: Tue, 27 Jan 2015 10:30:53 +0100 Subject: [PATCH] added flot library for plotting in browser --- user_interface/coral_scanner.js | 211 ++++++++++++-- user_interface/coral_scanner.pm | 37 ++- user_interface/has_settings.pm | 3 +- user_interface/jquery.flot.selection.js | 360 ++++++++++++++++++++++++ user_interface/jquery.mwiebusch.js | 21 ++ user_interface/pmt_ro.pl | 4 +- user_interface/pmt_ro.pm | 158 +++++++---- user_interface/shm_manager.pm | 9 +- 8 files changed, 717 insertions(+), 86 deletions(-) create mode 100644 user_interface/jquery.flot.selection.js diff --git a/user_interface/coral_scanner.js b/user_interface/coral_scanner.js index 3a5df4f..854f2cc 100644 --- a/user_interface/coral_scanner.js +++ b/user_interface/coral_scanner.js @@ -7,7 +7,7 @@ var timer; var scan_meta; var coral_scanner_settings; var pmt_ro_settings; - +var spectrum; @@ -28,6 +28,7 @@ $(document).ready(function(){ unfolds($("#show_main_controls"),$("#main_controls_container")); unfolds($("#show_pmt_ro_settings"),$("#pmt_ro_settings_container")); unfolds($("#show_table_control_settings"),$("#table_control_settings_container")); + unfolds($("#show_pmt_spectrum"),$("#pmt_spectrum_container")); $("#button_home").click(function(){ home(); @@ -45,6 +46,20 @@ $(document).ready(function(){ get_scan_svg(); }); + $("#button_program_padiwa").click(function(){ + apply_device_settings(); + }); + $("#button_plot_spectrum").click(function(){ + spectrum = get_spectrum_JSON(); + plot_spectrum(); + }); + $("#button_clear_spectrum").click(function(){ + clear_spectrum(); + }); + $("#button_record_spectrum").click(function(){ + record_spectrum(); + }); + $("#button_count").click(function(){ $('#text_count_out').val(""); $.ajax({ @@ -70,6 +85,10 @@ $(document).ready(function(){ signal_thresh(); }); + $('#checkbox_log_spectrum').change(function(){ +// alert($(this).prop('checked')); + plot_spectrum(); + }); $( "#progressbar" ).progressbar({ value: 100 @@ -78,17 +97,112 @@ $(document).ready(function(){ get_coral_scanner_settings(); get_pmt_ro_settings(); - + spectrum = get_spectrum_JSON(); + plot_spectrum(); get_scan_meta(); // get_scan_svg(); set_clear_timer(); +// make_flot(); }); +function make_flot(){ + + var d1 = []; + for (var i = 0; i < 14; i += 0.5) { + d1.push([i, Math.sin(i)]); + } + + var d2 = [[0, 3], [4, 8], [8, 5], [9, 13]]; + + var d3 = []; + for (var i = 0; i < 14; i += 0.5) { + d3.push([i, Math.cos(i)]); + } + + var d4 = []; + for (var i = 0; i < 14; i += 0.1) { + d4.push([i, Math.sqrt(i * 10)]); + } + + var d5 = []; + for (var i = 0; i < 14; i += 0.5) { + d5.push([i, Math.sqrt(i)]); + } + + var d6 = []; + for (var i = 0; i < 14; i += 0.5 + Math.random()) { + d6.push([i, Math.sqrt(2*i + Math.sin(i) + 5)]); + } + + $.plot("#spectrum_plot_container", [{ + data: d1, + lines: { show: true, fill: true } + }, { + data: d2, + bars: { show: true } + }, { + data: d3, + points: { show: true } + }, { + data: d4, + lines: { show: true } + }, { + data: d5, + lines: { show: true }, + points: { show: true } + }, { + data: d6, + lines: { show: true, steps: true } + }]); + + +} + +function plot_spectrum() { + var data = []; + for (x in spectrum){ + data.push( + { + data: spectrum[x].data, + bars: { show: true , barWidth: 0.8*parseFloat(spectrum[x].meta.bin_width), align: "center" }, + label: x + } + ); + } + + var options = { + selection: { + mode: "xy" + }, + xaxis : { + autoscaleMargin: .1 + }, + legend : { + position : "nw" + } + }; + + + if($('#checkbox_log_spectrum').prop('checked')){ + $.extend(options,{ + yaxis: { + transform: function (v) { return Math.log(v); }, + inverseTransform: function (v) { return Math.exp(v); } + } + }); + } + + + flot_w_selectZoom('#spectrum_plot_container', data, options); + +} + + @@ -161,21 +275,21 @@ function get_scan_meta(){ } -function get_scan_status_report(){ - $.ajax({ - url: "coral_scanner.pl", - cache: false, - async: true, - dataType: "text", - data: { - sub : "scan_status", - report : "true" - }, - success: function(answer) { - $("#scan_status_container").html("
"+answer+"
"); - } - }); -} +// function get_scan_status_report(){ +// $.ajax({ +// url: "coral_scanner.pl", +// cache: false, +// async: true, +// dataType: "text", +// data: { +// sub : "scan_status", +// report : "true" +// }, +// success: function(answer) { +// $("#scan_status_container").html("
"+answer+"
"); +// } +// }); +// } function get_scan_status(){ $.ajax({ @@ -213,6 +327,20 @@ function home(){ }); } +function apply_device_settings(){ + $.ajax({ + url: "pmt_ro.pl", + cache: false, + async: false, + dataType: "text", + data: { + sub : "apply_device_settings" + }, + success: function(answer) { + } + }); +} + function signal_thresh(){ $.ajax({ url: "pmt_ro.pl", @@ -286,4 +414,53 @@ function load_settings(url){ } }); return return_obj; +} + +function get_spectrum_JSON(){ + var return_obj; + $.ajax({ + url: "pmt_ro.pl", + cache: false, + async: false, + dataType: "json", + data: { + sub : "spectrum_JSON", + }, + success: function(answer) { + return_obj = answer; + } + }); + return return_obj; +} + +function clear_spectrum(){ + $.ajax({ + url: "pmt_ro.pl", + cache: false, + async: false, + dataType: "text", + data: { + sub : "clear_spectrum", + }, + success: function(answer) { + alert(answer); + } + }); +} + +function record_spectrum(){ + $.ajax({ + url: "coral_scanner.pl", + cache: false, + async: true, + dataType: "text", + data: { + sub : "record_spectrum", + name : $('#text_spectrum_name').val() + }, + success: function(answer) { + spectrum = get_spectrum_JSON(); + plot_spectrum(); + } + }); } \ No newline at end of file diff --git a/user_interface/coral_scanner.pm b/user_interface/coral_scanner.pm index 4e6a41b..83ee2e2 100644 --- a/user_interface/coral_scanner.pm +++ b/user_interface/coral_scanner.pm @@ -98,6 +98,8 @@ sub main_html { {-src => './jquery.min.js'}, {-src => './jquery.timer.js'}, {-src => './jquery-ui.js'}, + {-src => './jquery.flot.js'}, + {-src => './jquery.flot.selection.js'}, {-src => './jquery.mwiebusch.js'}, {-src => './coral_scanner.js'}, # {-src => './SVGPan.js'}, @@ -125,7 +127,7 @@ sub main_html { print br; print '
'; print br; - print ""; + print ""; print br; print br; print "estimated scan duration: ".hms_string($self->scan_ETA()); @@ -149,20 +151,34 @@ sub main_html { print ""; print ""; print ""; + print ""; print ""; print br br; print "PMT test:"; print br; print ""; - print ""; + print ""; print br; print ""; - print ""; + print ""; print " [s] "; - print ""; + print ""; + + print ""; + print "

spectrum

"; + print "
"; + print '
'; + print ""; + print ""; + print ""; + print br br; + print "record name: "; + print ""; + print ""; + print "
"; print "

pmt_ro settings

"; print "
"; @@ -368,6 +384,19 @@ sub start_scan { $self->scan_sample(); } +sub record_spectrum { + my $self= shift; + my %options = @_; + my $name = $options{name} || "signal"; + + daemonize(); + $self->{pmt_ro}->spectral_scan_onesided( + name => $name + ); + + return " "; +} + sub home { my $self= shift; daemonize(); diff --git a/user_interface/has_settings.pm b/user_interface/has_settings.pm index 5fc8e27..3fb6e69 100644 --- a/user_interface/has_settings.pm +++ b/user_interface/has_settings.pm @@ -26,11 +26,10 @@ sub load_settings { } sub save_settings { +# make sure load settings is executed first, or you will lose your data! my $self=shift; my %options = @_; - $self->require_run("load_settings"); - my $settings_file = $self->{settings_file}; $self->{settings} = { %{$self->{settings}}, %options}; diff --git a/user_interface/jquery.flot.selection.js b/user_interface/jquery.flot.selection.js new file mode 100644 index 0000000..d3c20fa --- /dev/null +++ b/user_interface/jquery.flot.selection.js @@ -0,0 +1,360 @@ +/* Flot plugin for selecting regions of a plot. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin supports these options: + +selection: { + mode: null or "x" or "y" or "xy", + color: color, + shape: "round" or "miter" or "bevel", + minSize: number of pixels +} + +Selection support is enabled by setting the mode to one of "x", "y" or "xy". +In "x" mode, the user will only be able to specify the x range, similarly for +"y" mode. For "xy", the selection becomes a rectangle where both ranges can be +specified. "color" is color of the selection (if you need to change the color +later on, you can get to it with plot.getOptions().selection.color). "shape" +is the shape of the corners of the selection. + +"minSize" is the minimum size a selection can be in pixels. This value can +be customized to determine the smallest size a selection can be and still +have the selection rectangle be displayed. When customizing this value, the +fact that it refers to pixels, not axis units must be taken into account. +Thus, for example, if there is a bar graph in time mode with BarWidth set to 1 +minute, setting "minSize" to 1 will not make the minimum selection size 1 +minute, but rather 1 pixel. Note also that setting "minSize" to 0 will prevent +"plotunselected" events from being fired when the user clicks the mouse without +dragging. + +When selection support is enabled, a "plotselected" event will be emitted on +the DOM element you passed into the plot function. The event handler gets a +parameter with the ranges selected on the axes, like this: + + placeholder.bind( "plotselected", function( event, ranges ) { + alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to) + // similar for yaxis - with multiple axes, the extra ones are in + // x2axis, x3axis, ... + }); + +The "plotselected" event is only fired when the user has finished making the +selection. A "plotselecting" event is fired during the process with the same +parameters as the "plotselected" event, in case you want to know what's +happening while it's happening, + +A "plotunselected" event with no arguments is emitted when the user clicks the +mouse to remove the selection. As stated above, setting "minSize" to 0 will +destroy this behavior. + +The plugin allso adds the following methods to the plot object: + +- setSelection( ranges, preventEvent ) + + Set the selection rectangle. The passed in ranges is on the same form as + returned in the "plotselected" event. If the selection mode is "x", you + should put in either an xaxis range, if the mode is "y" you need to put in + an yaxis range and both xaxis and yaxis if the selection mode is "xy", like + this: + + setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } }); + + setSelection will trigger the "plotselected" event when called. If you don't + want that to happen, e.g. if you're inside a "plotselected" handler, pass + true as the second parameter. If you are using multiple axes, you can + specify the ranges on any of those, e.g. as x2axis/x3axis/... instead of + xaxis, the plugin picks the first one it sees. + +- clearSelection( preventEvent ) + + Clear the selection rectangle. Pass in true to avoid getting a + "plotunselected" event. + +- getSelection() + + Returns the current selection in the same format as the "plotselected" + event. If there's currently no selection, the function returns null. + +*/ + +(function ($) { + function init(plot) { + var selection = { + first: { x: -1, y: -1}, second: { x: -1, y: -1}, + show: false, + active: false + }; + + // FIXME: The drag handling implemented here should be + // abstracted out, there's some similar code from a library in + // the navigation plugin, this should be massaged a bit to fit + // the Flot cases here better and reused. Doing this would + // make this plugin much slimmer. + var savedhandlers = {}; + + var mouseUpHandler = null; + + function onMouseMove(e) { + if (selection.active) { + updateSelection(e); + + plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]); + } + } + + function onMouseDown(e) { + if (e.which != 1) // only accept left-click + return; + + // cancel out any text selections + document.body.focus(); + + // prevent text selection and drag in old-school browsers + if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) { + savedhandlers.onselectstart = document.onselectstart; + document.onselectstart = function () { return false; }; + } + if (document.ondrag !== undefined && savedhandlers.ondrag == null) { + savedhandlers.ondrag = document.ondrag; + document.ondrag = function () { return false; }; + } + + setSelectionPos(selection.first, e); + + selection.active = true; + + // this is a bit silly, but we have to use a closure to be + // able to whack the same handler again + mouseUpHandler = function (e) { onMouseUp(e); }; + + $(document).one("mouseup", mouseUpHandler); + } + + function onMouseUp(e) { + mouseUpHandler = null; + + // revert drag stuff for old-school browsers + if (document.onselectstart !== undefined) + document.onselectstart = savedhandlers.onselectstart; + if (document.ondrag !== undefined) + document.ondrag = savedhandlers.ondrag; + + // no more dragging + selection.active = false; + updateSelection(e); + + if (selectionIsSane()) + triggerSelectedEvent(); + else { + // this counts as a clear + plot.getPlaceholder().trigger("plotunselected", [ ]); + plot.getPlaceholder().trigger("plotselecting", [ null ]); + } + + return false; + } + + function getSelection() { + if (!selectionIsSane()) + return null; + + if (!selection.show) return null; + + var r = {}, c1 = selection.first, c2 = selection.second; + $.each(plot.getAxes(), function (name, axis) { + if (axis.used) { + var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]); + r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) }; + } + }); + return r; + } + + function triggerSelectedEvent() { + var r = getSelection(); + + plot.getPlaceholder().trigger("plotselected", [ r ]); + + // backwards-compat stuff, to be removed in future + if (r.xaxis && r.yaxis) + plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]); + } + + function clamp(min, value, max) { + return value < min ? min: (value > max ? max: value); + } + + function setSelectionPos(pos, e) { + var o = plot.getOptions(); + var offset = plot.getPlaceholder().offset(); + var plotOffset = plot.getPlotOffset(); + pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width()); + pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height()); + + if (o.selection.mode == "y") + pos.x = pos == selection.first ? 0 : plot.width(); + + if (o.selection.mode == "x") + pos.y = pos == selection.first ? 0 : plot.height(); + } + + function updateSelection(pos) { + if (pos.pageX == null) + return; + + setSelectionPos(selection.second, pos); + if (selectionIsSane()) { + selection.show = true; + plot.triggerRedrawOverlay(); + } + else + clearSelection(true); + } + + function clearSelection(preventEvent) { + if (selection.show) { + selection.show = false; + plot.triggerRedrawOverlay(); + if (!preventEvent) + plot.getPlaceholder().trigger("plotunselected", [ ]); + } + } + + // function taken from markings support in Flot + function extractRange(ranges, coord) { + var axis, from, to, key, axes = plot.getAxes(); + + for (var k in axes) { + axis = axes[k]; + if (axis.direction == coord) { + key = coord + axis.n + "axis"; + if (!ranges[key] && axis.n == 1) + key = coord + "axis"; // support x1axis as xaxis + if (ranges[key]) { + from = ranges[key].from; + to = ranges[key].to; + break; + } + } + } + + // backwards-compat stuff - to be removed in future + if (!ranges[key]) { + axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0]; + from = ranges[coord + "1"]; + to = ranges[coord + "2"]; + } + + // auto-reverse as an added bonus + if (from != null && to != null && from > to) { + var tmp = from; + from = to; + to = tmp; + } + + return { from: from, to: to, axis: axis }; + } + + function setSelection(ranges, preventEvent) { + var axis, range, o = plot.getOptions(); + + if (o.selection.mode == "y") { + selection.first.x = 0; + selection.second.x = plot.width(); + } + else { + range = extractRange(ranges, "x"); + + selection.first.x = range.axis.p2c(range.from); + selection.second.x = range.axis.p2c(range.to); + } + + if (o.selection.mode == "x") { + selection.first.y = 0; + selection.second.y = plot.height(); + } + else { + range = extractRange(ranges, "y"); + + selection.first.y = range.axis.p2c(range.from); + selection.second.y = range.axis.p2c(range.to); + } + + selection.show = true; + plot.triggerRedrawOverlay(); + if (!preventEvent && selectionIsSane()) + triggerSelectedEvent(); + } + + function selectionIsSane() { + var minSize = plot.getOptions().selection.minSize; + return Math.abs(selection.second.x - selection.first.x) >= minSize && + Math.abs(selection.second.y - selection.first.y) >= minSize; + } + + plot.clearSelection = clearSelection; + plot.setSelection = setSelection; + plot.getSelection = getSelection; + + plot.hooks.bindEvents.push(function(plot, eventHolder) { + var o = plot.getOptions(); + if (o.selection.mode != null) { + eventHolder.mousemove(onMouseMove); + eventHolder.mousedown(onMouseDown); + } + }); + + + plot.hooks.drawOverlay.push(function (plot, ctx) { + // draw selection + if (selection.show && selectionIsSane()) { + var plotOffset = plot.getPlotOffset(); + var o = plot.getOptions(); + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + var c = $.color.parse(o.selection.color); + + ctx.strokeStyle = c.scale('a', 0.8).toString(); + ctx.lineWidth = 1; + ctx.lineJoin = o.selection.shape; + ctx.fillStyle = c.scale('a', 0.4).toString(); + + var x = Math.min(selection.first.x, selection.second.x) + 0.5, + y = Math.min(selection.first.y, selection.second.y) + 0.5, + w = Math.abs(selection.second.x - selection.first.x) - 1, + h = Math.abs(selection.second.y - selection.first.y) - 1; + + ctx.fillRect(x, y, w, h); + ctx.strokeRect(x, y, w, h); + + ctx.restore(); + } + }); + + plot.hooks.shutdown.push(function (plot, eventHolder) { + eventHolder.unbind("mousemove", onMouseMove); + eventHolder.unbind("mousedown", onMouseDown); + + if (mouseUpHandler) + $(document).unbind("mouseup", mouseUpHandler); + }); + + } + + $.plot.plugins.push({ + init: init, + options: { + selection: { + mode: null, // one of null, "x", "y" or "xy" + color: "#e8cfac", + shape: "round", // one of "round", "miter", or "bevel" + minSize: 5 // minimum number of pixels + } + }, + name: 'selection', + version: '1.1' + }); +})(jQuery); diff --git a/user_interface/jquery.mwiebusch.js b/user_interface/jquery.mwiebusch.js index 10a3201..a0e88fd 100644 --- a/user_interface/jquery.mwiebusch.js +++ b/user_interface/jquery.mwiebusch.js @@ -28,3 +28,24 @@ function unfolds(button,container){ } } + +function flot_w_selectZoom(id,data,options) { + $.plot(id, data, options); + + $(id).bind("plotselected", function (event, ranges) { + // clamp the zooming to prevent eternal zoom + if (ranges.xaxis.to - ranges.xaxis.from < 0.00001) { + ranges.xaxis.to = ranges.xaxis.from + 0.00001; + } + if (ranges.yaxis.to - ranges.yaxis.from < 0.00001) { + ranges.yaxis.to = ranges.yaxis.from + 0.00001; + } + // do the zooming + plot = $.plot(id, data, + $.extend(true, {}, options, { + xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to }, + yaxis: { min: ranges.yaxis.from, max: ranges.yaxis.to } + }) + ); + }); +} \ No newline at end of file diff --git a/user_interface/pmt_ro.pl b/user_interface/pmt_ro.pl index ec0f614..5b173f6 100755 --- a/user_interface/pmt_ro.pl +++ b/user_interface/pmt_ro.pl @@ -31,7 +31,9 @@ my $dispatch_table = { spectral_scan => 1, spectral_scan_onesided => 1, dead_time => 1, - apply_device_settings => 1 + apply_device_settings => 1, + spectrum_JSON => 1, + clear_spectrum => 1 }; CGI_dispatch::dispatch_sub(package => $self, dispatch_table => $dispatch_table); \ No newline at end of file diff --git a/user_interface/pmt_ro.pm b/user_interface/pmt_ro.pm index b173b7b..eaa2c75 100644 --- a/user_interface/pmt_ro.pm +++ b/user_interface/pmt_ro.pm @@ -9,6 +9,9 @@ use POSIX; use FileHandle; use regio; +use shm_manager; +use JSON; + use misc_subs; use has_settings; our @ISA = qw/has_settings/; # assimilate the methods of the has_settings class @@ -48,7 +51,11 @@ sub new { is_calibrated => 0, dead_time => 265, # corresponds to 2 us dead time signal_thresh => 0, - veto_thresh => 0 + veto_thresh => 0, + spectrum_start => -2000, + spectrum_stop => 0, + spectrum_bins => 24, + spectrum_delay => 1 }; $self->{has_run} = {}; # remember which subs already have run @@ -60,7 +67,12 @@ sub new { %options }; bless($self, $class); - $self->load_settings(); + $self->load_settings(); + + $self->{spectrum_shm} = shm_manager->new( + shmPath => "./", + shmName => __PACKAGE__.".spectrum" ); + $self->{spectrum_shm}->initShm(); return $self; } @@ -93,47 +105,47 @@ sub apply_device_settings { return; } -sub spectral_scan { - my $self = shift; - my %options = @_; - -# $self->require_run("load_settings"); - $self->require_run("setup_regio"); - - die "device zero offset calibration has to be performed first!\n - run subroutine zero_calib!\n" unless $self->{settings}->{is_calibrated}; - - my $start=$options{start}; - my $stop=$options{stop}; - my $bins=$options{bins}||64; - my $delay=$options{delay}||1; - my $verbose=$options{verbose}; - - my $spec_width = $stop-$start; - my $bin_width = $spec_width/$bins; - - my $file = FileHandle->new("./test.dat", 'w'); - - my $counts; - my $bin_pos; - my $spectrum; - - print "#bin\t#bin_pos\t#counts\n" if $verbose; - for (my $i=0; $i<$bins; $i++){ - $self->veto_thresh(value => floor($start+$bin_width*$i) ); - $self->signal_thresh(value => floor($start+$bin_width*($i+1)) ); - $bin_pos = floor($start+$bin_width*($i+0.5)); - $counts = $self->count(channel => "net", delay => $delay); - $spectrum->{$i} = { - counts => $counts, - bin_pos => $bin_pos - }; - print "$i\t$bin_pos\t$counts\n" if $verbose; - print $file "$i\t$bin_pos\t$counts\n"; - } - return $spectrum; - -} +# sub spectral_scan { +# my $self = shift; +# my %options = @_; +# +# # $self->require_run("load_settings"); +# $self->require_run("setup_regio"); +# +# die "device zero offset calibration has to be performed first!\n +# run subroutine zero_calib!\n" unless $self->{settings}->{is_calibrated}; +# +# my $start=$options{start}; +# my $stop=$options{stop}; +# my $bins=$options{bins}||64; +# my $delay=$options{delay}||1; +# my $verbose=$options{verbose}; +# +# my $spec_width = $stop-$start; +# my $bin_width = $spec_width/$bins; +# +# my $file = FileHandle->new("./test.dat", 'w'); +# +# my $counts; +# my $bin_pos; +# my $spectrum; +# +# print "#bin\t#bin_pos\t#counts\n" if $verbose; +# for (my $i=0; $i<$bins; $i++){ +# $self->veto_thresh(value => floor($start+$bin_width*$i) ); +# $self->signal_thresh(value => floor($start+$bin_width*($i+1)) ); +# $bin_pos = floor($start+$bin_width*($i+0.5)); +# $counts = $self->count(channel => "net", delay => $delay); +# $spectrum->{$i} = { +# counts => $counts, +# bin_pos => $bin_pos +# }; +# print "$i\t$bin_pos\t$counts\n" if $verbose; +# print $file "$i\t$bin_pos\t$counts\n"; +# } +# return $spectrum; +# +# } sub spectral_scan_onesided { my $self = shift; @@ -145,12 +157,12 @@ sub spectral_scan_onesided { die "device zero offset calibration has to be performed first!\n run subroutine zero_calib!\n" unless $self->{settings}->{is_calibrated}; - my $start=$options{start}; - my $stop=$options{stop}; - my $bins=$options{bins}||64; - my $delay=$options{delay}||1; - my $verbose=$options{verbose}; - my $tofile=$options{tofile}; + my $start = (defined($options{start})) ? ($options{start}) : ($self->{settings}->{spectrum_start}); + my $stop = (defined($options{stop})) ? ($options{stop}) : ($self->{settings}->{spectrum_stop}); + my $bins = (defined($options{bins})) ? ($options{bins}) : ($self->{settings}->{spectrum_bins}); + my $delay = (defined($options{delay})) ? ($options{delay}) : ($self->{settings}->{spectrum_delay}); + my $name = (defined($options{name})) ? ($options{name}) : "signal"; + my $verbose = $options{verbose}; my $file = FileHandle->new("./cumul_spec.dat", 'w'); @@ -158,23 +170,31 @@ sub spectral_scan_onesided { my $bin_width = $spec_width/$bins; my $counts; - my $bin_pos; - my $spectrum; + my $thresh; + my $spectrum = {}; + + $spectrum->{meta} = { + bin_width => $bin_width, + spec_width => $spec_width, + delay => $delay, + start => $start, + stop => $stop + }; + $spectrum->{data} = []; - my $cumulation; + + $self->{spectrum_shm}->updateShm({$name => $spectrum}); #write empty spectrum - print "#bin\t#bin_pos\t#counts\n" if $verbose; + print "#bin\t#thresh\t#counts\n" if $verbose; for (my $i=0; $i<$bins; $i++){ # $self->veto_thresh(value => floor($start+$bin_width*$i) ); $self->signal_thresh(value => floor($start+$bin_width*$i) ); - $bin_pos = floor($start+$bin_width*($i+0.5)); + $thresh = floor($start+$bin_width*$i); $counts = $self->count(channel => "signal", delay => $delay); - $spectrum->{$i} = { - counts => $counts, - bin_pos => $bin_pos - }; - print "$i\t$bin_pos\t$counts\n" if $verbose; - print $file "$i\t$bin_pos\t$counts\n"; + $spectrum->{data}->[$i] = [$thresh,$counts]; + $self->{spectrum_shm}->updateShm({ $name => $spectrum }); #update spectrum + print "$i\t$thresh\t$counts\n" if $verbose; + print $file "$i\t$thresh\t$counts\n"; } @@ -183,6 +203,24 @@ sub spectral_scan_onesided { } +sub spectrum_JSON { + my $self = shift; + + my $spectrum = $self->{spectrum_shm}->readShm(); + print encode_json $spectrum; + return " "; + +} + +sub clear_spectrum { + my $self = shift; + + my $spectrum = {}; + $self->{spectrum_shm}->writeShm($spectrum); + return "cleared"; +} + + sub signal_thresh { # reads or sets signal threshold my $self = shift; diff --git a/user_interface/shm_manager.pm b/user_interface/shm_manager.pm index 191f5d1..289475c 100644 --- a/user_interface/shm_manager.pm +++ b/user_interface/shm_manager.pm @@ -16,8 +16,13 @@ sub new { }; die "shm_manager must get an shmName" unless defined($self->{shmName}); - - $self->{shmFile} = "/dev/shm/".$self->{shmName}; + if(defined($self->{shmPath})){ + $self->{shmPath} =~ s/\/+$//; #remove trailing "/" + $self->{shmPath} .= "/"; + $self->{shmFile} = $self->{shmPath}.$self->{shmName}; + } else { + $self->{shmFile} = "/dev/shm/".$self->{shmName}; + } # $self->{dataDir} = $self->{shmFile}."_data"; # $self->{dataFile} = $self->{dataDir}."/data"; -- 2.43.0